Phased missions¶
A flight takes off, cruises and lands. Each phase needs different
equipment: both engines to take off, either one to cruise, both again to
land. The components are the same aircraft's throughout, so an engine lost
in the cruise, which the cruise tolerates, is still lost at landing. The
phases are not independent, and the mission's reliability is not the
product of theirs. PhasedMission works it out.
A mission¶
Each phase is a name, a duration and a NonRepairableRBD over the mission's
components. A node name is the same component in every phase that uses it,
with the same model, and its life runs through the whole mission, whether
or not the current phase uses it:
import surpyval as surv
from repyability import NonRepairableRBD, PhasedMission
engine = surv.Weibull.from_params([1500.0, 1.4])
gear = surv.Weibull.from_params([4000.0, 2.0])
nav = surv.Exponential.from_params([2e-4])
parts = {"e1": engine, "e2": engine, "gear": gear, "nav": nav}
def diagram(edges):
used = {n for edge in edges for n in edge} - {"s", "t"}
return NonRepairableRBD(edges, {n: parts[n] for n in used})
both_engines = diagram([("s", "e1"), ("e1", "e2"), ("e2", "gear"), ("gear", "t")])
either_engine = diagram([("s", "e1"), ("s", "e2"), ("e1", "nav"), ("e2", "nav"), ("nav", "t")])
flight = PhasedMission([
("take-off", 0.2, both_engines),
("cruise", 10.0, either_engine),
("landing", 0.3, both_engines),
])
flight.unreliability() # -> 0.003963
failing = flight.phase_failure_probabilities()
failing["cruise"] # -> 0.002039
failing["landing"] # -> 0.001917 mostly an engine lost in the cruise
The mission succeeds if every phase's diagram works to the phase's end
(its components only fail, so working at the end is working throughout).
phase_failure_probabilities gives the chance of failing in each phase:
getting through the earlier ones and not this one. They add up to the
mission's unreliability(), 1 - reliability() computed to its own
precision when it is small.
Treating the phases as independent, each with its own diagram's chance of lasting from its start to its end, misses the engine lost in the cruise, and halves the risk:
alone = (
float(both_engines.sf(0.2))
* float(either_engine.sf(10.2)) / float(either_engine.sf(0.2))
* float(both_engines.sf(10.5)) / float(both_engines.sf(10.2))
)
1 - alone # -> 0.002083
- Components. Any node model with
sfandff: surpyval distributions, fixed probabilities (which fail at the start, if at all, as a demand does), nested RBDs and the composite nodes. A repeated node is the component it repeats, as in any diagram. A phase can leave a component out; it still ages. - Phases. In order, with distinct names; a phase of no duration needs its diagram working at that instant (a check before take-off, say).
- Not modelled. Repair between or during phases, a component ageing faster in one phase than another (it has one lifetime model), and common-cause groups, which a phase may not have.
Exact and simulated¶
reliability(), unreliability() and phase_failure_probabilities() are
exact by default. Each component's life through the mission is a chain of
independent segments, one per phase, the k-th survived with probability
R(T_k) / R(T_{k-1}) (Esary and Ziehms, 1975): a component works at the end
of phase j if it survives its first j segments. Each phase's structure
over the segments is a binary decision diagram, built from its diagram's
modules and core as the exact engine reduces it (after Zang, Sun and
Trivedi, 1999), with each component's segments decided together, in phase
order; the mission is the phases' diagrams combined by conjunction. No path
set is listed, so a meshed phase, such as a chain of bridges with tens of
thousands of path sets, is exact in a fraction of a second. A diagram of more
than a million nodes refuses the exact values, and says to simulate.
Setting repyability.rbd.phased_mission.METHOD = "paths" decomposes the
phases' minimal path sets over the segments instead, as before: slower
beyond the smallest missions.
method="simulate" draws each component's life once per mission instead:
mc_samples missions (by default 10,000), seeded with seed.
reliability_interval gives the simulated reliability with a confidence
interval, and takes the Monte-Carlo options of the other simulations:
tolerance (simulate in batches of mc_samples until the interval is that
narrow, up to max_samples), confidence and antithetic pairs.
interval = flight.reliability_interval(1_000_000, seed=1)
interval.estimate # -> 0.996079 simulated
flight.reliability() # -> 0.996037