From 3f75fc77cd1cbb6a01929b4a6fbdafad756cb325 Mon Sep 17 00:00:00 2001 From: Luis Aleixo Date: Thu, 26 Oct 2023 16:29:20 +0200 Subject: [PATCH] injected parameters in models, model and report generator --- caimira/apps/calculator/model_generator.py | 43 ++++++--------------- caimira/apps/calculator/report_generator.py | 9 +++-- caimira/models.py | 27 ++++++------- 3 files changed, 32 insertions(+), 47 deletions(-) diff --git a/caimira/apps/calculator/model_generator.py b/caimira/apps/calculator/model_generator.py index a5fce869..b844aa68 100644 --- a/caimira/apps/calculator/model_generator.py +++ b/caimira/apps/calculator/model_generator.py @@ -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__) @@ -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 @@ -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)), @@ -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)) @@ -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. @@ -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 diff --git a/caimira/apps/calculator/report_generator.py b/caimira/apps/calculator/report_generator.py index 6efb9177..12c2eddc 100644 --- a/caimira/apps/calculator/report_generator.py +++ b/caimira/apps/calculator/report_generator.py @@ -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): @@ -201,8 +202,8 @@ 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 @@ -210,7 +211,9 @@ def conditional_prob_inf_given_vl_dist(infection_probability: models._Vectorised 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, diff --git a/caimira/models.py b/caimira/models.py index 24ef62e2..d1dc1a87 100644 --- a/caimira/models.py +++ b/caimira/models.py @@ -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 @@ -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) @@ -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): """ @@ -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: """ @@ -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: @@ -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: @@ -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 @@ -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 @@ -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): """