From b5d6ce974f9989d4c284cb83f683b09c4b2bf964 Mon Sep 17 00:00:00 2001 From: Nicolas Mounet Date: Wed, 28 Apr 2021 11:50:08 +0200 Subject: [PATCH 01/11] Adding test for InfectedPopulation class (Vectorisation) --- cara/tests/test_infected_population.py | 49 ++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 cara/tests/test_infected_population.py diff --git a/cara/tests/test_infected_population.py b/cara/tests/test_infected_population.py new file mode 100644 index 00000000..ab9e36d3 --- /dev/null +++ b/cara/tests/test_infected_population.py @@ -0,0 +1,49 @@ +import numpy as np +import pytest + +import cara.models + +@pytest.mark.parametrize( + "override_params", [ + {'viral_load_in_sputum': np.array([5e8, 1e9])}, + {'coefficient_of_infectivity': np.array([0.02, 0.05])}, + {'η_exhale': np.array([0.92, 0.95])}, + {'η_leaks': np.array([0.15, 0.20])}, + + ] +) +def test_infected_population_vectorisation(override_params): + defaults = { + 'virus_halflife': 1.1, + 'viral_load_in_sputum': 1e9, + 'coefficient_of_infectivity': 0.02, + 'η_exhale': 0.95, + 'η_leaks': 0.15, + } + defaults.update(override_params) + + office_hours = cara.models.SpecificInterval(present_times=[(8,17)]) + infected = cara.models.InfectedPopulation( + number=1, + presence=office_hours, + mask=cara.models.Mask( + η_exhale=defaults['η_exhale'], + η_leaks=defaults['η_leaks'], + η_inhale=0.3, + ), + activity=cara.models.Activity( + 0.51, + 0.75, + ), + virus=cara.models.Virus( + halflife=defaults['virus_halflife'], + viral_load_in_sputum=defaults['viral_load_in_sputum'], + coefficient_of_infectivity=defaults['coefficient_of_infectivity'], + ), + expiration=cara.models.Expiration( + ejection_factor=(0.084, 0.009, 0.003, 0.002), + ), + ) + emission_rate = infected.emission_rate(10) + assert isinstance(emission_rate, np.ndarray) + assert emission_rate.shape == (2, ) From 5afa228fb53612054413059d2a44ce8628150e71 Mon Sep 17 00:00:00 2001 From: Nicolas Mounet Date: Wed, 28 Apr 2021 14:18:36 +0200 Subject: [PATCH 02/11] Adding exposure model vectorisation test --- cara/tests/models/test_exposure_model.py | 70 ++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/cara/tests/models/test_exposure_model.py b/cara/tests/models/test_exposure_model.py index e69de29b..9bbb1f55 100644 --- a/cara/tests/models/test_exposure_model.py +++ b/cara/tests/models/test_exposure_model.py @@ -0,0 +1,70 @@ +import numpy as np +import pytest + +import cara.models + + +@pytest.mark.parametrize( + "override_params", [ + {'volume': np.array([100, 120])}, + {'air_change': np.array([100, 120])}, + {'virus_halflife': np.array([1.1, 1.5])}, + {'viral_load_in_sputum': np.array([5e8, 1e9])}, + {'coefficient_of_infectivity': np.array([0.02, 0.05])}, + {'η_exhale': np.array([0.92, 0.95])}, + {'η_leaks': np.array([0.15, 0.20])}, + {'η_inhale': np.array([0.3, 0.35])}, + ] +) +def test_exposure_model_vectorisation(override_params): + defaults = { + 'volume': 75, + 'air_change': 100, + 'virus_halflife': 1.1, + 'viral_load_in_sputum': 1e9, + 'coefficient_of_infectivity': 0.02, + 'η_exhale': 0.95, + 'η_leaks': 0.15, + 'η_inhale': 0.3, + } + defaults.update(override_params) + + always = cara.models.PeriodicInterval(240, 240) # TODO: This should be a thing on an interval. + office_hours = cara.models.SpecificInterval(present_times=[(8,17)]) + c_model = cara.models.ConcentrationModel( + cara.models.Room(defaults['volume']), + cara.models.AirChange(always, defaults['air_change']), + cara.models.InfectedPopulation( + number=1, + presence=office_hours, + mask=cara.models.Mask( + η_exhale=defaults['η_exhale'], + η_leaks=defaults['η_leaks'], + η_inhale=defaults['η_inhale'], + ), + activity=cara.models.Activity( + 0.51, + 0.75, + ), + virus=cara.models.Virus( + halflife=defaults['virus_halflife'], + viral_load_in_sputum=defaults['viral_load_in_sputum'], + coefficient_of_infectivity=defaults['coefficient_of_infectivity'], + ), + expiration=cara.models.Expiration( + ejection_factor=(0.084, 0.009, 0.003, 0.002), + ), + ) + ) + e_model = cara.models.ExposureModel( + concentration_model=c_model, + exposed=cara.models.Population( + number=10, + presence=office_hours, + activity=c_model.infected.activity, + mask=c_model.infected.mask, + ) + ) + expected_new_cases = e_model.expected_new_cases() + assert isinstance(expected_new_cases, np.ndarray) + assert expected_new_cases.shape == (2, ) From 487c930f8f3f308786a58705352b75e2fa9b3cad Mon Sep 17 00:00:00 2001 From: Nicolas Mounet Date: Wed, 28 Apr 2021 14:20:13 +0200 Subject: [PATCH 03/11] Dealing with vectorised concentrations in exposure model --- cara/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cara/models.py b/cara/models.py index bee4a0ff..1a71f1aa 100644 --- a/cara/models.py +++ b/cara/models.py @@ -701,7 +701,7 @@ class ExposureModel: def integrate(fn, start, stop): values = np.linspace(start, stop) - return np.trapz([fn(v) for v in values], values) + return np.trapz([fn(v) for v in values], values, axis=0) for start, stop in self.exposed.presence.boundaries(): exposure += integrate(self.concentration_model.concentration, start, stop) From dd236a936ad80b1868e3a6d61f39389392f30077 Mon Sep 17 00:00:00 2001 From: Nicolas Mounet Date: Wed, 28 Apr 2021 14:22:23 +0200 Subject: [PATCH 04/11] Adding _VectorisedFloat types in ExposureModel outputs and for eta_inhale of Mask --- cara/models.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cara/models.py b/cara/models.py index 1a71f1aa..88eaf91a 100644 --- a/cara/models.py +++ b/cara/models.py @@ -460,7 +460,7 @@ class Mask: η_leaks: _VectorisedFloat #: Filtration efficiency of masks when inhaling. - η_inhale: float + η_inhale: _VectorisedFloat #: Particle sizes in cm. particle_sizes: typing.Tuple[float, float, float, float] = ( @@ -695,7 +695,7 @@ class ExposureModel: #: The number of times the exposure event is repeated (default 1). repeats: int = 1 - def quanta_exposure(self) -> float: + def quanta_exposure(self) -> _VectorisedFloat: """The number of virus quanta per meter^3.""" exposure = 0.0 @@ -707,7 +707,7 @@ class ExposureModel: exposure += integrate(self.concentration_model.concentration, start, stop) return exposure * self.repeats - def infection_probability(self): + def infection_probability(self) -> _VectorisedFloat: exposure = self.quanta_exposure() inf_aero = ( @@ -719,12 +719,12 @@ class ExposureModel: # Probability of infection. return (1 - np.exp(-inf_aero)) * 100 - def expected_new_cases(self): + def expected_new_cases(self) -> _VectorisedFloat: prob = self.infection_probability() exposed_occupants = self.exposed.number return prob * exposed_occupants / 100 - def reproduction_number(self): + def reproduction_number(self) -> _VectorisedFloat: """ The reproduction number can be thought of as the expected number of cases directly generated by one infected case in a population. From c6c03888f787a657d0b8514e0b3bfeb51c5c1ed1 Mon Sep 17 00:00:00 2001 From: Nicolas Mounet Date: Wed, 28 Apr 2021 16:40:42 +0200 Subject: [PATCH 05/11] test on exposure model more modular --- cara/tests/models/test_exposure_model.py | 75 ++++++++++++------------ 1 file changed, 39 insertions(+), 36 deletions(-) diff --git a/cara/tests/models/test_exposure_model.py b/cara/tests/models/test_exposure_model.py index 9bbb1f55..1cfab871 100644 --- a/cara/tests/models/test_exposure_model.py +++ b/cara/tests/models/test_exposure_model.py @@ -3,6 +3,43 @@ import pytest import cara.models +def exposure_model_from_params(params): + always = cara.models.PeriodicInterval(240, 240) # TODO: This should be a thing on an interval. + office_hours = cara.models.SpecificInterval(present_times=[(8,17)]) + c_model = cara.models.ConcentrationModel( + cara.models.Room(params['volume']), + cara.models.AirChange(always, params['air_change']), + cara.models.InfectedPopulation( + number=1, + presence=office_hours, + mask=cara.models.Mask( + η_exhale=params['η_exhale'], + η_leaks=params['η_leaks'], + η_inhale=params['η_inhale'], + ), + activity=cara.models.Activity( + 0.51, + 0.75, + ), + virus=cara.models.Virus( + halflife=params['virus_halflife'], + viral_load_in_sputum=params['viral_load_in_sputum'], + coefficient_of_infectivity=params['coefficient_of_infectivity'], + ), + expiration=cara.models.Expiration( + ejection_factor=(0.084, 0.009, 0.003, 0.002), + ), + ) + ) + return cara.models.ExposureModel( + concentration_model=c_model, + exposed=cara.models.Population( + number=10, + presence=office_hours, + activity=c_model.infected.activity, + mask=c_model.infected.mask, + ) + ) @pytest.mark.parametrize( "override_params", [ @@ -29,42 +66,8 @@ def test_exposure_model_vectorisation(override_params): } defaults.update(override_params) - always = cara.models.PeriodicInterval(240, 240) # TODO: This should be a thing on an interval. - office_hours = cara.models.SpecificInterval(present_times=[(8,17)]) - c_model = cara.models.ConcentrationModel( - cara.models.Room(defaults['volume']), - cara.models.AirChange(always, defaults['air_change']), - cara.models.InfectedPopulation( - number=1, - presence=office_hours, - mask=cara.models.Mask( - η_exhale=defaults['η_exhale'], - η_leaks=defaults['η_leaks'], - η_inhale=defaults['η_inhale'], - ), - activity=cara.models.Activity( - 0.51, - 0.75, - ), - virus=cara.models.Virus( - halflife=defaults['virus_halflife'], - viral_load_in_sputum=defaults['viral_load_in_sputum'], - coefficient_of_infectivity=defaults['coefficient_of_infectivity'], - ), - expiration=cara.models.Expiration( - ejection_factor=(0.084, 0.009, 0.003, 0.002), - ), - ) - ) - e_model = cara.models.ExposureModel( - concentration_model=c_model, - exposed=cara.models.Population( - number=10, - presence=office_hours, - activity=c_model.infected.activity, - mask=c_model.infected.mask, - ) - ) + e_model = exposure_model_from_params(defaults) expected_new_cases = e_model.expected_new_cases() assert isinstance(expected_new_cases, np.ndarray) assert expected_new_cases.shape == (2, ) + From 8e0d9d0b1ea9609ddeedaa0352e51f25b1b25025 Mon Sep 17 00:00:00 2001 From: Nicolas Mounet Date: Wed, 28 Apr 2021 18:05:14 +0200 Subject: [PATCH 06/11] adding a test on exposure model, to check scalar vs vector --- cara/tests/models/test_exposure_model.py | 30 ++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/cara/tests/models/test_exposure_model.py b/cara/tests/models/test_exposure_model.py index 1cfab871..d7223f0c 100644 --- a/cara/tests/models/test_exposure_model.py +++ b/cara/tests/models/test_exposure_model.py @@ -71,3 +71,33 @@ def test_exposure_model_vectorisation(override_params): assert isinstance(expected_new_cases, np.ndarray) assert expected_new_cases.shape == (2, ) + +@pytest.mark.parametrize( + "vector_param", [ + 'volume', 'air_change', 'virus_halflife', + 'viral_load_in_sputum', 'coefficient_of_infectivity', 'η_exhale', + 'η_leaks', 'η_inhale', + ] +) +def test_exposure_model_compare_scalar_vector(vector_param): + defaults = { + 'volume': 75, + 'air_change': 100, + 'virus_halflife': 1.1, + 'viral_load_in_sputum': 1e9, + 'coefficient_of_infectivity': 0.02, + 'η_exhale': 0.95, + 'η_leaks': 0.15, + 'η_inhale': 0.3, + } + e_model_scalar = exposure_model_from_params(defaults) + expected_new_cases_scalar = e_model_scalar.expected_new_cases() + assert isinstance(expected_new_cases_scalar, float) + + defaults[vector_param] = np.ones(3)*defaults[vector_param] + e_model_vector = exposure_model_from_params(defaults) + expected_new_cases_vector = e_model_vector.expected_new_cases() + assert isinstance(expected_new_cases_vector, np.ndarray) + assert expected_new_cases_vector.shape == (3, ) + assert np.all(expected_new_cases_vector==expected_new_cases_scalar) + From 87e52ddf6677d5e08d71ac782566044035c1b7d9 Mon Sep 17 00:00:00 2001 From: Nicolas Mounet Date: Thu, 29 Apr 2021 08:45:55 +0200 Subject: [PATCH 07/11] Harmonizing ventilation tests capitalization --- cara/tests/test_ventilation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cara/tests/test_ventilation.py b/cara/tests/test_ventilation.py index f10a6263..86a1dbf2 100644 --- a/cara/tests/test_ventilation.py +++ b/cara/tests/test_ventilation.py @@ -68,7 +68,7 @@ def test_hinged_window(baseline_hingedwindow, window_width, )}, ] ) -def test_HingedWindow_vectorisation(override_params): +def test_hinged_window_vectorisation(override_params): defaults = { 'window_height': 0.15, 'window_width': 0.15, From dc6ea75058dd54c56eeefed4b1a89293882d2180 Mon Sep 17 00:00:00 2001 From: Nicolas Mounet Date: Thu, 29 Apr 2021 09:09:37 +0200 Subject: [PATCH 08/11] Adding hepa and hvac vectorisation tests --- cara/tests/test_ventilation.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/cara/tests/test_ventilation.py b/cara/tests/test_ventilation.py index 86a1dbf2..134acf57 100644 --- a/cara/tests/test_ventilation.py +++ b/cara/tests/test_ventilation.py @@ -92,6 +92,28 @@ def test_sliding_window(baseline_slidingwindow): assert baseline_slidingwindow.discharge_coefficient == 0.6 +def test_hvac_mechanical_vectorisation(): + room = models.Room(volume=50) + interval = models.SpecificInterval(((0, 4), (5, 9))) + t = 0.5 + q_air_mech = np.array([250., 500.]) + v = models.HVACMechanical(interval,q_air_mech) + assert isinstance(v.air_exchange(room, t), np.ndarray) + npt.assert_array_equal(v.air_exchange(room, t), + np.array([250/room.volume, 500/room.volume])) + + +def test_hepa_filter_vectorisation(): + room = models.Room(volume=50) + interval = models.SpecificInterval(((0, 4), (5, 9))) + t = 0.5 + q_air_mech = np.array([250., 500.]) + v = models.HEPAFilter(interval,q_air_mech) + assert isinstance(v.air_exchange(room, t), np.ndarray) + npt.assert_array_equal(v.air_exchange(room, t), + np.array([250/room.volume, 500/room.volume])) + + def test_multiple(baseline_slidingwindow, baseline_hingedwindow): v = models.MultipleVentilation([baseline_hingedwindow, baseline_slidingwindow]) room = models.Room(75) From ac8e912579b510e06a86b460a5e427158b623cc5 Mon Sep 17 00:00:00 2001 From: Nicolas Mounet Date: Thu, 29 Apr 2021 09:13:57 +0200 Subject: [PATCH 09/11] Proper typing in HVACMechanical and HEPAFilter classes --- cara/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cara/models.py b/cara/models.py index eaec0591..0c04eac0 100644 --- a/cara/models.py +++ b/cara/models.py @@ -364,7 +364,7 @@ class HEPAFilter(Ventilation): #: The rate at which the HEPA exchanges air (when switched on) # in m^3/h - q_air_mech: float + q_air_mech: _VectorisedFloat def air_exchange(self, room: Room, time: float) -> _VectorisedFloat: # If the HEPA is off, no air is being exchanged. @@ -381,7 +381,7 @@ class HVACMechanical(Ventilation): #: The rate at which the HVAC exchanges air (when switched on) # in m^3/h - q_air_mech: float + q_air_mech: _VectorisedFloat def air_exchange(self, room: Room, time: float) -> _VectorisedFloat: # If the HVAC is off, no air is being exchanged. From c3cf37d3daae89f86f86c1d33861f08449cf823f Mon Sep 17 00:00:00 2001 From: Nicolas Mounet Date: Mon, 3 May 2021 22:22:25 +0200 Subject: [PATCH 10/11] Simplifying test_exposure_model (towards unit tests rather than integration tests) --- cara/tests/models/test_exposure_model.py | 161 ++++++++++------------- 1 file changed, 69 insertions(+), 92 deletions(-) diff --git a/cara/tests/models/test_exposure_model.py b/cara/tests/models/test_exposure_model.py index d7223f0c..0bcfd955 100644 --- a/cara/tests/models/test_exposure_model.py +++ b/cara/tests/models/test_exposure_model.py @@ -1,103 +1,80 @@ +import typing + import numpy as np +import numpy.testing import pytest -import cara.models +from cara import models +from cara.models import ExposureModel -def exposure_model_from_params(params): - always = cara.models.PeriodicInterval(240, 240) # TODO: This should be a thing on an interval. - office_hours = cara.models.SpecificInterval(present_times=[(8,17)]) - c_model = cara.models.ConcentrationModel( - cara.models.Room(params['volume']), - cara.models.AirChange(always, params['air_change']), - cara.models.InfectedPopulation( - number=1, - presence=office_hours, - mask=cara.models.Mask( - η_exhale=params['η_exhale'], - η_leaks=params['η_leaks'], - η_inhale=params['η_inhale'], - ), - activity=cara.models.Activity( - 0.51, - 0.75, - ), - virus=cara.models.Virus( - halflife=params['virus_halflife'], - viral_load_in_sputum=params['viral_load_in_sputum'], - coefficient_of_infectivity=params['coefficient_of_infectivity'], - ), - expiration=cara.models.Expiration( - ejection_factor=(0.084, 0.009, 0.003, 0.002), - ), - ) - ) - return cara.models.ExposureModel( - concentration_model=c_model, - exposed=cara.models.Population( - number=10, - presence=office_hours, - activity=c_model.infected.activity, - mask=c_model.infected.mask, - ) - ) -@pytest.mark.parametrize( - "override_params", [ - {'volume': np.array([100, 120])}, - {'air_change': np.array([100, 120])}, - {'virus_halflife': np.array([1.1, 1.5])}, - {'viral_load_in_sputum': np.array([5e8, 1e9])}, - {'coefficient_of_infectivity': np.array([0.02, 0.05])}, - {'η_exhale': np.array([0.92, 0.95])}, - {'η_leaks': np.array([0.15, 0.20])}, - {'η_inhale': np.array([0.3, 0.35])}, - ] -) -def test_exposure_model_vectorisation(override_params): - defaults = { - 'volume': 75, - 'air_change': 100, - 'virus_halflife': 1.1, - 'viral_load_in_sputum': 1e9, - 'coefficient_of_infectivity': 0.02, - 'η_exhale': 0.95, - 'η_leaks': 0.15, - 'η_inhale': 0.3, - } - defaults.update(override_params) +class KnownConcentrations(models.ConcentrationModel): + """ + A ConcentrationModel which is based on pre-known quanta concentrations and + which therefore doesn't need other components. Useful for testing. - e_model = exposure_model_from_params(defaults) - expected_new_cases = e_model.expected_new_cases() - assert isinstance(expected_new_cases, np.ndarray) - assert expected_new_cases.shape == (2, ) + """ + def __init__(self, concentration_function: typing.Callable) -> None: + self._func = concentration_function + + def concentration(self, time: float) -> models._VectorisedFloat: # noqa + return self._func(time) + + +halftime = models.PeriodicInterval(120, 60) +populations = [ + # A simple scalar population. + models.Population( + 10, halftime, models.Mask.types['Type I'], + models.Activity.types['Standing'], + ), + # A population with some array component for η_inhale. + models.Population( + 10, halftime, models.Mask(0.95, 0.15, np.array([0.3, 0.35])), + models.Activity.types['Standing'], + ), +] @pytest.mark.parametrize( - "vector_param", [ - 'volume', 'air_change', 'virus_halflife', - 'viral_load_in_sputum', 'coefficient_of_infectivity', 'η_exhale', - 'η_leaks', 'η_inhale', - ] -) -def test_exposure_model_compare_scalar_vector(vector_param): - defaults = { - 'volume': 75, - 'air_change': 100, - 'virus_halflife': 1.1, - 'viral_load_in_sputum': 1e9, - 'coefficient_of_infectivity': 0.02, - 'η_exhale': 0.95, - 'η_leaks': 0.15, - 'η_inhale': 0.3, - } - e_model_scalar = exposure_model_from_params(defaults) - expected_new_cases_scalar = e_model_scalar.expected_new_cases() - assert isinstance(expected_new_cases_scalar, float) + "population, cm, expected_exposure",[ + [populations[1], KnownConcentrations(lambda t: 1.2), np.array([14.4, 14.4])], + [populations[0], KnownConcentrations(lambda t: np.array([1.2, 2.4])), np.array([14.4, 28.8])], + [populations[1], KnownConcentrations(lambda t: np.array([1.2, 2.4])), np.array([14.4, 28.8])], + ]) +def test_exposure_model_ndarray(population, cm, expected_exposure): + model = ExposureModel(cm, population) + np.testing.assert_almost_equal( + model.quanta_exposure(), expected_exposure + ) - defaults[vector_param] = np.ones(3)*defaults[vector_param] - e_model_vector = exposure_model_from_params(defaults) - expected_new_cases_vector = e_model_vector.expected_new_cases() - assert isinstance(expected_new_cases_vector, np.ndarray) - assert expected_new_cases_vector.shape == (3, ) - assert np.all(expected_new_cases_vector==expected_new_cases_scalar) + assert isinstance(model.infection_probability(), np.ndarray) + assert model.infection_probability().shape == (2,) + +@pytest.mark.parametrize("population", populations) +def test_exposure_model_ndarray_and_float_mix(population): + cm = KnownConcentrations(lambda t: 1.2 if np.floor(t) % 2 else np.array([1.2, 1.2])) + model = ExposureModel(cm, population) + + expected_exposure = np.array([14.4, 14.4]) + np.testing.assert_almost_equal( + model.quanta_exposure(), expected_exposure + ) + + assert isinstance(model.infection_probability(), np.ndarray) + + +@pytest.mark.parametrize("population", populations) +def test_exposure_model_compare_scalar_vector(population): + cm_scalar = KnownConcentrations(lambda t: 1.2) + cm_array = KnownConcentrations(lambda t: np.array([1.2, 1.2])) + model_scalar = ExposureModel(cm_scalar, population) + model_array = ExposureModel(cm_array, population) + expected_exposure = 14.4 + np.testing.assert_almost_equal( + model_scalar.quanta_exposure(), expected_exposure + ) + np.testing.assert_almost_equal( + model_array.quanta_exposure(), np.array([expected_exposure]*2) + ) From 99c0e4e1f8fa9a842a3d44622c91e74b458b7e9e Mon Sep 17 00:00:00 2001 From: Nicolas Mounet Date: Tue, 4 May 2021 05:57:21 +0200 Subject: [PATCH 11/11] Extending vectorised tests to expected_new_cases --- cara/tests/models/test_exposure_model.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cara/tests/models/test_exposure_model.py b/cara/tests/models/test_exposure_model.py index 0bcfd955..15f51303 100644 --- a/cara/tests/models/test_exposure_model.py +++ b/cara/tests/models/test_exposure_model.py @@ -49,7 +49,9 @@ def test_exposure_model_ndarray(population, cm, expected_exposure): ) assert isinstance(model.infection_probability(), np.ndarray) + assert isinstance(model.expected_new_cases(), np.ndarray) assert model.infection_probability().shape == (2,) + assert model.expected_new_cases().shape == (2,) @pytest.mark.parametrize("population", populations) @@ -63,6 +65,7 @@ def test_exposure_model_ndarray_and_float_mix(population): ) assert isinstance(model.infection_probability(), np.ndarray) + assert isinstance(model.expected_new_cases(), np.ndarray) @pytest.mark.parametrize("population", populations)