Result types¶
AnalysisRoute
dataclass
¶
AnalysisRoute(route, reason, nodes=(), engine=None, engine_reason='', twin='')
How one analysis of an RBD is computed.
Attributes:
| Name | Type | Description |
|---|---|---|
route |
str
|
|
reason |
str
|
Why. For |
nodes |
tuple
|
The nodes that decide the route, if any: those whose values are numerical or simulated, or that the method refuses. |
engine |
str or None
|
For a |
engine_reason |
str
|
Why that engine. |
twin |
str
|
For a |
to_dict ¶
to_dict()
The route as plain data, ready for json.dumps: its
fields by name, the nodes as a list.
Returns:
| Type | Description |
|---|---|
dict
|
|
AnalysisRoutes ¶
Bases: dict
The routes of a diagram's analyses, as analysis_routes() gives
them: a dict of AnalysisRoute by method
name, which prints one method a line, name: route -- reason (the
reason's first sentence).
Examples:
>>> import surpyval as surv
>>> from repyability import NonRepairableRBD
>>> rbd = NonRepairableRBD(
... [("s", "c"), ("c", "t")],
... {"c": surv.Exponential.from_params([0.01])},
... )
>>> routes = rbd.analysis_routes()
>>> routes["mean"].route
'numerical'
>>> print(routes)
Hf: exact -- The structure function over the node reliabilities, exactly.
allocate_redundancy: exact -- ...
...
unreliability_interval: simulated -- ...
to_dict ¶
to_dict()
The routes as plain data, ready for json.dumps: each
route's to_dict() by method name.
Returns:
| Type | Description |
|---|---|
dict
|
|
AvailabilityResult
dataclass
¶
AvailabilityResult(timeline, availability, system_uptime, time_simulated_to, criticalities, node_uptime, node_downtime, system_downtime, system_failures, system_restorations, n_simulations, cost=None, system_planned_outages=0, uptimes=None, antithetic=False, capacity_timeline=None, capacity=None, capacity_time=None, demand=None, delivered=None, opportunistic_renewals=None, control_variate=None, conditional=None, delivered_control=None)
Bases: _ResultMapping
The result of RepairableRBD.availability().
Holds the simulated availability over time, the system's and every
node's up and down totals, the system failure and restoration counts,
and the criticality measures, all summed over the n_simulations
simulations of the window [0, time_simulated_to].
One expected value: mean_availability is the run's estimate
of the expected availability over the window, the one
mean_availability_interval gives an interval for. By default it is
exact where the exact methods work it out (method="exact", see
ControlVariate), and otherwise taken
given the histories of the system's modules where they apply
("conditional", see
ConditionalRun); else it is the
simulations' own ("simulated"). The simulations' own average,
system_uptime / (n_simulations * time_simulated_to), is
sample_mean_availability whatever the method, as the curve, the
totals and the counts are theirs. Against a demand, the
delivered_fraction is taken the same way (exact where
mission_capacity works it out, given the modules' histories, or
the simulations' own, sample_delivered_fraction), and
delivered_fraction_interval says which. Its repr summarises the
run.
The properties mean_up_time, mean_down_time and
failure_frequency are simulation estimates derived from the fields
below; their exact steady-state counterparts are the
RepairableRBD.mean_up_time(), mean_down_time() and
system_failure_frequency() methods. availability_se and
availability_interval give the sampling uncertainty of the
availability curve, and mean_availability_interval that of
mean_availability.
Like the other result types it is also a read-only mapping of its
fields, so dict-style access (result["availability"],
result.keys(), dict(result)) keeps working.
Attributes:
| Name | Type | Description |
|---|---|---|
timeline |
ndarray
|
Event times at which the (mean) system availability changes: 0,
every time at which some simulated system changed state, and
|
availability |
ndarray
|
Mean system availability at each time in |
system_uptime |
float
|
Total system uptime summed over all simulations. |
time_simulated_to |
float
|
The |
criticalities |
Criticalities
|
The importance/criticality measures (see
|
node_uptime |
dict
|
Total uptime per node summed over all simulations. |
node_downtime |
dict
|
Total downtime per node summed over all simulations; a node's uptime
plus downtime equals |
system_downtime |
float
|
Total system downtime summed over all simulations. |
system_failures |
int or float
|
Number of system failures observed across all simulations (changes
from up to down caused by a failure, including the zero-length
outages an instantly repaired component causes); with
|
system_restorations |
int or float
|
Number of system restorations observed across all simulations
(changes from down to up, after a failure or a planned outage);
with |
n_simulations |
int
|
The number of simulations run ( |
cost |
(CostResult, optional)
|
The simulated cost distribution, when the RBD declares any costs;
|
system_planned_outages |
int or float
|
Number of planned outages of the system observed across all
simulations: changes from up to down caused by preventive
maintenance or an inspection that takes time. 0 without either;
with |
uptimes |
(ndarray, optional)
|
The system's up time in each simulation, in order (they sum to
|
antithetic |
bool
|
Whether the simulations ran in antithetic pairs (simulations |
capacity_timeline |
(ndarray, optional)
|
With capacities (the |
capacity |
(ndarray, optional)
|
The simulated systems' mean capacity at each time in
|
capacity_time |
(dict, optional)
|
Capacity -> the time spent at it, summed over the simulations: they
add up to |
demand |
(float, optional)
|
The demand the delivered fraction is measured against (see
|
delivered |
(ndarray, optional)
|
Each simulation's delivered fraction, in order: the integral of
|
delivered_control |
(ControlVariate, optional)
|
With a demand, the control of the delivered fractions by the
system itself where |
opportunistic_renewals |
(dict, optional)
|
With maintenance groups (see |
control_variate |
(ControlVariate, optional)
|
The exact twin the run was controlled by (see
|
conditional |
(ConditionalRun, optional)
|
For a system with dependent modules (see
|
Examples:
>>> import surpyval as surv
>>> from repyability import RepairableRBD
>>> rbd = RepairableRBD(
... [("s", "c"), ("c", "t")],
... {
... "c": {
... "reliability": surv.Exponential.from_params([0.1]),
... "repairability": surv.Exponential.from_params([1.0]),
... }
... },
... )
>>> result = rbd.availability(t_simulation=50, mc_samples=200, seed=0)
>>> float(result.availability[0]) # every simulation starts up
1.0
>>> round(result.mean_availability, 4) # exact; long run: 10 / 11
0.9107
>>> round(result.sample_mean_availability, 4) # the simulations' own
0.9117
>>> result.mean_availability_interval().method
'exact'
>>> lower, upper = result.availability_interval(0.95)
>>> a = result.availability
>>> bool(((lower <= a) & (a <= upper)).all())
True
>>> result["availability"] is result.availability
True
mean_capacity
property
¶
mean_capacity
Simulation estimate of the average capacity over the window:
each capacity times the time spent at it, over
n_simulations * time_simulated_to. Over a long window it
approaches the exact long-run capacity_distribution().mean.
Returns:
| Type | Description |
|---|---|
float or None
|
The average capacity ( |
delivered_fraction
property
¶
delivered_fraction
The run's estimate of the expected fraction of the demand
delivered over the window: the production availability, the
estimate delivered_fraction_interval gives, exact where
mission_capacity works it out, given the modules' histories
where those apply, and otherwise the simulations' own,
sample_delivered_fraction (as mean_availability is taken,
see the class's notes). Over a long window it approaches the
exact long-run capacity_distribution().delivered_fraction(demand).
Returns:
| Type | Description |
|---|---|
float or None
|
The delivered fraction, in |
sample_delivered_fraction
property
¶
sample_delivered_fraction
The simulations' own delivered fraction over the window, the
mean of delivered, whatever delivered_fraction is.
Returns:
| Type | Description |
|---|---|
float or None
|
The fraction of the demand the simulated systems delivered, or None without a demand. |
mean_availability
property
¶
mean_availability
The run's estimate of the expected availability over the window:
the estimate mean_availability_interval gives, exact
where the exact methods work it out, given the modules' histories
where those apply, and otherwise the simulations' own,
sample_mean_availability (see the class's notes). The
long-run value is RepairableRBD.mean_availability().
Returns:
| Type | Description |
|---|---|
float
|
The estimated mean availability over the window. |
sample_mean_availability
property
¶
sample_mean_availability
The simulations' own mean availability over the window,
system_uptime / (n_simulations * time_simulated_to), whatever
mean_availability is.
Returns:
| Type | Description |
|---|---|
float
|
The fraction of the simulated time the system was up. |
availability_se
property
¶
availability_se
Pointwise standard error of the availability estimate.
At each time in timeline the availability is the proportion of
the n_simulations systems that were up, so its sampling standard
error is the binomial sqrt(A (1 - A) / n). It is 0 where the
estimate is 0 or 1; availability_interval stays informative
there. It treats the simulations as independent, which antithetic
ones are not. For a run of the modules alone (conditional=True,
see conditional), each simulation's value is its chance of being
up then, and the error is that of their mean, from their mean
square.
Returns:
| Type | Description |
|---|---|
ndarray
|
The standard error at each time in |
mean_up_time
property
¶
mean_up_time
Simulation estimate of the Mean Up Time,
system uptime / (system failures + planned outages): an up
period ends at either. Infinite if the system was never observed to
go down (0.0 if it was never up).
Note: estimated from a finite window, so each simulation's final
(unfinished) up period is censored; for windows that are short
relative to the up-down cycle this biases the estimate. Prefer the
exact RepairableRBD.mean_up_time() for steady-state values.
Returns:
| Type | Description |
|---|---|
float
|
The estimated mean up time, possibly |
mean_down_time
property
¶
mean_down_time
Simulation estimate of the Mean Down Time,
system downtime / system restorations. Infinite if downtime was
observed but never restored (0.0 if the system was never down).
Note: estimated from a finite window (final unfinished down periods
are censored), so short windows bias the estimate. Prefer the exact
RepairableRBD.mean_down_time() for steady-state values.
Returns:
| Type | Description |
|---|---|
float
|
The estimated mean down time, possibly |
failure_frequency
property
¶
failure_frequency
Simulation estimate of the system failure frequency (failures per
unit time), system failures / total simulated time. Planned
outages are not failures.
The total simulated time is n_simulations * time_simulated_to.
The exact steady-state counterpart is
RepairableRBD.system_failure_frequency(); over a short window the
estimate is biased, as every simulation starts with its components
working.
Returns:
| Type | Description |
|---|---|
float
|
The estimated number of system failures per unit time. |
to_dict ¶
to_dict()
The result as plain data, ready for json.dumps (#235): each
field by name, arrays as lists, numpy numbers as Python ones, the
results it holds as their own to_dict(), and keys JSON cannot
hold (tuple node names, say) as their text.
Returns:
| Type | Description |
|---|---|
dict
|
The fields, as plain data. Infinite and undefined values stay
floats ( |
delivered_fraction_interval ¶
delivered_fraction_interval(confidence=0.95)
Confidence interval for the expected delivered fraction over the window.
Around delivered_fraction, as mean_availability_interval
is around the availability: an exact one has no error, and the
interval is that value; otherwise it is the mean of the
simulations' delivered fractions (the conditional ones, given the
modules' histories, where those apply), with standard error std
/ sqrt(n) (of antithetic pairs' means, for an antithetic run),
and the interval, clipped to [0, 1], describes the simulation
error. Its method says how the estimate was found:
"exact" (mission_capacity works it out, see
delivered_control), "conditional" (see
ConditionalRun) or
"simulated", the simulations' own.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
confidence
|
float
|
The confidence level, strictly between 0 and 1, by default 0.95. |
0.95
|
Returns:
| Type | Description |
|---|---|
ConfidenceInterval
|
The estimate, bounds, standard error, number of simulations and method. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
mean_availability_interval ¶
mean_availability_interval(confidence=0.95)
Confidence interval for the expected availability over the window.
Around mean_availability: an exact one has no error, and the
interval is that value; otherwise it is the mean of the
simulations' values (their fractions of the window up,
uptimes / time_simulated_to, controlled or conditional ones),
which by the central limit theorem is normal with standard error
std / sqrt(n) of those values (of antithetic pairs' means, for
an antithetic run), from which the interval estimate +/- z *
standard_error is built, clipped to [0, 1]. It describes the
simulation error, and narrows like 1 / sqrt(n);
availability(tolerance=...) runs until it is narrow enough. Its
method says how the estimate was found: "exact", by the
exact methods (a control_variate that is the system itself, see
ControlVariate);
"control_variate", the simulations' fractions controlled by an
exact twin; "conditional", each simulation's expected fraction
up given its modules' histories (see
ConditionalRun); "simulated",
the simulations' own.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
confidence
|
float
|
The confidence level, strictly between 0 and 1, by default 0.95. |
0.95
|
Returns:
| Type | Description |
|---|---|
ConfidenceInterval
|
The estimate, bounds, standard error, number of simulations and method. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
availability_interval ¶
availability_interval(confidence=0.95)
Pointwise confidence band for the availability curve.
Uses the Wilson score interval for a binomial proportion, which
remains well-behaved when the estimated availability is at or near 0
or 1 (where the plain normal interval collapses to zero width). For
a run of the modules alone (conditional=True, see
conditional), whose simulations' values are chances rather than
0 or 1, the normal interval of their mean (availability_se).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
confidence
|
float
|
The confidence level, strictly between 0 and 1, by default 0.95. |
0.95
|
Returns:
| Type | Description |
|---|---|
tuple[ndarray, ndarray]
|
The lower and upper bounds at each time in |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
CapacityDistribution
dataclass
¶
CapacityDistribution(levels, probabilities)
Bases: _ResultMapping
The exact distribution of a system's capacity: how much it can deliver.
Returned by NonRepairableRBD.capacity_distribution (at a time, or
at each of several), RepairableRBD.capacity_distribution (in the
long run) and RBD.system_capacity. Each component carries its
capacity while it works and nothing once it has failed, and the
system's capacity is the most that can flow through the diagram from
the input to the output. Like the other result types it is also a
read-only mapping of its fields.
Attributes:
| Name | Type | Description |
|---|---|---|
levels |
ndarray
|
The capacities the system can have, in increasing order: 0 when it
is down, and each total its working components can carry. |
probabilities |
ndarray
|
The probability of each level: one per level, or for several times one row per level and one column per time. They sum to 1. In the long run, the fraction of time the system spends at each level. |
Examples:
Three pumps of 50 units each, in parallel, each available 90% of the time:
>>> from repyability import RBD
>>> pumps = RBD(
... [("s", "a"), ("s", "b"), ("s", "c"),
... ("a", "t"), ("b", "t"), ("c", "t")],
... capacity={"a": 50, "b": 50, "c": 50},
... )
>>> capacity = pumps.system_capacity({"a": 0.9, "b": 0.9, "c": 0.9})
>>> capacity.levels.tolist()
[0.0, 50.0, 100.0, 150.0]
>>> capacity.probabilities.round(4).tolist()
[0.001, 0.027, 0.243, 0.729]
Two of the three meet a demand of 100:
>>> round(capacity.meets(100), 4)
0.972
>>> round(capacity.mean, 4)
135.0
>>> round(capacity.delivered_fraction(100), 4)
0.9855
mean
property
¶
mean
The expected capacity.
In the long run, the average capacity over time. Infinite if the
capacity can be infinite (see levels). A property, as the other
results' values are (a method until 0.13, #235).
Returns:
| Type | Description |
|---|---|
float or ndarray
|
The expected capacity, per time for several times. |
to_dict ¶
to_dict()
The result as plain data, ready for json.dumps (#235): each
field by name, arrays as lists, numpy numbers as Python ones, the
results it holds as their own to_dict(), and keys JSON cannot
hold (tuple node names, say) as their text.
Returns:
| Type | Description |
|---|---|
dict
|
The fields, as plain data. Infinite and undefined values stay
floats ( |
meets ¶
meets(demand)
The probability that the capacity meets a demand: that it is at
least demand.
For a non-repairable system at a time, it is the system's
reliability for that demand; in the long run, the fraction of time
the system can meet it. With every component's capacity positive,
meets of any demand above 0 but no more than the smallest level
above 0 is the system's reliability (or availability): the
capacity is positive exactly when the system works.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
demand
|
float
|
The demand, in the capacities' units. A capacity within
rounding of it (a relative |
required |
Returns:
| Type | Description |
|---|---|
float or ndarray
|
The probability, per time for several times. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
delivered_fraction ¶
delivered_fraction(demand)
The expected fraction of a demand the system delivers:
E[min(capacity, demand)] / demand.
A system with more capacity than the demand delivers the demand, and one with less delivers what it can. In the long run, this is the fraction of the demand met over time: the production availability.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
demand
|
float
|
The demand, a positive, finite number in the capacities' units. |
required |
Returns:
| Type | Description |
|---|---|
float or ndarray
|
The fraction, in |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Criticalities
dataclass
¶
Criticalities(operational_criticality_index, iou, failure_criticality_index, restoration_criticality_index)
Bases: _ResultMapping
The importance/criticality measures from an availability simulation.
RepairableRBD.availability attributes the system's behaviour to its
nodes in four ways, each keyed by node name. Every measure is a ratio
of totals summed over all the simulations, and is 0 where its
denominator is 0 (e.g. the down-time measures when the system was never
down).
- Operational criticality index:
up[i]is the time node i and the system were both up over the system's total up time (the share of the system's up time during which node i was up);down[i]is the time both were down over the system's total down time. - Intersection over union:
up[i]is the time both were up over the time either was up;down[i]is the time both were down over the time either was down. It is 1 when node i's up (or down) periods coincide with the system's. - Failure criticality index: how often node i's failures took the
system down, per system failure and per failure of node i (see
FailureCriticalityIndex). - Restoration criticality index: how often node i's restorations
brought the system back, per system restoration and per restoration
of node i (see
RestorationCriticalityIndex).
The first two have an entry for every node; the last two only for the nodes that failed, or were restored, at least once.
Attributes:
| Name | Type | Description |
|---|---|---|
operational_criticality_index |
UpDownImportance
|
Time the system and a node were jointly up/down, over the system's up/down time. |
iou |
UpDownImportance
|
Intersection-over-union of a node's and the system's up/down intervals. |
failure_criticality_index |
FailureCriticalityIndex
|
How much each node drives system failures. |
restoration_criticality_index |
RestorationCriticalityIndex
|
How much each node drives system restorations. |
Examples:
>>> import surpyval as surv
>>> from repyability import RepairableRBD
>>> unit = {
... "reliability": surv.Exponential.from_params([0.1]),
... "repairability": surv.Exponential.from_params([1.0]),
... }
>>> rbd = RepairableRBD(
... [("s", "a"), ("a", "b"), ("b", "t")], {"a": unit, "b": unit}
... )
>>> result = rbd.availability(t_simulation=50, mc_samples=200, seed=0)
>>> crit = result.criticalities
In series the system is up only while every node is up, but each node is down for only about half of the system's down time:
>>> oci = crit.operational_criticality_index
>>> {node: round(float(v), 4) for node, v in oci.up.items()}
{'a': 1.0, 'b': 1.0}
>>> {node: round(float(v), 2) for node, v in oci.down.items()}
{'a': 0.5, 'b': 0.55}
>>> crit["iou"] is crit.iou # dict-style access also works
True
to_dict ¶
to_dict()
The result as plain data, ready for json.dumps (#235): each
field by name, arrays as lists, numpy numbers as Python ones, the
results it holds as their own to_dict(), and keys JSON cannot
hold (tuple node names, say) as their text.
Returns:
| Type | Description |
|---|---|
dict
|
The fields, as plain data. Infinite and undefined values stay
floats ( |
UpDownImportance
dataclass
¶
UpDownImportance(up, down)
Bases: _ResultMapping
An importance measure split by system state.
Holds a per-node measure computed twice: once from the system's and the
nodes' up (working) time, and once from their down (failed) time. Used
for the operational criticality index and the intersection-over-union
measures of Criticalities, which defines
them.
Attributes:
| Name | Type | Description |
|---|---|---|
up |
dict
|
Node -> the measure computed from up time. |
down |
dict
|
Node -> the measure computed from down time. |
Examples:
A parallel pair is down only while both nodes are, so the share of the system's down time during which each node is down (the down-time operational criticality index) is 1:
>>> import surpyval as surv
>>> from repyability import RepairableRBD
>>> unit = {
... "reliability": surv.Exponential.from_params([0.1]),
... "repairability": surv.Exponential.from_params([1.0]),
... }
>>> rbd = RepairableRBD(
... [("s", "a"), ("s", "b"), ("a", "t"), ("b", "t")],
... {"a": unit, "b": unit},
... )
>>> result = rbd.availability(t_simulation=50, mc_samples=200, seed=0)
>>> oci = result.criticalities.operational_criticality_index
>>> {node: round(float(v), 4) for node, v in oci.down.items()}
{'a': 1.0, 'b': 1.0}
>>> oci["down"] is oci.down
True
to_dict ¶
to_dict()
The result as plain data, ready for json.dumps (#235): each
field by name, arrays as lists, numpy numbers as Python ones, the
results it holds as their own to_dict(), and keys JSON cannot
hold (tuple node names, say) as their text.
Returns:
| Type | Description |
|---|---|
dict
|
The fields, as plain data. Infinite and undefined values stay
floats ( |
FailureCriticalityIndex
dataclass
¶
FailureCriticalityIndex(per_system_failure, per_component_failure)
Bases: _ResultMapping
Fractions relating a node's failures to system failures.
A node causes a system failure when its own failure takes the system
from up to down. The counts are summed over all the simulations of
RepairableRBD.availability, and only nodes that failed at least
once appear (so not a node held working or broken).
Attributes:
| Name | Type | Description |
|---|---|---|
per_system_failure |
dict
|
For each node, the fraction of system failures that the node caused. These sum to 1 over the nodes, or are all 0 if the system never failed. |
per_component_failure |
dict
|
For each node, the fraction of the node's own failures that caused a system failure. |
Examples:
>>> import surpyval as surv
>>> from repyability import RepairableRBD
>>> unit = {
... "reliability": surv.Exponential.from_params([0.1]),
... "repairability": surv.Exponential.from_params([1.0]),
... }
>>> rbd = RepairableRBD(
... [("s", "a"), ("a", "b"), ("b", "t")], {"a": unit, "b": unit}
... )
>>> result = rbd.availability(t_simulation=50, mc_samples=200, seed=0)
>>> fci = result.criticalities.failure_criticality_index
>>> round(sum(fci.per_system_failure.values()), 4)
1.0
In series a node's failure brings the system down unless the other node is already down for repair:
>>> share = fci.per_component_failure
>>> {node: round(share[node], 2) for node in sorted(share)}
{'a': 0.92, 'b': 0.89}
to_dict ¶
to_dict()
The result as plain data, ready for json.dumps (#235): each
field by name, arrays as lists, numpy numbers as Python ones, the
results it holds as their own to_dict(), and keys JSON cannot
hold (tuple node names, say) as their text.
Returns:
| Type | Description |
|---|---|
dict
|
The fields, as plain data. Infinite and undefined values stay
floats ( |
RestorationCriticalityIndex
dataclass
¶
RestorationCriticalityIndex(by_system, by_component)
Bases: _ResultMapping
Fractions relating a node's restorations to system restorations.
A node causes a system restoration when its own restoration takes the
system from down to up. The counts are summed over all the simulations
of RepairableRBD.availability, and only nodes restored at least
once appear (so not a node held working or broken).
Attributes:
| Name | Type | Description |
|---|---|---|
by_system |
dict
|
For each node, the fraction of system restorations that the node caused. These sum to 1 over the nodes, or are all 0 if the system was never restored. |
by_component |
dict
|
For each node, the fraction of the node's own restorations that restored the system. |
Examples:
In a parallel pair the system comes back as soon as either node is repaired, so each node restores it about half the time:
>>> import surpyval as surv
>>> from repyability import RepairableRBD
>>> unit = {
... "reliability": surv.Exponential.from_params([0.1]),
... "repairability": surv.Exponential.from_params([1.0]),
... }
>>> rbd = RepairableRBD(
... [("s", "a"), ("s", "b"), ("a", "t"), ("b", "t")],
... {"a": unit, "b": unit},
... )
>>> result = rbd.availability(t_simulation=50, mc_samples=200, seed=0)
>>> share = result.criticalities.restoration_criticality_index.by_system
>>> {node: round(share[node], 1) for node in sorted(share)}
{'a': 0.5, 'b': 0.5}
to_dict ¶
to_dict()
The result as plain data, ready for json.dumps (#235): each
field by name, arrays as lists, numpy numbers as Python ones, the
results it holds as their own to_dict(), and keys JSON cannot
hold (tuple node names, say) as their text.
Returns:
| Type | Description |
|---|---|
dict
|
The fields, as plain data. Infinite and undefined values stay
floats ( |
CostResult
dataclass
¶
CostResult(samples, t_simulation, n_simulations, by_category, by_component, acquisition_cost=0.0, antithetic=False, control_variate=None, conditional=None)
Bases: _ResultMapping
The simulated cost of running the system over a window.
Returned by RepairableRBD.cost and as the cost of the
AvailabilityResult of a priced RBD.
Each of the n_simulations replications of the availability
simulation yields one total cost over the window: repair/replace
costs charged at each component failure, plus the cost of each
preventive replacement, plus any per-component downtime cost, plus the
system-downtime cost. samples is that
distribution — the point of simulating rather than stopping at the
exact RepairableRBD.expected_cost_rate() is the spread:
percentile(90) answers "what could a bad window cost", which a mean
cannot.
One expected value: mean is the run's estimate of the
expected cost of a window, the one mean_interval gives an interval
for, and cost_rate and the breakdowns follow it. By default it is
exact where the exact methods work it out (method="exact", see
ControlVariate), and otherwise taken
given the histories of the system's modules where they apply
("conditional", see
ConditionalRun); else it is the
simulations' own ("simulated"). The simulations' own mean, the
average of samples, is sample_mean whatever the method: with
control_variate=False and conditional=False the two are the
same.
Two different uncertainties live here. std and percentile
describe how much the cost of a window varies; that is a property of
the system and does not shrink with more replications. mean_se and
mean_interval describe how precisely the expected cost has been
estimated: 0 for an exact one, and otherwise a standard error that
shrinks like 1 / sqrt(n_simulations).
Its repr summarises the run: the estimate, its standard error and method, the simulations' own mean and spread.
Attributes:
| Name | Type | Description |
|---|---|---|
samples |
ndarray
|
Total cost of each replication over the window (length
|
t_simulation |
float
|
The window length each replication was run for. |
n_simulations |
int
|
The number of replications. |
by_category |
dict
|
The expected cost of a window split into |
by_component |
dict
|
The expected cost attributable to each costed component (its
repair, replace, preventive, inspection and own downtime cost; the
system-downtime cost is not attributed to components), found as
|
acquisition_cost |
float
|
The one-off cost of buying the components (the sum of their
|
antithetic |
bool
|
Whether the replications ran in antithetic pairs (replications
|
control_variate |
(ControlVariate, optional)
|
The exact twin the run was controlled by (see
|
conditional |
(ConditionalRun, optional)
|
For a system with dependent modules (see
|
Examples:
One component with MTTF 10 and MTTR 1, at 100 per repair, and 50 per hour of system downtime, over windows of 100 hours:
>>> import surpyval as surv
>>> from repyability import RepairableRBD
>>> rbd = RepairableRBD(
... [("s", "c"), ("c", "t")],
... {
... "c": {
... "reliability": surv.Exponential.from_params([0.1]),
... "repairability": surv.Exponential.from_params([1.0]),
... "repair_cost": 100.0,
... }
... },
... downtime_cost_rate=50.0,
... )
>>> result = rbd.cost(t_simulation=100.0, mc_samples=200, seed=0)
One component's expected cost over a window is exact, so by default
mean is that cost, with no error, and the breakdowns split it;
the simulations' own mean and spread are beside it:
>>> round(result.mean, 2), result.mean_se
(1360.33, 0.0)
>>> round(result.by_category["repair"], 2) # 100 per failure
909.92
>>> round(result.by_category["system_downtime"], 2) # 50 per hour down
450.41
>>> round(result.sample_mean, 2), round(result.std, 2)
(1387.42, 436.54)
>>> result.mean_interval(0.95).method
'exact'
The simulations' own estimate, and its interval, are a
control_variate=False away:
>>> own = rbd.cost(
... t_simulation=100.0, mc_samples=200, seed=0, control_variate=False
... )
>>> round(own.mean, 2), round(own.by_category["repair"], 2)
(1387.42, 927.0)
>>> interval = own.mean_interval(0.95)
>>> interval.method, round(interval.lower, 2), round(interval.upper, 2)
('simulated', 1326.92, 1447.92)
mean
property
¶
mean
The run's estimate of the expected cost of a window: the
estimate mean_interval gives, exact where the exact methods
work it out, given the modules' histories where those apply, and
otherwise the simulations' own, sample_mean (see the class's
notes and mean_interval().method).
Returns:
| Type | Description |
|---|---|
float
|
The estimated expected cost over the window. |
sample_mean
property
¶
sample_mean
The simulations' own mean cost over the window: the average of
samples, whatever mean is.
Returns:
| Type | Description |
|---|---|
float
|
The mean of |
std
property
¶
std
Sample standard deviation of the window's total cost.
How much the cost of one window varies from window to window (with
ddof=1, so it is nan, with a numpy warning, for a single
replication).
Returns:
| Type | Description |
|---|---|
float
|
The standard deviation of |
Raises:
| Type | Description |
|---|---|
ValueError
|
For a conditional run (see |
mean_se
property
¶
mean_se
The standard error of mean: how far it is likely to be from
the true expected cost. 0.0 for an exact mean; otherwise that of
the values it is the mean of, std / sqrt(n_simulations) of the
simulations' own (sample_se), controlled or conditional ones;
for an antithetic run, of the pairs' means. nan where it cannot be
known (fewer than two simulations, or a conditional run whose
modules never changed state).
Returns:
| Type | Description |
|---|---|
float
|
The standard error of the mean. |
sample_se
property
¶
sample_se
The standard error of sample_mean, std / sqrt(
n_simulations) (for an antithetic run, of the pairs' means; nan
for fewer than two).
Returns:
| Type | Description |
|---|---|
float
|
The standard error of the simulations' own mean. |
cost_rate
property
¶
cost_rate
The expected cost per unit time over the window, mean /
t_simulation; it converges to the exact
RepairableRBD.expected_cost_rate() as the window grows.
Over a short window it differs from the long-run rate because every simulation starts with all components working.
Returns:
| Type | Description |
|---|---|
float
|
The mean cost per unit time over the window. |
to_dict ¶
to_dict()
The result as plain data, ready for json.dumps (#235): each
field by name, arrays as lists, numpy numbers as Python ones, the
results it holds as their own to_dict(), and keys JSON cannot
hold (tuple node names, say) as their text.
Returns:
| Type | Description |
|---|---|
dict
|
The fields, as plain data. Infinite and undefined values stay
floats ( |
mean_interval ¶
mean_interval(confidence=0.95)
Confidence interval for the expected total cost over the window.
Around mean, with standard error mean_se: by the central
limit theorem a mean of simulations' values is normal, so the
interval is mean +/- z * mean_se for the normal quantile z
of confidence, its lower bound clipped at 0; an exact mean
has no error, and the interval is that value. Use it to judge
whether mc_samples was large enough; for the range a single
window's cost could fall in, use percentile instead. Its
method says how mean was found: "exact", by the exact
methods (a control_variate that is the system itself, see
ControlVariate);
"control_variate", the simulations' values controlled by an
exact twin; "conditional", each simulation's expected cost
given its modules' histories (see
ConditionalRun); "simulated",
the simulations' own.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
confidence
|
float
|
The confidence level, strictly between 0 and 1, by default 0.95. |
0.95
|
Returns:
| Type | Description |
|---|---|
ConfidenceInterval
|
The estimate ( |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
percentile ¶
percentile(q)
The q-th percentile of the window's total cost (e.g.
percentile(90) for a planning-case budget).
Computed from samples with numpy.percentile (linear
interpolation between samples).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
q
|
float
|
The percentile, between 0 and 100: |
required |
Returns:
| Type | Description |
|---|---|
float
|
The cost below which about |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
ExpectedEvents
dataclass
¶
ExpectedEvents(window, system_failures, system_planned_outages, system_downtime, node_failures, node_corrective, node_preventive, node_inspections, node_downtime)
Bases: _ResultMapping
What a system is expected to do over a window from new: returned by
RepairableRBD.expected_events.
Every value is exact (numerical, with no simulation): the mean of what
RepairableRBD.availability counts in each simulation of the window,
and divides by n_simulations. Events at the window's end itself
fall outside it, as in the simulation. Each value is a float for one
window, or an array in the shape of the windows given.
Attributes:
| Name | Type | Description |
|---|---|---|
window |
float or ndarray
|
The window's length, from new. |
system_failures |
float or ndarray
|
The expected number of system failures in the window (changes from up to down caused by a failure, including the zero-length outages an instantly repaired component causes). |
system_planned_outages |
float or ndarray
|
The expected number of planned outages of the system: changes from up to down caused by preventive maintenance that takes time. |
system_downtime |
float or ndarray
|
The expected time the system is down in the window,
|
node_failures |
dict
|
Each component's expected number of failures (a nested RBD's: its system failures). |
node_corrective |
dict
|
Each component's expected number of corrective actions, at each of
which |
node_preventive |
dict
|
Each component's expected number of preventive replacements (age or
block), at each of which its preventive cost is charged (a nested
RBD's: its planned outages, as the simulation counts them for the
node; its own components' maintenance is counted by its own
|
node_inspections |
dict
|
Each component's expected number of tests (of hidden failures). |
node_downtime |
dict
|
Each component's expected time down in the window. |
Examples:
One component with failure rate 0.1 and repair rate 1, over 10 time
units, fails 0.1 * (10 / 1.1 + 0.1 / 1.1 ** 2 * (1 - exp(-11)))
times on average:
>>> import surpyval as surv
>>> from repyability import RepairableRBD
>>> rbd = RepairableRBD(
... [("s", "c"), ("c", "t")],
... {
... "c": {
... "reliability": surv.Exponential.from_params([0.1]),
... "repairability": surv.Exponential.from_params([1.0]),
... }
... },
... )
>>> window = rbd.expected_events(10.0)
>>> round(window.system_failures, 4)
0.9174
>>> round(window.node_downtime["c"], 3)
0.826
to_dict ¶
to_dict()
The result as plain data, ready for json.dumps (#235): each
field by name, arrays as lists, numpy numbers as Python ones, the
results it holds as their own to_dict(), and keys JSON cannot
hold (tuple node names, say) as their text.
Returns:
| Type | Description |
|---|---|
dict
|
The fields, as plain data. Infinite and undefined values stay
floats ( |
RateBreakdown
dataclass
¶
RateBreakdown(x, rate, node_rate, jump_times, jumps, node_jumps)
Bases: _ResultMapping
How fast a system's availability (or reliability) is changing at
each time, and each node's part in it: returned by
RepairableRBD.availability_rate and
NonRepairableRBD.reliability_rate.
With independent nodes the system's value is multilinear in theirs, so
its rate is the sum, over the nodes, of each one's Birnbaum importance
times its own rate: rate is sum(node_rate.values()), and a
node's part says how much it is pulling the system down (negative) or
up then. Where a scheduled event makes a node's availability jump (a
block replacement or test that takes it off line), the system's may
jump too: jumps holds the system's jumps, split among the nodes in
node_jumps, whose parts add up to them.
Attributes:
| Name | Type | Description |
|---|---|---|
x |
float or ndarray
|
The times. |
rate |
float or ndarray
|
The system's rate of change at each time, per unit time: after anything that happens at it. |
node_rate |
dict
|
Each node's part in the rate. |
jump_times |
ndarray
|
The times, after 0 and up to the last of |
jumps |
ndarray
|
The system's jump at each. |
node_jumps |
dict
|
Each node's part in the jumps. |
Examples:
One unit, failing at rate 0.1 and repaired at rate 1, from new: its
availability A(t) = (1 + 0.1 exp(-1.1 t)) / 1.1 falls at
0.1 exp(-1.1 t):
>>> import surpyval as surv
>>> from repyability import RepairableRBD
>>> rbd = RepairableRBD(
... [("s", "c"), ("c", "t")],
... {
... "c": {
... "reliability": surv.Exponential.from_params([0.1]),
... "repairability": surv.Exponential.from_params([1.0]),
... }
... },
... )
>>> round(rbd.availability_rate(1.0).rate, 5) # -0.1 * exp(-1.1)
-0.03329
to_dict ¶
to_dict()
The result as plain data, ready for json.dumps (#235): each
field by name, arrays as lists, numpy numbers as Python ones, the
results it holds as their own to_dict(), and keys JSON cannot
hold (tuple node names, say) as their text.
Returns:
| Type | Description |
|---|---|
dict
|
The fields, as plain data. Infinite and undefined values stay
floats ( |
ExpectedCost
dataclass
¶
ExpectedCost(window, mean, by_category, by_component, acquisition_cost=0.0, discount_rate=0.0)
Bases: _ResultMapping
The expected cost of running a system over a window from new:
returned by RepairableRBD.expected_cost.
Exact (numerical, with no simulation): the mean that
RepairableRBD.cost estimates, by the same categories, from each
category's expected events (see
ExpectedEvents) times its mean cost,
and the expected downtimes times their rates. Each value is a float for
one window, or an array in the shape of the windows given.
Attributes:
| Name | Type | Description |
|---|---|---|
window |
float or ndarray
|
The window's length, from new. |
mean |
float or ndarray
|
The expected running cost of the window: the sum of
|
by_category |
dict
|
The expected cost of |
by_component |
dict
|
The expected cost attributable to each costed component (its repair, replace, preventive, inspection and own downtime cost). |
acquisition_cost |
float
|
The one-off cost of buying the components, not in |
discount_rate |
float
|
The continuous rate the costs were discounted at: with one
above 0, every value but |
Examples:
One component with MTTF 10 and MTTR 1, at 100 per repair, and 50 per unit time of system downtime, over 100 time units:
>>> import surpyval as surv
>>> from repyability import RepairableRBD
>>> rbd = RepairableRBD(
... [("s", "c"), ("c", "t")],
... {
... "c": {
... "reliability": surv.Exponential.from_params([0.1]),
... "repairability": surv.Exponential.from_params([1.0]),
... "repair_cost": 100.0,
... }
... },
... downtime_cost_rate=50.0,
... )
>>> cost = rbd.expected_cost(100.0)
>>> round(cost.mean, 2) # cost() simulates about this, with its spread
1360.33
>>> round(cost.by_category["repair"], 2) # 100 per failure
909.92
>>> round(cost.by_category["system_downtime"], 2) # 50 per unit down
450.41
>>> round(cost.cost_rate, 2), round(rbd.expected_cost_rate(), 2)
(13.6, 13.64)
total
property
¶
total
The cost of owning the system for the window from new:
acquisition_cost + mean.
Returns:
| Type | Description |
|---|---|
float or ndarray
|
The total cost for each window. |
cost_rate
property
¶
cost_rate
The mean cost per unit time over the window, mean / window
(nan for a window of 0); it approaches
RepairableRBD.expected_cost_rate() as the window grows (but
for a discounted cost, whose rate falls away).
Returns:
| Type | Description |
|---|---|
float or ndarray
|
The cost rate for each window. |
to_dict ¶
to_dict()
The result as plain data, ready for json.dumps (#235): each
field by name, arrays as lists, numpy numbers as Python ones, the
results it holds as their own to_dict(), and keys JSON cannot
hold (tuple node names, say) as their text.
Returns:
| Type | Description |
|---|---|
dict
|
The fields, as plain data. Infinite and undefined values stay
floats ( |
ConfidenceInterval
dataclass
¶
ConfidenceInterval(estimate, lower, upper, confidence, standard_error, n_samples, method=None)
Bases: _ResultMapping
A Monte-Carlo estimate with its sampling uncertainty.
Returned by NonRepairableRBD.mean_time_to_failure_interval and
CostResult.mean_interval.
The estimate is a sample mean, which by the central limit theorem is
approximately normal, so the interval is
estimate +/- z * standard_error for the normal quantile z of the
confidence level; both methods clip the lower bound at 0, as the MTTF
and costs they estimate are non-negative. It describes how precisely
the mean has been estimated, and narrows like 1 / sqrt(n_samples).
Like the other result types it is also a read-only mapping of its
fields.
Attributes:
| Name | Type | Description |
|---|---|---|
estimate |
float
|
The point estimate (the sample mean). |
lower |
float
|
Lower bound of the confidence interval. |
upper |
float
|
Upper bound of the confidence interval. |
confidence |
float
|
The confidence level the bounds correspond to (e.g. 0.95). |
standard_error |
float
|
The standard error of the estimate. |
n_samples |
int
|
The number of Monte-Carlo samples the estimate was computed from. |
method |
(str, optional)
|
How the estimate was found. For a |
Examples:
The MTTF of a single exponential component with mean 10, from 10,000 simulated lifetimes:
>>> import surpyval as surv
>>> from repyability import NonRepairableRBD
>>> rbd = NonRepairableRBD(
... [("s", "c"), ("c", "t")],
... {"c": surv.Exponential.from_params([0.1])},
... )
>>> interval = rbd.mean_time_to_failure_interval(mc_samples=10_000, seed=0)
>>> round(interval.estimate, 2), round(interval.standard_error, 2)
(9.91, 0.1)
>>> round(interval.lower, 2), round(interval.upper, 2)
(9.71, 10.1)
>>> interval["confidence"], interval.n_samples
(0.95, 10000)
to_dict ¶
to_dict()
The result as plain data, ready for json.dumps (#235): each
field by name, arrays as lists, numpy numbers as Python ones, the
results it holds as their own to_dict(), and keys JSON cannot
hold (tuple node names, say) as their text.
Returns:
| Type | Description |
|---|---|
dict
|
The fields, as plain data. Infinite and undefined values stay
floats ( |
ControlVariate
dataclass
¶
ControlVariate(twin, exact, coefficient, correlation, itself=False)
Bases: _ResultMapping
The exact twin a simulation run was controlled by, with
RepairableRBD.availability's or cost's control_variate; by
default, the system itself where the exact methods take it (see
itself).
The twin is the system with its components failing and repaired
independently: the same diagram, components and models, without what
ties them together (a limit on repair crews, maintenance groups) or
what the exact methods over time do not take (a standby group's
switching, its units then operating together; imperfect repair;
replacement on condition; inspections they do not take), so that its
expected values over the window are exact. It is simulated alongside
the system with common random numbers (each of its streams draws what
the system's stream of the same name draws), so its values move with
the system's, and its error against its exact value shows how far the
system's own mean is off. Each simulation's controlled value is x -
coefficient * (twin - exact): their mean estimates the system's mean
without bias (but for the coefficient's coming from the same run, an
error of order 1 / n), with 1 - correlation**2 times the
variance of the plain mean.
Attributes:
| Name | Type | Description |
|---|---|---|
twin |
ndarray
|
The twin's value in each simulation, in order: its fraction of the window up, or its cost. |
exact |
float
|
The twin's exact expected value over the window
( |
coefficient |
float
|
The multiple of the twin's error taken off: |
correlation |
float
|
The correlation of the system's values with the twin's, over the same. |
itself |
bool
|
Whether the twin is the system itself, whose expected values the exact methods work out (independent components, or tied together only as the exact methods take them: crews, standby groups or common-cause groups their chains follow): its exact value is then the system's, which the intervals give, with no error. A run takes it by default. |
Examples:
Four simulations whose twin was up 96.5% of the time on average, where its exact mean is 95%: the system's own mean, 95.25%, is taken down by about as much.
>>> import numpy as np
>>> from repyability import ControlVariate
>>> x = np.array([0.90, 0.95, 0.99, 0.97])
>>> twin = np.array([0.92, 0.96, 1.00, 0.98])
>>> control = ControlVariate.of(x, twin, exact=0.95)
>>> round(control.coefficient, 4), round(control.correlation, 4)
(1.1286, 0.9981)
>>> round(float(control.controlled(x).mean()), 4)
0.9356
>>> round(control.variance_reduction)
261
variance_reduction
property
¶
variance_reduction
How many times as many plain simulations the controlled estimate
is worth: 1 / (1 - correlation**2), the plain mean's variance
over the controlled mean's (infinite for a twin that is the system
itself).
Returns:
| Type | Description |
|---|---|
float
|
The factor. |
to_dict ¶
to_dict()
The result as plain data, ready for json.dumps (#235): each
field by name, arrays as lists, numpy numbers as Python ones, the
results it holds as their own to_dict(), and keys JSON cannot
hold (tuple node names, say) as their text.
Returns:
| Type | Description |
|---|---|
dict
|
The fields, as plain data. Infinite and undefined values stay
floats ( |
of
classmethod
¶
of(values, twin, exact, antithetic=False, itself=False)
The control of a run whose simulations gave values, by a
twin that gave twin (in the same order) and whose exact mean is
exact.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
values
|
array_like
|
The system's value in each simulation. |
required |
twin
|
array_like
|
The twin's value in each simulation. |
required |
exact
|
float
|
The twin's exact expected value. |
required |
antithetic
|
bool
|
Whether the simulations come in antithetic pairs, by default False: the coefficient is then worked out from the pairs' means. |
False
|
itself
|
bool
|
Whether the twin is the system itself, by default False. |
False
|
Returns:
| Type | Description |
|---|---|
ControlVariate
|
The twin's values, its exact value, the coefficient and the correlation. |
controlled ¶
controlled(values)
The controlled values of the simulations whose own values
(in the same order as the twin's) these were.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
values
|
array_like
|
The system's value in each simulation. |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
|
ConditionalRun
dataclass
¶
ConditionalRun(modules, states, availability_square=None, whole=False, uptimes=None, costs=None, delivered=None)
Bases: _ResultMapping
How a run's expected values were taken given its modules' histories:
RepairableRBD.availability or cost on a system with
dependent modules, by default or with conditional=True.
The modules are the nodes whose values over time the exact methods do
not work out (a standby group of other lives, a nested RBD that needs
simulating, imperfect repair, a maintenance group's members, the nodes
limited repair crews serve). Every other node is independent of them,
so each simulation's expected values given its modules' histories
are worked out exactly (the system given each joint state of the
modules, once). Their mean, and its interval
(mean_availability_interval, the cost's mean_interval), estimate
the window's expected values without bias, and more precisely than the
simulations' own values, whose randomness from the other nodes is gone.
By default (whole) the whole system is simulated, as a plain run
simulates it, and only the means (mean_availability, the cost's
mean and breakdowns) and their intervals take the simulations'
expected values given their modules (uptimes, costs):
everything else in the result, the spread included, is the
simulations' own (sample_mean_availability, the cost's
sample_mean). With conditional=True only the modules are
simulated, which costs only their events: the result's own values are
then the expected ones, whose spread is less than a window's own, so
it gives no percentiles.
Attributes:
| Name | Type | Description |
|---|---|---|
modules |
tuple
|
The nodes simulated as modules, in the order of the RBD's components. |
states |
int
|
The joint states of the modules (each up or down) the simulations
met: the system was worked out exactly given each. Just one, and
the modules never changed state in the run: their outages were not
sampled, and every simulation's expected values given them are the
same, which says nothing of the error. The means and their
intervals are then the simulations' own ( |
availability_square |
(ndarray, optional)
|
With |
whole |
bool
|
Whether the whole system was simulated, by default False: True for
the default run of a system with modules, whose means alone take
the values below; False with |
uptimes |
(ndarray, optional)
|
With |
costs |
(ndarray, optional)
|
With |
delivered |
(ndarray, optional)
|
With |
informative
property
¶
informative
Whether the modules changed state in the run, so that the spread of the simulations' expected values given them estimates the error, or there are none, and the values are exact.
unknown
property
¶
unknown
Whether the error of the run's means is unknown: a run of the modules alone in which they never changed state.
to_dict ¶
to_dict()
The result as plain data, ready for json.dumps (#235): each
field by name, arrays as lists, numpy numbers as Python ones, the
results it holds as their own to_dict(), and keys JSON cannot
hold (tuple node names, say) as their text.
Returns:
| Type | Description |
|---|---|
dict
|
The fields, as plain data. Infinite and undefined values stay
floats ( |
UncertaintyResult
dataclass
¶
UncertaintyResult(samples, nominal, n_draws)
Bases: _ResultMapping
The spread of a system quantity over plausible node models.
Returned by NonRepairableRBD.sf_uncertainty. Each of the
n_draws draws gives every uncertain node a plausible model (its
parameters drawn from what is known about them), and the system
quantity is computed exactly for that draw. The samples therefore
describe epistemic uncertainty, about what the models are, and not
the aleatory variability that the models themselves describe. Their
percentiles give uncertainty (credible) intervals; they do not narrow
as n_draws grows, which only makes them more precise.
Its repr summarises the draws: the nominal value, the median and the 90% interval.
Attributes:
| Name | Type | Description |
|---|---|---|
samples |
ndarray
|
One value per draw ( |
nominal |
float or ndarray
|
The value with every node's own model: the point estimate. |
n_draws |
int
|
The number of draws. |
Examples:
A pump whose Weibull model was fitted (by surpyval) to 50 failure times, in series with a valve that is 99% reliable. The fit's parameter covariance gives the draws:
>>> import numpy as np
>>> import surpyval as surv
>>> from repyability import NonRepairableRBD
>>> pump = surv.Weibull.fit(np.linspace(200, 1800, 50))
>>> valve = surv.FixedEventProbability.from_params(0.01)
>>> rbd = NonRepairableRBD(
... [("s", "pump"), ("pump", "valve"), ("valve", "t")],
... {"pump": pump, "valve": valve},
... )
>>> result = rbd.sf_uncertainty(500, {"pump": "fit"}, n_draws=5000, seed=0)
>>> round(result.nominal, 3), round(result.median, 3)
(0.847, 0.846)
>>> lower, upper = result.interval(0.9)
>>> round(lower, 3), round(upper, 3)
(0.777, 0.906)
mean
property
¶
mean
The mean over the draws (per time, for an array of times).
Returns:
| Type | Description |
|---|---|
float or ndarray
|
The mean of |
median
property
¶
median
The median over the draws (per time, for an array of times).
Returns:
| Type | Description |
|---|---|
float or ndarray
|
The median of |
std
property
¶
std
The standard deviation over the draws (per time).
Returns:
| Type | Description |
|---|---|
float or ndarray
|
The sample standard deviation of |
to_dict ¶
to_dict()
The result as plain data, ready for json.dumps (#235): each
field by name, arrays as lists, numpy numbers as Python ones, the
results it holds as their own to_dict(), and keys JSON cannot
hold (tuple node names, say) as their text.
Returns:
| Type | Description |
|---|---|
dict
|
The fields, as plain data. Infinite and undefined values stay
floats ( |
percentile ¶
percentile(q)
The q-th percentile over the draws (per time).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
q
|
float
|
The percentile, in [0, 100], as numpy takes it: |
required |
Returns:
| Type | Description |
|---|---|
float or ndarray
|
The percentile of |
interval ¶
interval(level=0.9)
The equal-tailed uncertainty interval over the draws (per time).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
level
|
float
|
The probability the interval holds, in (0, 1), by default 0.9: from the 5th to the 95th percentile. |
0.9
|
Returns:
| Type | Description |
|---|---|
tuple
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
UncertaintyImportance
dataclass
¶
UncertaintyImportance(method, variance, first_order, total)
Bases: _ResultMapping
Each uncertain input's part in the uncertainty of a system quantity:
returned by NonRepairableRBD.uncertainty_importance.
The inputs are the uncertainties given (a node, a tuple of nodes of one population, a common-cause group's model), by the keys they were given under. Which input's uncertainty widens the interval most is where more data would narrow it most.
Attributes:
| Name | Type | Description |
|---|---|---|
method |
str
|
|
variance |
float or ndarray
|
The quantity's variance over the inputs' uncertainty: the delta method's approximation, the sum of its parts, or the draws'. |
first_order |
dict
|
Each input's first-order share of the variance. The delta method's
part, |
total |
dict
|
Each input's total share: Sobol's total index,
|
Examples:
>>> import surpyval as surv
>>> import scipy.stats as st
>>> from repyability import NonRepairableRBD
>>> rbd = NonRepairableRBD(
... [("s", "a"), ("a", "b"), ("b", "t")],
... {
... "a": surv.Exponential.from_params([0.01]),
... "b": surv.Exponential.from_params([0.01]),
... },
... )
>>> rates = {
... "a": {"failure_rate": st.uniform(0.005, 0.01)},
... "b": {"failure_rate": st.uniform(0.008, 0.004)},
... }
>>> parts = rbd.uncertainty_importance(10.0, rates)
>>> {node: round(share, 3) for node, share in parts.first_order.items()}
{'a': 0.862, 'b': 0.138}
to_dict ¶
to_dict()
The result as plain data, ready for json.dumps (#235): each
field by name, arrays as lists, numpy numbers as Python ones, the
results it holds as their own to_dict(), and keys JSON cannot
hold (tuple node names, say) as their text.
Returns:
| Type | Description |
|---|---|
dict
|
The fields, as plain data. Infinite and undefined values stay
floats ( |
RedundancyAllocation
dataclass
¶
RedundancyAllocation(units, reliability, cost, method, resources=dict(), mix=dict(), strategy=dict())
Bases: _ResultMapping
The result of NonRepairableRBD.allocate_redundancy().
The chosen number of copies of each costed node, with the system reliability and total cost that allocation gives. Like the other result types it is also a read-only mapping of its fields.
Attributes:
| Name | Type | Description |
|---|---|---|
units |
dict
|
How many identical copies of each costed node to fit in active parallel. Always at least 1: the original unit. |
reliability |
float
|
The system reliability with that allocation (at the mission time
|
cost |
float
|
Total cost of the allocation, |
method |
str
|
|
resources |
dict
|
The total of every resource the copies use, keyed by resource
( |
mix |
dict
|
For each node given a choice of component types, how many copies
of each type it uses, |
strategy |
dict
|
The redundancy strategy of each costed node, |
Examples:
Two components in series, 90% and 80% reliable, one cost unit each, with a budget of 3:
>>> from surpyval import FixedEventProbability
>>> from repyability import NonRepairableRBD
>>> rbd = NonRepairableRBD(
... [("s", "a"), ("a", "b"), ("b", "t")],
... {
... "a": FixedEventProbability.from_params(0.1),
... "b": FixedEventProbability.from_params(0.2),
... },
... )
>>> best = rbd.allocate_redundancy({"a": 1.0, "b": 1.0}, budget=3)
>>> best.units, round(best.reliability, 4), best.cost, best.method
({'a': 1, 'b': 2}, 0.864, 3.0, 'exact')
>>> best.resources
{'cost': 3.0}
>>> best["units"] is best.units
True
to_dict ¶
to_dict()
The result as plain data, ready for json.dumps (#235): each
field by name, arrays as lists, numpy numbers as Python ones, the
results it holds as their own to_dict(), and keys JSON cannot
hold (tuple node names, say) as their text.
Returns:
| Type | Description |
|---|---|
dict
|
The fields, as plain data. Infinite and undefined values stay
floats ( |
ReliabilityRedundancyAllocation
dataclass
¶
ReliabilityRedundancyAllocation(units, component_reliability, reliability, cost, resources)
Bases: _ResultMapping
The component reliability and number of copies chosen for each
node by NonRepairableRBD.allocate_reliability_redundancy.
Attributes:
| Name | Type | Description |
|---|---|---|
units |
dict
|
The number of active copies of each node. |
component_reliability |
dict
|
The reliability chosen for each node's components (every copy of a node is the same). |
reliability |
float
|
The system reliability. |
cost |
float
|
The total of the |
resources |
dict
|
The total of each resource the copies use. |
Examples:
>>> import math
>>> from surpyval import FixedEventProbability
>>> from repyability import NonRepairableRBD
>>> rbd = NonRepairableRBD(
... [("s", "a"), ("a", "t")],
... {"a": FixedEventProbability.from_params(0.1)},
... )
>>> def cost(r, n):
... return n * (10 + (-1 / math.log(r)) ** 1.5)
>>> best = rbd.allocate_reliability_redundancy(
... {"a": cost}, budget=50, bounds=(0.5, 0.999)
... )
>>> best.units, round(best.component_reliability["a"], 3)
({'a': 3}, 0.754)
>>> round(best.reliability, 4), round(best.cost, 4)
(0.9851, 50.0)
to_dict ¶
to_dict()
The result as plain data, ready for json.dumps (#235): each
field by name, arrays as lists, numpy numbers as Python ones, the
results it holds as their own to_dict(), and keys JSON cannot
hold (tuple node names, say) as their text.
Returns:
| Type | Description |
|---|---|
dict
|
The fields, as plain data. Infinite and undefined values stay
floats ( |
TotalCostAllocation
dataclass
¶
TotalCostAllocation(units, total_cost, acquisition_cost, cost_rate, availability, horizon, method, trains=None, discount_rate=0.0)
Bases: _ResultMapping
The result of RepairableRBD.allocate_redundancy().
How many copies of each node (or train of nodes) give a repairable system the lowest total cost of ownership over a horizon: buying the copies, running them (repairs, replacements, maintenance, inspections, their own downtime), and the cost of the system being down. Like the other result types it is also a read-only mapping of its fields.
Attributes:
| Name | Type | Description |
|---|---|---|
units |
dict
|
How many identical copies of each node considered to fit in active parallel, each repaired independently, and of each train to have alongside it (under the train's name). Always at least 1: the original unit or train. |
total_cost |
float
|
The total cost of owning the system for |
acquisition_cost |
float
|
The one-off cost of buying every component, each copy included. |
cost_rate |
float
|
The long-run running cost per unit time: the |
availability |
float
|
The system's long-run availability with those copies: its
|
horizon |
float
|
The horizon the total cost is taken over. |
method |
str
|
|
trains |
dict or None
|
The trains considered ( |
discount_rate |
float
|
The continuous discount rate the running costs were discounted at (0: undiscounted). |
Examples:
A pump that fails on average every 1000 hours and takes 10 to repair, bought for 20,000 and repaired for 500, when an hour without pumping costs 100, over ten years (87,600 hours):
>>> import surpyval as surv
>>> from repyability import RepairableRBD
>>> rbd = RepairableRBD(
... [("s", "pump"), ("pump", "t")],
... {
... "pump": {
... "reliability": surv.Exponential.from_params([1e-3]),
... "repairability": surv.Exponential.from_params([0.1]),
... "repair_cost": 500.0,
... "acquisition_cost": 20000.0,
... }
... },
... downtime_cost_rate=100.0,
... )
>>> best = rbd.allocate_redundancy(87600.0)
>>> best.units, round(best.total_cost), round(best.acquisition_cost)
({'pump': 2}, 127591, 40000)
>>> round(best.availability, 6), best.method
(0.999902, 'exact')
to_dict ¶
to_dict()
The result as plain data, ready for json.dumps (#235): each
field by name, arrays as lists, numpy numbers as Python ones, the
results it holds as their own to_dict(), and keys JSON cannot
hold (tuple node names, say) as their text.
Returns:
| Type | Description |
|---|---|
dict
|
The fields, as plain data. Infinite and undefined values stay
floats ( |
MaintenancePlan
dataclass
¶
MaintenancePlan(intervals, cost_rate, availability, offsets=None)
Bases: _ResultMapping
The result of RepairableRBD.optimal_replacement_intervals() and
RepairableRBD.optimal_inspection_intervals().
The interval chosen for each component, and the system's exact long-run values with them. Like the other result types it is also a read-only mapping of its fields.
Attributes:
| Name | Type | Description |
|---|---|---|
intervals |
dict
|
Node name -> its chosen interval: of age replacement ( |
cost_rate |
float
|
The system's long-run cost per unit time with those intervals: its
|
availability |
float
|
The system's long-run availability with them: its
|
offsets |
dict or None
|
For |
to_dict ¶
to_dict()
The result as plain data, ready for json.dumps (#235): each
field by name, arrays as lists, numpy numbers as Python ones, the
results it holds as their own to_dict(), and keys JSON cannot
hold (tuple node names, say) as their text.
Returns:
| Type | Description |
|---|---|
dict
|
The fields, as plain data. Infinite and undefined values stay
floats ( |
Lever
dataclass
¶
Lever(key, name, value, discrete, bounds, calendar=False)
Bases: _ResultMapping
One lever of a diagram: a value that its parameter_sensitivity
moves.
RepairableRBD.levers and
NonRepairableRBD.levers list
them, in the order parameter_sensitivity reports them, and
with_levers builds the diagram with them at other values. Like the
other result types it is also a read-only mapping of its fields.
Attributes:
| Name | Type | Description |
|---|---|---|
key |
Hashable
|
Whose lever it is, as |
name |
str
|
The lever, as |
value |
float
|
Its value in the diagram. |
discrete |
bool
|
Whether it counts something (standby units, repair crews), its sensitivity the change one more makes; else its sensitivity is a derivative. |
bounds |
tuple of float
|
|
calendar |
bool
|
Whether it moves the period of a calendar that its component
shares with others (a block replacement's or a test's interval,
with other components' block replacements or tests): the long-run
values jump as it leaves the common calendar, so its long-run
sensitivity takes its schedule apart from the others' (see
|
to_dict ¶
to_dict()
The result as plain data, ready for json.dumps (#235): each
field by name, arrays as lists, numpy numbers as Python ones, the
results it holds as their own to_dict(), and keys JSON cannot
hold (tuple node names, say) as their text.
Returns:
| Type | Description |
|---|---|
dict
|
The fields, as plain data. Infinite and undefined values stay
floats ( |
DemonstrationPlan
dataclass
¶
DemonstrationPlan(n, test_multiple, test_time, failures, consumer_risk, producer_risk)
Bases: _ResultMapping
The result of demonstration_plan and mtbf_demonstration_plan.
A demonstration test that keeps both of its risks: a design no better than the target passes it with a chance of at most the consumer's risk, and a good design fails it with a chance of at most the producer's. Like the other result types it is also a read-only mapping of its fields.
Attributes:
| Name | Type | Description |
|---|---|---|
n |
int or None
|
The number of units to test (an attribute test); None for a test of a constant failure rate. |
test_multiple |
float or None
|
How many missions long each unit's test is (an attribute test); None for a test of a constant failure rate. |
test_time |
float or None
|
The total unit time to test (a test of a constant failure rate); None for an attribute test. |
failures |
int
|
The failures the test allows: it passes with at most so many. |
consumer_risk |
float
|
The chance that a design at the target passes. |
producer_risk |
float
|
The chance that a design at the good level fails. |
to_dict ¶
to_dict()
The result as plain data, ready for json.dumps (#235): each
field by name, arrays as lists, numpy numbers as Python ones, the
results it holds as their own to_dict(), and keys JSON cannot
hold (tuple node names, say) as their text.
Returns:
| Type | Description |
|---|---|
dict
|
The fields, as plain data. Infinite and undefined values stay
floats ( |
AvailabilityAllocation
dataclass
¶
AvailabilityAllocation(availability, mttf, mttr, system_availability)
Bases: _ResultMapping
The result of RepairableRBD.availability_allocation() and
RepairableRBD.mttf_mttr_allocation().
What each component must achieve for the system to meet an availability target, and the system's exact long-run availability if they do. Like the other result types it is also a read-only mapping of its fields.
Attributes:
| Name | Type | Description |
|---|---|---|
availability |
dict
|
Node name -> the long-run availability allocated to each component.
The components that keep theirs (see |
mttf |
dict
|
Node name -> an MTTF, for each component allocated an availability:
from |
mttr |
dict
|
Node name -> an MTTR, for the same components: from
|
system_availability |
float
|
The system's long-run availability with the allocated availabilities,
as |
to_dict ¶
to_dict()
The result as plain data, ready for json.dumps (#235): each
field by name, arrays as lists, numpy numbers as Python ones, the
results it holds as their own to_dict(), and keys JSON cannot
hold (tuple node names, say) as their text.
Returns:
| Type | Description |
|---|---|
dict
|
The fields, as plain data. Infinite and undefined values stay
floats ( |
SparesDemand
dataclass
¶
SparesDemand(probabilities, horizon, fleet, method, members=None)
Bases: _ResultMapping
How many spares a component uses over a horizon: the distribution of its replacements, from new, for one system or a fleet of them.
Returned (one per component) by RepairableRBD.spares_demand. A
component uses a spare at each failure and each preventive replacement
(a standby group, at each of its units' failures). Like the other result
types it is also a read-only mapping of its fields.
Attributes:
| Name | Type | Description |
|---|---|---|
probabilities |
ndarray
|
The probability of each number of replacements, |
horizon |
float
|
The time they are counted over, from new. |
fleet |
int
|
How many systems use them, each from new. |
method |
str
|
|
members |
tuple or None
|
The components whose spares a part pools (see |
Examples:
A component with a constant failure rate of 0.01, replaced in no time, uses a Poisson number of spares, 10 on average in 1,000 hours:
>>> import surpyval as surv
>>> from repyability import RepairableRBD
>>> rbd = RepairableRBD(
... [("s", "pump"), ("pump", "t")],
... {
... "pump": {
... "reliability": surv.Exponential.from_params([0.01]),
... "repairability": "instant",
... }
... },
... )
>>> demand = rbd.spares_demand(1000.0)["pump"]
>>> round(demand.mean, 4)
10.0
>>> demand.stock(0.95) # covers the 1,000 hours 95% of the time
15
mean
property
¶
mean
The expected number of spares used (a property, as the other results' values are).
Returns:
| Type | Description |
|---|---|
float
|
The mean number of replacements. |
std
property
¶
std
The standard deviation of the number of spares used (a
property, as mean is).
Returns:
| Type | Description |
|---|---|
float
|
The standard deviation of the number of replacements. |
to_dict ¶
to_dict()
The result as plain data, ready for json.dumps (#235): each
field by name, arrays as lists, numpy numbers as Python ones, the
results it holds as their own to_dict(), and keys JSON cannot
hold (tuple node names, say) as their text.
Returns:
| Type | Description |
|---|---|
dict
|
The fields, as plain data. Infinite and undefined values stay
floats ( |
covered ¶
covered(stock)
The probability that stock spares cover the horizon's
demand: that it is stock or fewer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
stock
|
int
|
The spares held, none replenished. |
required |
Returns:
| Type | Description |
|---|---|
float
|
The probability. |
stock ¶
stock(probability)
The fewest spares that cover the horizon's demand with at least
probability, none replenished.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
probability
|
float
|
The chance of not running out, in |
required |
Returns:
| Type | Description |
|---|---|
int
|
The stock. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
SparesStock
dataclass
¶
SparesStock(stock, fill_rate, stockout_probability, lead_time, fleet, on_order, on_order_at_demand, members=None)
Bases: _ResultMapping
The stock of a component's spares that meets a target when each
spare used is reordered at once and arrives a lead time later
(one-for-one, or (S - 1, S), replenishment), in the long run.
Returned (one per component) by RepairableRBD.spares_stock. With a
stock of S, a spare is on the shelf when fewer than S are on
order: those used in the last lead time. Like the other result types
it is also a read-only mapping of its fields.
Attributes:
| Name | Type | Description |
|---|---|---|
stock |
int
|
The fewest spares that meet the targets. |
fill_rate |
float
|
With that stock, the fraction of demands met from the shelf. |
stockout_probability |
float
|
With that stock, the fraction of time none is on the shelf. |
lead_time |
float
|
The time a spare ordered takes to arrive. |
fleet |
int
|
How many systems draw on the stock. |
on_order |
ndarray
|
The distribution of how many spares are on order at a random time: the demand in a lead time. |
on_order_at_demand |
ndarray
|
The same, as a demand finds it (not counting itself). |
members |
tuple or None
|
The components whose spares a part pools (see |
Examples:
A component with a constant failure rate of 0.01, replaced in no time, and a lead time of 300 hours: the spares on order are Poisson, 3 on average, and 7 on the shelf meet 96.6% of demands:
>>> import surpyval as surv
>>> from repyability import RepairableRBD
>>> rbd = RepairableRBD(
... [("s", "pump"), ("pump", "t")],
... {
... "pump": {
... "reliability": surv.Exponential.from_params([0.01]),
... "repairability": "instant",
... }
... },
... )
>>> stock = rbd.spares_stock(300.0, fill_rate=0.95)["pump"]
>>> stock.stock
7
>>> round(stock.fill_rate, 4)
0.9665
to_dict ¶
to_dict()
The result as plain data, ready for json.dumps (#235): each
field by name, arrays as lists, numpy numbers as Python ones, the
results it holds as their own to_dict(), and keys JSON cannot
hold (tuple node names, say) as their text.
Returns:
| Type | Description |
|---|---|
dict
|
The fields, as plain data. Infinite and undefined values stay
floats ( |
fill_rate_for ¶
fill_rate_for(stock)
The fraction of demands a stock of stock meets from the
shelf: that a demand finds fewer than stock on order.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
stock
|
int
|
The spares held. |
required |
Returns:
| Type | Description |
|---|---|
float
|
The fill rate. |
stockout_probability_for ¶
stockout_probability_for(stock)
The fraction of time a stock of stock leaves the shelf
empty: that stock or more are on order.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
stock
|
int
|
The spares held. |
required |
Returns:
| Type | Description |
|---|---|
float
|
The stock-out probability. |
SimulationChunk ¶
SimulationChunk(ranges, settings, tally)
Simulations of a repairable system's run, by their positions in it.
Made by a RepairableRBD's
simulate_chunk for one
range of simulations; merged with the run's other chunks by merge;
saved with to_dict or to_json and loaded with from_dict or
from_json; and turned into the run's
AvailabilityResult by its
availability_from_chunks.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ranges
|
iterable of (int, int)
|
The simulations held, as |
required |
settings
|
dict
|
The run they belong to (see |
required |
tally
|
object
|
Their totals, as the simulation keeps them. |
required |
Attributes:
| Name | Type | Description |
|---|---|---|
ranges |
list[tuple[int, int]]
|
The simulations held, as |
settings |
dict
|
The run they belong to: |
merge
staticmethod
¶
merge(chunks)
One chunk of the simulations of chunks, which must be of the
same run (the same system, window, seed and settings) and hold
different simulations. They are put in order of their first
simulations, and each must end before the next begins.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
chunks
|
iterable of SimulationChunk or dict
|
The chunks, or their |
required |
Returns:
| Type | Description |
|---|---|
SimulationChunk
|
Their simulations, in order. The chunks given are unchanged. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If there are none, they belong to different runs, or their simulations overlap or interleave. |
to_dict ¶
to_dict()
The chunk as JSON data (from_dict loads it).
Returns:
| Type | Description |
|---|---|
dict
|
Its ranges, run settings and totals, with the RePyability version that made it. |
to_npz ¶
to_npz()
The chunk as the bytes of a NumPy .npz file (from_npz
loads it): its per-simulation values and the curve's changes (or
counts) as arrays, and the rest, the ranges, settings and exact
totals, as JSON in an array of its own. Compact, and read without
pickle, so a worker can send it back to any coordinator (see
repyability.rbd.shards).
Returns:
| Type | Description |
|---|---|
bytes
|
The |
from_npz
classmethod
¶
from_npz(data)
The chunk to_npz saved.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
bytes
|
|
required |
Returns:
| Type | Description |
|---|---|
SimulationChunk
|
The chunk. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
from_dict
classmethod
¶
from_dict(data)
The chunk to_dict saved.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
dict
|
|
required |
Returns:
| Type | Description |
|---|---|
SimulationChunk
|
The chunk. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
to_json ¶
to_json(fp=None)
The chunk as a JSON document (from_json loads it): returned,
or written to fp.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fp
|
(str, PathLike or file)
|
A path, or a file opened for writing, to write the document to; by default None: it is returned. |
None
|
Returns:
| Type | Description |
|---|---|
str or None
|
|
from_json
classmethod
¶
from_json(text)
The chunk to_json saved.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
text
|
(str, PathLike or file)
|
|
required |
Returns:
| Type | Description |
|---|---|
SimulationChunk
|
The chunk. |
TimelineSimulation
dataclass
¶
TimelineSimulation(system, components, time_simulated_to, n_simulations, antithetic=False, engine='python', method='event loop', start=0)
Bases: _ResultMapping
Simulated up/down histories of a repairable system and its components, one per simulation, as timelines.
Returned by
RepairableRBD.simulate_timelines.
Each Timelines works out its measures for
every simulation at once (uptime, failures, first_failure,
availability_curve(), ...), indexes to one simulation's
Timeline, and merges with others. Like the
other result types it is also a read-only mapping of its fields.
Attributes:
| Name | Type | Description |
|---|---|---|
system |
Timelines
|
The system's histories, as the event loop has them: each change's cause is the component whose change made it. |
components |
dict
|
Node -> its |
time_simulated_to |
float
|
The window's end. |
n_simulations |
int
|
How many histories each holds. |
antithetic |
bool
|
Whether the simulations came in antithetic pairs. |
engine |
str
|
The engine that made them: |
method |
str
|
How they were made: |
start |
int
|
The run's simulation the first history is (see |
Examples:
>>> import surpyval as surv
>>> from repyability import RepairableRBD
>>> unit = {
... "reliability": surv.Weibull.from_params([100, 1.5]),
... "repairability": surv.Exponential.from_params([0.5]),
... }
>>> rbd = RepairableRBD(
... [("s", "a"), ("s", "b"), ("a", "t"), ("b", "t")],
... {"a": unit, "b": unit},
... )
>>> runs = rbd.simulate_timelines(
... 1000.0, mc_samples=500, seed=1, engine="python"
... )
>>> runs.method, len(runs.system)
('event loop', 500)
>>> pair, pump = runs.system.failures, runs.components["a"].failures
>>> bool(pair.mean() < pump.mean())
True
Two halves of the run, made apart and joined, are the run:
>>> halves = [
... rbd.simulate_timelines(1000.0, mc_samples=250, seed=1, start=s)
... for s in (0, 250)
... ]
>>> TimelineSimulation.join(halves).system == runs.system
True
to_dict ¶
to_dict()
The result as plain data, ready for json.dumps (#235): each
field by name, arrays as lists, numpy numbers as Python ones, the
results it holds as their own to_dict(), and keys JSON cannot
hold (tuple node names, say) as their text.
Returns:
| Type | Description |
|---|---|
dict
|
The fields, as plain data. Infinite and undefined values stay
floats ( |
join
classmethod
¶
join(parts)
Consecutive ranges of one run's simulations, made apart (with
simulate_timelines' start, in other processes or on other
machines, say), as the one result of them all.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
parts
|
iterable of TimelineSimulation
|
The ranges, in any order: together, simulations |
required |
Returns:
| Type | Description |
|---|---|
TimelineSimulation
|
Their histories one after another, from the first range's
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If there are no parts, or they are not consecutive ranges of a run over one window, with the same components and pairing. |
run_shard ¶
run_shard(shard)
Run a shard (see the module docstring): its simulations of its run, by the engine it names, here.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
shard
|
bytes or str
|
The shard, as |
required |
Returns:
| Type | Description |
|---|---|
bytes
|
Its partial: the simulations' totals, as a |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Examples:
>>> import surpyval as surv
>>> from repyability import RepairableRBD, run_shard
>>> E = surv.Exponential.from_params
>>> rbd = RepairableRBD(
... [("s", "a"), ("a", "t")],
... {"a": {"reliability": E([0.1]), "repairability": E([1.0])}},
... )
>>> shards = rbd.shards(100.0, 2048, seed=3, size=1024)
>>> partials = [run_shard(shard) for shard in reversed(shards)]
>>> merged = rbd.availability_from_chunks(partials, mc_samples=2048)
>>> whole = rbd.availability(100.0, mc_samples=2048, seed=3)
>>> merged.system_uptime == whole.system_uptime
True
MaintenancePolicy
dataclass
¶
MaintenancePolicy(interval, cost_rate)
Bases: _ResultMapping
An optimal preventive-maintenance policy for a single component.
Returned by the optimal_replacement_policy() method of
NonRepairable (age replacement: replace
at age interval or on failure, whichever comes first) and by the
optimal_overhaul_policy() method of
Repairable (periodic overhaul: repair each
failure and renew the unit every interval). It is an immutable
(frozen) dataclass, read as a mapping too, with to_dict() for
JSON.
Attributes:
| Name | Type | Description |
|---|---|---|
interval |
float
|
The optimal preventive interval, in the time units of the
component's models: the replacement age for age replacement, or
the overhaul interval for a repairable component. |
cost_rate |
float
|
The long-run expected cost per unit time under the policy. When
|
Examples:
>>> import surpyval as surv
>>> from repyability import NonRepairable
>>> unit = NonRepairable(surv.Weibull.from_params([1000, 2.5]))
>>> unit.set_costs_planned_and_unplanned(cp=1, cu=5)
>>> policy = unit.optimal_replacement_policy()
>>> round(policy.interval, 1), round(policy.cost_rate, 6)
(493.0, 0.003462)
to_dict ¶
to_dict()
The result as plain data, ready for json.dumps (#235): each
field by name, arrays as lists, numpy numbers as Python ones, the
results it holds as their own to_dict(), and keys JSON cannot
hold (tuple node names, say) as their text.
Returns:
| Type | Description |
|---|---|
dict
|
The fields, as plain data. Infinite and undefined values stay
floats ( |
FailureLimitPolicy
dataclass
¶
FailureLimitPolicy(failure_count, cost_rate)
Bases: _ResultMapping
An optimal failure-limit (replace-at-N-th-failure) policy.
Returned by the optimal_failure_limit_policy() method of
Repairable: rather than renewing at a
fixed age, the unit is repaired on each failure and replaced at the
failure_count-th failure. It is an immutable (frozen) dataclass,
read as a mapping too, with to_dict() for JSON.
Attributes:
| Name | Type | Description |
|---|---|---|
failure_count |
int
|
The optimal number of failures per replacement cycle: repair the
first |
cost_rate |
float
|
The long-run expected cost per unit time under the policy,
|
Examples:
>>> import surpyval as surv
>>> from surpyval.recurrent import GeneralizedRenewal
>>> from repyability import Repairable
>>> grp = GeneralizedRenewal.fit_from_parameters(
... [100.0, 2.0], 0.4, kijima="i", dist=surv.Weibull
... )
>>> unit = Repairable(grp)
>>> unit.set_repair_and_overhaul_costs(cr=1.0, co=5.0)
>>> policy = unit.optimal_failure_limit_policy(
... seed=1, mc_samples=200, max_failures=15
... )
>>> policy.failure_count, round(policy.cost_rate, 3)
(7, 0.031)
to_dict ¶
to_dict()
The result as plain data, ready for json.dumps (#235): each
field by name, arrays as lists, numpy numbers as Python ones, the
results it holds as their own to_dict(), and keys JSON cannot
hold (tuple node names, say) as their text.
Returns:
| Type | Description |
|---|---|
dict
|
The fields, as plain data. Infinite and undefined values stay
floats ( |
FailureForecast
dataclass
¶
FailureForecast(window, age, probability, expected_failures, expected_cost)
Bases: _ResultMapping
The failures of a part replaced when it fails, over a window from
its current unit's age: returned by
NonRepairable.failures.
Attributes:
| Name | Type | Description |
|---|---|---|
window |
float or ndarray
|
The window's length, in the models' time unit. |
age |
float
|
The current unit's age at the window's start. |
probability |
float or ndarray
|
The chance the current unit fails in the window. |
expected_failures |
float or ndarray
|
The expected number of failures in the window, each a replacement: the current unit's, and its replacements'. |
expected_cost |
(float, ndarray or None)
|
Their expected cost: the unplanned cost times
|
to_dict ¶
to_dict()
The result as plain data, ready for json.dumps (#235): each
field by name, arrays as lists, numpy numbers as Python ones, the
results it holds as their own to_dict(), and keys JSON cannot
hold (tuple node names, say) as their text.
Returns:
| Type | Description |
|---|---|
dict
|
The fields, as plain data. Infinite and undefined values stay
floats ( |
SkipValue
dataclass
¶
SkipValue(resets, every, time_to_next, discount_rate, replace, skip, npv, best)
Bases: _ResultMapping
Whether to skip the next planned replacement of a part, right after
a failure: returned by
NonRepairable.skip_next_replacement.
Attributes:
| Name | Type | Description |
|---|---|---|
resets |
bool
|
Whether the plan resets at each replacement. |
every |
float
|
The plan's interval, in the life's time unit. |
time_to_next |
float
|
The time from the failure to the next planned replacement:
|
discount_rate |
float
|
The continuous discount rate a unit of the life's time. |
replace |
float
|
The present-value cost of doing the next planned replacement.
On a plan that does not reset, the costs to the following planned
replacement: the replacement, the new unit's failures to then and
the failures before it. On a plan that resets, the cost of the
whole future (the first cycle, then the plan from new), which
with no discounting is endless: |
skip |
float
|
The present-value cost of skipping it, over the same comparison:
the failures to the following planned replacement; or, on a plan
that resets, the whole future from the longer first cycle
( |
npv |
float
|
What skipping saves, |
best |
str
|
|
to_dict ¶
to_dict()
The result as plain data, ready for json.dumps (#235): each
field by name, arrays as lists, numpy numbers as Python ones, the
results it holds as their own to_dict(), and keys JSON cannot
hold (tuple node names, say) as their text.
Returns:
| Type | Description |
|---|---|
dict
|
The fields, as plain data. Infinite and undefined values stay
floats ( |
NextReplacement
dataclass
¶
NextReplacement(age, best, now, choices, replace_from_age, cost_rate)
Bases: _ResultMapping
When to replace a part in service next: returned by
NonRepairable.optimal_next_replacement.
Attributes:
| Name | Type | Description |
|---|---|---|
age |
float
|
The part's age now. |
best |
ReplacementChoice
|
The cheapest choice (on a tie, the earlier in |
now |
bool
|
Whether the best is to replace it now. |
choices |
list of ReplacementChoice
|
Every choice: now, each outage by its time, a planned replacement at its best time, and run to failure. |
replace_from_age |
float
|
The age from which replacing now beats every later choice: the largest break-even age. |
cost_rate |
float
|
The value of a unit of the part's service, the baseline: the
long-run cost a unit of time of the policy given, or of the part's
|
to_dict ¶
to_dict()
The result as plain data, ready for json.dumps (#235): each
field by name, arrays as lists, numpy numbers as Python ones, the
results it holds as their own to_dict(), and keys JSON cannot
hold (tuple node names, say) as their text.
Returns:
| Type | Description |
|---|---|
dict
|
The fields, as plain data. Infinite and undefined values stay
floats ( |
ReplacementChoice
dataclass
¶
ReplacementChoice(kind, time, price, cost, break_even_age)
Bases: _ResultMapping
One choice of when to replace a part next: an entry of
NextReplacement.
Attributes:
| Name | Type | Description |
|---|---|---|
kind |
str
|
|
time |
float
|
When, from now: 0 for now, the outage's time, the planned
replacement's best time (0 where no later time beats replacing
at once, which is then replacing now, at |
price |
float
|
What the replacement costs if the part has not failed by then (at failure, the failure price). |
cost |
float
|
The expected cost until the part is replaced (at the failure price
if it fails first), less the time it serves at |
break_even_age |
float or None
|
The youngest age at which replacing now costs no more than this
choice ( |
to_dict ¶
to_dict()
The result as plain data, ready for json.dumps (#235): each
field by name, arrays as lists, numpy numbers as Python ones, the
results it holds as their own to_dict(), and keys JSON cannot
hold (tuple node names, say) as their text.
Returns:
| Type | Description |
|---|---|
dict
|
The fields, as plain data. Infinite and undefined values stay
floats ( |
CalendarPolicy
dataclass
¶
CalendarPolicy(cost_rate, cost_rate_se, replacement_rates, cost_rates, method, basis, step, period, determined_until, threshold_only, settings=dict(), life=None)
Bases: _ResultMapping
The long-run optimal replacement policy of a part over a calendar
of opportunities. Returned by
NonRepairable.optimal_calendar_policy.
At each opportunity the policy replaces the part if it is at least a
threshold age, which depends on the time: a shut shortly before a
cheaper one waits for an older part than one long before it.
schedule() lists the thresholds with the long-run chance of a
replacement at each, next_replacement(age) says when a unit in
service will be replaced, and replacement_rates and
cost_rates say what the policy does and costs in the long run,
by kind.
Attributes:
| Name | Type | Description |
|---|---|---|
cost_rate |
float
|
The long-run average cost a unit of time, |
cost_rate_se |
float or None
|
Its standard error, where it is estimated by sampling (None where it is worked out). |
replacement_rates |
dict
|
Replacements a unit of time in the long run, by kind: each
opportunity's name, |
cost_rates |
dict
|
What each kind costs a unit of time in the long run (its rate
times its price); they sum to |
method |
str
|
|
basis |
str
|
|
step |
float or None
|
The grid's step, sized from the life and put on the opportunities' times (None for integration, which has none). |
period |
float or None
|
The time after which the thresholds repeat (None if they do not, or too long to keep). |
determined_until |
float or None
|
For a calendar with given dates: the first time at which the policy depends on how the calendar goes on after its last date (repeating it, or leaving only replacement on any day). None for a periodic calendar. |
threshold_only |
bool
|
Whether, at every time worked out, the ages that replace are all those from the first (else the threshold is the youngest that replaces, and some older ones do not: a life whose hazard falls; the rates by kind then take every age from the youngest). |
settings |
dict
|
How it was worked out: the calendar's tail, sweeps, bounds, samples, windows. |
life |
dict or None
|
The saved form of the life it was worked out for (as the
diagrams save a model), which a
|
Examples:
A bearing with a Weibull life (scale 1000 days, shape 2.5), shuts every 91 days at 3 and every 364 days at 1, no replacement between them, and 20 at a failure:
>>> import surpyval as surv
>>> from repyability import Calendar, NonRepairable, Opportunity
>>> bearing = NonRepairable(surv.Weibull.from_params([1000.0, 2.5]))
>>> calendar = Calendar(
... [
... Opportunity("quarterly", 3.0, period=91.0),
... Opportunity("annual", 1.0, period=364.0),
... ],
... failure=20.0,
... anytime=None,
... )
>>> policy = bearing.optimal_calendar_policy(calendar)
>>> policy.method, policy.basis, policy.period
('dp', 'exact', 364.0)
>>> round(policy.cost_rate * 364, 3)
2.527
>>> {k: round(v * 364, 3) for k, v in policy.replacement_rates.items()}
{'quarterly': 0.0, 'annual': 0.962, 'failure': 0.078}
>>> policy.next_replacement(age=300.0, now=200.0)
PlannedReplacement(time=364.0, kind='annual', price=1.0)
to_dict ¶
to_dict()
The result as plain data, ready for json.dumps (#235): each
field by name, arrays as lists, numpy numbers as Python ones, the
results it holds as their own to_dict(), and keys JSON cannot
hold (tuple node names, say) as their text.
Returns:
| Type | Description |
|---|---|
dict
|
The fields, as plain data. Infinite and undefined values stay
floats ( |
next_replacement ¶
next_replacement(age, now=0.0)
When the unit in service, age old at time now, will be
replaced on the policy, if it does not fail first: the first
time, at or after now, at which a planned replacement is open
and the unit is at least its threshold. A unit older than the
oldest age the policy was worked out to (settings["max_age"])
is past every threshold, and is replaced at the next
opportunity, or on any day now where the calendar allows one.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
age
|
float
|
The unit's age now, at least 0 (0: just put in, so not replaced again now). |
required |
now
|
float
|
The time now, in the calendar's time, by default 0. |
0.0
|
Returns:
| Type | Description |
|---|---|
PlannedReplacement
|
When, at which opportunity, and at what price. |
schedule ¶
schedule(kinds=(), until=None)
The planned replacements open from time 0 up to until, with
each one's threshold and the chance the policy replaces the unit
there.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
kinds
|
tuple of str
|
The kinds to list, by name; by default every opportunity.
|
()
|
until
|
float
|
The time to list them up to (not including). By default the period over which the thresholds repeat, or the end of a calendar's given dates; give it where there is neither. |
None
|
Returns:
| Type | Description |
|---|---|
list of (float, str, float, float)
|
Each as |
PlannedReplacement
dataclass
¶
PlannedReplacement(time, kind, price)
Bases: _ResultMapping
A unit's next planned replacement on a calendar policy: returned by
CalendarPolicy.next_replacement.
Attributes:
| Name | Type | Description |
|---|---|---|
time |
float
|
When, in the calendar's time ( |
kind |
str or None
|
The opportunity it falls at ( |
price |
float or None
|
What the replacement costs there; None if none. |
to_dict ¶
to_dict()
The result as plain data, ready for json.dumps (#235): each
field by name, arrays as lists, numpy numbers as Python ones, the
results it holds as their own to_dict(), and keys JSON cannot
hold (tuple node names, say) as their text.
Returns:
| Type | Description |
|---|---|
dict
|
The fields, as plain data. Infinite and undefined values stay
floats ( |