diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ea8cc3..39e74b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -526,6 +526,15 @@ and `rbd._sensitivity`'s `levers` and `Lever` are gone (use `levers()`, ### Fixed +- **A fixed maintenance duration is fixed whatever numpy's rounding.** A + duration known exactly (an `ExactEventTime`) is followed as a fixed + time, where a spread one is convolved; it was told from its variance, + a difference of two numbers about the duration squared, against a + tolerance a few units of rounding wide: on numpy 2.0 the variance came + out at 1e-15, and the exact and numerical analyses of a component + maintained for a fixed time refused ("its later age replacements ... + are too many ... to follow exactly"). The tolerance is a millionth of + the duration. - **A shut at an offset off the grid keeps its period (#319).** The grid's step for `NonRepairable.optimal_calendar_policy` was sized to put every period, offset and date on it, and fell back to the diff --git a/repyability/rbd/_point_availability.py b/repyability/rbd/_point_availability.py index 37223be..dd94b58 100644 --- a/repyability/rbd/_point_availability.py +++ b/repyability/rbd/_point_availability.py @@ -831,7 +831,10 @@ def __init__( mean, variance = _moments( sf, np.linspace(0.0, self.top, 4097), self.splits ) - if variance <= (1e-9 * self.top) ** 2: + # A fixed time, to rounding: the variance is a difference of two + # numbers about ``top ** 2``, so it is uncertain by a few units of + # rounding in them (numpy 2.0 sums them to 1e-15 at a top of 5). + if variance <= (1e-6 * self.top) ** 2: self.fixed = mean return from scipy.signal import fftconvolve diff --git a/repyability/tests/test_point_availability.py b/repyability/tests/test_point_availability.py index 195e7cb..87353fb 100644 --- a/repyability/tests/test_point_availability.py +++ b/repyability/tests/test_point_availability.py @@ -491,7 +491,9 @@ def test_matches_the_simulation(name): def test_instant_repair_is_always_up(): rbd = alone(unit(W([100.0, 2.0]), "instant")) assert rbd.point_availability([0.0, 10.0, 1e4]).tolist() == [1, 1, 1] - assert rbd.mission_availability(100.0) == 1.0 + # To rounding: the integral of a constant 1 sums the quadrature's + # weights, exact to a few units of rounding in some numpy versions. + assert rbd.mission_availability(100.0) == pytest.approx(1.0, abs=1e-15) def test_units_dead_on_arrival_and_units_that_never_fail():