diff --git a/README.md b/README.md
index 516dc81b..1be26e55 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
CARA is a risk assessment tool developed to model the concentration of viruses in enclosed spaces, in order to inform space-management decisions.
-CARA models the concentration profile of potential infectious viruses in enclosed spaces with clear and intuitive graphs.
+CARA models the concentration profile of potential virions in enclosed spaces with clear and intuitive graphs.
The user can set a number of parameters, including room volume, exposure time, activity type, mask-wearing and ventilation.
The report generated indicates how to avoid exceeding critical concentrations and chains of airborne transmission in spaces such as individual offices, meeting rooms and labs.
diff --git a/cara/apps/calculator/report_generator.py b/cara/apps/calculator/report_generator.py
index 93c7ff53..d10ca634 100644
--- a/cara/apps/calculator/report_generator.py
+++ b/cara/apps/calculator/report_generator.py
@@ -116,7 +116,7 @@ def plot(times, concentrations, model: models.ExposureModel):
ax.set_xlabel('Time of day')
ax.set_ylabel('Mean concentration ($q/m^3$)')
- ax.set_title('Mean concentration of infectious quanta')
+ ax.set_title('Mean concentration of virions')
ax.xaxis.set_major_formatter(matplotlib.dates.DateFormatter("%H:%M"))
# Plot presence of exposed person
@@ -237,7 +237,7 @@ def comparison_plot(scenarios: typing.Dict[str, dict], sample_times: np.ndarray)
ax.set_xlabel('Time of day')
ax.set_ylabel('Mean concentration ($q/m^3$)')
- ax.set_title('Mean concentration of infectious quanta')
+ ax.set_title('Mean concentration of virions')
return fig
diff --git a/cara/apps/calculator/templates/base/calculator.report.html.j2 b/cara/apps/calculator/templates/base/calculator.report.html.j2
index 1ddaf02a..d45a089a 100644
--- a/cara/apps/calculator/templates/base/calculator.report.html.j2
+++ b/cara/apps/calculator/templates/base/calculator.report.html.j2
@@ -257,7 +257,7 @@
Notes for alternative scenarios:
- - This graph shows the concentration of infectious quanta in the air. The filtration of Type I and FFP2 masks, if worn, applies not only to the emission rate but also to the individual exposure (i.e. inhalation).
+
- This graph shows the concentration of virions in the air. The filtration of Type I and FFP2 masks, if worn, applies not only to the emission rate but also to the individual exposure (i.e. inhalation).
For this reason, scenarios with different types of mask will show the same concentration on the graph but have different absorbed doses and infection probabilities.
- If you have selected more sophisticated options, such as HEPA filtration or FFP2 masks, alternatives will be indicated in the plot as the "base scenario with/without...", representing a variation on the inputs inserted in the form.
The other alternative scenarios shown for comparison will not include either HEPA filtration or FFP2 masks.
@@ -288,7 +288,7 @@
CARA is a risk assessment tool developed to model the concentration of viruses in enclosed spaces, in order to inform space-management decisions.
- CARA models the concentration profile of potential infectious viruses in enclosed spaces with clear and intuitive graphs.
+ CARA models the concentration profile of potential virions in enclosed spaces with clear and intuitive graphs.
The user can set a number of parameters, including room volume, exposure time, activity type, mask-wearing and ventilation.
The report generated indicates how to avoid exceeding critical concentrations and chains of airborne transmission in spaces such as individual offices, meeting rooms and labs.
diff --git a/cara/apps/calculator/templates/calculator.form.html.j2 b/cara/apps/calculator/templates/calculator.form.html.j2
index 1e38f217..712432fb 100644
--- a/cara/apps/calculator/templates/calculator.form.html.j2
+++ b/cara/apps/calculator/templates/calculator.form.html.j2
@@ -380,7 +380,7 @@ v{{ calculator_version }} Please sen
CARA is a risk assessment tool developed to model the concentration of viruses in enclosed spaces, in order to inform space-management decisions.
- CARA models the concentration profile of potential infectious viruses in enclosed spaces with clear and intuitive graphs.
+ CARA models the concentration profile of virions in enclosed spaces with clear and intuitive graphs.
The user can set a number of parameters, including room volume, exposure time, activity type, mask-wearing and ventilation.
The report generated indicates how to avoid exceeding critical concentrations and chains of airborne transmission in spaces such as individual offices, meeting rooms and labs.
diff --git a/cara/apps/calculator/templates/userguide.html.j2 b/cara/apps/calculator/templates/userguide.html.j2
index 4ddf3827..ede8c8e3 100644
--- a/cara/apps/calculator/templates/userguide.html.j2
+++ b/cara/apps/calculator/templates/userguide.html.j2
@@ -15,7 +15,7 @@ If you are using the expert version of the tool, you should look at the expert
CARA is a risk assessment tool developed to model the concentration of viruses in enclosed spaces, in order to inform space-management decisions.
- CARA models the concentration profile of potential infectious viruses in enclosed spaces with clear and intuitive graphs.
+ CARA models the concentration profile of potential virions in enclosed spaces with clear and intuitive graphs.
The user can set a number of parameters, including room volume, exposure time, activity type, mask-wearing and ventilation.
The report generated indicates how to avoid exceeding critical concentrations and chains of airborne transmission in spaces such as individual offices, meeting rooms and labs.
@@ -183,15 +183,15 @@ It is estimated based on the emission rate of virus into the simulated volume, a
This probability is valid for the simulation duration - i.e. the start and end time.
If you are using the natural ventilation option, the simulation is only valid for the selected month, because the following or preceding month will have a different average temperature profile.
The expected number of new cases for the simulation is calculated based on the probability of infection, multiplied by the number of exposed occupants.
-The graph shows the variation in the concentration of infectious viruses within the simulated volume.
+
The graph shows the variation in the concentration of virions within the simulated volume.
It is determined by:
- The presence of the infected person, who emits airborne viruses in the volume.
- The emission rate is related to the type of activity of the infected person (sitting, light exercise), their level of vocalisation (breathing, talking or shouting).
-- The accumulation of infectious quanta in the volume, which is driven, among other factors, by ventilation (if applicable).
+- The accumulation of virions in the volume, which is driven, among other factors, by ventilation (if applicable).
- In a mechanical ventilation scenario, the removal rate is constant, based on fresh airflow supply in and out of the simulated space.
- Under natural ventilation conditions, the effectiveness of ventilation relies upon the hourly temperature difference between the inside and outside air temperature.
-- A HEPA filter removes infectious virus from the air at a constant rate and is modelled in the same way as mechanical ventilation, however air passed through a HEPA filter is recycled (i.e. it is not fresh air).
+- A HEPA filter removes virions from the air at a constant rate and is modelled in the same way as mechanical ventilation, however air passed through a HEPA filter is recycled (i.e. it is not fresh air).
diff --git a/cara/apps/expert.py b/cara/apps/expert.py
index 7a324a7c..66fe01d2 100644
--- a/cara/apps/expert.py
+++ b/cara/apps/expert.py
@@ -141,7 +141,7 @@ class ExposureModelResult(View):
ax.set_xlabel('Time (hours)')
ax.set_ylabel('Concentration ($q/m^3$)')
- ax.set_title('Concentration of infectious quanta')
+ ax.set_title('Concentration of virions')
else:
self.ax.ignore_existing_data_limits = True
self.line.set_data(ts, concentration)
@@ -154,7 +154,7 @@ class ExposureModelResult(View):
def update_textual_result(self, model: models.ExposureModel):
lines = []
P = model.infection_probability()
- lines.append(f'Emission rate (quanta/hr): {np.round(model.concentration_model.infected.emission_rate_when_present(),0)}')
+ lines.append(f'Emission rate (virus/hr): {np.round(model.concentration_model.infected.emission_rate_when_present(),0)}')
lines.append(f'Probability of infection: {np.round(P, 0)}%')
lines.append(f'Number of exposed: {model.exposed.number}')
@@ -186,7 +186,7 @@ class ExposureComparissonResult(View):
ax.spines['top'].set_visible(False)
ax.set_xlabel('Time (hours)')
ax.set_ylabel('Concentration ($q/m^3$)')
- ax.set_title('Concentration of infectious quanta')
+ ax.set_title('Concentration of virions')
return ax
def scenarios_updated(self, scenarios: typing.Sequence[ScenarioType], _):
diff --git a/cara/apps/templates/about.html.j2 b/cara/apps/templates/about.html.j2
index 9ca990c2..8bb5cde9 100644
--- a/cara/apps/templates/about.html.j2
+++ b/cara/apps/templates/about.html.j2
@@ -24,7 +24,7 @@ The model used is based on scientific publications relating to airborne transmis
The tool helps assess the potential dose of infectious airborne viruses in indoor gatherings, with people seated, standing, moving around, while breathing, speaking or shouting/singing. The model is based on the Wells-Riley model of aerosol disease transmission, which assumes a fixed value for the average infectious dose. The dose-response models for respiratory diseases is more accurate, although since this parameter for SARS-CoV-2 is not known so far, the Wells-Riley method is recommended in the health science community (see References).
The methodology of the model is divided into three parts:
- - Estimating the emission rate of infectious viruses.
+ - Estimating the emission rate of virions.
- Modeling the concentration evolution of viruses within a given volume and consequent inhalation dose during the exposure time.
- Estimating the probability of a COVID-19 infection, the expected number of new cases arising from the transmission event and the basic reproduction rate (R0).
diff --git a/cara/models.py b/cara/models.py
index 585fb824..240bac76 100644
--- a/cara/models.py
+++ b/cara/models.py
@@ -420,8 +420,8 @@ class Virus:
#: RNA copies / mL
viral_load_in_sputum: _VectorisedFloat
- #: RNA-copies per quantum
- quantum_infectious_dose: _VectorisedFloat
+ #: Dose to initiate infection, in RNA copies
+ infectious_dose: _VectorisedFloat
#: Pre-populated examples of Viruses.
types: typing.ClassVar[typing.Dict[str, "Virus"]]
@@ -458,20 +458,20 @@ Virus.types = {
# It is somewhere between 1000 or 10 SARS-CoV viruses,
# as per https://www.dhs.gov/publication/st-master-question-list-covid-19
# 50 comes from Buonanno et al.
- quantum_infectious_dose=50.,
+ infectious_dose=50.,
),
'SARS_CoV_2_B117': SARSCoV2(
# also called VOC-202012/01
viral_load_in_sputum=1e9,
- quantum_infectious_dose=30.,
+ infectious_dose=30.,
),
'SARS_CoV_2_P1': SARSCoV2(
viral_load_in_sputum=1e9,
- quantum_infectious_dose=1/0.045,
+ infectious_dose=1/0.045,
),
'SARS_CoV_2_B16172': SARSCoV2(
viral_load_in_sputum=1e9,
- quantum_infectious_dose=30/1.6,
+ infectious_dose=30/1.6,
),
}
@@ -677,7 +677,7 @@ class InfectedPopulation(Population):
Note that the rate is not currently time-dependent.
"""
- # Emission Rate (infectious quantum / h)
+ # Emission Rate (virions / h)
# Note on units: exhalation rate is in m^3/h, aerosols in mL/cm^3
# and viral load in virus/mL -> 1e6 conversion factor
aerosols = self.expiration.aerosols(self.mask)
@@ -685,15 +685,14 @@ class InfectedPopulation(Population):
ER = (self.virus.viral_load_in_sputum *
self.activity.exhalation_rate *
10 ** 6 *
- aerosols /
- self.virus.quantum_infectious_dose)
+ aerosols)
# For superspreading event, where ejection_factor is infinite we fix the ER
# based on Miller et al. (2020).
if isinstance(aerosols, np.ndarray):
- ER[np.isinf(aerosols)] = 970
+ ER[np.isinf(aerosols)] = 970 * self.virus.infectious_dose
elif np.isinf(aerosols):
- ER = 970
+ ER = 970 * self.virus.infectious_dose
return ER
@@ -804,7 +803,7 @@ class ConcentrationModel:
def concentration(self, time: float) -> _VectorisedFloat:
"""
- Virus quanta concentration, as a function of time.
+ Virus exposure concentration, as a function of time.
The formulas used here assume that all parameters (ventilation,
emission rate) are constant between two state changes - only
the value of these parameters at the next state change, are used.
@@ -867,8 +866,8 @@ class ExposureModel:
#: The fraction of viruses actually deposited in the respiratory tract
fraction_deposited: _VectorisedFloat = 0.6
- def quanta_exposure(self) -> _VectorisedFloat:
- """The number of virus quanta per meter^3."""
+ def exposure(self) -> _VectorisedFloat:
+ """The number of virus per meter^3."""
exposure = 0.0
for start, stop in self.exposed.presence.boundaries():
@@ -877,7 +876,7 @@ class ExposureModel:
return exposure * self.repeats
def infection_probability(self) -> _VectorisedFloat:
- exposure = self.quanta_exposure()
+ exposure = self.exposure()
inf_aero = (
self.exposed.activity.inhalation_rate *
@@ -886,7 +885,7 @@ class ExposureModel:
)
# Probability of infection.
- return (1 - np.exp(-inf_aero)) * 100
+ return (1 - np.exp(-(inf_aero/self.concentration_model.virus.infectious_dose))) * 100
def expected_new_cases(self) -> _VectorisedFloat:
prob = self.infection_probability()
diff --git a/cara/monte_carlo/data.py b/cara/monte_carlo/data.py
index 57c7ebba..613b830c 100644
--- a/cara/monte_carlo/data.py
+++ b/cara/monte_carlo/data.py
@@ -43,18 +43,18 @@ symptomatic_vl_frequencies = LogCustomKernel(
virus_distributions = {
'SARS_CoV_2': mc.SARSCoV2(
viral_load_in_sputum=symptomatic_vl_frequencies,
- quantum_infectious_dose=100,
+ infectious_dose=100,
),
'SARS_CoV_2_B117': mc.SARSCoV2(
viral_load_in_sputum=symptomatic_vl_frequencies,
- quantum_infectious_dose=60,
+ infectious_dose=60,
),
'SARS_CoV_2_P1': mc.SARSCoV2(
viral_load_in_sputum=symptomatic_vl_frequencies,
- quantum_infectious_dose=100/2.25,
+ infectious_dose=100/2.25,
),
'SARS_CoV_2_B16172': mc.SARSCoV2(
viral_load_in_sputum=symptomatic_vl_frequencies,
- quantum_infectious_dose=60/1.6,
+ infectious_dose=60/1.6,
),
}
diff --git a/cara/tests/models/test_concentration_model.py b/cara/tests/models/test_concentration_model.py
index 26707236..370ec24d 100644
--- a/cara/tests/models/test_concentration_model.py
+++ b/cara/tests/models/test_concentration_model.py
@@ -13,7 +13,6 @@ from cara import models
{'humidity': np.array([0.5, 0.4])},
{'air_change': np.array([100, 120])},
{'viral_load_in_sputum': np.array([5e8, 1e9])},
- {'quantum_infectious_dose': np.array([50, 20])},
]
)
def test_concentration_model_vectorisation(override_params):
@@ -21,8 +20,7 @@ def test_concentration_model_vectorisation(override_params):
'volume': 75,
'humidity': 0.5,
'air_change': 100,
- 'viral_load_in_sputum': 1e9,
- 'quantum_infectious_dose': 50,
+ 'viral_load_in_sputum': 1e9
}
defaults.update(override_params)
@@ -43,7 +41,7 @@ def test_concentration_model_vectorisation(override_params):
),
virus=models.SARSCoV2(
viral_load_in_sputum=defaults['viral_load_in_sputum'],
- quantum_infectious_dose=defaults['quantum_infectious_dose'],
+ infectious_dose=50.,
),
expiration=models.Expiration((1., 0., 0.)),
)
diff --git a/cara/tests/models/test_exposure_model.py b/cara/tests/models/test_exposure_model.py
index 1c6c6d3d..d8d80b40 100644
--- a/cara/tests/models/test_exposure_model.py
+++ b/cara/tests/models/test_exposure_model.py
@@ -3,26 +3,27 @@ import typing
import numpy as np
import numpy.testing
import pytest
+from dataclasses import dataclass
from cara import models
from cara.models import ExposureModel
+@dataclass(frozen=True)
class KnownConcentrations(models.ConcentrationModel):
"""
- A ConcentrationModel which is based on pre-known quanta concentrations and
+ A ConcentrationModel which is based on pre-known exposure concentrations and
which therefore doesn't need other components. Useful for testing.
"""
- def __init__(self, concentration_function: typing.Callable) -> None:
- self._func = concentration_function
+ concentration_function: typing.Callable
def infectious_virus_removal_rate(self, time: float) -> models._VectorisedFloat:
# very large decay constant -> same as constant concentration
return 1.e50
def _concentration_limit(self, time: float) -> models._VectorisedFloat:
- return self._func(time)
+ return self.concentration_function(time)
def state_change_times(self):
return [0, 24]
@@ -31,7 +32,7 @@ class KnownConcentrations(models.ConcentrationModel):
return 24
def concentration(self, time: float) -> models._VectorisedFloat: # noqa
- return self._func(time)
+ return self.concentration_function(time)
halftime = models.PeriodicInterval(120, 60)
@@ -49,33 +50,47 @@ populations = [
# A population with some array component for inhalation_rate.
models.Population(
10, halftime, models.Mask.types['Type I'],
- models.Activity(np.array([0.51,0.57]), 0.57),
+ models.Activity(np.array([0.51, 0.57]), 0.57),
),
]
+dummy_room = models.Room(50, 0.5)
+dummy_ventilation = models._VentilationBase()
+dummy_infected_population = models.InfectedPopulation(
+ number=1,
+ presence=halftime,
+ mask=models.Mask.types['Type I'],
+ activity=models.Activity.types['Standing'],
+ virus=models.Virus.types['SARS_CoV_2_B117'],
+ expiration=models.Expiration.types['Talking']
+)
+
+
+def known_concentrations(func):
+ return KnownConcentrations(dummy_room, dummy_ventilation, dummy_infected_population, func)
@pytest.mark.parametrize(
- "population, cm, f_dep, expected_exposure, expected_probability",[
- [populations[1], KnownConcentrations(lambda t: 1.2), 1.,
- np.array([14.4, 14.4]), np.array([99.6803184113, 99.5181053773])],
+ "population, cm, f_dep, expected_exposure, expected_probability", [
+ [populations[1], known_concentrations(lambda t: 36), 1.,
+ np.array([432, 432]), np.array([99.6803184113, 99.5181053773])],
- [populations[2], KnownConcentrations(lambda t: 1.2), 1.,
- np.array([14.4, 14.4]), np.array([97.4574432074, 98.3493482895])],
+ [populations[2], known_concentrations(lambda t: 36), 1.,
+ np.array([432, 432]), np.array([97.4574432074, 98.3493482895])],
- [populations[0], KnownConcentrations(lambda t: np.array([1.2, 2.4])), 1.,
- np.array([14.4, 28.8]), np.array([98.3493482895, 99.9727534893])],
+ [populations[0], known_concentrations(lambda t: np.array([36, 72])), 1.,
+ np.array([432, 864]), np.array([98.3493482895, 99.9727534893])],
- [populations[1], KnownConcentrations(lambda t: np.array([1.2, 2.4])), 1.,
- np.array([14.4, 28.8]), np.array([99.6803184113, 99.9976777757])],
+ [populations[1], known_concentrations(lambda t: np.array([36, 72])), 1.,
+ np.array([432, 864]), np.array([99.6803184113, 99.9976777757])],
- [populations[0], KnownConcentrations(lambda t: 2.4), np.array([0.5, 1.]),
- 28.8, np.array([98.3493482895, 99.9727534893])],
+ [populations[0], known_concentrations(lambda t: 72), np.array([0.5, 1.]),
+ 864, np.array([98.3493482895, 99.9727534893])],
])
def test_exposure_model_ndarray(population, cm, f_dep,
expected_exposure, expected_probability):
- model = ExposureModel(cm, population, fraction_deposited = f_dep)
+ model = ExposureModel(cm, population, fraction_deposited=f_dep)
np.testing.assert_almost_equal(
- model.quanta_exposure(), expected_exposure
+ model.exposure(), expected_exposure
)
np.testing.assert_almost_equal(
model.infection_probability(), expected_probability, decimal=10
@@ -89,12 +104,13 @@ def test_exposure_model_ndarray(population, cm, f_dep,
@pytest.mark.parametrize("population", populations)
def test_exposure_model_ndarray_and_float_mix(population):
- cm = KnownConcentrations(lambda t: 0 if np.floor(t) % 2 else np.array([1.2, 1.2]))
+ cm = known_concentrations(lambda t: 0 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
+ model.exposure(), expected_exposure
)
assert isinstance(model.infection_probability(), np.ndarray)
@@ -103,22 +119,23 @@ def test_exposure_model_ndarray_and_float_mix(population):
@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]))
+ cm_scalar = known_concentrations(lambda t: 1.2)
+ cm_array = known_concentrations(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
+ model_scalar.exposure(), expected_exposure
)
np.testing.assert_almost_equal(
- model_array.quanta_exposure(), np.array([expected_exposure]*2)
+ model_array.exposure(), np.array([expected_exposure]*2)
)
@pytest.fixture
def conc_model():
- interesting_times = models.SpecificInterval(([0, 1], [1.01, 1.02], [12, 24]))
+ interesting_times = models.SpecificInterval(
+ ([0, 1], [1.01, 1.02], [12, 24]))
always = models.SpecificInterval(((0, 24),))
return models.ConcentrationModel(
models.Room(25),
@@ -133,23 +150,51 @@ def conc_model():
)
)
-# expected quanta were computed with a trapezoidal integration, using
+# expected exposure were computed with a trapezoidal integration, using
# a mesh of 10'000 pts per exposed presence interval.
-@pytest.mark.parametrize("exposed_time_interval, expected_quanta", [
- [(0, 1), 5.3334352],
- [(1, 1.01), 0.061759078],
- [(1.01, 1.02), 0.060016487],
- [(12, 12.01), 0.0019012647],
- [(12, 24), 75.513005],
- [(0, 24), 81.956988],
- ]
+
+
+@pytest.mark.parametrize("exposed_time_interval, expected_exposure", [
+ [(0, 1), 266.67176],
+ [(1, 1.01), 3.0879539],
+ [(1.01, 1.02), 3.00082435],
+ [(12, 12.01), 0.095063235],
+ [(12, 24), 3775.65025],
+ [(0, 24), 4097.8494],
+]
)
def test_exposure_model_integral_accuracy(exposed_time_interval,
- expected_quanta, conc_model):
+ expected_exposure, conc_model):
presence_interval = models.SpecificInterval((exposed_time_interval,))
population = models.Population(
10, presence_interval, models.Mask.types['Type I'],
models.Activity.types['Standing'],
)
model = ExposureModel(conc_model, population, fraction_deposited=1.)
- np.testing.assert_allclose(model.quanta_exposure(), expected_quanta)
+ np.testing.assert_allclose(model.exposure(), expected_exposure)
+
+
+def test_infectious_dose_vectorisation():
+ infected_population = models.InfectedPopulation(
+ number=1,
+ presence=halftime,
+ mask=models.Mask.types['Type I'],
+ activity=models.Activity.types['Standing'],
+ virus=models.SARSCoV2(
+ viral_load_in_sputum=1e9,
+ infectious_dose=np.array([50, 20, 30]),
+ ),
+ expiration=models.Expiration.types['Talking']
+ )
+ cm = KnownConcentrations(
+ dummy_room, dummy_ventilation, infected_population, lambda t: 1.2)
+
+ presence_interval = models.SpecificInterval(((0, 1),))
+ population = models.Population(
+ 10, presence_interval, models.Mask.types['Type I'],
+ models.Activity.types['Standing'],
+ )
+ model = ExposureModel(cm, population, fraction_deposited=1.0)
+ inf_probability = model.infection_probability()
+ assert isinstance(inf_probability, np.ndarray)
+ assert inf_probability.shape == (3, )
diff --git a/cara/tests/test_infected_population.py b/cara/tests/test_infected_population.py
index 7acef7f7..638acfa3 100644
--- a/cara/tests/test_infected_population.py
+++ b/cara/tests/test_infected_population.py
@@ -7,14 +7,12 @@ import cara.models
@pytest.mark.parametrize(
"override_params", [
{'viral_load_in_sputum': np.array([5e8, 1e9])},
- {'quantum_infectious_dose': np.array([50, 20])},
{'exhalation_rate': np.array([0.75, 0.81])},
]
)
def test_infected_population_vectorisation(override_params):
defaults = {
'viral_load_in_sputum': 1e9,
- 'quantum_infectious_dose': 50,
'exhalation_rate': 0.75,
}
defaults.update(override_params)
@@ -33,7 +31,7 @@ def test_infected_population_vectorisation(override_params):
),
virus=cara.models.Virus(
viral_load_in_sputum=defaults['viral_load_in_sputum'],
- quantum_infectious_dose=defaults['quantum_infectious_dose'],
+ infectious_dose=50.,
),
expiration=cara.models.Expiration((1., 0., 0.)),
)
diff --git a/cara/tests/test_known_quantities.py b/cara/tests/test_known_quantities.py
index d8597cf8..03d99376 100644
--- a/cara/tests/test_known_quantities.py
+++ b/cara/tests/test_known_quantities.py
@@ -7,7 +7,7 @@ import cara.data as data
def test_no_mask_superspeading_emission_rate(baseline_model):
- expected_rate = 970.
+ expected_rate = 48500.
npt.assert_allclose(
[baseline_model.infected.emission_rate(t) for t in [0, 1, 4, 4.5, 5, 8, 9]],
[0, expected_rate, expected_rate, 0, 0, expected_rate, 0],
@@ -44,7 +44,7 @@ def test_concentrations(baseline_model):
concentrations = [baseline_model.concentration(t) for t in ts]
npt.assert_allclose(
concentrations,
- [0.000000e+00, 0.41611256, 1.3205628e-14, 0.41611256, 4.1909001e-28],
+ [0.000000e+00, 20.805628, 6.602814e-13, 20.805628, 2.09545e-26],
rtol=1e-6
)
@@ -354,16 +354,16 @@ def build_exposure_model(concentration_model):
)
-# expected quanta were computed with a trapezoidal integration, using
+# expected exposure were computed with a trapezoidal integration, using
# a mesh of 100'000 pts per exposed presence interval.
@pytest.mark.parametrize(
- "month, expected_quanta",
+ "month, expected_exposure",
[
- ['Jan', 9.930854],
- ['Jun', 37.962708],
+ ['Jan', 496.5427],
+ ['Jun', 1898.1354],
],
)
-def test_quanta_hourly_dep(month,expected_quanta):
+def test_exposure_hourly_dep(month,expected_exposure):
m = build_exposure_model(
build_hourly_dependent_model(
month,
@@ -371,20 +371,20 @@ def test_quanta_hourly_dep(month,expected_quanta):
intervals_presence_infected=((8, 12), (13, 17))
)
)
- quanta = m.quanta_exposure()
- npt.assert_allclose(quanta, expected_quanta)
+ exposure = m.exposure()
+ npt.assert_allclose(exposure, expected_exposure)
-# expected quanta were computed with a trapezoidal integration, using
+# expected exposure were computed with a trapezoidal integration, using
# a mesh of 100'000 pts per exposed presence interval and 25 pts per hour
# for the temperature discretization.
@pytest.mark.parametrize(
- "month, expected_quanta",
+ "month, expected_exposure",
[
- ['Jan', 9.993842],
- ['Jun', 40.151985],
+ ['Jan', 499.6921],
+ ['Jun', 2007.59925],
],
)
-def test_quanta_hourly_dep_refined(month,expected_quanta):
+def test_exposure_hourly_dep_refined(month,expected_exposure):
m = build_exposure_model(
build_hourly_dependent_model(
month,
@@ -393,5 +393,5 @@ def test_quanta_hourly_dep_refined(month,expected_quanta):
temperatures=data.GenevaTemperatures,
)
)
- quanta = m.quanta_exposure()
- npt.assert_allclose(quanta, expected_quanta, rtol=0.02)
+ exposure = m.exposure()
+ npt.assert_allclose(exposure, expected_exposure, rtol=0.02)
diff --git a/cara/tests/test_monte_carlo.py b/cara/tests/test_monte_carlo.py
index 23e139fd..5c14622d 100644
--- a/cara/tests/test_monte_carlo.py
+++ b/cara/tests/test_monte_carlo.py
@@ -84,6 +84,6 @@ def test_build_concentration_model(baseline_mc_model: cara.monte_carlo.Concentra
def test_build_exposure_model(baseline_mc_exposure_model: cara.monte_carlo.ExposureModel):
model = baseline_mc_exposure_model.build_model(7)
assert isinstance(model, cara.models.ExposureModel)
- prob = model.quanta_exposure()
+ prob = model.exposure()
assert isinstance(prob, np.ndarray)
assert prob.shape == (7, )
diff --git a/cara/tests/test_monte_carlo_full_models.py b/cara/tests/test_monte_carlo_full_models.py
index 7c08757f..5c608102 100644
--- a/cara/tests/test_monte_carlo_full_models.py
+++ b/cara/tests/test_monte_carlo_full_models.py
@@ -233,42 +233,42 @@ def skagit_chorale_mc():
@pytest.mark.parametrize(
- "mc_model, expected_pi, expected_new_cases, expected_dose, expected_qR",
+ "mc_model, expected_pi, expected_new_cases, expected_dose, expected_ER",
[
- ["shared_office_mc", 10.7, 0.32, 0.954, 10.9],
- ["classroom_mc", 36.1, 6.85, 13.0, 474.4],
- ["ski_cabin_mc", 16.3, 0.49, 0.599, 123.4],
- ["gym_mc", 2.25, 0.63, 0.01307, 16.4],
- ["waiting_room_mc", 9.72, 1.36, 0.571, 58.9],
- ["skagit_chorale_mc",29.9, 17.9, 1.90, 1414],
+ ["shared_office_mc", 10.7, 0.32, 57.24, 654],
+ ["classroom_mc", 36.1, 6.85, 780.0, 28464],
+ ["ski_cabin_mc", 16.3, 0.49, 35.94, 7404],
+ ["gym_mc", 2.25, 0.63, 0.7842, 984],
+ ["waiting_room_mc", 9.72, 1.36, 34.26, 3534],
+ ["skagit_chorale_mc",29.9, 17.9, 190.0, 141400],
]
)
def test_report_models(mc_model, expected_pi, expected_new_cases,
- expected_dose, expected_qR, request):
+ expected_dose, expected_ER, request):
mc_model = request.getfixturevalue(mc_model)
exposure_model = mc_model.build_model(size=SAMPLE_SIZE)
npt.assert_allclose(exposure_model.infection_probability().mean(),
expected_pi, rtol=TOLERANCE)
npt.assert_allclose(exposure_model.expected_new_cases().mean(),
expected_new_cases, rtol=TOLERANCE)
- npt.assert_allclose(exposure_model.quanta_exposure().mean(),
+ npt.assert_allclose(exposure_model.exposure().mean(),
expected_dose, rtol=TOLERANCE)
npt.assert_allclose(
exposure_model.concentration_model.infected.emission_rate_when_present().mean(),
- expected_qR, rtol=TOLERANCE)
+ expected_ER, rtol=TOLERANCE)
@pytest.mark.parametrize(
- "mask_type, month, expected_pi, expected_dose, expected_qR",
+ "mask_type, month, expected_pi, expected_dose, expected_ER",
[
- ["No mask", "Jul", 30.0, 6.764, 64.9],
- ["Type I", "Jul", 10.2, 1.223, 11.7],
- ["FFP2", "Jul", 4.0, 1.223, 11.7],
- ["Type I", "Feb", 4.25, 0.357, 11.7],
+ ["No mask", "Jul", 30.0, 405.84, 3894],
+ ["Type I", "Jul", 10.2, 73.38, 702],
+ ["FFP2", "Jul", 4.0, 73.38, 702],
+ ["Type I", "Feb", 4.25, 21.42, 702],
],
)
def test_small_shared_office_Geneva(mask_type, month, expected_pi,
- expected_dose, expected_qR):
+ expected_dose, expected_ER):
concentration_mc = mc.ConcentrationModel(
room=models.Room(volume=33, humidity=0.5),
ventilation=models.MultipleVentilation(
@@ -309,8 +309,8 @@ def test_small_shared_office_Geneva(mask_type, month, expected_pi,
exposure_model = exposure_mc.build_model(size=SAMPLE_SIZE)
npt.assert_allclose(exposure_model.infection_probability().mean(),
expected_pi, rtol=TOLERANCE)
- npt.assert_allclose(exposure_model.quanta_exposure().mean(),
+ npt.assert_allclose(exposure_model.exposure().mean(),
expected_dose, rtol=TOLERANCE)
npt.assert_allclose(
exposure_model.concentration_model.infected.emission_rate_when_present().mean(),
- expected_qR, rtol=TOLERANCE)
+ expected_ER, rtol=TOLERANCE)