Compare commits
101 commits
master
...
paper/2021
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
25fe14c1aa | ||
|
|
cbae4a01a3 | ||
|
|
adb8dccbad | ||
|
|
0c992e459f | ||
|
|
42ed330ad4 | ||
|
|
e6818c6661 | ||
|
|
d83b86de4b | ||
|
|
29f191dd2a | ||
|
|
f50ce6d15b | ||
|
|
4f63325617 | ||
|
|
9810ee3f1f | ||
|
|
cb7d8abeb8 | ||
|
|
c4d43c099c | ||
|
|
34b1f89d69 | ||
|
|
f9c92329d8 | ||
|
|
0ce14e56f3 | ||
|
|
9fe5ecc04f | ||
|
|
6e14f8dbb6 | ||
|
|
8807942603 | ||
|
|
2bc31d88f6 | ||
|
|
8c1ab2f504 | ||
|
|
4dac6237c2 | ||
|
|
95cfddcdbe | ||
|
|
fe00584c89 | ||
|
|
b92b2ec132 | ||
|
|
eb8feb6ed4 | ||
|
|
902396fcf7 | ||
|
|
c81e9b1c14 | ||
|
|
e8f4db16fa | ||
|
|
ca120716ff | ||
|
|
b820b3d889 | ||
|
|
5f9091f18b | ||
|
|
83caf48282 | ||
|
|
52ee8f3c38 | ||
|
|
16e705f5bb | ||
|
|
ba2524a1ea | ||
|
|
a4b1e5adf2 | ||
|
|
e152b296c8 | ||
|
|
cca80d86ea | ||
|
|
f0c59b0c61 | ||
|
|
57b6a668c0 | ||
|
|
c4afd66747 | ||
|
|
b00af20e82 | ||
|
|
3ea400af2a | ||
|
|
38b48d3341 | ||
|
|
afb49ee1e7 | ||
|
|
a4114edaf9 | ||
|
|
b76152015b | ||
|
|
919ba2f51e | ||
|
|
1b6f5eea75 | ||
|
|
dc86171bd1 | ||
|
|
9686739cc6 | ||
|
|
7aec37c47a | ||
|
|
25f4981c96 | ||
|
|
e93e744dec | ||
|
|
7be638c92f | ||
|
|
32a2a75bc9 | ||
|
|
f2b1335beb | ||
|
|
5c07820a9e | ||
|
|
32f9fa0d50 | ||
|
|
9d0c93947d | ||
|
|
d79469ffba | ||
|
|
5cdf955d58 | ||
|
|
9dda1e81e0 | ||
|
|
ffa52ee901 | ||
|
|
0f0def394b | ||
|
|
3724009060 | ||
|
|
90ae11ebc4 | ||
|
|
cbc5ffb203 | ||
|
|
4d95dd2ced | ||
|
|
9e04e2e2f2 | ||
|
|
93662ad875 | ||
|
|
c60f363726 | ||
|
|
5ee92afbe1 | ||
|
|
68e7c1c13d | ||
|
|
0350b1e8e6 | ||
|
|
158320e522 | ||
|
|
0a2fc3ef17 | ||
|
|
b2d794316a | ||
|
|
f5ee4c3882 | ||
|
|
60cb0ef455 | ||
|
|
d33affa36f | ||
|
|
8daa03881c | ||
|
|
e7decf52b2 | ||
|
|
81dcbcd39e | ||
|
|
2988f2085b | ||
|
|
90040a62a2 | ||
|
|
ae085826dd | ||
|
|
d03ff7f4f0 | ||
|
|
b59ae0236d | ||
|
|
f614027b02 | ||
|
|
5ddddf10f8 | ||
|
|
415ade0185 | ||
|
|
160dcf6a60 | ||
|
|
c1938889b8 | ||
|
|
9a43db9c5a | ||
|
|
4b63281f02 | ||
|
|
f6340d7986 | ||
|
|
0935056df2 | ||
|
|
52c4eb7c2c | ||
|
|
ae4db5a4b0 |
7 changed files with 80 additions and 23 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -4,6 +4,8 @@ __pycache__
|
||||||
*.DS_Store
|
*.DS_Store
|
||||||
*.pyc
|
*.pyc
|
||||||
|
|
||||||
|
data.csv
|
||||||
|
|
||||||
# Editor stuff
|
# Editor stuff
|
||||||
*.swp
|
*.swp
|
||||||
.idea
|
.idea
|
||||||
|
|
|
||||||
|
|
@ -246,7 +246,7 @@ class FormData:
|
||||||
ventilation=self.ventilation(),
|
ventilation=self.ventilation(),
|
||||||
infected=self.infected_population(),
|
infected=self.infected_population(),
|
||||||
),
|
),
|
||||||
exposed=self.exposed_population()
|
exposed=self.exposed_population(),
|
||||||
)
|
)
|
||||||
|
|
||||||
def build_model(self, sample_size=_DEFAULT_MC_SAMPLE_SIZE) -> models.ExposureModel:
|
def build_model(self, sample_size=_DEFAULT_MC_SAMPLE_SIZE) -> models.ExposureModel:
|
||||||
|
|
|
||||||
|
|
@ -339,3 +339,4 @@ class ReportGenerator:
|
||||||
def render(self, context: dict) -> str:
|
def render(self, context: dict) -> str:
|
||||||
template = self._template_environment().get_template("calculator.report.html.j2")
|
template = self._template_environment().get_template("calculator.report.html.j2")
|
||||||
return template.render(**context)
|
return template.render(**context)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,6 @@ from .utils import method_cache
|
||||||
|
|
||||||
from .dataclass_utils import nested_replace
|
from .dataclass_utils import nested_replace
|
||||||
|
|
||||||
|
|
||||||
# Define types for items supporting vectorisation. In the future this may be replaced
|
# 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
|
# by ``np.ndarray[<type>]`` once/if that syntax is supported. Note that vectorization
|
||||||
# implies 1d arrays: multi-dimensional arrays are not supported.
|
# implies 1d arrays: multi-dimensional arrays are not supported.
|
||||||
|
|
@ -429,6 +428,9 @@ class Virus:
|
||||||
#: Dose to initiate infection, in RNA copies
|
#: Dose to initiate infection, in RNA copies
|
||||||
infectious_dose: _VectorisedFloat
|
infectious_dose: _VectorisedFloat
|
||||||
|
|
||||||
|
#: viable-to-RNA virus ratio as a function of the viral load
|
||||||
|
viable_to_RNA: _VectorisedFloat
|
||||||
|
|
||||||
#: Pre-populated examples of Viruses.
|
#: Pre-populated examples of Viruses.
|
||||||
types: typing.ClassVar[typing.Dict[str, "Virus"]]
|
types: typing.ClassVar[typing.Dict[str, "Virus"]]
|
||||||
|
|
||||||
|
|
@ -465,19 +467,23 @@ Virus.types = {
|
||||||
# as per https://www.dhs.gov/publication/st-master-question-list-covid-19
|
# as per https://www.dhs.gov/publication/st-master-question-list-covid-19
|
||||||
# 50 comes from Buonanno et al.
|
# 50 comes from Buonanno et al.
|
||||||
infectious_dose=50.,
|
infectious_dose=50.,
|
||||||
|
viable_to_RNA = 0.5,
|
||||||
),
|
),
|
||||||
'SARS_CoV_2_B117': SARSCoV2(
|
'SARS_CoV_2_B117': SARSCoV2(
|
||||||
# also called VOC-202012/01
|
# also called VOC-202012/01
|
||||||
viral_load_in_sputum=1e9,
|
viral_load_in_sputum=1e9,
|
||||||
infectious_dose=30.,
|
infectious_dose=30.,
|
||||||
|
viable_to_RNA = 0.5,
|
||||||
),
|
),
|
||||||
'SARS_CoV_2_P1': SARSCoV2(
|
'SARS_CoV_2_P1': SARSCoV2(
|
||||||
viral_load_in_sputum=1e9,
|
viral_load_in_sputum=1e9,
|
||||||
infectious_dose=1/0.045,
|
infectious_dose=1/0.045,
|
||||||
|
viable_to_RNA = 0.5,
|
||||||
),
|
),
|
||||||
'SARS_CoV_2_B16172': SARSCoV2(
|
'SARS_CoV_2_B16172': SARSCoV2(
|
||||||
viral_load_in_sputum=1e9,
|
viral_load_in_sputum=1e9,
|
||||||
infectious_dose=30/1.6,
|
infectious_dose=30/1.6,
|
||||||
|
viable_to_RNA = 0.5,
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -539,7 +545,7 @@ class _ExpirationBase:
|
||||||
#: Pre-populated examples of Expirations.
|
#: Pre-populated examples of Expirations.
|
||||||
types: typing.ClassVar[typing.Dict[str, "_ExpirationBase"]]
|
types: typing.ClassVar[typing.Dict[str, "_ExpirationBase"]]
|
||||||
|
|
||||||
def aerosols(self, mask: Mask):
|
def aerosols(self, mask: Mask, cn_B: float, cn_L: float):
|
||||||
"""
|
"""
|
||||||
total volume of aerosols expired per volume of air (mL/cm^3).
|
total volume of aerosols expired per volume of air (mL/cm^3).
|
||||||
"""
|
"""
|
||||||
|
|
@ -561,19 +567,19 @@ class Expiration(_ExpirationBase):
|
||||||
BLO_factors: typing.Tuple[float, float, float]
|
BLO_factors: typing.Tuple[float, float, float]
|
||||||
|
|
||||||
@cached()
|
@cached()
|
||||||
def aerosols(self, mask: Mask):
|
def aerosols(self, mask: Mask, cn_B: float, cn_L: float):
|
||||||
""" Result is in mL.cm^-3 """
|
""" Result is in mL.cm^-3 """
|
||||||
def volume(d):
|
def volume(d):
|
||||||
return (np.pi * d**3) / 6.
|
return (np.pi * d**3) / 6.
|
||||||
|
|
||||||
def _Bmode(d: float) -> float:
|
def _Bmode(d: float, cn_B: float) -> float:
|
||||||
# B-mode (see ref. above).
|
# B-mode (see ref. above).
|
||||||
return ( (1 / d) * (0.1 / (np.sqrt(2 * np.pi) * 0.262364)) *
|
return ( (1 / d) * (cn_B / (np.sqrt(2 * np.pi) * 0.262364)) *
|
||||||
np.exp(-1 * (np.log(d) - 0.989541) ** 2 / (2 * 0.262364 ** 2)))
|
np.exp(-1 * (np.log(d) - 0.989541) ** 2 / (2 * 0.262364 ** 2)))
|
||||||
|
|
||||||
def _Lmode(d: float) -> float:
|
def _Lmode(d: float, cn_L: float) -> float:
|
||||||
# L-mode (see ref. above).
|
# L-mode (see ref. above).
|
||||||
return ( (1 / d) * (1.0 / (np.sqrt(2 * np.pi) * 0.506818)) *
|
return ( (1 / d) * (cn_L / (np.sqrt(2 * np.pi) * 0.506818)) *
|
||||||
np.exp(-1 * (np.log(d) - 1.38629) ** 2 / (2 * 0.506818 ** 2)))
|
np.exp(-1 * (np.log(d) - 1.38629) ** 2 / (2 * 0.506818 ** 2)))
|
||||||
|
|
||||||
def _Omode(d: float) -> float:
|
def _Omode(d: float) -> float:
|
||||||
|
|
@ -582,8 +588,8 @@ class Expiration(_ExpirationBase):
|
||||||
np.exp(-1 * (np.log(d) - 4.97673) ** 2 / (2 * 0.585005 ** 2)))
|
np.exp(-1 * (np.log(d) - 4.97673) ** 2 / (2 * 0.585005 ** 2)))
|
||||||
|
|
||||||
def integrand(d: float) -> float:
|
def integrand(d: float) -> float:
|
||||||
return (self.BLO_factors[0] * _Bmode(d) +
|
return (self.BLO_factors[0] * _Bmode(d, cn_B) +
|
||||||
self.BLO_factors[1] * _Lmode(d) +
|
self.BLO_factors[1] * _Lmode(d, cn_L) +
|
||||||
self.BLO_factors[2] * _Omode(d)
|
self.BLO_factors[2] * _Omode(d)
|
||||||
) * volume(d) * (1 - mask.exhale_efficiency(d))
|
) * volume(d) * (1 - mask.exhale_efficiency(d))
|
||||||
|
|
||||||
|
|
@ -608,9 +614,9 @@ class MultipleExpiration(_ExpirationBase):
|
||||||
raise ValueError("expirations and weigths should contain the"
|
raise ValueError("expirations and weigths should contain the"
|
||||||
"same number of elements")
|
"same number of elements")
|
||||||
|
|
||||||
def aerosols(self, mask: Mask):
|
def aerosols(self, mask: Mask, cn_B: float, cn_L: float):
|
||||||
return np.array([
|
return np.array([
|
||||||
weight * expiration.aerosols(mask) / sum(self.weights)
|
weight * expiration.aerosols(mask, cn_B, cn_L) / sum(self.weights)
|
||||||
for weight,expiration in zip(self.weights,self.expirations)
|
for weight,expiration in zip(self.weights,self.expirations)
|
||||||
]).sum(axis=0)
|
]).sum(axis=0)
|
||||||
|
|
||||||
|
|
@ -676,8 +682,11 @@ class InfectedPopulation(Population):
|
||||||
#: The type of expiration that is being emitted whilst doing the activity.
|
#: The type of expiration that is being emitted whilst doing the activity.
|
||||||
expiration: _ExpirationBase
|
expiration: _ExpirationBase
|
||||||
|
|
||||||
@method_cache
|
#: The percentage of host immunity
|
||||||
def emission_rate_when_present(self) -> _VectorisedFloat:
|
host_immunity: float = 0.
|
||||||
|
|
||||||
|
|
||||||
|
def emission_rate_when_present(self, cn_B: float = 0.06, cn_L: float = 0.2) -> _VectorisedFloat:
|
||||||
"""
|
"""
|
||||||
The emission rate if the infected population is present.
|
The emission rate if the infected population is present.
|
||||||
|
|
||||||
|
|
@ -687,7 +696,7 @@ class InfectedPopulation(Population):
|
||||||
# Emission Rate (virions / h)
|
# Emission Rate (virions / h)
|
||||||
# Note on units: exhalation rate is in m^3/h, aerosols in mL/cm^3
|
# Note on units: exhalation rate is in m^3/h, aerosols in mL/cm^3
|
||||||
# and viral load in virus/mL -> 1e6 conversion factor
|
# and viral load in virus/mL -> 1e6 conversion factor
|
||||||
aerosols = self.expiration.aerosols(self.mask)
|
aerosols = self.expiration.aerosols(self.mask, cn_B, cn_L)
|
||||||
|
|
||||||
ER = (self.virus.viral_load_in_sputum *
|
ER = (self.virus.viral_load_in_sputum *
|
||||||
self.activity.exhalation_rate *
|
self.activity.exhalation_rate *
|
||||||
|
|
@ -719,7 +728,7 @@ class InfectedPopulation(Population):
|
||||||
# with a declaration of state change time, as is the case for things
|
# with a declaration of state change time, as is the case for things
|
||||||
# like Ventilation.
|
# like Ventilation.
|
||||||
|
|
||||||
return self.emission_rate_when_present()
|
return self.emission_rate_when_present(cn_B=0.06, cn_L=0.2)
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
|
|
@ -906,7 +915,33 @@ class ExposureModel:
|
||||||
repeats: int = 1
|
repeats: int = 1
|
||||||
|
|
||||||
#: The fraction of viruses actually deposited in the respiratory tract
|
#: The fraction of viruses actually deposited in the respiratory tract
|
||||||
fraction_deposited: _VectorisedFloat = 0.6
|
"""
|
||||||
|
To be updated in the future.
|
||||||
|
The diameter value of 1.37 is the correspondent to a fraction_deposited of 0.6
|
||||||
|
"""
|
||||||
|
d = 1.37
|
||||||
|
IF = 1 - 0.5 * (1 - (1 / (1 + (0.00076*(d**2.8)))))
|
||||||
|
DF = IF * (0.0587 + (0.911/(1 + np.exp(4.77 + 1.485 * np.log(d)))) + (0.943/(1 + np.exp(0.508 - 2.58 * np.log(d)))))
|
||||||
|
fraction_deposited: _VectorisedFloat = DF
|
||||||
|
|
||||||
|
def _normed_exposure_between_bounds(self, time1: float, time2: float) -> _VectorisedFloat:
|
||||||
|
"""The number of virions per meter^3 between any two times, normalized
|
||||||
|
by the emission rate of the infected population"""
|
||||||
|
for start, stop in self.exposed.presence.boundaries():
|
||||||
|
if start > time2:
|
||||||
|
normed_exposure = 0.
|
||||||
|
break
|
||||||
|
elif time2 <= stop:
|
||||||
|
normed_exposure = self.concentration_model.normed_integrated_concentration(time1, time2)
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
normed_exposure = self.concentration_model.normed_integrated_concentration(time1, time2)
|
||||||
|
return normed_exposure
|
||||||
|
|
||||||
|
def exposure_between_bounds(self, time1: float, time2: float) -> _VectorisedFloat:
|
||||||
|
"""The number of virions per meter^3 between any two times."""
|
||||||
|
return (self._normed_exposure_between_bounds(time1, time2) *
|
||||||
|
self.concentration_model.infected.emission_rate_when_present())
|
||||||
|
|
||||||
def _normed_exposure(self) -> _VectorisedFloat:
|
def _normed_exposure(self) -> _VectorisedFloat:
|
||||||
"""
|
"""
|
||||||
|
|
@ -928,10 +963,11 @@ class ExposureModel:
|
||||||
def infection_probability(self) -> _VectorisedFloat:
|
def infection_probability(self) -> _VectorisedFloat:
|
||||||
exposure = self.exposure()
|
exposure = self.exposure()
|
||||||
|
|
||||||
|
# Dose
|
||||||
inf_aero = (
|
inf_aero = (
|
||||||
self.exposed.activity.inhalation_rate *
|
self.exposed.activity.inhalation_rate *
|
||||||
(1 - self.exposed.mask.inhale_efficiency()) *
|
(1 - self.exposed.mask.inhale_efficiency()) *
|
||||||
exposure * self.fraction_deposited
|
exposure * self.fraction_deposited * (self.concentration_model.infected.virus.viable_to_RNA * (1 - self.concentration_model.infected.host_immunity))
|
||||||
)
|
)
|
||||||
|
|
||||||
# Probability of infection.
|
# Probability of infection.
|
||||||
|
|
|
||||||
|
|
@ -38,24 +38,33 @@ symptomatic_vl_frequencies = LogCustomKernel(
|
||||||
kernel_bandwidth=0.1
|
kernel_bandwidth=0.1
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# From https://doi.org/10.1093/cid/ciaa1579
|
||||||
|
infectious_virus_distribution = Uniform(0.15, 0.45)
|
||||||
|
|
||||||
|
# From discussion with virologists
|
||||||
|
infectious_dose_distribution = Uniform(10., 100.)
|
||||||
|
|
||||||
# From CERN-OPEN-2021-04 and refererences therein
|
# From CERN-OPEN-2021-04 and refererences therein
|
||||||
virus_distributions = {
|
virus_distributions = {
|
||||||
'SARS_CoV_2': mc.SARSCoV2(
|
'SARS_CoV_2': mc.SARSCoV2(
|
||||||
viral_load_in_sputum=symptomatic_vl_frequencies,
|
viral_load_in_sputum=symptomatic_vl_frequencies,
|
||||||
infectious_dose=100,
|
infectious_dose=infectious_dose_distribution,
|
||||||
|
viable_to_RNA=infectious_virus_distribution,
|
||||||
),
|
),
|
||||||
'SARS_CoV_2_B117': mc.SARSCoV2(
|
'SARS_CoV_2_B117': mc.SARSCoV2(
|
||||||
viral_load_in_sputum=symptomatic_vl_frequencies,
|
viral_load_in_sputum=symptomatic_vl_frequencies,
|
||||||
infectious_dose=60,
|
infectious_dose=infectious_dose_distribution,
|
||||||
|
viable_to_RNA=infectious_virus_distribution,
|
||||||
),
|
),
|
||||||
'SARS_CoV_2_P1': mc.SARSCoV2(
|
'SARS_CoV_2_P1': mc.SARSCoV2(
|
||||||
viral_load_in_sputum=symptomatic_vl_frequencies,
|
viral_load_in_sputum=symptomatic_vl_frequencies,
|
||||||
infectious_dose=100/2.25,
|
infectious_dose=infectious_dose_distribution,
|
||||||
|
viable_to_RNA=infectious_virus_distribution,
|
||||||
),
|
),
|
||||||
'SARS_CoV_2_B16172': mc.SARSCoV2(
|
'SARS_CoV_2_B16172': mc.SARSCoV2(
|
||||||
viral_load_in_sputum=symptomatic_vl_frequencies,
|
viral_load_in_sputum=symptomatic_vl_frequencies,
|
||||||
infectious_dose=60/1.6,
|
infectious_dose=infectious_dose_distribution,
|
||||||
|
viable_to_RNA=infectious_virus_distribution,
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -67,4 +76,6 @@ virus_distributions = {
|
||||||
mask_distributions = {
|
mask_distributions = {
|
||||||
'Type I': mc.Mask(Uniform(0.25, 0.80)),
|
'Type I': mc.Mask(Uniform(0.25, 0.80)),
|
||||||
'FFP2': mc.Mask(Uniform(0.83, 0.91)),
|
'FFP2': mc.Mask(Uniform(0.83, 0.91)),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -84,3 +84,4 @@ webencodings==0.5.1
|
||||||
websocket-client==1.1.0
|
websocket-client==1.1.0
|
||||||
wheel==0.36.2
|
wheel==0.36.2
|
||||||
widgetsnbextension==3.5.1
|
widgetsnbextension==3.5.1
|
||||||
|
pandas==1.3.2
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,12 @@ ignore_missing_imports = True
|
||||||
[mypy-scipy.*]
|
[mypy-scipy.*]
|
||||||
ignore_missing_imports = True
|
ignore_missing_imports = True
|
||||||
|
|
||||||
|
[mypy-tqdm.*]
|
||||||
|
ignore_missing_imports = True
|
||||||
|
|
||||||
|
[mypy-pandas.*]
|
||||||
|
ignore_missing_imports = True
|
||||||
|
|
||||||
[mypy-timezonefinder.*]
|
[mypy-timezonefinder.*]
|
||||||
ignore_missing_imports = True
|
ignore_missing_imports = True
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue