Remove suprespreading event from the expiration types

This commit is contained in:
Nicolas Mounet 2021-09-16 13:55:04 +02:00 committed by Andre Henriques
parent 092d566dbe
commit af3ded7935
4 changed files with 62 additions and 42 deletions

View file

@ -620,7 +620,6 @@ _ExpirationBase.types = {
'Talking': Expiration((1., 1., 1.)),
'Shouting': Expiration((1., 5., 5.)),
'Singing': Expiration((1., 5., 5.)),
'Superspreading event': Expiration((np.inf, 0., 0.)),
}
@ -669,44 +668,21 @@ class Population:
@dataclass(frozen=True)
class InfectedPopulation(Population):
class _PopulationWithVirus(Population):
#: The virus with which the population is infected.
virus: Virus
#: The type of expiration that is being emitted whilst doing the activity.
expiration: _ExpirationBase
@method_cache
def emission_rate_when_present(self) -> _VectorisedFloat:
"""
The emission rate if the infected population is present.
Note that the rate is not currently time-dependent.
The emission rate if the infected population is present
(in virions / h). It should not be a function of time.
"""
# 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)
ER = (self.virus.viral_load_in_sputum *
self.activity.exhalation_rate *
10 ** 6 *
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 * self.virus.infectious_dose
elif np.isinf(aerosols):
ER = 970 * self.virus.infectious_dose
return ER * self.number
raise NotImplementedError("Subclass must implement")
def emission_rate(self, time) -> _VectorisedFloat:
"""
The emission rate of the population.
The emission rate of the population vs time.
"""
# Note: The original model avoids time dependence on the emission rate
# at the cost of implementing a piecewise (on time) concentration function.
@ -722,11 +698,49 @@ class InfectedPopulation(Population):
return self.emission_rate_when_present()
@dataclass(frozen=True)
class EmittingPopulation(_PopulationWithVirus):
#: The emission rate of a single individual, in virions / h.
known_individual_emission_rate: float
@method_cache
def emission_rate_when_present(self) -> _VectorisedFloat:
"""
The emission rate if the infected population is present.
"""
return self.known_individual_emission_rate * self.number
@dataclass(frozen=True)
class InfectedPopulation(_PopulationWithVirus):
#: The type of expiration that is being emitted whilst doing the activity.
expiration: _ExpirationBase
@method_cache
def emission_rate_when_present(self) -> _VectorisedFloat:
"""
The emission rate if the infected population is present.
Note that the rate is not currently time-dependent.
"""
# 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)
ER = (self.virus.viral_load_in_sputum *
self.activity.exhalation_rate *
10 ** 6 *
aerosols)
return ER * self.number
@dataclass(frozen=True)
class ConcentrationModel:
room: Room
ventilation: _VentilationBase
infected: InfectedPopulation
infected: _PopulationWithVirus
@property
def virus(self):

View file

@ -13,13 +13,15 @@ def baseline_model():
active=models.SpecificInterval(((0., 24.), )),
air_exch=30.,
),
infected=models.InfectedPopulation(
infected=models.EmittingPopulation(
number=1,
virus=models.Virus.types['SARS_CoV_2'],
presence=models.SpecificInterval(((0., 4.), (5., 8.))),
mask=models.Mask.types['No mask'],
activity=models.Activity.types['Light activity'],
expiration=models.Expiration.types['Superspreading event'],
known_individual_emission_rate=970 * 50,
# superspreading event, where ejection factor is fixed based
# on Miller et al. (2020) - 50 represents the infectious dose.
),
)
return model

View file

@ -144,13 +144,15 @@ def conc_model():
return models.ConcentrationModel(
models.Room(25),
models.AirChange(always, 5),
models.InfectedPopulation(
models.EmittingPopulation(
number=1,
presence=interesting_times,
mask=models.Mask.types['No mask'],
activity=models.Activity.types['Seated'],
virus=models.Virus.types['SARS_CoV_2'],
expiration=models.Expiration.types['Superspreading event'],
known_individual_emission_rate=970 * 50,
# superspreading event, where ejection factor is fixed based
# on Miller et al. (2020) - 50 represents the infectious dose.
)
)

View file

@ -66,13 +66,15 @@ def build_model(interval_duration):
active=models.PeriodicInterval(period=120, duration=interval_duration),
q_air_mech=500.,
),
infected=models.InfectedPopulation(
infected=models.EmittingPopulation(
number=1,
virus=models.Virus.types['SARS_CoV_2'],
presence=models.SpecificInterval(((0., 4.), (5., 8.))),
mask=models.Mask.types['No mask'],
activity=models.Activity.types['Light activity'],
expiration=models.Expiration.types['Superspreading event'],
known_individual_emission_rate=970 * 50,
# superspreading event, where ejection factor is fixed based
# on Miller et al. (2020) - 50 represents the infectious dose.
),
)
return model
@ -226,13 +228,13 @@ def build_hourly_dependent_model(
outside_temp=outside_temp,
window_height=1.6, opening_length=0.6,
),
infected=models.InfectedPopulation(
infected=models.EmittingPopulation(
number=1,
virus=models.Virus.types['SARS_CoV_2'],
presence=models.SpecificInterval(intervals_presence_infected),
mask=models.Mask.types['No mask'],
activity=models.Activity.types['Light activity'],
expiration=models.Expiration.types['Superspreading event'],
known_individual_emission_rate=970 * 50,
),
)
return model
@ -247,13 +249,13 @@ def build_constant_temp_model(outside_temp, intervals_open=((7.5, 8.5),)):
outside_temp=models.PiecewiseConstant((0., 24.), (outside_temp,)),
window_height=1.6, opening_length=0.6,
),
infected=models.InfectedPopulation(
infected=models.EmittingPopulation(
number=1,
virus=models.Virus.types['SARS_CoV_2'],
presence=models.SpecificInterval(((0., 4.), (5., 7.5))),
mask=models.Mask.types['No mask'],
activity=models.Activity.types['Light activity'],
expiration=models.Expiration.types['Superspreading event'],
known_individual_emission_rate=970 * 50,
),
)
return model
@ -275,13 +277,13 @@ def build_hourly_dependent_model_multipleventilation(month, intervals_open=((7.5
model = models.ConcentrationModel(
room=models.Room(volume=75),
ventilation=vent,
infected=models.InfectedPopulation(
infected=models.EmittingPopulation(
number=1,
virus=models.Virus.types['SARS_CoV_2'],
presence=models.SpecificInterval(((0., 4.), (5., 7.5))),
mask=models.Mask.types['No mask'],
activity=models.Activity.types['Light activity'],
expiration=models.Expiration.types['Superspreading event'],
known_individual_emission_rate=970 * 50,
),
)
return model