2021-06-04 07:24:38 +00:00
|
|
|
import numpy as np
|
|
|
|
|
import numpy.testing as npt
|
|
|
|
|
import pytest
|
2022-05-09 15:15:14 +00:00
|
|
|
from retry import retry
|
2021-06-04 07:24:38 +00:00
|
|
|
|
2024-06-20 08:14:38 +00:00
|
|
|
import caimira.calculator.models.monte_carlo as mc
|
|
|
|
|
from caimira.calculator.models import models, data
|
|
|
|
|
from caimira.calculator.models.monte_carlo.data import activity_distributions, virus_distributions, expiration_distributions, infectious_dose_distribution, viable_to_RNA_ratio_distribution
|
|
|
|
|
from caimira.calculator.validators.virus.virus_validator import build_expiration
|
2021-06-04 07:24:38 +00:00
|
|
|
|
2022-04-22 14:50:17 +00:00
|
|
|
SAMPLE_SIZE = 500_000
|
|
|
|
|
TOLERANCE = 0.05
|
2021-06-04 07:24:38 +00:00
|
|
|
|
2022-01-28 10:18:47 +00:00
|
|
|
# Load the weather data (temperature in kelvin) for Toronto.
|
|
|
|
|
toronto_coordinates = (43.667, 79.400)
|
|
|
|
|
toronto_hourly_temperatures_celsius_per_hour = data.get_hourly_temperatures_celsius_per_hour(
|
|
|
|
|
toronto_coordinates)
|
|
|
|
|
|
|
|
|
|
# Toronto hourly temperatures as piecewise constant function (in Kelvin).
|
|
|
|
|
TorontoTemperatures_hourly = {
|
|
|
|
|
month: models.PiecewiseConstant(
|
|
|
|
|
# NOTE: It is important that the time type is float, not np.float, in
|
|
|
|
|
# order to allow hashability (for caching).
|
|
|
|
|
tuple(float(time) for time in range(25)),
|
|
|
|
|
tuple(273.15 + np.array(temperatures)),
|
|
|
|
|
)
|
|
|
|
|
for month, temperatures in toronto_hourly_temperatures_celsius_per_hour.items()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Same Toronto temperatures on a finer temperature mesh (every 6 minutes).
|
|
|
|
|
TorontoTemperatures = {
|
|
|
|
|
month: TorontoTemperatures_hourly[month].refine(refine_factor=10)
|
|
|
|
|
for month, temperatures in toronto_hourly_temperatures_celsius_per_hour.items()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2022-06-09 07:46:31 +00:00
|
|
|
# References values for infection_probability and expected new cases
|
2021-06-04 07:24:38 +00:00
|
|
|
# in the following tests, were obtained from the feature/mc branch
|
2022-02-17 10:54:10 +00:00
|
|
|
@pytest.fixture
|
2023-11-29 07:54:58 +00:00
|
|
|
def shared_office_mc(data_registry):
|
2021-06-04 09:58:23 +00:00
|
|
|
"""
|
2021-11-10 15:49:32 +00:00
|
|
|
Corresponds to the 1st line of Table 4 in https://doi.org/10.1101/2021.10.14.21264988
|
2021-06-04 09:58:23 +00:00
|
|
|
"""
|
2021-06-04 07:24:38 +00:00
|
|
|
concentration_mc = mc.ConcentrationModel(
|
2023-11-29 07:54:58 +00:00
|
|
|
data_registry=data_registry,
|
2022-04-22 13:20:20 +00:00
|
|
|
room=models.Room(volume=50, inside_temp=models.PiecewiseConstant((0., 24.), (298,)), humidity=0.5),
|
2022-01-12 16:50:27 +00:00
|
|
|
ventilation=models.MultipleVentilation(
|
2021-11-08 13:58:55 +00:00
|
|
|
ventilations=(
|
2021-06-04 07:24:38 +00:00
|
|
|
models.SlidingWindow(
|
2023-11-29 07:54:58 +00:00
|
|
|
data_registry=data_registry,
|
2021-11-08 13:58:55 +00:00
|
|
|
active=models.PeriodicInterval(period=120, duration=120),
|
|
|
|
|
outside_temp=data.GenevaTemperatures['Jun'],
|
|
|
|
|
window_height=1.6,
|
|
|
|
|
opening_length=0.2,
|
2021-06-04 07:24:38 +00:00
|
|
|
),
|
2021-11-08 13:58:55 +00:00
|
|
|
models.AirChange(active=models.PeriodicInterval(period=120, duration=120), air_exch=0.25),
|
|
|
|
|
)
|
2021-06-04 07:24:38 +00:00
|
|
|
),
|
|
|
|
|
infected=mc.InfectedPopulation(
|
2023-11-29 07:54:58 +00:00
|
|
|
data_registry=data_registry,
|
2021-06-04 07:24:38 +00:00
|
|
|
number=1,
|
2022-01-12 16:50:27 +00:00
|
|
|
presence=mc.SpecificInterval(present_times=((0, 3.5), (4.5, 9))),
|
2023-11-29 07:54:58 +00:00
|
|
|
virus=virus_distributions(data_registry)['SARS_CoV_2_DELTA'],
|
2021-11-08 13:58:55 +00:00
|
|
|
mask=models.Mask.types['No mask'],
|
2023-11-29 07:54:58 +00:00
|
|
|
activity=activity_distributions(data_registry)['Seated'],
|
|
|
|
|
expiration=build_expiration(data_registry, {'Speaking': 0.33, 'Breathing': 0.67}),
|
2021-09-17 14:36:07 +00:00
|
|
|
host_immunity=0.,
|
2021-06-04 07:24:38 +00:00
|
|
|
),
|
2021-10-05 13:44:37 +00:00
|
|
|
evaporation_factor=0.3,
|
2021-06-04 07:24:38 +00:00
|
|
|
)
|
2021-06-04 09:58:23 +00:00
|
|
|
return mc.ExposureModel(
|
2023-12-11 17:00:31 +00:00
|
|
|
data_registry=data_registry,
|
2021-06-04 07:24:38 +00:00
|
|
|
concentration_model=concentration_mc,
|
2022-03-29 10:19:14 +00:00
|
|
|
short_range=(),
|
2021-06-04 07:24:38 +00:00
|
|
|
exposed=mc.Population(
|
|
|
|
|
number=3,
|
2022-01-12 16:50:27 +00:00
|
|
|
presence=mc.SpecificInterval(present_times=((0, 3.5), (4.5, 9))),
|
2023-11-29 07:54:58 +00:00
|
|
|
activity=activity_distributions(data_registry)['Seated'],
|
2021-11-08 13:58:55 +00:00
|
|
|
mask=models.Mask.types['No mask'],
|
2021-09-22 09:51:09 +00:00
|
|
|
host_immunity=0.,
|
2022-09-21 07:14:44 +00:00
|
|
|
),
|
|
|
|
|
geographical_data=models.Cases(),
|
2021-06-04 07:24:38 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
2021-06-04 09:58:23 +00:00
|
|
|
@pytest.fixture
|
2023-11-29 07:54:58 +00:00
|
|
|
def classroom_mc(data_registry):
|
2021-06-04 09:58:23 +00:00
|
|
|
"""
|
2021-11-10 15:49:32 +00:00
|
|
|
Corresponds to the 2nd line of Table 4 in https://doi.org/10.1101/2021.10.14.21264988
|
2021-06-04 09:58:23 +00:00
|
|
|
"""
|
2021-06-04 07:24:38 +00:00
|
|
|
concentration_mc = mc.ConcentrationModel(
|
2023-11-29 07:54:58 +00:00
|
|
|
data_registry=data_registry,
|
2022-04-22 13:20:20 +00:00
|
|
|
room=models.Room(volume=160, inside_temp=models.PiecewiseConstant((0., 24.), (293,)), humidity=0.3),
|
2022-01-12 16:50:27 +00:00
|
|
|
ventilation=models.MultipleVentilation(
|
2021-11-08 13:58:55 +00:00
|
|
|
ventilations=(
|
2021-06-04 07:24:38 +00:00
|
|
|
models.SlidingWindow(
|
2023-11-29 07:54:58 +00:00
|
|
|
data_registry=data_registry,
|
2021-11-08 13:58:55 +00:00
|
|
|
active=models.PeriodicInterval(period=120, duration=120),
|
2022-01-28 10:18:47 +00:00
|
|
|
outside_temp=TorontoTemperatures['Dec'],
|
2021-11-08 13:58:55 +00:00
|
|
|
window_height=1.6,
|
|
|
|
|
opening_length=0.2,
|
2021-06-04 07:24:38 +00:00
|
|
|
),
|
2021-11-08 13:58:55 +00:00
|
|
|
models.AirChange(active=models.PeriodicInterval(period=120, duration=120), air_exch=0.25),
|
|
|
|
|
)
|
2021-06-04 07:24:38 +00:00
|
|
|
),
|
|
|
|
|
infected=mc.InfectedPopulation(
|
2023-11-29 07:54:58 +00:00
|
|
|
data_registry=data_registry,
|
2021-06-04 07:24:38 +00:00
|
|
|
number=1,
|
2021-11-08 13:58:55 +00:00
|
|
|
presence=models.SpecificInterval(((0, 2), (2.5, 4), (5, 7), (7.5, 9))),
|
2023-11-29 07:54:58 +00:00
|
|
|
virus=virus_distributions(data_registry)['SARS_CoV_2_ALPHA'],
|
2021-11-08 13:58:55 +00:00
|
|
|
mask=models.Mask.types["No mask"],
|
2023-11-29 07:54:58 +00:00
|
|
|
activity=activity_distributions(data_registry)['Light activity'],
|
|
|
|
|
expiration=build_expiration(data_registry, 'Speaking'),
|
2021-09-17 14:36:07 +00:00
|
|
|
host_immunity=0.,
|
2021-06-04 07:24:38 +00:00
|
|
|
),
|
2021-10-05 13:44:37 +00:00
|
|
|
evaporation_factor=0.3,
|
2021-06-04 07:24:38 +00:00
|
|
|
)
|
2021-06-04 09:58:23 +00:00
|
|
|
return mc.ExposureModel(
|
2023-12-11 17:00:31 +00:00
|
|
|
data_registry=data_registry,
|
2021-06-04 07:24:38 +00:00
|
|
|
concentration_model=concentration_mc,
|
2022-03-29 10:19:14 +00:00
|
|
|
short_range=(),
|
2021-06-04 07:24:38 +00:00
|
|
|
exposed=mc.Population(
|
|
|
|
|
number=19,
|
2021-11-08 13:58:55 +00:00
|
|
|
presence=models.SpecificInterval(((0, 2), (2.5, 4), (5, 7), (7.5, 9))),
|
2023-11-29 07:54:58 +00:00
|
|
|
activity=activity_distributions(data_registry)['Seated'],
|
2021-11-08 13:58:55 +00:00
|
|
|
mask=models.Mask.types["No mask"],
|
2021-09-22 09:51:09 +00:00
|
|
|
host_immunity=0.,
|
2021-06-04 07:24:38 +00:00
|
|
|
),
|
2022-09-21 07:14:44 +00:00
|
|
|
geographical_data=models.Cases(),
|
2021-06-04 07:24:38 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
2021-06-04 09:58:23 +00:00
|
|
|
@pytest.fixture
|
2023-11-29 07:54:58 +00:00
|
|
|
def ski_cabin_mc(data_registry):
|
2021-06-04 09:58:23 +00:00
|
|
|
"""
|
2021-11-10 15:49:32 +00:00
|
|
|
Corresponds to the 3rd line of Table 4 in https://doi.org/10.1101/2021.10.14.21264988
|
2021-06-04 09:58:23 +00:00
|
|
|
"""
|
2021-06-04 07:24:38 +00:00
|
|
|
concentration_mc = mc.ConcentrationModel(
|
2023-11-29 07:54:58 +00:00
|
|
|
data_registry=data_registry,
|
2021-11-08 13:58:55 +00:00
|
|
|
room=models.Room(volume=10, humidity=0.3),
|
|
|
|
|
ventilation=models.MultipleVentilation(
|
|
|
|
|
(models.AirChange(active=models.PeriodicInterval(period=120, duration=120), air_exch=0.0),
|
|
|
|
|
models.AirChange(active=models.PeriodicInterval(period=120, duration=120), air_exch=0.25))),
|
2021-06-04 07:24:38 +00:00
|
|
|
infected=mc.InfectedPopulation(
|
2023-11-29 07:54:58 +00:00
|
|
|
data_registry=data_registry,
|
2021-06-04 07:24:38 +00:00
|
|
|
number=1,
|
2021-11-08 13:58:55 +00:00
|
|
|
presence=models.SpecificInterval(((0, 20/60),)),
|
2023-11-29 07:54:58 +00:00
|
|
|
virus=virus_distributions(data_registry)['SARS_CoV_2_DELTA'],
|
2021-11-08 13:58:55 +00:00
|
|
|
mask=models.Mask.types['No mask'],
|
2023-11-29 07:54:58 +00:00
|
|
|
activity=activity_distributions(data_registry)['Moderate activity'],
|
|
|
|
|
expiration=build_expiration(data_registry, 'Speaking'),
|
2021-09-17 14:36:07 +00:00
|
|
|
host_immunity=0.,
|
2021-06-04 07:24:38 +00:00
|
|
|
),
|
2021-10-05 13:44:37 +00:00
|
|
|
evaporation_factor=0.3,
|
2021-06-04 07:24:38 +00:00
|
|
|
)
|
2021-06-04 09:58:23 +00:00
|
|
|
return mc.ExposureModel(
|
2023-12-11 17:00:31 +00:00
|
|
|
data_registry=data_registry,
|
2021-06-04 07:24:38 +00:00
|
|
|
concentration_model=concentration_mc,
|
2022-03-29 10:19:14 +00:00
|
|
|
short_range=(),
|
2021-06-04 07:24:38 +00:00
|
|
|
exposed=mc.Population(
|
|
|
|
|
number=3,
|
2021-11-08 13:58:55 +00:00
|
|
|
presence=models.SpecificInterval(((0, 20/60),)),
|
2023-11-29 07:54:58 +00:00
|
|
|
activity=activity_distributions(data_registry)['Moderate activity'],
|
2021-11-08 13:58:55 +00:00
|
|
|
mask=models.Mask.types['No mask'],
|
2021-09-22 09:51:09 +00:00
|
|
|
host_immunity=0.,
|
2021-06-04 07:24:38 +00:00
|
|
|
),
|
2022-09-21 07:14:44 +00:00
|
|
|
geographical_data=models.Cases(),
|
2021-06-04 07:24:38 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
2021-11-08 13:58:55 +00:00
|
|
|
@pytest.fixture
|
2023-11-29 07:54:58 +00:00
|
|
|
def skagit_chorale_mc(data_registry):
|
2021-11-08 13:58:55 +00:00
|
|
|
"""
|
2023-11-29 07:54:58 +00:00
|
|
|
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.
|
2021-11-08 13:58:55 +00:00
|
|
|
"""
|
|
|
|
|
concentration_mc = mc.ConcentrationModel(
|
2023-11-29 07:54:58 +00:00
|
|
|
data_registry=data_registry,
|
2021-11-08 13:58:55 +00:00
|
|
|
room=models.Room(volume=810, humidity=0.5),
|
|
|
|
|
ventilation=models.AirChange(
|
|
|
|
|
active=models.PeriodicInterval(period=120, duration=120),
|
|
|
|
|
air_exch=0.7),
|
|
|
|
|
infected=mc.InfectedPopulation(
|
2023-11-29 07:54:58 +00:00
|
|
|
data_registry=data_registry,
|
2021-11-08 13:58:55 +00:00
|
|
|
number=1,
|
|
|
|
|
presence=models.SpecificInterval(((0, 2.5), )),
|
|
|
|
|
virus=mc.SARSCoV2(
|
|
|
|
|
viral_load_in_sputum=10**9,
|
2024-03-14 15:19:38 +00:00
|
|
|
infectious_dose=infectious_dose_distribution(),
|
|
|
|
|
viable_to_RNA_ratio=viable_to_RNA_ratio_distribution(),
|
2021-11-08 13:58:55 +00:00
|
|
|
transmissibility_factor=1.,
|
|
|
|
|
),
|
|
|
|
|
mask=models.Mask.types['No mask'],
|
2023-11-29 07:54:58 +00:00
|
|
|
activity=activity_distributions(data_registry)['Moderate activity'],
|
|
|
|
|
expiration=build_expiration(data_registry, 'Shouting'),
|
2021-11-08 13:58:55 +00:00
|
|
|
host_immunity=0.,
|
|
|
|
|
),
|
|
|
|
|
evaporation_factor=0.3,
|
|
|
|
|
)
|
|
|
|
|
return mc.ExposureModel(
|
2023-12-11 17:00:31 +00:00
|
|
|
data_registry=data_registry,
|
2021-11-08 13:58:55 +00:00
|
|
|
concentration_model=concentration_mc,
|
2022-03-29 10:19:14 +00:00
|
|
|
short_range=(),
|
2021-11-08 13:58:55 +00:00
|
|
|
exposed=mc.Population(
|
|
|
|
|
number=60,
|
|
|
|
|
presence=models.SpecificInterval(((0, 2.5), )),
|
2023-11-29 07:54:58 +00:00
|
|
|
activity=activity_distributions(data_registry)['Moderate activity'],
|
2021-11-08 13:58:55 +00:00
|
|
|
mask=models.Mask.types['No mask'],
|
|
|
|
|
host_immunity=0.,
|
2022-09-21 07:14:44 +00:00
|
|
|
),
|
|
|
|
|
geographical_data=models.Cases(),
|
2021-11-08 13:58:55 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
2023-11-29 07:54:58 +00:00
|
|
|
def bus_ride_mc(data_registry):
|
2021-11-08 13:58:55 +00:00
|
|
|
"""
|
2023-11-29 07:54:58 +00:00
|
|
|
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.
|
2021-11-08 13:58:55 +00:00
|
|
|
"""
|
|
|
|
|
concentration_mc = mc.ConcentrationModel(
|
2023-11-29 07:54:58 +00:00
|
|
|
data_registry=data_registry,
|
2021-11-08 13:58:55 +00:00
|
|
|
room=models.Room(volume=45, humidity=0.5),
|
|
|
|
|
ventilation=models.AirChange(
|
|
|
|
|
active=models.PeriodicInterval(period=120, duration=120),
|
|
|
|
|
air_exch=1.25),
|
|
|
|
|
infected=mc.InfectedPopulation(
|
2023-11-29 07:54:58 +00:00
|
|
|
data_registry=data_registry,
|
2021-11-08 13:58:55 +00:00
|
|
|
number=1,
|
|
|
|
|
presence=models.SpecificInterval(((0, 1.67), )),
|
|
|
|
|
virus=mc.SARSCoV2(
|
|
|
|
|
viral_load_in_sputum=5*10**8,
|
2024-03-14 15:19:38 +00:00
|
|
|
infectious_dose=infectious_dose_distribution(),
|
|
|
|
|
viable_to_RNA_ratio=viable_to_RNA_ratio_distribution(),
|
2021-11-08 13:58:55 +00:00
|
|
|
transmissibility_factor=1.,
|
|
|
|
|
),
|
|
|
|
|
mask=models.Mask.types['No mask'],
|
2023-11-29 07:54:58 +00:00
|
|
|
activity=activity_distributions(data_registry)['Seated'],
|
|
|
|
|
expiration=build_expiration(data_registry, 'Speaking'),
|
2021-11-08 13:58:55 +00:00
|
|
|
host_immunity=0.,
|
|
|
|
|
),
|
|
|
|
|
evaporation_factor=0.3,
|
|
|
|
|
)
|
|
|
|
|
return mc.ExposureModel(
|
2023-12-11 17:00:31 +00:00
|
|
|
data_registry=data_registry,
|
2021-11-08 13:58:55 +00:00
|
|
|
concentration_model=concentration_mc,
|
2022-03-29 10:19:14 +00:00
|
|
|
short_range=(),
|
2021-11-08 13:58:55 +00:00
|
|
|
exposed=mc.Population(
|
|
|
|
|
number=67,
|
|
|
|
|
presence=models.SpecificInterval(((0, 1.67), )),
|
2023-11-29 07:54:58 +00:00
|
|
|
activity=activity_distributions(data_registry)['Seated'],
|
2021-11-08 13:58:55 +00:00
|
|
|
mask=models.Mask.types['No mask'],
|
|
|
|
|
host_immunity=0.,
|
2022-09-21 07:14:44 +00:00
|
|
|
),
|
|
|
|
|
geographical_data=models.Cases(),
|
2021-11-08 13:58:55 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
2021-06-04 09:58:23 +00:00
|
|
|
@pytest.fixture
|
2023-11-29 07:54:58 +00:00
|
|
|
def gym_mc(data_registry):
|
2021-06-04 09:58:23 +00:00
|
|
|
"""
|
2021-11-08 13:58:55 +00:00
|
|
|
Gym model for testing
|
2021-06-04 09:58:23 +00:00
|
|
|
"""
|
2021-06-04 07:24:38 +00:00
|
|
|
concentration_mc = mc.ConcentrationModel(
|
2023-11-29 07:54:58 +00:00
|
|
|
data_registry=data_registry,
|
2021-06-04 07:24:38 +00:00
|
|
|
room=models.Room(volume=300, humidity=0.5),
|
|
|
|
|
ventilation=models.AirChange(
|
2021-08-05 13:48:24 +00:00
|
|
|
active=models.SpecificInterval(((0., 24.),)),
|
2021-06-04 07:24:38 +00:00
|
|
|
air_exch=6,
|
|
|
|
|
),
|
|
|
|
|
infected=mc.InfectedPopulation(
|
2023-11-29 07:54:58 +00:00
|
|
|
data_registry=data_registry,
|
2021-06-04 07:24:38 +00:00
|
|
|
number=2,
|
2023-11-29 07:54:58 +00:00
|
|
|
virus=virus_distributions(data_registry)['SARS_CoV_2_ALPHA'],
|
2021-08-05 13:48:24 +00:00
|
|
|
presence=mc.SpecificInterval(((0., 1.),)),
|
2021-06-04 07:24:38 +00:00
|
|
|
mask=models.Mask.types["No mask"],
|
2023-11-29 07:54:58 +00:00
|
|
|
activity=activity_distributions(data_registry)['Heavy exercise'],
|
2023-12-11 17:00:31 +00:00
|
|
|
expiration=expiration_distributions(data_registry)['Breathing'],
|
2021-09-17 14:36:07 +00:00
|
|
|
host_immunity=0.,
|
2021-06-04 07:24:38 +00:00
|
|
|
),
|
2021-10-05 13:44:37 +00:00
|
|
|
evaporation_factor=0.3,
|
2021-06-04 07:24:38 +00:00
|
|
|
)
|
2021-06-04 09:58:23 +00:00
|
|
|
return mc.ExposureModel(
|
2023-12-11 17:00:31 +00:00
|
|
|
data_registry=data_registry,
|
2021-06-04 07:24:38 +00:00
|
|
|
concentration_model=concentration_mc,
|
2022-03-29 10:19:14 +00:00
|
|
|
short_range=(),
|
2021-06-04 07:24:38 +00:00
|
|
|
exposed=mc.Population(
|
|
|
|
|
number=28,
|
|
|
|
|
presence=concentration_mc.infected.presence,
|
|
|
|
|
activity=models.Activity.types['Heavy exercise'],
|
|
|
|
|
mask=concentration_mc.infected.mask,
|
2021-09-22 09:51:09 +00:00
|
|
|
host_immunity=0.,
|
2021-06-04 07:24:38 +00:00
|
|
|
),
|
2022-09-21 07:14:44 +00:00
|
|
|
geographical_data=models.Cases(),
|
2021-06-04 07:24:38 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
2021-06-04 09:58:23 +00:00
|
|
|
@pytest.fixture
|
2023-11-29 07:54:58 +00:00
|
|
|
def waiting_room_mc(data_registry):
|
2021-06-04 09:58:23 +00:00
|
|
|
"""
|
2021-11-08 13:58:55 +00:00
|
|
|
Waiting room model for testing
|
2021-06-04 09:58:23 +00:00
|
|
|
"""
|
2021-06-04 07:24:38 +00:00
|
|
|
concentration_mc = mc.ConcentrationModel(
|
2023-11-29 07:54:58 +00:00
|
|
|
data_registry=data_registry,
|
2021-06-04 07:24:38 +00:00
|
|
|
room=models.Room(volume=100, humidity=0.5),
|
|
|
|
|
ventilation=models.AirChange(
|
2021-08-05 13:48:24 +00:00
|
|
|
active=models.SpecificInterval(((0., 24.),)),
|
2021-06-04 07:24:38 +00:00
|
|
|
air_exch=0.25,
|
|
|
|
|
),
|
|
|
|
|
infected=mc.InfectedPopulation(
|
2023-11-29 07:54:58 +00:00
|
|
|
data_registry=data_registry,
|
2021-06-04 07:24:38 +00:00
|
|
|
number=1,
|
2023-11-29 07:54:58 +00:00
|
|
|
virus=virus_distributions(data_registry)['SARS_CoV_2_ALPHA'],
|
2021-08-05 13:48:24 +00:00
|
|
|
presence=mc.SpecificInterval(((0., 2.),)),
|
2021-06-04 07:24:38 +00:00
|
|
|
mask=models.Mask.types["No mask"],
|
2023-11-29 07:54:58 +00:00
|
|
|
activity=activity_distributions(data_registry)['Seated'],
|
|
|
|
|
expiration=build_expiration(data_registry, {'Speaking': 0.3, 'Breathing': 0.7}),
|
2021-09-17 14:36:07 +00:00
|
|
|
host_immunity=0.,
|
2021-06-04 07:24:38 +00:00
|
|
|
),
|
2021-10-05 13:44:37 +00:00
|
|
|
evaporation_factor=0.3,
|
2021-06-04 07:24:38 +00:00
|
|
|
)
|
2021-06-04 09:58:23 +00:00
|
|
|
return mc.ExposureModel(
|
2023-12-11 17:00:31 +00:00
|
|
|
data_registry=data_registry,
|
2021-06-04 07:24:38 +00:00
|
|
|
concentration_model=concentration_mc,
|
2022-03-29 10:19:14 +00:00
|
|
|
short_range=(),
|
2021-06-04 07:24:38 +00:00
|
|
|
exposed=mc.Population(
|
|
|
|
|
number=14,
|
|
|
|
|
presence=concentration_mc.infected.presence,
|
|
|
|
|
activity=models.Activity.types['Seated'],
|
|
|
|
|
mask=concentration_mc.infected.mask,
|
2021-09-22 09:51:09 +00:00
|
|
|
host_immunity=0.,
|
2021-06-04 07:24:38 +00:00
|
|
|
),
|
2022-09-21 07:14:44 +00:00
|
|
|
geographical_data=models.Cases(),
|
2021-06-04 07:24:38 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
2023-12-01 06:35:07 +00:00
|
|
|
# In the following tests all the initial expected values for emission
|
|
|
|
|
# rate were multiplied by the average of the distribution of fraction
|
|
|
|
|
# of infected viruses (0.305, for a uniform distribution from 0.01 to 0.6)
|
|
|
|
|
# following the change of convention that this ratio should be
|
|
|
|
|
# applied at the emission level.
|
2022-05-31 15:44:12 +00:00
|
|
|
@retry(tries=10)
|
2021-06-04 09:58:23 +00:00
|
|
|
@pytest.mark.parametrize(
|
2023-04-06 08:22:53 +00:00
|
|
|
"mc_model, expected_pi, expected_new_cases, expected_dose, expected_ER_per_person",
|
2021-06-04 09:58:23 +00:00
|
|
|
[
|
2023-12-01 06:35:07 +00:00
|
|
|
["shared_office_mc", 5.38, 0.16, 3.350, 1056*0.305],
|
|
|
|
|
["classroom_mc", 8.21, 1.56, 11.356, 7416*0.305],
|
|
|
|
|
["ski_cabin_mc", 12.92, 0.39, 21.796, 10231*0.305],
|
|
|
|
|
["skagit_chorale_mc",61.01, 36.53, 84.730, 190422*0.305],
|
|
|
|
|
["bus_ride_mc", 10.59, 7.06, 6.650, 5419*0.305],
|
|
|
|
|
["gym_mc", 0.52, 0.14, 0.249, 1450*0.305/2.], # there are two infected in this case
|
|
|
|
|
["waiting_room_mc", 1.53, 0.21, 0.844, 929*0.305],
|
2021-06-04 09:58:23 +00:00
|
|
|
]
|
|
|
|
|
)
|
|
|
|
|
def test_report_models(mc_model, expected_pi, expected_new_cases,
|
2023-04-06 08:22:53 +00:00
|
|
|
expected_dose, expected_ER_per_person, request):
|
2021-06-04 09:58:23 +00:00
|
|
|
mc_model = request.getfixturevalue(mc_model)
|
|
|
|
|
exposure_model = mc_model.build_model(size=SAMPLE_SIZE)
|
2021-06-04 07:24:38 +00:00
|
|
|
npt.assert_allclose(exposure_model.infection_probability().mean(),
|
2021-06-04 09:58:23 +00:00
|
|
|
expected_pi, rtol=TOLERANCE)
|
2021-06-04 07:24:38 +00:00
|
|
|
npt.assert_allclose(exposure_model.expected_new_cases().mean(),
|
2021-06-04 09:58:23 +00:00
|
|
|
expected_new_cases, rtol=TOLERANCE)
|
2022-02-03 14:14:33 +00:00
|
|
|
npt.assert_allclose(exposure_model.deposited_exposure().mean(),
|
2021-06-04 09:58:23 +00:00
|
|
|
expected_dose, rtol=TOLERANCE)
|
|
|
|
|
npt.assert_allclose(
|
2023-04-06 08:22:53 +00:00
|
|
|
exposure_model.concentration_model.infected.emission_rate_per_person_when_present().mean(),
|
|
|
|
|
expected_ER_per_person, rtol=TOLERANCE)
|
2021-06-04 07:24:38 +00:00
|
|
|
|
|
|
|
|
|
2022-06-02 14:35:17 +00:00
|
|
|
@retry(tries=10)
|
2021-06-04 07:24:38 +00:00
|
|
|
@pytest.mark.parametrize(
|
2021-08-06 08:24:25 +00:00
|
|
|
"mask_type, month, expected_pi, expected_dose, expected_ER",
|
2021-06-04 07:24:38 +00:00
|
|
|
[
|
2023-12-01 06:35:07 +00:00
|
|
|
["No mask", "Jul", 7.689, 10.050, 1034.435*0.305],
|
|
|
|
|
["Type I", "Jul", 1.663, 0.938, 193.52*0.305],
|
|
|
|
|
["FFP2", "Jul", 0.523, 0.253, 193.52*0.305],
|
|
|
|
|
["Type I", "Feb", 0.659, 0.325, 193.52*0.305],
|
|
|
|
|
["Cloth", "Feb", 2.653, 1.741, 673.10*0.305],
|
|
|
|
|
["Cloth", "Jul", 5.322, 5.064, 673.10*0.305],
|
2021-06-04 07:24:38 +00:00
|
|
|
],
|
|
|
|
|
)
|
2023-11-29 07:54:58 +00:00
|
|
|
def test_small_shared_office_Geneva(data_registry, mask_type, month, expected_pi,
|
2022-03-29 10:19:14 +00:00
|
|
|
expected_dose, expected_ER):
|
2021-06-04 07:24:38 +00:00
|
|
|
concentration_mc = mc.ConcentrationModel(
|
2023-11-29 07:54:58 +00:00
|
|
|
data_registry=data_registry,
|
2022-04-22 13:20:20 +00:00
|
|
|
room=models.Room(volume=33, inside_temp=models.PiecewiseConstant((0., 24.), (293,)), humidity=0.5),
|
2021-06-04 07:24:38 +00:00
|
|
|
ventilation=models.MultipleVentilation(
|
|
|
|
|
(
|
|
|
|
|
models.SlidingWindow(
|
2023-11-29 07:54:58 +00:00
|
|
|
data_registry=data_registry,
|
2021-08-05 13:48:24 +00:00
|
|
|
active=models.SpecificInterval(((0., 24.),)),
|
2021-06-04 07:24:38 +00:00
|
|
|
outside_temp=data.GenevaTemperatures[month],
|
|
|
|
|
window_height=1.5, opening_length=0.2,
|
|
|
|
|
),
|
|
|
|
|
models.AirChange(
|
2021-08-05 13:48:24 +00:00
|
|
|
active=models.SpecificInterval(((0., 24.),)),
|
2021-06-04 07:24:38 +00:00
|
|
|
air_exch=0.25,
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
infected=mc.InfectedPopulation(
|
2023-11-29 07:54:58 +00:00
|
|
|
data_registry=data_registry,
|
2021-06-04 07:24:38 +00:00
|
|
|
number=1,
|
2023-11-29 07:54:58 +00:00
|
|
|
virus=virus_distributions(data_registry)['SARS_CoV_2_ALPHA'],
|
2021-08-05 13:48:24 +00:00
|
|
|
presence=mc.SpecificInterval(((9., 10+2/3), (10+5/6, 12.5), (13.5, 15+2/3), (15+5/6, 18.))),
|
2021-06-04 07:24:38 +00:00
|
|
|
mask=models.Mask.types[mask_type],
|
2023-11-29 07:54:58 +00:00
|
|
|
activity=activity_distributions(data_registry)['Seated'],
|
|
|
|
|
expiration=build_expiration(data_registry, {'Speaking': 0.33, 'Breathing': 0.67}),
|
2021-09-17 14:36:07 +00:00
|
|
|
host_immunity=0.,
|
2021-06-04 07:24:38 +00:00
|
|
|
),
|
2021-10-05 13:44:37 +00:00
|
|
|
evaporation_factor=0.3,
|
2021-06-04 07:24:38 +00:00
|
|
|
)
|
|
|
|
|
exposure_mc = mc.ExposureModel(
|
2023-12-12 10:58:19 +00:00
|
|
|
data_registry=data_registry,
|
2021-06-04 07:24:38 +00:00
|
|
|
concentration_model=concentration_mc,
|
2022-03-29 10:19:14 +00:00
|
|
|
short_range=(),
|
2021-06-04 07:24:38 +00:00
|
|
|
exposed=mc.Population(
|
|
|
|
|
number=1,
|
|
|
|
|
presence=concentration_mc.infected.presence,
|
2023-11-29 07:54:58 +00:00
|
|
|
activity=activity_distributions(data_registry)['Seated'],
|
2021-06-04 07:24:38 +00:00
|
|
|
mask=concentration_mc.infected.mask,
|
2021-09-22 09:51:09 +00:00
|
|
|
host_immunity=0.,
|
2021-06-04 07:24:38 +00:00
|
|
|
),
|
2022-09-21 07:14:44 +00:00
|
|
|
geographical_data=models.Cases(),
|
2021-06-04 07:24:38 +00:00
|
|
|
)
|
|
|
|
|
exposure_model = exposure_mc.build_model(size=SAMPLE_SIZE)
|
|
|
|
|
npt.assert_allclose(exposure_model.infection_probability().mean(),
|
2021-06-04 09:58:23 +00:00
|
|
|
expected_pi, rtol=TOLERANCE)
|
2022-02-03 14:14:33 +00:00
|
|
|
npt.assert_allclose(exposure_model.deposited_exposure().mean(),
|
2021-06-04 09:58:23 +00:00
|
|
|
expected_dose, rtol=TOLERANCE)
|
|
|
|
|
npt.assert_allclose(
|
2023-04-06 08:22:53 +00:00
|
|
|
exposure_model.concentration_model.infected.emission_rate_per_person_when_present().mean(),
|
2021-08-06 08:24:25 +00:00
|
|
|
expected_ER, rtol=TOLERANCE)
|