diff --git a/caimira/models.py b/caimira/models.py index 82e7a852..b29a7e9a 100644 --- a/caimira/models.py +++ b/caimira/models.py @@ -1331,38 +1331,16 @@ class ExposureModel: 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. """ - def verify_ventilation(vent: Ventilation): - # Check if any of the ventilation parameters is an array instance. - # Note that most of the ventilation parameters are part of the WindowOpening class (inheritance). - if (isinstance(vent, WindowOpening) and ( - not all(np.isscalar(value) for value in vent.outside_temp.values) or - not np.isscalar(vent.window_height) or - not np.isscalar(vent.opening_length) or - (isinstance(vent, HingedWindow) and not np.isscalar(vent.window_width)))): - raise ValueError("Ventilation parameters and diameter cannot be arrays at the same time.") - # The q_air_mech parameter is only part of the HEPAFilter class, and - # the air_exch parameter is only part of the AirChange class. - if (isinstance(vent, HEPAFilter) and not np.isscalar(vent.q_air_mech) or - isinstance(vent, AirChange) and not np.isscalar(vent.air_exch)): - raise ValueError("Ventilation rate and diameter cannot be arrays at the same time.") - infected_population = self.concentration_model.infected - if (isinstance(infected_population, InfectedPopulation) - and not np.isscalar(infected_population.expiration.diameter)): - # Verify if the ventilation is initialized as MultipleVentilation. - if isinstance(self.concentration_model.ventilation, MultipleVentilation): - for vent in self.concentration_model.ventilation.ventilations: - verify_ventilation(vent) - else: - verify_ventilation(self.concentration_model.ventilation) - # Check if the room volume is an array instance. - if not np.isscalar(self.concentration_model.room.volume): - raise ValueError("Room volume and diameter cannot be arrays at the same time.") - # Virus decay constant depends on the room humidity and inside_temp parameters. - if (not all(np.isscalar(value) for value in self.concentration_model.room.inside_temp.values) or - not np.isscalar(self.concentration_model.room.humidity)): - raise ValueError("Virus decay constant and diameter cannot be arrays at the same time.") + if isinstance(infected_population, InfectedPopulation) and not np.isscalar(infected_population.expiration.diameter) and not ( + all(np.isscalar(self.concentration_model.virus.decay_constant(self.concentration_model.room.humidity, self.concentration_model.room.inside_temp.value(time)) + + self.concentration_model.ventilation.air_exchange(self.concentration_model.room, time)) for time in self.concentration_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.") + 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 ecb95ae0..d199fa96 100644 --- a/caimira/tests/models/test_exposure_model.py +++ b/caimira/tests/models/test_exposure_model.py @@ -323,12 +323,12 @@ def test_probabilistic_exposure_probability(exposed_population, cm, def test_diameter_vectorisation_window_opening(diameter_dependent_model, sr_model, active, outside_temp, window_height, opening_length, cases_model): concentration = replace(diameter_dependent_model, - ventilation=models.WindowOpening(active=active, + ventilation=models.SlidingWindow(active=active, outside_temp=outside_temp, window_height=window_height, opening_length=opening_length) ) - with pytest.raises(ValueError, match="Ventilation parameters and diameter cannot be arrays at the same time."): + 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."): models.ExposureModel(concentration, sr_model, populations[0], cases_model) @@ -341,7 +341,7 @@ 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="Ventilation parameters and diameter cannot be arrays at the same time."): + 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."): models.ExposureModel(concentration, sr_model, populations[0], cases_model) @@ -350,7 +350,7 @@ def test_diameter_vectorisation_HEPA_filter(diameter_dependent_model, sr_model, concentration = replace(diameter_dependent_model, ventilation = models.HEPAFilter(active=models.PeriodicInterval(period=120, duration=120), q_air_mech=np.array([0.5, 1.])) ) - with pytest.raises(ValueError, match="Ventilation rate and diameter cannot be arrays at the same time."): + 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."): models.ExposureModel(concentration, sr_model, populations[1], cases_model) @@ -359,23 +359,22 @@ def test_diameter_vectorisation_air_change(diameter_dependent_model, sr_model, c concentration = replace(diameter_dependent_model, ventilation = models.AirChange(active=models.PeriodicInterval(period=120, duration=120), air_exch=np.array([0.5, 1.])) ) - with pytest.raises(ValueError, match="Ventilation rate and diameter cannot be arrays at the same time."): + 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."): models.ExposureModel(concentration, sr_model, populations[2], cases_model) @pytest.mark.parametrize( - "volume, inside_temp, humidity, error", [ - [np.array([50, 100]), models.PiecewiseConstant((0., 24.), (293,)), 0.3, - "Room volume and diameter cannot be arrays at the same time."], # Verify room volume vectorisation - [50, models.PiecewiseConstant((0., 24.), (np.array([293., 300.]),)), 0.3, - "Virus decay constant and diameter cannot be arrays at the same time."], # Verify room inside_temp vectorisation - [50, models.PiecewiseConstant((0., 24.), (293.,)), np.array([0.3, 0.5]), - "Virus decay constant and diameter cannot be arrays at the same time."], # Verify room humidity vectorisation + "inside_temp, humidity, error_message", [ + [models.PiecewiseConstant((0., 24.), (np.array([293., 300.]),)), 0.3, + "If the diameter is an array, none of the ventilation parameters, room volume 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 can be arrays at the same time."], # Verify room humidity vectorisation ] ) -def test_diameter_vectorisation_room(diameter_dependent_model, sr_model, cases_model, volume, inside_temp, humidity, error): +def test_diameter_vectorisation_room(diameter_dependent_model, sr_model, cases_model, inside_temp, humidity, error_message): concentration = replace(diameter_dependent_model, - room = models.Room(volume=volume, inside_temp=inside_temp, humidity=humidity)) - with pytest.raises(ValueError, match=error): + room = models.Room(volume=50, inside_temp=inside_temp, humidity=humidity)) + # The vectorised volume is not considered in the air_exchange method for the AirChange class. + with pytest.raises(ValueError, match=error_message): models.ExposureModel(concentration, sr_model, populations[0], cases_model) \ No newline at end of file