Merge branch 'feature/probability_infection' into 'feature/scientific_model_update'

Changed probability of infection formula

See merge request cara/cara!262
This commit is contained in:
Nicolas Mounet 2021-09-21 12:05:02 +02:00
commit c5a849f653
9 changed files with 44 additions and 13 deletions

View file

@ -449,6 +449,7 @@ class FormData:
presence=self.exposed_present_interval(),
activity=activity,
mask=self.mask(),
host_immunity=0.,
)
return exposed

View file

@ -507,6 +507,7 @@ baseline_model = models.ExposureModel(
presence=models.SpecificInterval(((8., 12.), (13., 17.))),
activity=models.Activity.types['Seated'],
mask=models.Mask.types['No mask'],
host_immunity=0.,
),
)

View file

@ -50,6 +50,7 @@ from .utils import method_cache
from .dataclass_utils import nested_replace
oneoverln2 = 1 / np.log(2)
# Define types for items supporting vectorisation. In the future this may be replaced
# by ``np.ndarray[<type>]`` once/if that syntax is supported. Note that vectorization
@ -432,6 +433,9 @@ class Virus:
#: viable-to-RNA virus ratio as a function of the viral load
viable_to_RNA_ratio: _VectorisedFloat
#: Reported increase of transmissibility of a VOC
transmissibility_factor: float
#: Pre-populated examples of Viruses.
types: typing.ClassVar[typing.Dict[str, "Virus"]]
@ -469,22 +473,26 @@ Virus.types = {
# 50 comes from Buonanno et al.
infectious_dose=50.,
viable_to_RNA_ratio = 0.5,
transmissibility_factor=1.0,
),
'SARS_CoV_2_B117': SARSCoV2(
# also called VOC-202012/01
viral_load_in_sputum=1e9,
infectious_dose=30.,
infectious_dose=50.,
viable_to_RNA_ratio = 0.5,
transmissibility_factor=0.6,
),
'SARS_CoV_2_P1': SARSCoV2(
viral_load_in_sputum=1e9,
infectious_dose=1/0.045,
infectious_dose=50.,
viable_to_RNA_ratio = 0.5,
transmissibility_factor=0.45,
),
'SARS_CoV_2_B16172': SARSCoV2(
viral_load_in_sputum=1e9,
infectious_dose=30/1.6,
infectious_dose=50.,
viable_to_RNA_ratio = 0.5,
transmissibility_factor=0.38,
),
}
@ -660,6 +668,11 @@ class Population:
#: The physical activity being carried out by the people.
activity: Activity
#: The ratio of virions that are inactivated by the person's immunity.
# This parameter considers the potential antibodies in the person,
# which might render inactive some RNA copies (virions).
host_immunity: float
def person_present(self, time):
return self.presence.triggered(time)
@ -719,12 +732,7 @@ class EmittingPopulation(_PopulationWithVirus):
@dataclass(frozen=True)
class InfectedPopulation(_PopulationWithVirus):
#: The type of expiration that is being emitted whilst doing the activity.
expiration: _ExpirationBase
#: The ratio of virions that are inactivated by the infected person's immunity.
# This parameter considers the potential antibodies in the infected person,
# which might render inactive some RNA copies (virions).
host_immunity: _VectorisedFloat
expiration: _ExpirationBase
@method_cache
def fraction_of_infectious_virus(self) -> _VectorisedFloat:
@ -995,8 +1003,13 @@ class ExposureModel:
exposure * self.fraction_deposited * f_inf
)
# Probability of infection.
return (1 - np.exp(-(inf_aero/self.concentration_model.virus.infectious_dose))) * 100
# oneoverln2 multiplied by ID_50 corresponds to ID_63.
infectious_dose = oneoverln2 * self.concentration_model.virus.infectious_dose
# Probability of infection.
return (1 - np.exp(-((inf_aero * (1 - self.exposed.host_immunity))/(infectious_dose *
self.concentration_model.virus.transmissibility_factor)))) * 100
def expected_new_cases(self) -> _VectorisedFloat:
prob = self.infection_probability()

View file

@ -111,21 +111,25 @@ virus_distributions = {
viral_load_in_sputum=symptomatic_vl_frequencies,
infectious_dose=infectious_dose_distribution,
viable_to_RNA_ratio=viable_to_RNA_ratio_distribution,
transmissibility_factor=1.,
),
'SARS_CoV_2_B117': mc.SARSCoV2(
viral_load_in_sputum=symptomatic_vl_frequencies,
infectious_dose=infectious_dose_distribution,
viable_to_RNA_ratio=viable_to_RNA_ratio_distribution,
transmissibility_factor=0.6,
),
'SARS_CoV_2_P1': mc.SARSCoV2(
viral_load_in_sputum=symptomatic_vl_frequencies,
infectious_dose=infectious_dose_distribution,
viable_to_RNA_ratio=viable_to_RNA_ratio_distribution,
transmissibility_factor=0.45,
),
'SARS_CoV_2_B16172': mc.SARSCoV2(
viral_load_in_sputum=symptomatic_vl_frequencies,
infectious_dose=infectious_dose_distribution,
viable_to_RNA_ratio=viable_to_RNA_ratio_distribution,
transmissibility_factor=0.38,
),
}

View file

@ -45,6 +45,7 @@ def test_concentration_model_vectorisation(override_params):
viable_to_RNA_ratio = 0.5,
),
expiration=models._ExpirationBase.types['Breathing'],
host_immunity=0.,
)
)
concentrations = c_model.concentration(10)
@ -65,6 +66,7 @@ def simple_conc_model():
activity=models.Activity.types['Seated'],
virus=models.Virus.types['SARS_CoV_2'],
expiration=models.Expiration.types['Breathing'],
host_immunity=0.,
)
)

View file

@ -193,7 +193,8 @@ def test_infectious_dose_vectorisation():
infectious_dose=np.array([50, 20, 30]),
viable_to_RNA_ratio = 0.5,
),
expiration=models.Expiration.types['Talking']
expiration=models.Expiration.types['Talking'],
host_immunity=0.,
)
cm = known_concentrations(lambda t: 1.2)
cm = replace(cm, infected=infected_population)

View file

@ -35,6 +35,7 @@ def test_infected_population_vectorisation(override_params):
viable_to_RNA_ratio = 0.5,
),
expiration=cara.models._ExpirationBase.types['Breathing'],
host_immunity=0.,
)
emission_rate = infected.emission_rate(10)
assert isinstance(emission_rate, np.ndarray)

View file

@ -54,6 +54,7 @@ def baseline_mc_model() -> cara.monte_carlo.ConcentrationModel:
mask=cara.models.Mask.types['No mask'],
activity=cara.models.Activity.types['Light activity'],
expiration=cara.models.Expiration.types['Breathing'],
host_immunity=0.,
),
)
return mc_model

View file

@ -45,6 +45,7 @@ def shared_office_mc():
mask=models.Mask(η_inhale=0.3),
activity=activity_distributions['Seated'],
expiration=build_expiration({'Talking': 0.3, 'Breathing': 0.7}),
host_immunity=0.,
),
)
return mc.ExposureModel(
@ -86,6 +87,7 @@ def classroom_mc():
mask=models.Mask.types['No mask'],
activity=activity_distributions['Light activity'],
expiration=expiration_distributions['Talking'],
host_immunity=0.,
),
)
return mc.ExposureModel(
@ -117,6 +119,7 @@ def ski_cabin_mc():
mask=models.Mask(η_inhale=0.3),
activity=activity_distributions['Moderate activity'],
expiration=expiration_distributions['Talking'],
host_immunity=0.,
),
)
return mc.ExposureModel(
@ -150,6 +153,7 @@ def gym_mc():
mask=models.Mask.types["No mask"],
activity=activity_distributions['Heavy exercise'],
expiration=expiration_distributions['Breathing'],
host_immunity=0.,
),
)
return mc.ExposureModel(
@ -181,7 +185,8 @@ def waiting_room_mc():
presence=mc.SpecificInterval(((0., 2.),)),
mask=models.Mask.types["No mask"],
activity=activity_distributions['Seated'],
expiration=build_expiration({'Talking': 0.3, 'Breathing': 0.7})
expiration=build_expiration({'Talking': 0.3, 'Breathing': 0.7}),
host_immunity=0.,
),
)
return mc.ExposureModel(
@ -215,6 +220,7 @@ def skagit_chorale_mc():
mask=models.Mask.types["No mask"],
activity=activity_distributions['Light activity'],
expiration=expiration_distribution((5., 5., 5.)),
host_immunity=0.,
),
)
return mc.ExposureModel(
@ -288,6 +294,7 @@ def test_small_shared_office_Geneva(mask_type, month, expected_pi,
mask=models.Mask.types[mask_type],
activity=activity_distributions['Seated'],
expiration=build_expiration({'Talking': 0.33, 'Breathing': 0.67}),
host_immunity=0.,
),
)
exposure_mc = mc.ExposureModel(