Skip to content

Commit

Permalink
injected parameters in models, model and report generator
Browse files Browse the repository at this point in the history
  • Loading branch information
lrdossan committed Oct 26, 2023
1 parent 3cc9059 commit 3f75fc7
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 47 deletions.
43 changes: 12 additions & 31 deletions caimira/apps/calculator/model_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from .defaults import (NO_DEFAULT, DEFAULT_MC_SAMPLE_SIZE, DEFAULTS, ACTIVITIES, ACTIVITY_TYPES, COFFEE_OPTIONS_INT, CONFIDENCE_LEVEL_OPTIONS,
MECHANICAL_VENTILATION_TYPES, MASK_TYPES, MASK_WEARING_OPTIONS, MONTH_NAMES, VACCINE_BOOSTER_TYPE, VACCINE_TYPE,
VENTILATION_TYPES, VIRUS_TYPES, VOLUME_TYPES, WINDOWS_OPENING_REGIMES, WINDOWS_TYPES)
from caimira.store.configuration import config

LOG = logging.getLogger(__name__)

Expand Down Expand Up @@ -314,10 +315,10 @@ def initialize_room(self) -> models.Room:

if self.arve_sensors_option == False:
if self.room_heating_option:
humidity = 0.3
humidity = config.room['defaults']['humidity_with_heating']
else:
humidity = 0.5
inside_temp = 293.
humidity = config.room['defaults']['humidity_without_heating']
inside_temp = config.room['defaults']['inside_temp']
else:
humidity = float(self.humidity)
inside_temp = self.inside_temp
Expand Down Expand Up @@ -373,7 +374,7 @@ def build_CO2_model(self, sample_size=DEFAULT_MC_SAMPLE_SIZE) -> models.CO2Conce
if (self.activity_type == 'precise'):
activity_defn, _ = self.generate_precise_activity_expiration()
else:
activity_defn = ACTIVITIES[ACTIVITY_TYPES.index(self.activity_type)]['activity']
activity_defn = activity_defn = ACTIVITIES[self.activity_type]['activity']

population = mc.SimplePopulation(
number=models.IntPiecewiseConstant(transition_times=tuple(transition_times), values=tuple(total_people)),
Expand Down Expand Up @@ -476,7 +477,8 @@ def ventilation(self) -> models._VentilationBase:
# This is a minimal, always present source of ventilation, due
# to the air infiltration from the outside.
# See CERN-OPEN-2021-004, p. 12.
infiltration_ventilation = models.AirChange(active=always_on, air_exch=0.25)
residual_vent: float = config.ventilation['infiltration_ventilation'] # type: ignore
infiltration_ventilation = models.AirChange(active=always_on, air_exch=residual_vent)
if self.hepa_option:
hepa = models.HEPAFilter(active=always_on, q_air_mech=self.hepa_amount)
return models.MultipleVentilation((ventilation, hepa, infiltration_ventilation))
Expand Down Expand Up @@ -511,9 +513,8 @@ def infected_population(self) -> mc.InfectedPopulation:
# Initializes the virus
virus = virus_distributions[self.virus_type]

activity_index = ACTIVITY_TYPES.index(self.activity_type)
activity_defn = ACTIVITIES[activity_index]['activity']
expiration_defn = ACTIVITIES[activity_index]['expiration']
activity_defn = ACTIVITIES[self.activity_type]['activity']
expiration_defn = ACTIVITIES[self.activity_type]['expiration']

if (self.activity_type == 'smallmeeting'):
# Conversation of N people is approximately 1/N% of the time speaking.
Expand All @@ -538,29 +539,9 @@ def infected_population(self) -> mc.InfectedPopulation:
return infected

def exposed_population(self) -> mc.Population:
scenario_activity = {
'office': 'Seated',
'controlroom-day': 'Seated',
'controlroom-night': 'Seated',
'smallmeeting': 'Seated',
'largemeeting': 'Standing',
'callcentre': 'Seated',
'library': 'Seated',
'training': 'Standing',
'training_attendee': 'Seated',
'lab':'Light activity',
'workshop': 'Moderate activity',
'gym':'Heavy exercise',
'household-day': 'Light activity',
'household-night': 'Seated',
'primary-school': 'Light activity',
'secondary-school': 'Light activity',
'university': 'Seated',
'restaurant': 'Seated',
'precise': self.precise_activity['physical_activity'] if self.activity_type == 'precise' else None,
}

activity_defn = scenario_activity[self.activity_type]
activity_defn = (self.precise_activity['physical_activity']
if self.activity_type == 'precise'
else str(config.population_scenario_activity[self.activity_type]['activity']))
activity = activity_distributions[activity_defn]

infected_occupants = self.infected_people
Expand Down
9 changes: 6 additions & 3 deletions caimira/apps/calculator/report_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from ... import monte_carlo as mc
from .model_generator import FormData, DEFAULT_MC_SAMPLE_SIZE
from ... import dataclass_utils
from caimira.store.configuration import config


def model_start_end(model: models.ExposureModel):
Expand Down Expand Up @@ -201,16 +202,18 @@ def conditional_prob_inf_given_vl_dist(infection_probability: models._Vectorised
for vl_log in viral_loads:
specific_prob = infection_probability[np.where((vl_log-step/2-specific_vl)*(vl_log+step/2-specific_vl)<0)[0]] #type: ignore
pi_means.append(specific_prob.mean())
lower_percentiles.append(np.quantile(specific_prob, 0.05))
upper_percentiles.append(np.quantile(specific_prob, 0.95))
lower_percentiles.append(np.quantile(specific_prob, config.conditional_prob_inf_given_viral_load['lower_percentile']))
upper_percentiles.append(np.quantile(specific_prob, config.conditional_prob_inf_given_viral_load['upper_percentile']))

return pi_means, lower_percentiles, upper_percentiles


def manufacture_conditional_probability_data(exposure_model: models.ExposureModel,
infection_probability: models._VectorisedFloat):

min_vl, max_vl, step = 2, 10, 8/100
min_vl = config.conditional_prob_inf_given_viral_load['min_vl']
max_vl = config.conditional_prob_inf_given_viral_load['max_vl']
step = (max_vl - min_vl)/100
viral_loads = np.arange(min_vl, max_vl, step)
specific_vl = np.log10(exposure_model.concentration_model.virus.viral_load_in_sputum)
pi_means, lower_percentiles, upper_percentiles = conditional_prob_inf_given_vl_dist(infection_probability, viral_loads,
Expand Down
27 changes: 14 additions & 13 deletions caimira/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
from .utils import method_cache

from .dataclass_utils import nested_replace
from caimira.store.configuration import config

oneoverln2 = 1 / np.log(2)
# Define types for items supporting vectorisation. In the future this may be replaced
Expand Down Expand Up @@ -347,7 +348,7 @@ def discharge_coefficient(self) -> _VectorisedFloat:
Average measured value of discharge coefficient for sliding or
side-hung windows.
"""
return 0.6
return config.ventilation['natural']['discharge_factor']['sliding']


@dataclass(frozen=True)
Expand Down Expand Up @@ -860,7 +861,7 @@ def fraction_of_infectious_virus(self) -> _VectorisedFloat:
The fraction of infectious virus.
"""
return 1.
return config.population_with_virus['fraction_of_infectious_virus']

def aerosols(self):
"""
Expand Down Expand Up @@ -1032,7 +1033,7 @@ def min_background_concentration(self) -> _VectorisedFloat:
(in the same unit as the concentration). Its the value towards which
the concentration will decay to.
"""
return 0.
return config.concentration_model['min_background_concentration']

def normalization_factor(self) -> _VectorisedFloat:
"""
Expand Down Expand Up @@ -1220,7 +1221,7 @@ class ConcentrationModel(_ConcentrationModelBase):
#: evaporation factor: the particles' diameter is multiplied by this
# factor as soon as they are in the air (but AFTER going out of the,
# mask, if any).
evaporation_factor: float = 0.3
evaporation_factor: float = config.particle['evaporation_factor']

@property
def population(self) -> InfectedPopulation:
Expand Down Expand Up @@ -1260,10 +1261,10 @@ class CO2ConcentrationModel(_ConcentrationModelBase):
CO2_emitters: SimplePopulation

#: CO2 concentration in the atmosphere (in ppm)
CO2_atmosphere_concentration: float = 440.44
CO2_atmosphere_concentration: float = config.concentration_model['CO2_concentration_model']['CO2_atmosphere_concentration']

#: CO2 fraction in the exhaled air
CO2_fraction_exhaled: float = 0.042
CO2_fraction_exhaled: float = config.concentration_model['CO2_concentration_model']['CO2_fraction_exhaled']

@property
def population(self) -> SimplePopulation:
Expand Down Expand Up @@ -1309,14 +1310,14 @@ def dilution_factor(self) -> _VectorisedFloat:
The dilution factor for the respective expiratory activity type.
'''
# Average mouth opening diameter (m)
mouth_diameter = 0.02
mouth_diameter: float = config.short_range_model['dilution_factor']['mouth_diameter']

# Breathing rate, from m3/h to m3/s
BR = np.array(self.activity.exhalation_rate/3600.)

# Exhalation coefficient. Ratio between the duration of a breathing cycle and the duration of
# the exhalation.
φ = 2
φ: float = config.short_range_model['dilution_factor']['exhalation_coefficient']

# Exhalation airflow, as per Jia et al. (2022)
Q_exh: _VectorisedFloat = φ * BR
Expand All @@ -1328,12 +1329,12 @@ def dilution_factor(self) -> _VectorisedFloat:
u0 = np.array(Q_exh/Am)

# Duration of the expiration period(s), assuming a 4s breath-cycle
tstar = 2.0
tstar: float = config.short_range_model['dilution_factor']['tstar']

# Streamwise and radial penetration coefficients
𝛽r1 = 0.18
𝛽r2 = 0.2
𝛽x1 = 2.4
𝛽r1: float = config.short_range_model['dilution_factor']['penetration_coefficients']['𝛽r1']
𝛽r2: float = config.short_range_model['dilution_factor']['penetration_coefficients']['𝛽r2']
𝛽x1: float = config.short_range_model['dilution_factor']['penetration_coefficients']['𝛽x1']

# Parameters in the jet-like stage
# Position of virtual origin
Expand Down Expand Up @@ -1489,7 +1490,7 @@ class ExposureModel:
geographical_data: Cases

#: The number of times the exposure event is repeated (default 1).
repeats: int = 1
repeats: int = config.exposure_model['repeats']

def __post_init__(self):
"""
Expand Down

0 comments on commit 3f75fc7

Please sign in to comment.