Modifying condition to trigger the averaging of the exposure in cara/models.py

This commit is contained in:
Nicolas Mounet 2021-09-15 10:37:50 +02:00
parent 84fcddcd86
commit 396339f7bb
4 changed files with 27 additions and 27 deletions

View file

@ -24,7 +24,7 @@ minutes_since_midnight = typing.NewType('minutes_since_midnight', int)
# Used to declare when an attribute of a class must have a value provided, and
# there should be no default value used.
_NO_DEFAULT = object()
_DEFAULT_MC_SAMPLE_SIZE = 50000
_DEFAULT_MC_SAMPLE_SIZE = 250000
@dataclasses.dataclass

View file

@ -580,7 +580,7 @@ class MultipleExpiration(_ExpirationBase):
Group together different modes of expiration, that represent
each the main expiration mode for a certain fraction of time (given by
the weights).
Obsolet class that can only be used with single diameters (it cannot
This class can only be used with single diameters (it cannot
be used with diameter distributions).
"""
expirations: typing.Tuple[_ExpirationBase, ...]
@ -900,6 +900,13 @@ class ConcentrationModel:
@dataclass(frozen=True)
class ExposureModel:
"""
Represents the exposure to a concentration of virions in the air.
NOTE: the infection probability formula assumes that if the diameter
is an array, then none of the ventilation parameters, room volume or virus
decay constant, are arrays as well.
TODO: implement a check this is the case, in __post_init__
"""
#: The virus concentration model which this exposure model should consider.
concentration_model: ConcentrationModel
@ -932,7 +939,12 @@ class ExposureModel:
corresponds to an integration on diameters).
"""
emission_rate = self.concentration_model.infected.emission_rate_when_present()
if np.isscalar(self.concentration_model.infected.expiration.diameter):
if (not isinstance(self.concentration_model.infected,InfectedPopulation)
or not isinstance(self.concentration_model.infected.expiration,Expiration)
or np.isscalar(self.concentration_model.infected.expiration.diameter)
):
# in all these cases, there is no distribution of
# diameters that need to be integrated over
return self._normed_exposure() * emission_rate
else:
# the mean of the diameter-dependent exposure (including

View file

@ -12,10 +12,9 @@ from cara import models
from cara.monte_carlo.data import expiration_distributions
# TODO: seed better the random number generators
# this is only for test_blend_expiration
np.random.seed(2000)
SAMPLE_SIZE = 500000
TOLERANCE = 0.01
SAMPLE_SIZE = 250000
TOLERANCE = 0.02
def test_model_from_dict(baseline_form_data):
form = model_generator.FormData.from_dict(baseline_form_data)

View file

@ -5,10 +5,12 @@ import pytest
import cara.monte_carlo as mc
from cara import models,data
from cara.monte_carlo.data import activity_distributions, virus_distributions
from cara.monte_carlo.data import expiration_distribution, expiration_distributions
from cara.apps.calculator.model_generator import build_expiration
# TODO: seed better the random number generators
np.random.seed(2000)
SAMPLE_SIZE = 50000
SAMPLE_SIZE = 250000
TOLERANCE = 0.05
# references values for infection_probability and expected new cases
@ -42,10 +44,7 @@ def shared_office_mc():
presence=mc.SpecificInterval(((0., 2.), (2.1, 4.), (5., 7.), (7.1, 9.))),
mask=models.Mask(η_inhale=0.3),
activity=activity_distributions['Seated'],
expiration=models.MultipleExpiration(
expirations=(models.Expiration.types['Talking'],
models.Expiration.types['Breathing']),
weights=(0.3, 0.7)),
expiration=build_expiration({'Talking': 0.3, 'Breathing': 0.7}),
),
)
return mc.ExposureModel(
@ -86,7 +85,7 @@ def classroom_mc():
presence=mc.SpecificInterval(((0., 2.), (2.5, 4.), (5., 7.), (7.5, 9.))),
mask=models.Mask.types['No mask'],
activity=activity_distributions['Light activity'],
expiration=models.Expiration.types['Talking'],
expiration=expiration_distributions['Talking'],
),
)
return mc.ExposureModel(
@ -117,7 +116,7 @@ def ski_cabin_mc():
presence=mc.SpecificInterval(((0., 1/3),)),
mask=models.Mask(η_inhale=0.3),
activity=activity_distributions['Moderate activity'],
expiration=models.Expiration.types['Talking'],
expiration=expiration_distributions['Talking'],
),
)
return mc.ExposureModel(
@ -150,7 +149,7 @@ def gym_mc():
presence=mc.SpecificInterval(((0., 1.),)),
mask=models.Mask.types["No mask"],
activity=activity_distributions['Heavy exercise'],
expiration=models.Expiration.types['Breathing'],
expiration=expiration_distributions['Breathing'],
),
)
return mc.ExposureModel(
@ -182,10 +181,7 @@ def waiting_room_mc():
presence=mc.SpecificInterval(((0., 2.),)),
mask=models.Mask.types["No mask"],
activity=activity_distributions['Seated'],
expiration=models.MultipleExpiration(
expirations=(models.Expiration.types['Talking'],
models.Expiration.types['Breathing']),
weights=(0.3, 0.7)),
expiration=build_expiration({'Talking': 0.3, 'Breathing': 0.7})
),
)
return mc.ExposureModel(
@ -218,11 +214,7 @@ def skagit_chorale_mc():
presence=mc.SpecificInterval(((0., 2.5),)),
mask=models.Mask.types["No mask"],
activity=activity_distributions['Light activity'],
expiration=models.Expiration(10.0761),
# The aerosol diameter given (10.0761 microns) is an equivalent
# diameter, chosen in such a way that the aerosol volume is
# the same as the total aerosol volume given by the full BLO model
# with (5, 5, 5) for the B/L/O weights.
expiration=expiration_distribution((5., 5., 5.)),
),
)
return mc.ExposureModel(
@ -295,10 +287,7 @@ def test_small_shared_office_Geneva(mask_type, month, expected_pi,
presence=mc.SpecificInterval(((9., 10+2/3), (10+5/6, 12.5), (13.5, 15+2/3), (15+5/6, 18.))),
mask=models.Mask.types[mask_type],
activity=activity_distributions['Seated'],
expiration=models.MultipleExpiration(
expirations=(models.Expiration.types['Talking'],
models.Expiration.types['Breathing']),
weights=(0.33, 0.67)),
expiration=build_expiration({'Talking': 0.33, 'Breathing': 0.67}),
),
)
exposure_mc = mc.ExposureModel(