Fixed ExposureModel in tests
This commit is contained in:
parent
01820fa4e0
commit
e8065e48c3
8 changed files with 100 additions and 56 deletions
|
|
@ -96,14 +96,6 @@ def interesting_times(model: models.ExposureModel, approx_n_pts=100) -> typing.L
|
|||
return nice_times
|
||||
|
||||
|
||||
def short_range_interesting_times(model: models.ExposureModel, times: typing.List[float]) -> typing.List[float]:
|
||||
short_range_times : typing.List[float] = []
|
||||
for period in model.concentration_model.infected.short_range_presence:
|
||||
start, finish = tuple(period.boundaries())
|
||||
short_range_times = short_range_times + [time for time in times if time >= start and time <= finish]
|
||||
return short_range_times
|
||||
|
||||
|
||||
def calculate_report_data(model: models.ExposureModel):
|
||||
times = interesting_times(model)
|
||||
short_range_intervals = []
|
||||
|
|
|
|||
|
|
@ -503,7 +503,11 @@ baseline_model = models.ExposureModel(
|
|||
),
|
||||
evaporation_factor=0.3,
|
||||
),
|
||||
short_range=[],
|
||||
short_range=models.ShortRangeModel(
|
||||
presence=[],
|
||||
expirations=[],
|
||||
dilutions=[],
|
||||
),
|
||||
exposed=models.Population(
|
||||
number=10,
|
||||
presence=models.SpecificInterval(((8., 12.), (13., 17.))),
|
||||
|
|
|
|||
|
|
@ -635,6 +635,9 @@ class _ExpirationBase:
|
|||
raise NotImplementedError("Subclass must implement")
|
||||
|
||||
def jet_origin_concentration(self):
|
||||
"""
|
||||
concentration of viruses at the jet origin (mL/m3).
|
||||
"""
|
||||
raise NotImplementedError("Subclass must implement")
|
||||
|
||||
|
||||
|
|
@ -896,7 +899,7 @@ class InfectedPopulation(_PopulationWithVirus):
|
|||
class ConcentrationModel:
|
||||
room: Room
|
||||
ventilation: _VentilationBase
|
||||
infected: _PopulationWithVirus
|
||||
infected: InfectedPopulation
|
||||
|
||||
#: evaporation factor: the particles' diameter is multiplied by this
|
||||
# factor as soon as they are in the air (but AFTER going out of the,
|
||||
|
|
@ -1078,7 +1081,7 @@ class ShortRangeModel:
|
|||
expirations: typing.List[Expiration]
|
||||
|
||||
#: The dilution factors for each of the expiratory activity in the short range interactions
|
||||
dilutions: _VectorisedFloat
|
||||
dilutions: typing.List[_VectorisedFloat]
|
||||
|
||||
def _normed_concentration(self, concentration_model: ConcentrationModel, time: float) -> _VectorisedFloat:
|
||||
# normalized only by the viral load
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import pytest
|
|||
|
||||
|
||||
@pytest.fixture
|
||||
def baseline_model():
|
||||
def baseline_concentration_model():
|
||||
model = models.ConcentrationModel(
|
||||
room=models.Room(volume=75),
|
||||
ventilation=models.AirChange(
|
||||
|
|
@ -30,14 +30,24 @@ def baseline_model():
|
|||
|
||||
|
||||
@pytest.fixture
|
||||
def baseline_exposure_model(baseline_model):
|
||||
def baseline_sr_model():
|
||||
return models.ShortRangeModel(
|
||||
presence=[],
|
||||
expirations=[],
|
||||
dilutions=[],
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def baseline_exposure_model(baseline_concentration_model, baseline_sr_model):
|
||||
return models.ExposureModel(
|
||||
baseline_model,
|
||||
baseline_concentration_model,
|
||||
baseline_sr_model,
|
||||
exposed=models.Population(
|
||||
number=1000,
|
||||
presence=baseline_model.infected.presence,
|
||||
activity=baseline_model.infected.activity,
|
||||
mask=baseline_model.infected.mask,
|
||||
presence=baseline_concentration_model.infected.presence,
|
||||
activity=baseline_concentration_model.infected.activity,
|
||||
mask=baseline_concentration_model.infected.mask,
|
||||
host_immunity=0.,
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -90,8 +90,8 @@ def known_concentrations(func):
|
|||
np.array([40.91708675, 91.46172332]), np.array([51.6749232285, 80.3196524031])],
|
||||
])
|
||||
def test_exposure_model_ndarray(population, cm,
|
||||
expected_exposure, expected_probability):
|
||||
model = ExposureModel(cm, population)
|
||||
expected_exposure, expected_probability, sr_model):
|
||||
model = ExposureModel(cm, sr_model, population)
|
||||
np.testing.assert_almost_equal(
|
||||
model.deposited_exposure(), expected_exposure
|
||||
)
|
||||
|
|
@ -110,10 +110,10 @@ def test_exposure_model_ndarray(population, cm,
|
|||
[populations[1], np.array([2.13410688, 1.98167067])],
|
||||
[populations[2], np.array([1.36390289, 1.52436206])],
|
||||
])
|
||||
def test_exposure_model_ndarray_and_float_mix(population, expected_deposited_exposure):
|
||||
def test_exposure_model_ndarray_and_float_mix(population, expected_deposited_exposure, sr_model):
|
||||
cm = known_concentrations(
|
||||
lambda t: 0. if np.floor(t) % 2 else np.array([1.2, 1.2]))
|
||||
model = ExposureModel(cm, population)
|
||||
model = ExposureModel(cm, sr_model, population)
|
||||
|
||||
np.testing.assert_almost_equal(
|
||||
model.deposited_exposure(), expected_deposited_exposure
|
||||
|
|
@ -128,17 +128,17 @@ def test_exposure_model_ndarray_and_float_mix(population, expected_deposited_exp
|
|||
[populations[1], np.array([2.13410688, 1.98167067])],
|
||||
[populations[2], np.array([1.36390289, 1.52436206])],
|
||||
])
|
||||
def test_exposure_model_vector(population, expected_deposited_exposure):
|
||||
def test_exposure_model_vector(population, expected_deposited_exposure, sr_model):
|
||||
cm_array = known_concentrations(lambda t: np.array([1.2, 1.2]))
|
||||
model_array = ExposureModel(cm_array, population)
|
||||
model_array = ExposureModel(cm_array, sr_model, population)
|
||||
np.testing.assert_almost_equal(
|
||||
model_array.deposited_exposure(), np.array(expected_deposited_exposure)
|
||||
)
|
||||
|
||||
|
||||
def test_exposure_model_scalar():
|
||||
def test_exposure_model_scalar(sr_model):
|
||||
cm_scalar = known_concentrations(lambda t: 1.2)
|
||||
model_scalar = ExposureModel(cm_scalar, populations[0])
|
||||
model_scalar = ExposureModel(cm_scalar, sr_model, populations[0])
|
||||
expected_deposited_exposure = 1.52436206
|
||||
np.testing.assert_almost_equal(
|
||||
model_scalar.deposited_exposure(), expected_deposited_exposure
|
||||
|
|
@ -169,6 +169,14 @@ def conc_model():
|
|||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def sr_model():
|
||||
return models.ShortRangeModel(
|
||||
presence=[],
|
||||
expirations=[],
|
||||
dilutions=[],
|
||||
)
|
||||
|
||||
# Expected deposited exposure were computed with a trapezoidal integration, using
|
||||
# a mesh of 10'000 pts per exposed presence interval.
|
||||
@pytest.mark.parametrize(
|
||||
|
|
@ -183,17 +191,17 @@ def conc_model():
|
|||
]
|
||||
)
|
||||
def test_exposure_model_integral_accuracy(exposed_time_interval,
|
||||
expected_deposited_exposure, conc_model):
|
||||
expected_deposited_exposure, conc_model, sr_model):
|
||||
presence_interval = models.SpecificInterval((exposed_time_interval,))
|
||||
population = models.Population(
|
||||
10, presence_interval, models.Mask.types['Type I'],
|
||||
models.Activity.types['Standing'], 0.,
|
||||
)
|
||||
model = ExposureModel(conc_model, population)
|
||||
model = ExposureModel(conc_model, sr_model, population)
|
||||
np.testing.assert_allclose(model.deposited_exposure(), expected_deposited_exposure)
|
||||
|
||||
|
||||
def test_infectious_dose_vectorisation():
|
||||
def test_infectious_dose_vectorisation(sr_model):
|
||||
infected_population = models.InfectedPopulation(
|
||||
number=1,
|
||||
presence=halftime,
|
||||
|
|
@ -216,7 +224,7 @@ def test_infectious_dose_vectorisation():
|
|||
10, presence_interval, models.Mask.types['Type I'],
|
||||
models.Activity.types['Standing'], 0.,
|
||||
)
|
||||
model = ExposureModel(cm, population)
|
||||
model = ExposureModel(cm, sr_model, population)
|
||||
inf_probability = model.infection_probability()
|
||||
assert isinstance(inf_probability, np.ndarray)
|
||||
assert inf_probability.shape == (3, )
|
||||
|
|
|
|||
|
|
@ -6,10 +6,10 @@ import cara.models as models
|
|||
import cara.data as data
|
||||
|
||||
|
||||
def test_no_mask_superspeading_emission_rate(baseline_model):
|
||||
def test_no_mask_superspeading_emission_rate(baseline_concentration_model):
|
||||
expected_rate = 48500.
|
||||
npt.assert_allclose(
|
||||
[baseline_model.infected.emission_rate(float(t)) for t in [0, 1, 4, 4.5, 5, 8, 9]],
|
||||
[baseline_concentration_model.infected.emission_rate(float(t)) for t in [0, 1, 4, 4.5, 5, 8, 9]],
|
||||
[0, expected_rate, expected_rate, 0, 0, expected_rate, 0],
|
||||
rtol=1e-12
|
||||
)
|
||||
|
|
@ -38,10 +38,10 @@ def baseline_periodic_hepa():
|
|||
)
|
||||
|
||||
|
||||
def test_concentrations(baseline_model):
|
||||
def test_concentrations(baseline_concentration_model):
|
||||
# expected concentrations were computed analytically
|
||||
ts = [0, 4, 5, 7, 10]
|
||||
concentrations = [baseline_model.concentration(float(t)) for t in ts]
|
||||
concentrations = [baseline_concentration_model.concentration(float(t)) for t in ts]
|
||||
npt.assert_allclose(
|
||||
concentrations,
|
||||
[0.000000e+00, 20.805628, 6.602814e-13, 20.805628, 2.09545e-26],
|
||||
|
|
@ -49,13 +49,13 @@ def test_concentrations(baseline_model):
|
|||
)
|
||||
|
||||
|
||||
def test_smooth_concentrations(baseline_model):
|
||||
def test_smooth_concentrations(baseline_concentration_model):
|
||||
# We don't care about the actual concentrations in this test, but rather
|
||||
# that the curve itself is smooth.
|
||||
dx = 0.002
|
||||
dy_limit = 0.2 # Anything more than this (in relative) is a bit steep.
|
||||
ts = np.arange(0, 10, dx)
|
||||
concentrations = [baseline_model.concentration(float(t)) for t in ts]
|
||||
concentrations = [baseline_concentration_model.concentration(float(t)) for t in ts]
|
||||
assert np.abs(np.diff(concentrations)).max()/np.mean(concentrations) < dy_limit
|
||||
|
||||
|
||||
|
|
@ -367,10 +367,11 @@ def test_concentrations_refine_times(time):
|
|||
npt.assert_allclose(m1.concentration(time), m2.concentration(time), rtol=1e-8)
|
||||
|
||||
|
||||
def build_exposure_model(concentration_model):
|
||||
def build_exposure_model(concentration_model, short_range_model):
|
||||
infected = concentration_model.infected
|
||||
return models.ExposureModel(
|
||||
concentration_model=concentration_model,
|
||||
short_range=short_range_model,
|
||||
exposed=models.Population(
|
||||
number=10,
|
||||
presence=infected.presence,
|
||||
|
|
@ -390,13 +391,13 @@ def build_exposure_model(concentration_model):
|
|||
['Jun', 1721.03336729],
|
||||
],
|
||||
)
|
||||
def test_exposure_hourly_dep(month,expected_deposited_exposure):
|
||||
def test_exposure_hourly_dep(month,expected_deposited_exposure, baseline_sr_model):
|
||||
m = build_exposure_model(
|
||||
build_hourly_dependent_model(
|
||||
month,
|
||||
intervals_open=((0., 24.), ),
|
||||
intervals_presence_infected=((8., 12.), (13., 17.))
|
||||
)
|
||||
), baseline_sr_model
|
||||
)
|
||||
deposited_exposure = m.deposited_exposure()
|
||||
npt.assert_allclose(deposited_exposure, expected_deposited_exposure)
|
||||
|
|
@ -411,14 +412,14 @@ def test_exposure_hourly_dep(month,expected_deposited_exposure):
|
|||
['Jun', 1799.17597184],
|
||||
],
|
||||
)
|
||||
def test_exposure_hourly_dep_refined(month,expected_deposited_exposure):
|
||||
def test_exposure_hourly_dep_refined(month,expected_deposited_exposure, baseline_sr_model):
|
||||
m = build_exposure_model(
|
||||
build_hourly_dependent_model(
|
||||
month,
|
||||
intervals_open=((0., 24.),),
|
||||
intervals_presence_infected=((8., 12.), (13., 17.)),
|
||||
temperatures=data.GenevaTemperatures,
|
||||
)
|
||||
), baseline_sr_model
|
||||
)
|
||||
deposited_exposure = m.deposited_exposure()
|
||||
npt.assert_allclose(deposited_exposure, expected_deposited_exposure, rtol=0.02)
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ def test_type_annotations():
|
|||
|
||||
|
||||
@pytest.fixture
|
||||
def baseline_mc_model() -> cara.monte_carlo.ConcentrationModel:
|
||||
def baseline_mc_concentration_model() -> cara.monte_carlo.ConcentrationModel:
|
||||
mc_model = cara.monte_carlo.ConcentrationModel(
|
||||
room=cara.monte_carlo.Room(volume=cara.monte_carlo.sampleable.Normal(75, 20)),
|
||||
ventilation=cara.monte_carlo.SlidingWindow(
|
||||
|
|
@ -62,21 +62,31 @@ def baseline_mc_model() -> cara.monte_carlo.ConcentrationModel:
|
|||
|
||||
|
||||
@pytest.fixture
|
||||
def baseline_mc_exposure_model(baseline_mc_model) -> cara.monte_carlo.ExposureModel:
|
||||
def baseline_mc_sr_model() -> cara.monte_carlo.ShortRangeModel:
|
||||
return cara.monte_carlo.ShortRangeModel(
|
||||
presence=[],
|
||||
expirations=[],
|
||||
dilutions=[],
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def baseline_mc_exposure_model(baseline_mc_concentration_model, baseline_mc_sr_model) -> cara.monte_carlo.ExposureModel:
|
||||
return cara.monte_carlo.ExposureModel(
|
||||
baseline_mc_model,
|
||||
baseline_mc_concentration_model,
|
||||
baseline_mc_sr_model,
|
||||
exposed=cara.models.Population(
|
||||
number=10,
|
||||
presence=baseline_mc_model.infected.presence,
|
||||
activity=baseline_mc_model.infected.activity,
|
||||
mask=baseline_mc_model.infected.mask,
|
||||
presence=baseline_mc_concentration_model.infected.presence,
|
||||
activity=baseline_mc_concentration_model.infected.activity,
|
||||
mask=baseline_mc_concentration_model.infected.mask,
|
||||
host_immunity=0.,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def test_build_concentration_model(baseline_mc_model: cara.monte_carlo.ConcentrationModel):
|
||||
model = baseline_mc_model.build_model(7)
|
||||
def test_build_concentration_model(baseline_mc_concentration_model: cara.monte_carlo.ConcentrationModel):
|
||||
model = baseline_mc_concentration_model.build_model(7)
|
||||
assert isinstance(model, cara.models.ConcentrationModel)
|
||||
assert isinstance(model.concentration(time=0.), float)
|
||||
conc = model.concentration(time=1.)
|
||||
|
|
|
|||
|
|
@ -41,7 +41,15 @@ TorontoTemperatures = {
|
|||
# in the following tests, were obtained from the feature/mc branch
|
||||
|
||||
@pytest.fixture
|
||||
def shared_office_mc():
|
||||
def sr_model_mc() -> mc.ShortRangeModel:
|
||||
return mc.ShortRangeModel(
|
||||
presence=[],
|
||||
expirations=[],
|
||||
dilutions=[],
|
||||
)
|
||||
|
||||
@pytest.fixture
|
||||
def shared_office_mc(sr_model_mc):
|
||||
"""
|
||||
Corresponds to the 1st line of Table 4 in https://doi.org/10.1101/2021.10.14.21264988
|
||||
"""
|
||||
|
|
@ -72,6 +80,7 @@ def shared_office_mc():
|
|||
)
|
||||
return mc.ExposureModel(
|
||||
concentration_model=concentration_mc,
|
||||
short_range=sr_model_mc,
|
||||
exposed=mc.Population(
|
||||
number=3,
|
||||
presence=mc.SpecificInterval(present_times=((0, 3.5), (4.5, 9))),
|
||||
|
|
@ -83,7 +92,7 @@ def shared_office_mc():
|
|||
|
||||
|
||||
@pytest.fixture
|
||||
def classroom_mc():
|
||||
def classroom_mc(sr_model_mc):
|
||||
"""
|
||||
Corresponds to the 2nd line of Table 4 in https://doi.org/10.1101/2021.10.14.21264988
|
||||
"""
|
||||
|
|
@ -114,6 +123,7 @@ def classroom_mc():
|
|||
)
|
||||
return mc.ExposureModel(
|
||||
concentration_model=concentration_mc,
|
||||
short_range=sr_model_mc,
|
||||
exposed=mc.Population(
|
||||
number=19,
|
||||
presence=models.SpecificInterval(((0, 2), (2.5, 4), (5, 7), (7.5, 9))),
|
||||
|
|
@ -125,7 +135,7 @@ def classroom_mc():
|
|||
|
||||
|
||||
@pytest.fixture
|
||||
def ski_cabin_mc():
|
||||
def ski_cabin_mc(sr_model_mc):
|
||||
"""
|
||||
Corresponds to the 3rd line of Table 4 in https://doi.org/10.1101/2021.10.14.21264988
|
||||
"""
|
||||
|
|
@ -147,6 +157,7 @@ def ski_cabin_mc():
|
|||
)
|
||||
return mc.ExposureModel(
|
||||
concentration_model=concentration_mc,
|
||||
short_range=sr_model_mc,
|
||||
exposed=mc.Population(
|
||||
number=3,
|
||||
presence=models.SpecificInterval(((0, 20/60),)),
|
||||
|
|
@ -158,7 +169,7 @@ def ski_cabin_mc():
|
|||
|
||||
|
||||
@pytest.fixture
|
||||
def skagit_chorale_mc():
|
||||
def skagit_chorale_mc(sr_model_mc):
|
||||
"""
|
||||
Corresponds to the 4th line of Table 4 in https://doi.org/10.1101/2021.10.14.21264988,
|
||||
assuming viral is 10**9 instead of a LogCustomKernel distribution.
|
||||
|
|
@ -186,6 +197,7 @@ def skagit_chorale_mc():
|
|||
)
|
||||
return mc.ExposureModel(
|
||||
concentration_model=concentration_mc,
|
||||
short_range=sr_model_mc,
|
||||
exposed=mc.Population(
|
||||
number=60,
|
||||
presence=models.SpecificInterval(((0, 2.5), )),
|
||||
|
|
@ -197,7 +209,7 @@ def skagit_chorale_mc():
|
|||
|
||||
|
||||
@pytest.fixture
|
||||
def bus_ride_mc():
|
||||
def bus_ride_mc(sr_model_mc):
|
||||
"""
|
||||
Corresponds to the 5th line of Table 4 in https://doi.org/10.1101/2021.10.14.21264988,
|
||||
assuming viral is 5*10**8 instead of a LogCustomKernel distribution.
|
||||
|
|
@ -225,6 +237,7 @@ def bus_ride_mc():
|
|||
)
|
||||
return mc.ExposureModel(
|
||||
concentration_model=concentration_mc,
|
||||
short_range=sr_model_mc,
|
||||
exposed=mc.Population(
|
||||
number=67,
|
||||
presence=models.SpecificInterval(((0, 1.67), )),
|
||||
|
|
@ -236,7 +249,7 @@ def bus_ride_mc():
|
|||
|
||||
|
||||
@pytest.fixture
|
||||
def gym_mc():
|
||||
def gym_mc(sr_model_mc):
|
||||
"""
|
||||
Gym model for testing
|
||||
"""
|
||||
|
|
@ -259,6 +272,7 @@ def gym_mc():
|
|||
)
|
||||
return mc.ExposureModel(
|
||||
concentration_model=concentration_mc,
|
||||
short_range=sr_model_mc,
|
||||
exposed=mc.Population(
|
||||
number=28,
|
||||
presence=concentration_mc.infected.presence,
|
||||
|
|
@ -270,7 +284,7 @@ def gym_mc():
|
|||
|
||||
|
||||
@pytest.fixture
|
||||
def waiting_room_mc():
|
||||
def waiting_room_mc(sr_model_mc):
|
||||
"""
|
||||
Waiting room model for testing
|
||||
"""
|
||||
|
|
@ -293,6 +307,7 @@ def waiting_room_mc():
|
|||
)
|
||||
return mc.ExposureModel(
|
||||
concentration_model=concentration_mc,
|
||||
short_range=sr_model_mc,
|
||||
exposed=mc.Population(
|
||||
number=14,
|
||||
presence=concentration_mc.infected.presence,
|
||||
|
|
@ -340,7 +355,7 @@ def test_report_models(mc_model, expected_pi, expected_new_cases,
|
|||
],
|
||||
)
|
||||
def test_small_shared_office_Geneva(mask_type, month, expected_pi,
|
||||
expected_dose, expected_ER):
|
||||
expected_dose, expected_ER, sr_model_mc):
|
||||
concentration_mc = mc.ConcentrationModel(
|
||||
room=models.Room(volume=33, humidity=0.5),
|
||||
ventilation=models.MultipleVentilation(
|
||||
|
|
@ -370,6 +385,7 @@ def test_small_shared_office_Geneva(mask_type, month, expected_pi,
|
|||
)
|
||||
exposure_mc = mc.ExposureModel(
|
||||
concentration_model=concentration_mc,
|
||||
short_range=sr_model_mc,
|
||||
exposed=mc.Population(
|
||||
number=1,
|
||||
presence=concentration_mc.infected.presence,
|
||||
|
|
|
|||
Loading…
Reference in a new issue