Fixed ExposureModel in tests

This commit is contained in:
Luis Aleixo 2022-02-17 11:54:10 +01:00
parent 01820fa4e0
commit e8065e48c3
8 changed files with 100 additions and 56 deletions

View file

@ -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 = []

View file

@ -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.))),

View file

@ -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

View file

@ -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.,
),
)

View file

@ -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, )

View file

@ -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)

View file

@ -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.)

View file

@ -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,