diff --git a/caimira/models.py b/caimira/models.py index 7dce7b77..4caf3717 100644 --- a/caimira/models.py +++ b/caimira/models.py @@ -1328,12 +1328,13 @@ class ExposureModel: def __post_init__(self): """ - The infection probability formula assumes that if the diameter - is an array, then none of the ventilation parameters, room volume or virus - decay constant, are arrays as well. - - The IVRR is the unique term in the exponential of the concentration formula, therefore - there is a check for the diameter-independent elements of the infectious_virus_removal_rate method. + When diameters are sampled (given as an array), + the Monte-Carlo integration over the diameters + assumes that all the parameters within the IVRR, + apart from the settling velocity, are NOT arrays. + In other words, the air exchange rate from the + ventilation, and the virus decay constant, must + not be given as arrays. """ c_model = self.concentration_model # Check if the diameter is vectorised. @@ -1342,8 +1343,8 @@ class ExposureModel: and not ( all(np.isscalar(c_model.virus.decay_constant(c_model.room.humidity, c_model.room.inside_temp.value(time)) + c_model.ventilation.air_exchange(c_model.room, time)) for time in c_model.state_change_times()))): - raise ValueError("If the diameter is an array, none of the ventilation parameters, " - "room volume or virus decay constant can be arrays at the same time.") + raise ValueError("If the diameter is an array, none of the ventilation parameters " + "or virus decay constant can be arrays at the same time.") def long_range_fraction_deposited(self) -> _VectorisedFloat: diff --git a/caimira/tests/models/test_exposure_model.py b/caimira/tests/models/test_exposure_model.py index dab571d7..f66943a5 100644 --- a/caimira/tests/models/test_exposure_model.py +++ b/caimira/tests/models/test_exposure_model.py @@ -311,25 +311,27 @@ def test_probabilistic_exposure_probability(exposed_population, cm, @pytest.mark.parametrize( - "active, outside_temp, window_height, opening_length", [ - [models.PeriodicInterval(period=120, duration=120), models.PiecewiseConstant((0., 12, 24.), - (np.array([293., 300.]), np.array([305., 310.]),)), 1., 1.,], # Verify (ventilation) outside_temp vectorisation. - [models.PeriodicInterval(period=120, duration=120), models.PiecewiseConstant((0., 24.), (293.,)), - np.array([1., 0.5]), 1.], # Verify (ventilation) window_height vectorisation. - [models.PeriodicInterval(period=120, duration=120), models.PiecewiseConstant((0., 24.), (293.,)), - 1., np.array([1., 0.5])], # Verify (ventilation) opening_length vectorisation. + "volume, outside_temp, window_height, opening_length", [ + [np.array([50, 100]), models.PiecewiseConstant((0., 24.), (293.,)), 1., 1.,], # Verify (room) volume vectorisation. + [50, models.PiecewiseConstant((0., 12, 24.), + (np.array([293., 300.]), np.array([305., 310.]),)), 1., 1.,], # Verify (ventilation) outside_temp vectorisation. + [50, models.PiecewiseConstant((0., 24.), (293.,)), + np.array([1., 0.5]), 1.], # Verify (ventilation) window_height vectorisation. + [50, models.PiecewiseConstant((0., 24.), (293.,)), + 1., np.array([1., 0.5])], # Verify (ventilation) opening_length vectorisation. ] ) -def test_diameter_vectorisation_window_opening(diameter_dependent_model, sr_model, active, outside_temp, +def test_diameter_vectorisation_window_opening(diameter_dependent_model, sr_model, volume, outside_temp, window_height, opening_length, cases_model): concentration = replace(diameter_dependent_model, - ventilation=models.SlidingWindow(active=active, + room = models.Room(volume=volume, inside_temp=models.PiecewiseConstant((0., 24.), (293.,)), humidity=0.3), + ventilation=models.SlidingWindow(active=models.PeriodicInterval(period=120, duration=120), outside_temp=outside_temp, window_height=window_height, - opening_length=opening_length) + opening_length=opening_length), ) - with pytest.raises(ValueError, match="If the diameter is an array, none of the ventilation parameters, " - "room volume or virus decay constant can be arrays at the same time."): + with pytest.raises(ValueError, match="If the diameter is an array, none of the ventilation parameters " + "or virus decay constant can be arrays at the same time."): models.ExposureModel(concentration, sr_model, populations[0], cases_model) @@ -342,8 +344,8 @@ def test_diameter_vectorisation_hinged_window(diameter_dependent_model, sr_model opening_length=1., window_width=np.array([1., 0.5])) ) - with pytest.raises(ValueError, match="If the diameter is an array, none of the ventilation parameters, " - "room volume or virus decay constant can be arrays at the same time."): + with pytest.raises(ValueError, match="If the diameter is an array, none of the ventilation parameters " + "or virus decay constant can be arrays at the same time."): models.ExposureModel(concentration, sr_model, populations[0], cases_model) @@ -353,8 +355,8 @@ def test_diameter_vectorisation_HEPA_filter(diameter_dependent_model, sr_model, ventilation = models.HEPAFilter(active=models.PeriodicInterval(period=120, duration=120), q_air_mech=np.array([0.5, 1.])) ) - with pytest.raises(ValueError, match="If the diameter is an array, none of the ventilation parameters, " - "room volume or virus decay constant can be arrays at the same time."): + with pytest.raises(ValueError, match="If the diameter is an array, none of the ventilation parameters " + "or virus decay constant can be arrays at the same time."): models.ExposureModel(concentration, sr_model, populations[1], cases_model) @@ -364,25 +366,28 @@ def test_diameter_vectorisation_air_change(diameter_dependent_model, sr_model, c ventilation = models.AirChange(active=models.PeriodicInterval(period=120, duration=120), air_exch=np.array([0.5, 1.])) ) - with pytest.raises(ValueError, match="If the diameter is an array, none of the ventilation parameters, " - "room volume or virus decay constant can be arrays at the same time."): + with pytest.raises(ValueError, match="If the diameter is an array, none of the ventilation parameters " + "or virus decay constant can be arrays at the same time."): models.ExposureModel(concentration, sr_model, populations[2], cases_model) @pytest.mark.parametrize( - "inside_temp, humidity, error_message", [ - [models.PiecewiseConstant((0., 12, 24.), (np.array([293., 300.]), np.array([305., 310.]))), 0.3, - "If the diameter is an array, none of the ventilation parameters, room volume or virus decay constant " + "volume, inside_temp, humidity, error_message", [ + [np.array([50, 100]), models.PiecewiseConstant((0., 24.), (293.,)), 0.3, + "If the diameter is an array, none of the ventilation parameters or virus decay constant " + "can be arrays at the same time."], # Verify room volume vectorisation + [50, models.PiecewiseConstant((0., 12, 24.), (np.array([293., 300.]), np.array([305., 310.]))), 0.3, + "If the diameter is an array, none of the ventilation parameters or virus decay constant " "can be arrays at the same time."], # Verify room inside_temp vectorisation - [models.PiecewiseConstant((0., 24.), (293.,)), np.array([0.3, 0.5]), - "If the diameter is an array, none of the ventilation parameters, room volume or virus decay constant " + [50, models.PiecewiseConstant((0., 24.), (293.,)), np.array([0.3, 0.5]), + "If the diameter is an array, none of the ventilation parameters or virus decay constant " "can be arrays at the same time."], # Verify room humidity vectorisation ] ) -def test_diameter_vectorisation_room(diameter_dependent_model, sr_model, cases_model, inside_temp, humidity, error_message): +def test_diameter_vectorisation_room(diameter_dependent_model, sr_model, cases_model, volume, inside_temp, humidity, error_message): concentration = replace(diameter_dependent_model, - room = models.Room(volume=50, inside_temp=inside_temp, humidity=humidity)) - # The Room volume is not considered in the air_exchange method for the AirChange class. + room = models.Room(volume=volume, inside_temp=inside_temp, humidity=humidity), + ventilation = models.HVACMechanical(active=models.SpecificInterval(((0., 24.), )), q_air_mech=100.)) with pytest.raises(ValueError, match=error_message): models.ExposureModel(concentration, sr_model, populations[0], cases_model) \ No newline at end of file