removed aerosols_without_mask in order to always use aerosols method with the correction conversion factor

This commit is contained in:
lrdossan 2024-06-17 12:12:00 +02:00
parent f59d56ed16
commit fa38282186
2 changed files with 18 additions and 47 deletions

View file

@ -692,13 +692,6 @@ class _ExpirationBase:
"""
raise NotImplementedError("Subclass must implement")
def aerosols_without_mask(self):
"""
Total volume of aerosols expired per volume of exhaled air
without considering the mask (mL.m^-3).
"""
raise NotImplementedError("Subclass must implement")
@dataclass(frozen=True)
class Expiration(_ExpirationBase):
@ -736,18 +729,6 @@ class Expiration(_ExpirationBase):
return self.cn * (volume(self.diameter) *
(1 - mask.exhale_efficiency(self.diameter))) * 1e-12
@cached()
def aerosols_without_mask(self):
"""
Total volume of aerosols expired per volume of exhaled air
without considering the mask. Result is in mL.m^-3.
"""
def volume(d):
return (np.pi * d**3) / 6.
# Final result converted from microns^3/cm3 to mL/m3
return self.cn * volume(self.diameter) * 1e-6
@dataclass(frozen=True)
class MultipleExpiration(_ExpirationBase):
@ -893,13 +874,6 @@ class _PopulationWithVirus(Population):
Total volume of aerosols expired per volume of exhaled air (mL/cm^3).
"""
raise NotImplementedError("Subclass must implement")
def aerosols_without_mask(self):
"""
Total volume of aerosols expired per volume of exhaled air
without considering the mask (mL.m^-3).
"""
raise NotImplementedError("Subclass must implement")
def emission_rate_per_aerosol_per_person_when_present(self) -> _VectorisedFloat:
"""
@ -1402,16 +1376,15 @@ class ShortRangeModel:
return factors
def _normed_jet_origin_concentration(self) -> _VectorisedFloat:
return self.expiration.aerosols_without_mask()
# The short range origin concentration does not consider the mask contribution.
return self.expiration.aerosols(mask=Mask.types['No mask'])
def _long_range_normed_concentration(self, concentration_model: ConcentrationModel, time: float) -> _VectorisedFloat:
"""
Virus long-range exposure concentration normalized by the
virus viral load and fraction of infectious virus, as function of time.
"""
return (concentration_model.concentration(time) / (
concentration_model.virus.viral_load_in_sputum *
concentration_model.infected.fraction_of_infectious_virus()))
return (concentration_model.concentration(time) / self.normalization_factor(concentration_model.infected))
def _normed_concentration(self, concentration_model: ConcentrationModel, time: float) -> _VectorisedFloat:
"""
@ -1425,9 +1398,9 @@ class ShortRangeModel:
# Verifies if the given time falls within a short-range interaction
if start <= time <= stop:
dilution = self.dilution_factor()
# Jet origin concentration normalized by the viral load and f_inf
# Jet origin concentration normalized by the emission rate (except the BR)
normed_jet_origin_concentration = self._normed_jet_origin_concentration()
# Long-range concentration normalized by the virus viral load and f_inf
# Long-range concentration normalized by the emission rate (except the BR)
long_range_normed_concentration = self._long_range_normed_concentration(concentration_model, time)
# The long-range concentration values are then approximated using interpolation:
@ -1443,10 +1416,8 @@ class ShortRangeModel:
return 0.
def normalization_factor(self, infected: InfectedPopulation) -> _VectorisedFloat:
# The normalization factor does not consider the BR contribution, and therefore the conversion factor.
return infected.emission_rate_per_aerosol_per_person_when_present() / (
infected.activity.exhalation_rate * 10 ** 6
)
# The normalization factor does not consider the BR contribution
return infected.emission_rate_per_aerosol_per_person_when_present() / infected.activity.exhalation_rate
def jet_origin_concentration(self, infected: InfectedPopulation) -> _VectorisedFloat:
return self._normed_jet_origin_concentration() * self.normalization_factor(infected)
@ -1491,16 +1462,16 @@ class ShortRangeModel:
return start, stop
def _normed_jet_exposure_between_bounds(self,
concentration_model: ConcentrationModel,
time1: float, time2: float):
"""
Get the part of the integrated short-range concentration of
viruses in the air, between the times start and stop, coming
from the jet concentration, normalized by the viral load, and
without dilution.
from the jet concentration, normalized by the viral load and
f_inf, and without dilution.
"""
start, stop = self.extract_between_bounds(time1, time2)
jet_origin = self.expiration.aerosols_without_mask()
# Note the conversion factor mL/cm^3 -> mL/m3
jet_origin = self.expiration.aerosols(mask=Mask.types['No mask']) * 10**6
return jet_origin * (stop - start)
def _normed_interpolated_longrange_exposure_between_bounds(
@ -1508,8 +1479,8 @@ class ShortRangeModel:
time1: float, time2: float):
"""
Get the part of the integrated short-range concentration due
to the background concentration, normalized by the viral load
and the breathing rate, and without dilution.
to the background concentration, normalized by the viral load,
f_inf, and the breathing rate, and without dilution.
One needs to interpolate the integrated long-range concentration
for the particle diameters defined here.
TODO: make sure any potential extrapolation has a
@ -1522,6 +1493,7 @@ class ShortRangeModel:
normed_int_concentration = (
concentration_model.integrated_concentration(start, stop)
/concentration_model.virus.viral_load_in_sputum
/concentration_model.infected.fraction_of_infectious_virus()
/concentration_model.infected.activity.exhalation_rate
)
normed_int_concentration_interpolated = np.interp(
@ -1737,8 +1709,7 @@ class ExposureModel:
deposited_exposure: _VectorisedFloat = 0.
for interaction in self.short_range:
start, stop = interaction.extract_between_bounds(time1, time2)
short_range_jet_exposure = interaction._normed_jet_exposure_between_bounds(
self.concentration_model, start, stop)
short_range_jet_exposure = interaction._normed_jet_exposure_between_bounds(start, stop)
short_range_lr_exposure = interaction._normed_interpolated_longrange_exposure_between_bounds(
self.concentration_model, start, stop)
dilution = interaction.dilution_factor()
@ -1772,8 +1743,8 @@ class ExposureModel:
# Then we multiply by the emission rate without the BR contribution (and conversion factor),
# and parameters of the vD equation (i.e. n_in).
deposited_exposure *= (
(self.concentration_model.infected.emission_rate_per_aerosol_per_person_when_present() /
(self.concentration_model.infected.activity.exhalation_rate * 10 ** 6)) *
(self.concentration_model.infected.emission_rate_per_aerosol_per_person_when_present() / (
self.concentration_model.infected.activity.exhalation_rate * 10**6)) *
(1 - self.exposed.mask.inhale_efficiency()))
# Long-range concentration
deposited_exposure += self.long_range_deposited_exposure_between_bounds(time1, time2)

View file

@ -49,7 +49,7 @@ def test_short_range_model_ndarray(concentration_model, short_range_model):
model = short_range_model.build_model(SAMPLE_SIZE)
assert isinstance(model._normed_concentration(concentration_model, 10.75), np.ndarray)
assert isinstance(model.short_range_concentration(concentration_model, 10.75), np.ndarray)
assert isinstance(model._normed_jet_exposure_between_bounds(concentration_model, 10.75, 10.85), np.ndarray)
assert isinstance(model._normed_jet_exposure_between_bounds(10.75, 10.85), np.ndarray)
assert isinstance(model._normed_interpolated_longrange_exposure_between_bounds(concentration_model, 10.75, 10.85), np.ndarray)
assert isinstance(model.short_range_concentration(concentration_model, 14.0), float)