From 4dbc231ca3fc2363f13029b1eeaa42682d122c74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20M=2E=20Requena=20Plens?= Date: Sun, 2 Aug 2026 14:57:55 +0200 Subject: [PATCH] Validate the multiple-shock times with the shared checker The two guards were written as `not t > 0.0` because that rejects NaN where `t <= 0.0` accepts it, with the reason in a comment beside each one. The library already has `require_positive` for exactly this, written the same way for the same reason: it rejects NaN and infinities, and it names the parameter that failed rather than both at once. Using it removes the last hand-written positivity check in the module, drops the marker that told the analyzer to look away, and makes the tests assert the message the rest of the library gives. Infinity is now rejected where it was accepted before, which is the fix the shared checker brings for free. --- CHANGELOG.md | 6 ++++++ .../vibration/human/multiple_shock.py | 10 +++------- .../human/test_multiple_shock_vibration.py | 17 +++++++++-------- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b317b16c9..8a5fe34ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -999,6 +999,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/). ### Changed +- The multiple-shock time guards go through `require_positive` like the rest + of the library. They were written as `not t > 0.0`, which rejects NaN where + `t <= 0.0` would accept it, and that reason lived in a comment. The shared + validator says the same thing in the form the whole package uses, rejects + infinities too, and names the parameter that failed instead of both at once. + - `phonometry.vibration` has three families, by who reads them. `vibration.structural` is the structural acoustics that feeds building prediction: mobility (ISO 7626), plate junctions, radiation efficiency, diff --git a/src/phonometry/vibration/human/multiple_shock.py b/src/phonometry/vibration/human/multiple_shock.py index d0e3f73cf..b0f9913c9 100644 --- a/src/phonometry/vibration/human/multiple_shock.py +++ b/src/phonometry/vibration/human/multiple_shock.py @@ -252,10 +252,8 @@ def daily_dose(dose: float, exposure_time: float, measurement_time: float) -> fl was measured (same unit as ``exposure_time``). :return: The daily dose :math:`D_{zd} = D_z (t_d/t_m)^{1/6}`, m/s2. """ - # Negated ">" rather than "<=" on purpose: it rejects NaN as well, which - # the opposite operator would let through. - if not measurement_time > 0.0 or not exposure_time > 0.0: # NOSONAR - NaN - raise ValueError("exposure_time and measurement_time must be positive.") + exposure_time = require_positive(exposure_time, "exposure_time") + measurement_time = require_positive(measurement_time, "measurement_time") return float(dose * (exposure_time / measurement_time) ** (1.0 / DOSE_EXPONENT)) @@ -358,9 +356,7 @@ def injury_risk( require_choice(sex, "sex", _SEXES) if years <= 0: raise ValueError("years must be a positive integer.") - # Negated ">" rather than "<=" on purpose: it rejects NaN as well. - if not days_per_year > 0.0: # NOSONAR - NaN - raise ValueError("days_per_year must be positive.") + days_per_year = require_positive(days_per_year, "days_per_year") mz = _mz_for_sex(sex) if mz is None else mz s_stat = static_stress(mz) ages = start_age + np.arange(years, dtype=np.float64) diff --git a/tests/vibration/human/test_multiple_shock_vibration.py b/tests/vibration/human/test_multiple_shock_vibration.py index 5abd570b6..051e9c74a 100644 --- a/tests/vibration/human/test_multiple_shock_vibration.py +++ b/tests/vibration/human/test_multiple_shock_vibration.py @@ -230,11 +230,12 @@ def test_response_peaks_rejects_2d() -> None: with pytest.raises(ValueError, match="1-D time series"): v.response_peaks(np.zeros((2, 100))) -def test_the_time_guards_reject_nan() -> None: - """``not t > 0`` is deliberate: ``t <= 0`` would let NaN through.""" - with pytest.raises(ValueError, match="must be positive"): - v.daily_dose(1.0, exposure_time=math.nan, measurement_time=1.0) - with pytest.raises(ValueError, match="must be positive"): - v.daily_dose(1.0, exposure_time=1.0, measurement_time=math.nan) - with pytest.raises(ValueError, match="days_per_year must be positive"): - v.injury_risk(0.5, start_age=20.0, years=20, days_per_year=math.nan) +def test_the_time_guards_reject_nan_and_infinity() -> None: + """The guards go through require_positive, which rejects both.""" + for bad in (math.nan, math.inf): + with pytest.raises(ValueError, match="'exposure_time' must be positive"): + v.daily_dose(1.0, exposure_time=bad, measurement_time=1.0) + with pytest.raises(ValueError, match="'measurement_time' must be positive"): + v.daily_dose(1.0, exposure_time=1.0, measurement_time=bad) + with pytest.raises(ValueError, match="'days_per_year' must be positive"): + v.injury_risk(0.5, start_age=20.0, years=20, days_per_year=bad)