From 1f3daa2f4120c929c98f231bed4f43b029374d20 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 16 Oct 2026 20:14:46 +0000 Subject: [PATCH] The crews' system has the jump_times hook; two tests follow dev's changes The window counts ask whatever chain they are given (_atom_groups, _before_period_end) for the times its states jump down, a common-cause group's members' tests that take time (#220): the groups' system has them, and the repair crews' (CrewSystem) did not, so every count over a window with limited crews around a nested RBD failed on the release pull request (#353). Its chain jumps nowhere at exact times: none. Two tests follow what dev changed. The cost analyses refuse a diagram with nothing priced (#351), so the condition test that compares a threshold no age reaches with running to failure prices the repairs. And the ample-crews test compared the chain's counts with the independent nodes' to 1e-6, where each path's quadrature is within about 2e-6 of the closed form over the shortest window (the chain's now the closer, since the chains' bends are the integrals' pieces): 5e-6. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_016Ng6hwUY8zznhm1jRfZJqZ --- repyability/rbd/_chain_transient.py | 7 +++++++ repyability/tests/test_chains_over_time.py | 4 +++- repyability/tests/test_condition_long_run.py | 14 ++++++++++++-- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/repyability/rbd/_chain_transient.py b/repyability/rbd/_chain_transient.py index be8fda79..5eb3d72e 100644 --- a/repyability/rbd/_chain_transient.py +++ b/repyability/rbd/_chain_transient.py @@ -336,6 +336,13 @@ def __init__( #: For the pieces of the integrals: the chain's knots and settling. self.curve = ChainCurve(chain) + def jump_times(self, stop: float) -> np.ndarray: + """The times in ``(0, stop)`` at which the chain's states jump + down at exact times: none, as its components fail and are + repaired at rates (a common-cause group's chain jumps at its + members' tests, see ``_ccf_chain.GroupsSystem.jump_times``).""" + return np.empty(0) + def evaluate(self, values: dict, x: np.ndarray): """At the times ``x``, with each nested RBD up with the probability ``values[node]`` (an array over ``x``): each nested RBD's Birnbaum diff --git a/repyability/tests/test_chains_over_time.py b/repyability/tests/test_chains_over_time.py index 4bd8813a..4ee68c33 100644 --- a/repyability/tests/test_chains_over_time.py +++ b/repyability/tests/test_chains_over_time.py @@ -558,11 +558,13 @@ def plant(crews): independent, chained = plant(2), plant(2) chained._crews_couple = lambda: True # type: ignore[method-assign] windows = np.array([5.0, 60.0, 400.0]) + # Each path integrates by its own quadrature: over the shortest window + # both are within about 2e-6 of the closed form (the chain's closer). for name in ("system_failures", "system_downtime"): np.testing.assert_allclose( chained.expected_events(windows)[name], independent.expected_events(windows)[name], - rtol=1e-6, + rtol=5e-6, atol=1e-10, ) np.testing.assert_allclose( diff --git a/repyability/tests/test_condition_long_run.py b/repyability/tests/test_condition_long_run.py index 84f9662b..8665331d 100644 --- a/repyability/tests/test_condition_long_run.py +++ b/repyability/tests/test_condition_long_run.py @@ -84,10 +84,20 @@ def test_a_threshold_of_0_replaces_at_every_inspection(duration): def test_a_threshold_no_inspection_reaches_runs_to_failure(): - never = RepairableRBD(PAIR, {"a": unit(threshold=1.0), "b": unit()}) + # Priced, as the cost analyses refuse a diagram with nothing priced. + never = RepairableRBD( + PAIR, {"a": unit(threshold=1.0, repair_cost=100.0), "b": unit()} + ) plain = RepairableRBD( PAIR, - {"a": {"reliability": LIFE, "repairability": REPAIR}, "b": unit()}, + { + "a": { + "reliability": LIFE, + "repairability": REPAIR, + "repair_cost": 100.0, + }, + "b": unit(), + }, ) for name in LONG_RUN: assert getattr(never, name)() == pytest.approx(