From bccbf09e4b9cb016b8f67ea6abebd48664cf646f Mon Sep 17 00:00:00 2001 From: Luis Aleixo Date: Thu, 7 Mar 2024 16:44:31 +0100 Subject: [PATCH 01/10] updated schema to reflect v2.0.0 --- .../apps/calculator/co2_model_generator.py | 2 +- caimira/apps/calculator/model_generator.py | 10 +- caimira/apps/calculator/report_generator.py | 10 +- caimira/models.py | 12 +- caimira/monte_carlo/data.py | 58 +-- caimira/store/data_registry.py | 420 ++++++++---------- 6 files changed, 243 insertions(+), 269 deletions(-) diff --git a/caimira/apps/calculator/co2_model_generator.py b/caimira/apps/calculator/co2_model_generator.py index e2584256..a4a6a9c2 100644 --- a/caimira/apps/calculator/co2_model_generator.py +++ b/caimira/apps/calculator/co2_model_generator.py @@ -159,7 +159,7 @@ def ventilation_transition_times(self) -> typing.Tuple[float, ...]: return tuple((self.CO2_data['times'][0], self.CO2_data['times'][-1])) def build_model(self, size=None) -> models.CO2DataModel: # type: ignore - size = size or self.data_registry.monte_carlo_sample_size + size = size or self.data_registry.monte_carlo['sample_size'] # Build a simple infected and exposed population for the case when presence # intervals and number of people are dynamic. Activity type is not needed. infected_presence = self.infected_present_interval() diff --git a/caimira/apps/calculator/model_generator.py b/caimira/apps/calculator/model_generator.py index 5d6e1a34..69c198dd 100644 --- a/caimira/apps/calculator/model_generator.py +++ b/caimira/apps/calculator/model_generator.py @@ -200,10 +200,10 @@ def initialize_room(self) -> models.Room: if self.arve_sensors_option == False: if self.room_heating_option: - humidity = self.data_registry.room['defaults']['humidity_with_heating'] + humidity = self.data_registry.room['humidity_with_heating'] else: - humidity = self.data_registry.room['defaults']['humidity_without_heating'] - inside_temp = self.data_registry.room['defaults']['inside_temp'] + humidity = self.data_registry.room['humidity_without_heating'] + inside_temp = self.data_registry.room['inside_temp'] else: humidity = float(self.humidity) inside_temp = self.inside_temp @@ -245,11 +245,11 @@ def build_mc_model(self) -> mc.ExposureModel: ) def build_model(self, sample_size=None) -> models.ExposureModel: - sample_size = sample_size or self.data_registry.monte_carlo_sample_size + sample_size = sample_size or self.data_registry.monte_carlo['sample_size'] return self.build_mc_model().build_model(size=sample_size) def build_CO2_model(self, sample_size=None) -> models.CO2ConcentrationModel: - sample_size = sample_size or self.data_registry.monte_carlo_sample_size + sample_size = sample_size or self.data_registry.monte_carlo['sample_size'] infected_population: models.InfectedPopulation = self.infected_population().build_model(sample_size) exposed_population: models.Population = self.exposed_population().build_model(sample_size) diff --git a/caimira/apps/calculator/report_generator.py b/caimira/apps/calculator/report_generator.py index 8ae92987..53f67249 100644 --- a/caimira/apps/calculator/report_generator.py +++ b/caimira/apps/calculator/report_generator.py @@ -233,8 +233,8 @@ def conditional_prob_inf_given_vl_dist( 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, data_registry.conditional_prob_inf_given_viral_load['lower_percentile'])) - upper_percentiles.append(np.quantile(specific_prob, data_registry.conditional_prob_inf_given_viral_load['upper_percentile'])) + lower_percentiles.append(np.quantile(specific_prob, 0.05)) + upper_percentiles.append(np.quantile(specific_prob, 0.95)) return pi_means, lower_percentiles, upper_percentiles @@ -245,8 +245,8 @@ def manufacture_conditional_probability_data( ): data_registry: DataRegistry = exposure_model.data_registry - min_vl = data_registry.conditional_prob_inf_given_viral_load['min_vl'] - max_vl = data_registry.conditional_prob_inf_given_viral_load['max_vl'] + min_vl = 2 + max_vl = 10 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) @@ -442,7 +442,7 @@ def scenario_statistics( sample_times: typing.List[float], compute_prob_exposure: bool ): - model = mc_model.build_model(size=mc_model.data_registry.monte_carlo_sample_size) + model = mc_model.build_model(size=mc_model.data_registry.monte_carlo['sample_size']) if (compute_prob_exposure): # It means we have data to calculate the total_probability_rule prob_probabilistic_exposure = model.total_probability_rule() diff --git a/caimira/models.py b/caimira/models.py index 441b70ff..b06c9d9b 100644 --- a/caimira/models.py +++ b/caimira/models.py @@ -879,7 +879,7 @@ def fraction_of_infectious_virus(self) -> _VectorisedFloat: The fraction of infectious virus. """ - return self.data_registry.population_with_virus['fraction_of_infectious_virus'] # type: ignore + return 1 def aerosols(self): """ @@ -1052,7 +1052,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 self.data_registry.concentration_model['min_background_concentration'] # type: ignore + return self.data_registry.concentration_model['virus_concentration_model']['min_background_concentration'] # type: ignore def normalization_factor(self) -> _VectorisedFloat: """ @@ -1242,7 +1242,7 @@ class ConcentrationModel(_ConcentrationModelBase): def __post_init__(self): if self.evaporation_factor is None: - self.evaporation_factor = self.data_registry.particle['evaporation_factor'] + self.evaporation_factor = self.data_registry.expiration_particle['particle']['evaporation_factor'] @property def population(self) -> InfectedPopulation: @@ -1335,7 +1335,7 @@ def dilution_factor(self) -> _VectorisedFloat: ''' The dilution factor for the respective expiratory activity type. ''' - _dilution_factor = self.data_registry.short_range_model['dilution_factor'] + _dilution_factor = self.data_registry.short_range_model['dilution_factor'] # Average mouth opening diameter (m) mouth_diameter: float = _dilution_factor['mouth_diameter'] # type: ignore @@ -1359,7 +1359,7 @@ def dilution_factor(self) -> _VectorisedFloat: tstar: float = _dilution_factor['tstar'] # type: ignore # Streamwise and radial penetration coefficients - _df_pc = _dilution_factor['penetration_coefficients'] + _df_pc = _dilution_factor['penetration_coefficients'] # type: ignore 𝛽r1: float = _df_pc['𝛽r1'] # type: ignore 𝛽r2: float = _df_pc['𝛽r2'] # type: ignore 𝛽x1: float = _df_pc['𝛽x1'] # type: ignore @@ -1585,7 +1585,7 @@ class ExposureModel: #: The number of times the exposure event is repeated (default 1). @property def repeats(self) -> int: - return self.data_registry.exposure_model['repeats'] # type: ignore + return 1 def __post_init__(self): """ diff --git a/caimira/monte_carlo/data.py b/caimira/monte_carlo/data.py index d82970aa..feac2368 100644 --- a/caimira/monte_carlo/data.py +++ b/caimira/monte_carlo/data.py @@ -148,21 +148,21 @@ class BLOmodel: # total concentration of aerosols for each mode. @property def cn(self) -> typing.Tuple[float, float, float]: - _cn = self.data_registry.BLOmodel['cn'] + _cn = self.data_registry.expiration_particle['BLOmodel']['cn'] # type: ignore return (_cn['B'],_cn['L'],_cn['O']) # Mean of the underlying normal distributions (represents the log of a # diameter in microns), for resp. the B, L and O modes. @property def mu(self) -> typing.Tuple[float, float, float]: - _mu = self.data_registry.BLOmodel['mu'] + _mu = self.data_registry.expiration_particle['BLOmodel']['mu'] # type: ignore return (_mu['B'], _mu['L'], _mu['O']) # Std deviation of the underlying normal distribution, for resp. # the B, L and O modes. @property def sigma(self) -> typing.Tuple[float, float, float]: - _sigma = self.data_registry.BLOmodel['sigma'] + _sigma = self.data_registry.expiration_particle['BLOmodel']['sigma'] # type: ignore return (_sigma['B'],_sigma['L'],_sigma['O']) def distribution(self, d): @@ -250,51 +250,51 @@ def activity_distributions(data_registry): def viral_load(data_registry): return np.linspace( weibull_min.ppf( - data_registry.covid_overal_vl_data['start'], - c=data_registry.covid_overal_vl_data['shape_factor'], - scale=data_registry.covid_overal_vl_data['scale_factor'] + data_registry.virological_data['covid_overal_vl_data']['start'], + c=data_registry.virological_data['covid_overal_vl_data']['shape_factor'], + scale=data_registry.virological_data['covid_overal_vl_data']['scale_factor'] ), weibull_min.ppf( - data_registry.covid_overal_vl_data['stop'], - c=data_registry.covid_overal_vl_data['shape_factor'], - scale=data_registry.covid_overal_vl_data['scale_factor'] + data_registry.virological_data['covid_overal_vl_data']['stop'], + c=data_registry.virological_data['covid_overal_vl_data']['shape_factor'], + scale=data_registry.virological_data['covid_overal_vl_data']['scale_factor'] ), - int(data_registry.covid_overal_vl_data['num']) + int(data_registry.virological_data['covid_overal_vl_data']['num']) ) def frequencies_pdf(data_registry): return weibull_min.pdf( viral_load(data_registry), - c=data_registry.covid_overal_vl_data['shape_factor'], - scale=data_registry.covid_overal_vl_data['scale_factor'] + c=data_registry.virological_data['covid_overal_vl_data']['shape_factor'], + scale=data_registry.virological_data['covid_overal_vl_data']['scale_factor'] ) def covid_overal_vl_data(data_registry): return LogCustom( - bounds=(data_registry.covid_overal_vl_data['min_bound'], data_registry.covid_overal_vl_data['max_bound']), + bounds=(data_registry.virological_data['covid_overal_vl_data']['min_bound'], data_registry.virological_data['covid_overal_vl_data']['max_bound']), function=lambda d: np.interp( d, viral_load(data_registry), frequencies_pdf(data_registry), - data_registry.covid_overal_vl_data['interpolation_fp_left'], - data_registry.covid_overal_vl_data['interpolation_fp_right'] + data_registry.virological_data['covid_overal_vl_data']['interpolation_fp_left'], + data_registry.virological_data['covid_overal_vl_data']['interpolation_fp_right'] ), - max_function=data_registry.covid_overal_vl_data['max_function'] + max_function=data_registry.virological_data['covid_overal_vl_data']['max_function'] ) # Derived from data in doi.org/10.1016/j.ijid.2020.09.025 and # https://iosh.com/media/8432/aerosol-infection-risk-hospital-patient-care-full-report.pdf (page 60) def viable_to_RNA_ratio_distribution(data_registry): - return Uniform(data_registry.viable_to_RNA_ratio_distribution['low'], data_registry.viable_to_RNA_ratio_distribution['high']) + return Uniform(data_registry.virological_data['viable_to_RNA_ratio_distribution']['low'], data_registry.virological_data['viable_to_RNA_ratio_distribution']['high']) # From discussion with virologists def infectious_dose_distribution(data_registry): - return Uniform(data_registry.infectious_dose_distribution['low'], data_registry.infectious_dose_distribution['high']) + return Uniform(data_registry.virological_data['infectious_dose_distribution']['low'], data_registry.virological_data['infectious_dose_distribution']['high']) # From https://doi.org/10.1101/2021.10.14.21264988 and references therein def virus_distributions(data_registry): - vd = data_registry.virus_distributions + vd = data_registry.virological_data['virus_distributions'] return { 'SARS_CoV_2': mc.SARSCoV2( viral_load_in_sputum=evaluate_vl(vd['SARS_CoV_2']['viral_load_in_sputum'], data_registry), @@ -392,10 +392,10 @@ def expiration_distribution( def expiration_BLO_factors(data_registry): - breathing = data_registry.expiration_BLO_factors['Breathing'] - speaking = data_registry.expiration_BLO_factors['Speaking'] - singing = data_registry.expiration_BLO_factors['Singing'] - shouting = data_registry.expiration_BLO_factors['Shouting'] + breathing = data_registry.expiration_particle['expiration_BLO_factors']['Breathing'] + speaking = data_registry.expiration_particle['expiration_BLO_factors']['Speaking'] + singing = data_registry.expiration_particle['expiration_BLO_factors']['Singing'] + shouting = data_registry.expiration_particle['expiration_BLO_factors']['Shouting'] return { 'Breathing': ( param_evaluation(breathing, 'B'), @@ -425,8 +425,8 @@ def expiration_distributions(data_registry): exp_type: expiration_distribution( data_registry=data_registry, BLO_factors=BLO_factors, - d_min=param_evaluation(data_registry.long_range_expiration_distributions, 'minimum_diameter'), - d_max=param_evaluation(data_registry.long_range_expiration_distributions, 'maximum_diameter') + d_min=param_evaluation(data_registry.expiration_particle['long_range_expiration_distributions'], 'minimum_diameter'), + d_max=param_evaluation(data_registry.expiration_particle['long_range_expiration_distributions'], 'maximum_diameter') ) for exp_type, BLO_factors in expiration_BLO_factors(data_registry).items() } @@ -437,8 +437,8 @@ def short_range_expiration_distributions(data_registry): exp_type: expiration_distribution( data_registry=data_registry, BLO_factors=BLO_factors, - d_min=param_evaluation(data_registry.short_range_expiration_distributions, 'minimum_diameter'), - d_max=param_evaluation(data_registry.short_range_expiration_distributions, 'maximum_diameter') + d_min=param_evaluation(data_registry.expiration_particle['short_range_expiration_distributions'], 'minimum_diameter'), + d_max=param_evaluation(data_registry.expiration_particle['short_range_expiration_distributions'], 'maximum_diameter') ) for exp_type, BLO_factors in expiration_BLO_factors(data_registry).items() } @@ -452,8 +452,8 @@ def short_range_expiration_distributions(data_registry): def short_range_distances(data_registry): return Custom( bounds=( - param_evaluation(data_registry.short_range_distances, 'minimum_distance'), - param_evaluation(data_registry.short_range_distances, 'maximum_distance') + param_evaluation(data_registry.short_range_model['conversational_distance'], 'minimum_distance'), + param_evaluation(data_registry.short_range_model['conversational_distance'], 'maximum_distance') ), function=lambda x: np.interp(x, distances, frequencies, left=0., right=0.), max_function=0.13 diff --git a/caimira/store/data_registry.py b/caimira/store/data_registry.py index cced1032..eb5759f3 100644 --- a/caimira/store/data_registry.py +++ b/caimira/store/data_registry.py @@ -6,23 +6,31 @@ class DataRegistry: version = None - BLOmodel = { - "cn": { - "B": 0.06, - "L": 0.2, - "O": 0.0010008, - }, - "mu": { - "B": 0.989541, - "L": 1.38629, - "O": 4.97673, - }, - "sigma": { - "B": 0.262364, - "L": 0.506818, - "O": 0.585005, - }, + expiration_particle = { + "long_range_expiration_distributions": { + "minimum_diameter": 0.1, + "maximum_diameter": 30, + }, + "short_range_expiration_distributions": { + "minimum_diameter": 0.1, + "maximum_diameter": 100, + }, + "BLOmodel": { + "cn": {"B": 0.06, "L": 0.2, "O": 0.0010008}, + "mu": {"B": 0.989541, "L": 1.38629, "O": 4.97673}, + "sigma": {"B": 0.262364, "L": 0.506818, "O": 0.585005}, + }, + "expiration_BLO_factors": { + "Breathing": {"B": 1., "L": 0., "O": 0., }, + "Speaking": {"B": 1., "L": 1., "O": 1., }, + "Singing": {"B": 1., "L": 5., "O": 5., }, + "Shouting": {"B": 1., "L": 5., "O": 5., }, + }, + "particle": { + "evaporation_factor": 0.3, + } } + activity_distributions = { "Seated": { "inhalation_rate": { @@ -105,160 +113,164 @@ class DataRegistry: }, }, } - symptomatic_vl_frequencies = { - "log_variable": [ - 2.46032, - 2.67431, - 2.85434, - 3.06155, - 3.25856, - 3.47256, - 3.66957, - 3.85979, - 4.09927, - 4.27081, - 4.47631, - 4.66653, - 4.87204, - 5.10302, - 5.27456, - 5.46478, - 5.6533, - 5.88428, - 6.07281, - 6.30549, - 6.48552, - 6.64856, - 6.85407, - 7.10373, - 7.30075, - 7.47229, - 7.66081, - 7.85782, - 8.05653, - 8.27053, - 8.48453, - 8.65607, - 8.90573, - 9.06878, - 9.27429, - 9.473, - 9.66152, - 9.87552, - ], - "frequencies": [ - 0.001206885, - 0.007851618, - 0.008078144, - 0.01502491, - 0.013258014, - 0.018528495, - 0.020053765, - 0.021896167, - 0.022047184, - 0.018604005, - 0.01547796, - 0.018075445, - 0.021503523, - 0.022349217, - 0.025097721, - 0.032875078, - 0.030594727, - 0.032573045, - 0.034717482, - 0.034792991, - 0.033267721, - 0.042887485, - 0.036846816, - 0.03876473, - 0.045016819, - 0.040063473, - 0.04883754, - 0.043944602, - 0.048142864, - 0.041588741, - 0.048762031, - 0.027921732, - 0.033871788, - 0.022122693, - 0.016927718, - 0.008833228, - 0.00478598, - 0.002807662, - ], - "kernel_bandwidth": 0.1, - } - covid_overal_vl_data = { - "shape_factor": 3.47, - "scale_factor": 7.01, - "start": 0.01, - "stop": 0.99, - "num": 30.0, - "min_bound": 2, - "max_bound": 10, - "interpolation_fp_left": 0, - "interpolation_fp_right": 0, - "max_function": 0.2, - } - viable_to_RNA_ratio_distribution = { - "low": 0.01, - "high": 0.6, - } - infectious_dose_distribution = { - "low": 10, - "high": 100, - } - virus_distributions = { - "SARS_CoV_2": { - "viral_load_in_sputum": ViralLoads.COVID_OVERALL.value, - "infectious_dose": InfectiousDoses.DISTRIBUTION.value, - "viable_to_RNA_ratio": ViableToRNARatios.DISTRIBUTION.value, - "transmissibility_factor": 1, - "infectiousness_days": 14, - }, - "SARS_CoV_2_ALPHA": { - "viral_load_in_sputum": ViralLoads.COVID_OVERALL.value, - "infectious_dose": InfectiousDoses.DISTRIBUTION.value, - "viable_to_RNA_ratio": ViableToRNARatios.DISTRIBUTION.value, - "transmissibility_factor": 0.78, - "infectiousness_days": 14, - }, - "SARS_CoV_2_BETA": { - "viral_load_in_sputum": ViralLoads.COVID_OVERALL.value, - "infectious_dose": InfectiousDoses.DISTRIBUTION.value, - "viable_to_RNA_ratio": ViableToRNARatios.DISTRIBUTION.value, - "transmissibility_factor": 0.8, - "infectiousness_days": 14, - }, - "SARS_CoV_2_GAMMA": { - "viral_load_in_sputum": ViralLoads.COVID_OVERALL.value, - "infectious_dose": InfectiousDoses.DISTRIBUTION.value, - "viable_to_RNA_ratio": ViableToRNARatios.DISTRIBUTION.value, - "transmissibility_factor": 0.72, - "infectiousness_days": 14, - }, - "SARS_CoV_2_DELTA": { - "viral_load_in_sputum": ViralLoads.COVID_OVERALL.value, - "infectious_dose": InfectiousDoses.DISTRIBUTION.value, - "viable_to_RNA_ratio": ViableToRNARatios.DISTRIBUTION.value, - "transmissibility_factor": 0.51, - "infectiousness_days": 14, - }, - "SARS_CoV_2_OMICRON": { - "viral_load_in_sputum": ViralLoads.COVID_OVERALL.value, - "infectious_dose": InfectiousDoses.DISTRIBUTION.value, - "viable_to_RNA_ratio": ViableToRNARatios.DISTRIBUTION.value, - "transmissibility_factor": 0.2, - "infectiousness_days": 14, - }, - "SARS_CoV_2_Other": { - "viral_load_in_sputum": ViralLoads.COVID_OVERALL.value, - "infectious_dose": InfectiousDoses.DISTRIBUTION.value, - "viable_to_RNA_ratio": ViableToRNARatios.DISTRIBUTION.value, - "transmissibility_factor": 0.1, - "infectiousness_days": 14, + + virological_data = { + "symptomatic_vl_frequencies": { + "log_variable": [ + 2.46032, + 2.67431, + 2.85434, + 3.06155, + 3.25856, + 3.47256, + 3.66957, + 3.85979, + 4.09927, + 4.27081, + 4.47631, + 4.66653, + 4.87204, + 5.10302, + 5.27456, + 5.46478, + 5.6533, + 5.88428, + 6.07281, + 6.30549, + 6.48552, + 6.64856, + 6.85407, + 7.10373, + 7.30075, + 7.47229, + 7.66081, + 7.85782, + 8.05653, + 8.27053, + 8.48453, + 8.65607, + 8.90573, + 9.06878, + 9.27429, + 9.473, + 9.66152, + 9.87552, + ], + "frequencies": [ + 0.001206885, + 0.007851618, + 0.008078144, + 0.01502491, + 0.013258014, + 0.018528495, + 0.020053765, + 0.021896167, + 0.022047184, + 0.018604005, + 0.01547796, + 0.018075445, + 0.021503523, + 0.022349217, + 0.025097721, + 0.032875078, + 0.030594727, + 0.032573045, + 0.034717482, + 0.034792991, + 0.033267721, + 0.042887485, + 0.036846816, + 0.03876473, + 0.045016819, + 0.040063473, + 0.04883754, + 0.043944602, + 0.048142864, + 0.041588741, + 0.048762031, + 0.027921732, + 0.033871788, + 0.022122693, + 0.016927718, + 0.008833228, + 0.00478598, + 0.002807662, + ], + "kernel_bandwidth": 0.1, + }, + 'covid_overal_vl_data': { + "shape_factor": 3.47, + "scale_factor": 7.01, + "start": 0.01, + "stop": 0.99, + "num": 30.0, + "min_bound": 2, + "max_bound": 10, + "interpolation_fp_left": 0, + "interpolation_fp_right": 0, + "max_function": 0.2, + }, + "viable_to_RNA_ratio_distribution": { + "low": 0.01, + "high": 0.6, + }, + "infectious_dose_distribution": { + "low": 10, + "high": 100, + }, + "virus_distributions": { + "SARS_CoV_2": { + "viral_load_in_sputum": ViralLoads.COVID_OVERALL.value, + "infectious_dose": InfectiousDoses.DISTRIBUTION.value, + "viable_to_RNA_ratio": ViableToRNARatios.DISTRIBUTION.value, + "transmissibility_factor": 1, + "infectiousness_days": 14, + }, + "SARS_CoV_2_ALPHA": { + "viral_load_in_sputum": ViralLoads.COVID_OVERALL.value, + "infectious_dose": InfectiousDoses.DISTRIBUTION.value, + "viable_to_RNA_ratio": ViableToRNARatios.DISTRIBUTION.value, + "transmissibility_factor": 0.78, + "infectiousness_days": 14, + }, + "SARS_CoV_2_BETA": { + "viral_load_in_sputum": ViralLoads.COVID_OVERALL.value, + "infectious_dose": InfectiousDoses.DISTRIBUTION.value, + "viable_to_RNA_ratio": ViableToRNARatios.DISTRIBUTION.value, + "transmissibility_factor": 0.8, + "infectiousness_days": 14, + }, + "SARS_CoV_2_GAMMA": { + "viral_load_in_sputum": ViralLoads.COVID_OVERALL.value, + "infectious_dose": InfectiousDoses.DISTRIBUTION.value, + "viable_to_RNA_ratio": ViableToRNARatios.DISTRIBUTION.value, + "transmissibility_factor": 0.72, + "infectiousness_days": 14, + }, + "SARS_CoV_2_DELTA": { + "viral_load_in_sputum": ViralLoads.COVID_OVERALL.value, + "infectious_dose": InfectiousDoses.DISTRIBUTION.value, + "viable_to_RNA_ratio": ViableToRNARatios.DISTRIBUTION.value, + "transmissibility_factor": 0.51, + "infectiousness_days": 14, + }, + "SARS_CoV_2_OMICRON": { + "viral_load_in_sputum": ViralLoads.COVID_OVERALL.value, + "infectious_dose": InfectiousDoses.DISTRIBUTION.value, + "viable_to_RNA_ratio": ViableToRNARatios.DISTRIBUTION.value, + "transmissibility_factor": 0.2, + "infectiousness_days": 14, + }, + "SARS_CoV_2_Other": { + "viral_load_in_sputum": ViralLoads.COVID_OVERALL.value, + "infectious_dose": InfectiousDoses.DISTRIBUTION.value, + "viable_to_RNA_ratio": ViableToRNARatios.DISTRIBUTION.value, + "transmissibility_factor": 0.1, + "infectiousness_days": 14, + }, }, } + mask_distributions = { "Type I": { "η_inhale": { @@ -301,50 +313,15 @@ class DataRegistry: "factor_exhale": 1, }, } - expiration_BLO_factors = { - "Breathing": { - "B": 1.0, - "L": 0.0, - "O": 0.0, - }, - "Speaking": { - "B": 1.0, - "L": 1.0, - "O": 1.0, - }, - "Singing": { - "B": 1.0, - "L": 5.0, - "O": 5.0, - }, - "Shouting": { - "B": 1.0, - "L": 5.0, - "O": 5.0, - }, - } - long_range_expiration_distributions = { - "minimum_diameter": 0.1, - "maximum_diameter": 30, - } - short_range_expiration_distributions = { - "minimum_diameter": 0.1, - "maximum_diameter": 100, - } - short_range_distances = { - "minimum_distance": 0.5, - "maximum_distance": 2.0, - } #################################### room = { - "defaults": { - "inside_temp": 293, - "humidity_with_heating": 0.3, - "humidity_without_heating": 0.5, - }, + "inside_temp": 293, + "humidity_with_heating": 0.3, + "humidity_without_heating": 0.5, } + ventilation = { "natural": { "discharge_factor": { @@ -353,19 +330,17 @@ class DataRegistry: }, "infiltration_ventilation": 0.25, } - particle = { - "evaporation_factor": 0.3, - } - population_with_virus = { - "fraction_of_infectious_virus": 1, - } + concentration_model = { - "min_background_concentration": 0.0, + "virus_concentration_model": { + "min_background_concentration": 0.0, + }, "CO2_concentration_model": { "CO2_atmosphere_concentration": 440.44, "CO2_fraction_exhaled": 0.042, }, } + short_range_model = { "dilution_factor": { "mouth_diameter": 0.02, @@ -377,17 +352,16 @@ class DataRegistry: "𝛽x1": 2.4, }, }, + "conversational_distance": { + "minimum_distance": 0.5, + "maximum_distance": 2.0, + }, } - exposure_model = { - "repeats": 1, - } - conditional_prob_inf_given_viral_load = { - "lower_percentile": 0.05, - "upper_percentile": 0.95, - "min_vl": 2, - "max_vl": 10, + + monte_carlo = { + "sample_size": 250000, } - monte_carlo_sample_size = 250000 + population_scenario_activity = { "office": {"placeholder": "Office", "activity": "Seated", "expiration": {"Speaking": 1, "Breathing": 2}}, "smallmeeting": { From 43ab73e1b79b4153ac6779050c82f9883cc2663c Mon Sep 17 00:00:00 2001 From: Luis Aleixo Date: Thu, 7 Mar 2024 17:11:04 +0100 Subject: [PATCH 02/10] changes on custom distributions definitions --- caimira/monte_carlo/data.py | 50 +++++++++++++++++----------------- caimira/store/data_registry.py | 31 ++++++++++----------- 2 files changed, 39 insertions(+), 42 deletions(-) diff --git a/caimira/monte_carlo/data.py b/caimira/monte_carlo/data.py index feac2368..ebf330d9 100644 --- a/caimira/monte_carlo/data.py +++ b/caimira/monte_carlo/data.py @@ -41,50 +41,50 @@ def evaluate_vtrr(value, data_registry: DataRegistry): sqrt2 = np.sqrt(2.) -def custom_distribution_lookup(dict: dict, key_part: str) -> typing.Any: +def custom_value_type_lookup(dict: dict, key_part: str) -> typing.Any: """ - Look up a custom distribution based on a partial key. + Look up a custom value type based on a partial key. Args: dict (dict): The root to search. - key_part (str): The distribution key to match. + key_part (str): The value type key to match. Returns: - str: The associated distribution. + str: The associated value. """ try: for key, value in dict.items(): if (key_part in key): - return value['associated_distribution'] + return value['associated_value'] except KeyError: return f"Key '{key_part}' not found." -def evaluate_custom_distribution(dist: str, params: typing.Dict) -> typing.Any: +def evaluate_custom_value_type(dist: str, params: typing.Dict) -> typing.Any: """ - Evaluate a custom distribution. + Evaluate a custom value type. Args: - dist (str): The type of distribution. - params (Dict): The parameters for the distribution. + dist (str): The type of value. + params (Dict): The parameters for the value type. Returns: - Any: The generated distribution. + Any: The generated value. Raises: - ValueError: If the distribution type is not recognized. + ValueError: If the value type is not recognized. """ - if dist == 'Linear Space': - return np.linspace(params['start'], params['stop'], params['num']) - elif dist == 'Normal': + if dist == 'Constant': + return params + elif dist == 'Normal distribution': return Normal(params['normal_mean_gaussian'], params['normal_standard_deviation_gaussian']) - elif dist == 'Log-normal': + elif dist == 'Log-normal distribution': return LogNormal(params['lognormal_mean_gaussian'], params['lognormal_standard_deviation_gaussian']) - elif dist == 'Uniform': + elif dist == 'Uniform distribution': return Uniform(params['low'], params['high']) else: - raise ValueError('Bad request - distribution not found.') + raise ValueError('Bad request - value type not found.') def param_evaluation(root: typing.Dict, param: typing.Union[str, typing.Any]) -> typing.Any: @@ -106,15 +106,15 @@ def param_evaluation(root: typing.Dict, param: typing.Union[str, typing.Any]) -> if isinstance(value, str): if value == 'Custom': - custom_distribution: typing.Dict = custom_distribution_lookup( + custom_value_type: typing.Dict = custom_value_type_lookup( root, 'custom distribution') - for d, p in custom_distribution.items(): - return evaluate_custom_distribution(d, p) + for d, p in custom_value_type.items(): + return evaluate_custom_value_type(d, p) elif isinstance(value, dict): - dist: str = root[param]['associated_distribution'] + dist: str = root[param]['associated_value'] params: typing.Dict = root[param]['parameters'] - return evaluate_custom_distribution(dist, params) + return evaluate_custom_value_type(dist, params) elif isinstance(value, float) or isinstance(value, int): return value @@ -347,21 +347,21 @@ def mask_distributions(data_registry): data_registry.mask_distributions['Type I'], 'η_inhale'), η_exhale=param_evaluation( data_registry.mask_distributions['Type I'], 'η_exhale') - if data_registry.mask_distributions['Type I']['Known filtration efficiency of masks when exhaling?'] == 'Yes' else None, + if data_registry.mask_distributions['Type I'].get('η_exhale') is not None else None ), 'FFP2': mc.Mask( η_inhale=param_evaluation( data_registry.mask_distributions['FFP2'], 'η_inhale'), η_exhale=param_evaluation( data_registry.mask_distributions['FFP2'], 'η_exhale') - if data_registry.mask_distributions['FFP2']['Known filtration efficiency of masks when exhaling?'] == 'Yes' else None, + if data_registry.mask_distributions['FFP2'].get('η_exhale') is not None else None ), 'Cloth': mc.Mask( η_inhale=param_evaluation( data_registry.mask_distributions['Cloth'], 'η_inhale'), η_exhale=param_evaluation( data_registry.mask_distributions['Cloth'], 'η_exhale') - if data_registry.mask_distributions['Cloth']['Known filtration efficiency of masks when exhaling?'] == 'Yes' else None, + if data_registry.mask_distributions['Cloth'].get('η_exhale') is not None else None ), } diff --git a/caimira/store/data_registry.py b/caimira/store/data_registry.py index eb5759f3..da3da011 100644 --- a/caimira/store/data_registry.py +++ b/caimira/store/data_registry.py @@ -34,14 +34,14 @@ class DataRegistry: activity_distributions = { "Seated": { "inhalation_rate": { - "associated_distribution": "Log-normal", + "associated_value": "Log-normal distribution", "parameters": { "lognormal_mean_gaussian": -0.6872121723362303, "lognormal_standard_deviation_gaussian": 0.10498338229297108, }, }, "exhalation_rate": { - "associated_distribution": "Log-normal", + "associated_value": "Log-normal distribution", "parameters": { "lognormal_mean_gaussian": -0.6872121723362303, "lognormal_standard_deviation_gaussian": 0.10498338229297108, @@ -50,14 +50,14 @@ class DataRegistry: }, "Standing": { "inhalation_rate": { - "associated_distribution": "Log-normal", + "associated_value": "Log-normal distribution", "parameters": { "lognormal_mean_gaussian": -0.5742377578494785, "lognormal_standard_deviation_gaussian": 0.09373162411398223, }, }, "exhalation_rate": { - "associated_distribution": "Log-normal", + "associated_value": "Log-normal distribution", "parameters": { "lognormal_mean_gaussian": -0.5742377578494785, "lognormal_standard_deviation_gaussian": 0.09373162411398223, @@ -66,14 +66,14 @@ class DataRegistry: }, "Light activity": { "inhalation_rate": { - "associated_distribution": "Log-normal", + "associated_value": "Log-normal distribution", "parameters": { "lognormal_mean_gaussian": 0.21380242785625422, "lognormal_standard_deviation_gaussian": 0.09435378091059601, }, }, "exhalation_rate": { - "associated_distribution": "Log-normal", + "associated_value": "Log-normal distribution", "parameters": { "lognormal_mean_gaussian": 0.21380242785625422, "lognormal_standard_deviation_gaussian": 0.09435378091059601, @@ -82,14 +82,14 @@ class DataRegistry: }, "Moderate activity": { "inhalation_rate": { - "associated_distribution": "Log-normal", + "associated_value": "Log-normal distribution", "parameters": { "lognormal_mean_gaussian": 0.551771330362601, "lognormal_standard_deviation_gaussian": 0.1894616357138137, }, }, "exhalation_rate": { - "associated_distribution": "Log-normal", + "associated_value": "Log-normal distribution", "parameters": { "lognormal_mean_gaussian": 0.551771330362601, "lognormal_standard_deviation_gaussian": 0.1894616357138137, @@ -98,14 +98,14 @@ class DataRegistry: }, "Heavy exercise": { "inhalation_rate": { - "associated_distribution": "Log-normal", + "associated_value": "Log-normal distribution", "parameters": { "lognormal_mean_gaussian": 1.1644665696723049, "lognormal_standard_deviation_gaussian": 0.21744554768657565, }, }, "exhalation_rate": { - "associated_distribution": "Log-normal", + "associated_value": "Log-normal distribution", "parameters": { "lognormal_mean_gaussian": 1.1644665696723049, "lognormal_standard_deviation_gaussian": 0.21744554768657565, @@ -274,37 +274,34 @@ class DataRegistry: mask_distributions = { "Type I": { "η_inhale": { - "associated_distribution": "Uniform", + "associated_value": "Uniform distribution", "parameters": { "low": 0.25, "high": 0.80, }, }, - "Known filtration efficiency of masks when exhaling?": "No", "factor_exhale": 1, }, "FFP2": { "η_inhale": { - "associated_distribution": "Uniform", + "associated_value": "Uniform distribution", "parameters": { "low": 0.83, "high": 0.91, }, }, - "Known filtration efficiency of masks when exhaling?": "No", "factor_exhale": 1, }, "Cloth": { "η_inhale": { - "associated_distribution": "Uniform", + "associated_value": "Uniform distribution", "parameters": { "low": 0.05, "high": 0.40, }, }, - "Known filtration efficiency of masks when exhaling?": "Yes", "η_exhale": { - "associated_distribution": "Uniform", + "associated_value": "Uniform distribution", "parameters": { "low": 0.20, "high": 0.50, From 647f79ffdae00a1107d0f5d9fb0ad3e6120b9259 Mon Sep 17 00:00:00 2001 From: Luis Aleixo Date: Thu, 7 Mar 2024 17:12:01 +0100 Subject: [PATCH 03/10] modified data service host (QA) --- caimira/store/data_service.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/caimira/store/data_service.py b/caimira/store/data_service.py index 80463f42..1ecbac82 100644 --- a/caimira/store/data_service.py +++ b/caimira/store/data_service.py @@ -20,7 +20,7 @@ def __init__( self._host = host @classmethod - def create(cls, host: str = "https://caimira-data-api.app.cern.ch"): + def create(cls, host: str = "https://caimira-data-api-qa.app.cern.ch"): """Factory.""" return cls(host) From f66aac703e076914737017c7d6c56ed63d82cae8 Mon Sep 17 00:00:00 2001 From: Luis Aleixo Date: Thu, 7 Mar 2024 17:16:43 +0100 Subject: [PATCH 04/10] modifed symptomatic_vl_frequencies to fetch data from registry --- caimira/monte_carlo/data.py | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/caimira/monte_carlo/data.py b/caimira/monte_carlo/data.py index ebf330d9..8958fe5e 100644 --- a/caimira/monte_carlo/data.py +++ b/caimira/monte_carlo/data.py @@ -229,19 +229,12 @@ def activity_distributions(data_registry): # From https://doi.org/10.1101/2021.10.14.21264988 and references therein -symptomatic_vl_frequencies = LogCustomKernel( - np.array((2.46032, 2.67431, 2.85434, 3.06155, 3.25856, 3.47256, 3.66957, 3.85979, 4.09927, 4.27081, - 4.47631, 4.66653, 4.87204, 5.10302, 5.27456, 5.46478, 5.6533, 5.88428, 6.07281, 6.30549, - 6.48552, 6.64856, 6.85407, 7.10373, 7.30075, 7.47229, 7.66081, 7.85782, 8.05653, 8.27053, - 8.48453, 8.65607, 8.90573, 9.06878, 9.27429, 9.473, 9.66152, 9.87552)), - np.array((0.001206885, 0.007851618, 0.008078144, 0.01502491, 0.013258014, 0.018528495, 0.020053765, - 0.021896167, 0.022047184, 0.018604005, 0.01547796, 0.018075445, 0.021503523, 0.022349217, - 0.025097721, 0.032875078, 0.030594727, 0.032573045, 0.034717482, 0.034792991, - 0.033267721, 0.042887485, 0.036846816, 0.03876473, 0.045016819, 0.040063473, 0.04883754, - 0.043944602, 0.048142864, 0.041588741, 0.048762031, 0.027921732, 0.033871788, - 0.022122693, 0.016927718, 0.008833228, 0.00478598, 0.002807662)), - kernel_bandwidth=0.1 -) +def symptomatic_vl_frequencies(data_registry): + return LogCustomKernel( + np.array(data_registry.virological_data['symptomatic_vl_frequencies']['log_variable']), + np.array(data_registry.virological_data['symptomatic_vl_frequencies']['frequencies']), + kernel_bandwidth=data_registry.virological_data['symptomatic_vl_frequencies']['kernel_bandwidth'] + ) # Weibull distribution with a shape factor of 3.47 and a scale factor of 7.01. From 9b84dc21f29703000d5e574c5c8dbaae2de361ac Mon Sep 17 00:00:00 2001 From: Luis Aleixo Date: Fri, 8 Mar 2024 10:07:10 +0100 Subject: [PATCH 05/10] added cast to int when fetching env. var. that enables data service --- caimira/apps/calculator/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/caimira/apps/calculator/__init__.py b/caimira/apps/calculator/__init__.py index 74435939..edfc75d1 100644 --- a/caimira/apps/calculator/__init__.py +++ b/caimira/apps/calculator/__init__.py @@ -573,7 +573,7 @@ def make_app( data_registry = DataRegistry() data_service = None - data_service_enabled = os.environ.get('DATA_SERVICE_ENABLED', 0) + data_service_enabled = int(os.environ.get('DATA_SERVICE_ENABLED', 0)) if data_service_enabled: data_service = DataService.create() return Application( From 7cc598ca7d141c56364a410d45926d48745a15dd Mon Sep 17 00:00:00 2001 From: Luis Aleixo Date: Fri, 8 Mar 2024 10:43:48 +0100 Subject: [PATCH 06/10] handled way to visualize custom value types (namely constant values); handled display of conditional probability plot --- caimira/apps/calculator/report_generator.py | 45 +++++++---- .../templates/base/calculator.report.html.j2 | 11 +-- caimira/monte_carlo/data.py | 79 +++++++++---------- caimira/store/data_registry.py | 7 -- 4 files changed, 76 insertions(+), 66 deletions(-) diff --git a/caimira/apps/calculator/report_generator.py b/caimira/apps/calculator/report_generator.py index 53f67249..d0288b03 100644 --- a/caimira/apps/calculator/report_generator.py +++ b/caimira/apps/calculator/report_generator.py @@ -19,6 +19,7 @@ from ... import monte_carlo as mc from .model_generator import VirusFormData from ... import dataclass_utils +from caimira.enums import ViralLoads def model_start_end(model: models.ExposureModel): @@ -168,12 +169,27 @@ def calculate_report_data(form: VirusFormData, model: models.ExposureModel, exec prob_dist_count, prob_dist_bins = np.histogram(prob/100, bins=100, density=True) prob_probabilistic_exposure = np.array(model.total_probability_rule()).mean() expected_new_cases = np.array(model.expected_new_cases()).mean() - uncertainties_plot_src = img2base64(_figure2bytes(uncertainties_plot(model, prob))) if form.conditional_probability_plot else None exposed_presence_intervals = [list(interval) for interval in model.exposed.presence_interval().boundaries()] - conditional_probability_data = {key: value for key, value in - zip(('viral_loads', 'pi_means', 'lower_percentiles', 'upper_percentiles'), - manufacture_conditional_probability_data(model, prob))} + if (model.data_registry.virological_data['virus_distributions'][form.virus_type]['viral_load_in_sputum'] == ViralLoads.COVID_OVERALL.value # type: ignore + and form.conditional_probability_plot): # Only generate this data if covid_overall_vl_data is selected. + + viral_load_in_sputum: models._VectorisedFloat = model.concentration_model.infected.virus.viral_load_in_sputum + viral_loads, pi_means, lower_percentiles, upper_percentiles = manufacture_conditional_probability_data(model, prob) + + uncertainties_plot_src = img2base64(_figure2bytes(uncertainties_plot(prob, viral_load_in_sputum, viral_loads, + pi_means, lower_percentiles, upper_percentiles))) + conditional_probability_data = {key: value for key, value in + zip(('viral_loads', 'pi_means', 'lower_percentiles', 'upper_percentiles'), + (viral_loads, pi_means, lower_percentiles, upper_percentiles))} + vl_dist = list(np.log10(viral_load_in_sputum)) + + else: + uncertainties_plot_src = None + conditional_probability_data = None + vl = model.concentration_model.virus.viral_load_in_sputum + if isinstance(vl, np.ndarray): vl_dist = list(np.log10(model.concentration_model.virus.viral_load_in_sputum)) + else: vl_dist = np.log10(model.concentration_model.virus.viral_load_in_sputum) return { "model_repr": repr(model), @@ -194,7 +210,7 @@ def calculate_report_data(form: VirusFormData, model: models.ExposureModel, exec "expected_new_cases": expected_new_cases, "uncertainties_plot_src": uncertainties_plot_src, "CO2_concentrations": CO2_concentrations, - "vl_dist": list(np.log10(model.concentration_model.virus.viral_load_in_sputum)), + "vl_dist": vl_dist, "conditional_probability_data": conditional_probability_data, } @@ -256,11 +272,12 @@ def manufacture_conditional_probability_data( return list(viral_loads), list(pi_means), list(lower_percentiles), list(upper_percentiles) -def uncertainties_plot(exposure_model: models.ExposureModel, prob: models._VectorisedFloat): - fig = plt.figure(figsize=(4, 7), dpi=110) - - infection_probability = prob / 100 - viral_loads, pi_means, lower_percentiles, upper_percentiles = manufacture_conditional_probability_data(exposure_model, infection_probability) +def uncertainties_plot(infection_probability: models._VectorisedFloat, + viral_load_in_sputum: models._VectorisedFloat, + viral_loads: models._VectorisedFloat, + pi_means: models._VectorisedFloat, + lower_percentiles: models._VectorisedFloat, + upper_percentiles: models._VectorisedFloat): fig, axs = plt.subplots(2, 3, gridspec_kw={'width_ratios': [5, 0.5] + [1], @@ -273,8 +290,8 @@ def uncertainties_plot(exposure_model: models.ExposureModel, prob: models._Vecto axs[0, 1].set_visible(False) - axs[0, 0].plot(viral_loads, pi_means, label='Predictive total probability') - axs[0, 0].fill_between(viral_loads, lower_percentiles, upper_percentiles, alpha=0.1, label='5ᵗʰ and 95ᵗʰ percentile') + axs[0, 0].plot(viral_loads, np.array(pi_means)/100, label='Predictive total probability') + axs[0, 0].fill_between(viral_loads, np.array(lower_percentiles)/100, np.array(upper_percentiles)/100, alpha=0.1, label='5ᵗʰ and 95ᵗʰ percentile') axs[0, 2].hist(infection_probability, bins=30, orientation='horizontal') axs[0, 2].set_xticks([]) @@ -285,8 +302,8 @@ def uncertainties_plot(exposure_model: models.ExposureModel, prob: models._Vecto axs[0, 2].set_xlim(0, highest_bar) axs[0, 2].text(highest_bar * 0.5, 0.5, - rf"$\bf{np.round(np.mean(infection_probability) * 100, 1)}$%", ha='center', va='center') - axs[1, 0].hist(np.log10(exposure_model.concentration_model.infected.virus.viral_load_in_sputum), + rf"$\bf{np.round(np.mean(infection_probability), 1)}$%", ha='center', va='center') + axs[1, 0].hist(np.log10(viral_load_in_sputum), bins=150, range=(2, 10), color='grey') axs[1, 0].set_facecolor("lightgrey") axs[1, 0].set_yticks([]) diff --git a/caimira/apps/templates/base/calculator.report.html.j2 b/caimira/apps/templates/base/calculator.report.html.j2 index 76c3d3f9..041cf3ef 100644 --- a/caimira/apps/templates/base/calculator.report.html.j2 +++ b/caimira/apps/templates/base/calculator.report.html.j2 @@ -214,11 +214,12 @@ draw_histogram("prob_inf_hist", {{ prob_inf }}, {{ prob_inf_sd }});
- -
- - -
+ {% if model.data_registry.virological_data['virus_distributions'][form.virus_type]['viral_load_in_sputum'] == 'Ref: Viral load - covid_overal_vl_data' %} +
+ + +
+ {% endif %} {% if form.conditional_probability_plot %}
diff --git a/caimira/monte_carlo/data.py b/caimira/monte_carlo/data.py index 8958fe5e..d6e523b0 100644 --- a/caimira/monte_carlo/data.py +++ b/caimira/monte_carlo/data.py @@ -14,25 +14,31 @@ from caimira.store.data_registry import DataRegistry -def evaluate_vl(value, data_registry: DataRegistry): - if value == ViralLoads.COVID_OVERALL.value: +def evaluate_vl(root: typing.Dict, value: str, data_registry: DataRegistry): + if root[value] == ViralLoads.COVID_OVERALL.value: return covid_overal_vl_data(data_registry) - elif value == ViralLoads.SYMPTOMATIC_FREQUENCIES.value: + elif root[value] == ViralLoads.SYMPTOMATIC_FREQUENCIES.value: return symptomatic_vl_frequencies + elif root[value] == 'Custom': + return param_evaluation(root, 'Viral load custom') else: raise ValueError(f"Invalid ViralLoads value {value}") -def evaluate_infectd(value, data_registry: DataRegistry): - if value == InfectiousDoses.DISTRIBUTION.value: +def evaluate_infectd(root: typing.Dict, value: str, data_registry: DataRegistry): + if root[value] == InfectiousDoses.DISTRIBUTION.value: return infectious_dose_distribution(data_registry) + elif root[value] == "Custom": + return param_evaluation(root, 'Infectious dose custom') else: raise ValueError(f"Invalid InfectiousDoses value {value}") -def evaluate_vtrr(value, data_registry: DataRegistry): - if value == ViableToRNARatios.DISTRIBUTION.value: +def evaluate_vtrr(root: typing.Dict, value: str, data_registry: DataRegistry): + if root[value] == ViableToRNARatios.DISTRIBUTION.value: return viable_to_RNA_ratio_distribution(data_registry) + elif root[value] == "Custom": + return param_evaluation(root, 'Viable to RNA ratio custom') else: raise ValueError(f"Invalid ViableToRNARatios value {value}") @@ -60,7 +66,7 @@ def custom_value_type_lookup(dict: dict, key_part: str) -> typing.Any: return f"Key '{key_part}' not found." -def evaluate_custom_value_type(dist: str, params: typing.Dict) -> typing.Any: +def evaluate_custom_value_type(value_type: str, params: typing.Dict) -> typing.Any: """ Evaluate a custom value type. @@ -75,13 +81,13 @@ def evaluate_custom_value_type(dist: str, params: typing.Dict) -> typing.Any: ValueError: If the value type is not recognized. """ - if dist == 'Constant': + if value_type == 'Constant value': return params - elif dist == 'Normal distribution': + elif value_type == 'Normal distribution': return Normal(params['normal_mean_gaussian'], params['normal_standard_deviation_gaussian']) - elif dist == 'Log-normal distribution': + elif value_type == 'Log-normal distribution': return LogNormal(params['lognormal_mean_gaussian'], params['lognormal_standard_deviation_gaussian']) - elif dist == 'Uniform distribution': + elif value_type == 'Uniform distribution': return Uniform(params['low'], params['high']) else: raise ValueError('Bad request - value type not found.') @@ -104,17 +110,10 @@ def param_evaluation(root: typing.Dict, param: typing.Union[str, typing.Any]) -> """ value = root.get(param) - if isinstance(value, str): - if value == 'Custom': - custom_value_type: typing.Dict = custom_value_type_lookup( - root, 'custom distribution') - for d, p in custom_value_type.items(): - return evaluate_custom_value_type(d, p) - - elif isinstance(value, dict): - dist: str = root[param]['associated_value'] + if isinstance(value, dict): + value_type: str = root[param]['associated_value'] params: typing.Dict = root[param]['parameters'] - return evaluate_custom_value_type(dist, params) + return evaluate_custom_value_type(value_type, params) elif isinstance(value, float) or isinstance(value, int): return value @@ -290,39 +289,39 @@ def virus_distributions(data_registry): vd = data_registry.virological_data['virus_distributions'] return { 'SARS_CoV_2': mc.SARSCoV2( - viral_load_in_sputum=evaluate_vl(vd['SARS_CoV_2']['viral_load_in_sputum'], data_registry), - infectious_dose=evaluate_infectd(vd['SARS_CoV_2']['infectious_dose'], data_registry), - viable_to_RNA_ratio=evaluate_vtrr(vd['SARS_CoV_2']['viable_to_RNA_ratio'], data_registry), + viral_load_in_sputum=evaluate_vl(vd['SARS_CoV_2'], 'viral_load_in_sputum', data_registry), + infectious_dose=evaluate_infectd(vd['SARS_CoV_2'], 'infectious_dose', data_registry), + viable_to_RNA_ratio=evaluate_vtrr(vd['SARS_CoV_2'], 'viable_to_RNA_ratio', data_registry), transmissibility_factor=vd['SARS_CoV_2']['transmissibility_factor'], ), 'SARS_CoV_2_ALPHA': mc.SARSCoV2( - viral_load_in_sputum=evaluate_vl(vd['SARS_CoV_2_ALPHA']['viral_load_in_sputum'], data_registry), - infectious_dose=evaluate_infectd(vd['SARS_CoV_2_ALPHA']['infectious_dose'], data_registry), - viable_to_RNA_ratio=evaluate_vtrr(vd['SARS_CoV_2_ALPHA']['viable_to_RNA_ratio'], data_registry), + viral_load_in_sputum=evaluate_vl(vd['SARS_CoV_2_ALPHA'], 'viral_load_in_sputum', data_registry), + infectious_dose=evaluate_infectd(vd['SARS_CoV_2_ALPHA'], 'infectious_dose', data_registry), + viable_to_RNA_ratio=evaluate_vtrr(vd['SARS_CoV_2_ALPHA'], 'viable_to_RNA_ratio', data_registry), transmissibility_factor=vd['SARS_CoV_2_ALPHA']['transmissibility_factor'], ), 'SARS_CoV_2_BETA': mc.SARSCoV2( - viral_load_in_sputum=evaluate_vl(vd['SARS_CoV_2_BETA']['viral_load_in_sputum'], data_registry), - infectious_dose=evaluate_infectd(vd['SARS_CoV_2_BETA']['infectious_dose'], data_registry), - viable_to_RNA_ratio=evaluate_vtrr(vd['SARS_CoV_2_BETA']['viable_to_RNA_ratio'], data_registry), + viral_load_in_sputum=evaluate_vl(vd['SARS_CoV_2_BETA'], 'viral_load_in_sputum', data_registry), + infectious_dose=evaluate_infectd(vd['SARS_CoV_2_BETA'], 'infectious_dose', data_registry), + viable_to_RNA_ratio=evaluate_vtrr(vd['SARS_CoV_2_BETA'], 'viable_to_RNA_ratio', data_registry), transmissibility_factor=vd['SARS_CoV_2_BETA']['transmissibility_factor'], ), 'SARS_CoV_2_GAMMA': mc.SARSCoV2( - viral_load_in_sputum=evaluate_vl(vd['SARS_CoV_2_GAMMA']['viral_load_in_sputum'], data_registry), - infectious_dose=evaluate_infectd(vd['SARS_CoV_2_GAMMA']['infectious_dose'], data_registry), - viable_to_RNA_ratio=evaluate_vtrr(vd['SARS_CoV_2_GAMMA']['viable_to_RNA_ratio'], data_registry), + viral_load_in_sputum=evaluate_vl(vd['SARS_CoV_2_GAMMA'], 'viral_load_in_sputum', data_registry), + infectious_dose=evaluate_infectd(vd['SARS_CoV_2_GAMMA'], 'infectious_dose', data_registry), + viable_to_RNA_ratio=evaluate_vtrr(vd['SARS_CoV_2_GAMMA'], 'viable_to_RNA_ratio', data_registry), transmissibility_factor=vd['SARS_CoV_2_GAMMA']['transmissibility_factor'], ), 'SARS_CoV_2_DELTA': mc.SARSCoV2( - viral_load_in_sputum=evaluate_vl(vd['SARS_CoV_2_DELTA']['viral_load_in_sputum'], data_registry), - infectious_dose=evaluate_infectd(vd['SARS_CoV_2_DELTA']['infectious_dose'], data_registry), - viable_to_RNA_ratio=evaluate_vtrr(vd['SARS_CoV_2_DELTA']['viable_to_RNA_ratio'], data_registry), + viral_load_in_sputum=evaluate_vl(vd['SARS_CoV_2_DELTA'], 'viral_load_in_sputum', data_registry), + infectious_dose=evaluate_infectd(vd['SARS_CoV_2_DELTA'], 'infectious_dose', data_registry), + viable_to_RNA_ratio=evaluate_vtrr(vd['SARS_CoV_2_DELTA'], 'viable_to_RNA_ratio', data_registry), transmissibility_factor=vd['SARS_CoV_2_DELTA']['transmissibility_factor'], ), 'SARS_CoV_2_OMICRON': mc.SARSCoV2( - viral_load_in_sputum=evaluate_vl(vd['SARS_CoV_2_OMICRON']['viral_load_in_sputum'], data_registry), - infectious_dose=evaluate_infectd(vd['SARS_CoV_2_OMICRON']['infectious_dose'], data_registry), - viable_to_RNA_ratio=evaluate_vtrr(vd['SARS_CoV_2_OMICRON']['viable_to_RNA_ratio'], data_registry), + viral_load_in_sputum=evaluate_vl(vd['SARS_CoV_2_OMICRON'], 'viral_load_in_sputum', data_registry), + infectious_dose=evaluate_infectd(vd['SARS_CoV_2_OMICRON'], 'infectious_dose', data_registry), + viable_to_RNA_ratio=evaluate_vtrr(vd['SARS_CoV_2_OMICRON'], 'viable_to_RNA_ratio', data_registry), transmissibility_factor=vd['SARS_CoV_2_OMICRON']['transmissibility_factor'], ), } diff --git a/caimira/store/data_registry.py b/caimira/store/data_registry.py index da3da011..59e4026b 100644 --- a/caimira/store/data_registry.py +++ b/caimira/store/data_registry.py @@ -261,13 +261,6 @@ class DataRegistry: "transmissibility_factor": 0.2, "infectiousness_days": 14, }, - "SARS_CoV_2_Other": { - "viral_load_in_sputum": ViralLoads.COVID_OVERALL.value, - "infectious_dose": InfectiousDoses.DISTRIBUTION.value, - "viable_to_RNA_ratio": ViableToRNARatios.DISTRIBUTION.value, - "transmissibility_factor": 0.1, - "infectiousness_days": 14, - }, }, } From fb56b8866fdb5d521dec30656c71f4cce28187cd Mon Sep 17 00:00:00 2001 From: Luis Aleixo Date: Thu, 14 Mar 2024 16:19:38 +0100 Subject: [PATCH 07/10] removal of viable_to_RNA_ratio_distribution and infectious_dose_distribution from data registry --- caimira/enums.py | 8 --- caimira/monte_carlo/data.py | 52 +++++--------- caimira/store/data_registry.py | 70 +++++++++++++------ caimira/tests/test_monte_carlo_full_models.py | 8 +-- 4 files changed, 70 insertions(+), 68 deletions(-) diff --git a/caimira/enums.py b/caimira/enums.py index 6a776e2c..8b66443b 100644 --- a/caimira/enums.py +++ b/caimira/enums.py @@ -3,11 +3,3 @@ class ViralLoads(Enum): COVID_OVERALL = "Ref: Viral load - covid_overal_vl_data" SYMPTOMATIC_FREQUENCIES = "Ref: Viral load - symptomatic_vl_frequencies" - - -class InfectiousDoses(Enum): - DISTRIBUTION = "Ref: Infectious dose - infectious_dose_distribution" - - -class ViableToRNARatios(Enum): - DISTRIBUTION = "Ref: Viable to RNA ratio - viable_to_RNA_ratio_distribution" diff --git a/caimira/monte_carlo/data.py b/caimira/monte_carlo/data.py index d6e523b0..d35ab37d 100644 --- a/caimira/monte_carlo/data.py +++ b/caimira/monte_carlo/data.py @@ -7,7 +7,7 @@ from scipy import special as sp from scipy.stats import weibull_min -from caimira.enums import ViralLoads, InfectiousDoses, ViableToRNARatios +from caimira.enums import ViralLoads import caimira.monte_carlo.models as mc from caimira.monte_carlo.sampleable import LogCustom, LogNormal, Normal, LogCustomKernel, CustomKernel, Uniform, Custom @@ -25,24 +25,6 @@ def evaluate_vl(root: typing.Dict, value: str, data_registry: DataRegistry): raise ValueError(f"Invalid ViralLoads value {value}") -def evaluate_infectd(root: typing.Dict, value: str, data_registry: DataRegistry): - if root[value] == InfectiousDoses.DISTRIBUTION.value: - return infectious_dose_distribution(data_registry) - elif root[value] == "Custom": - return param_evaluation(root, 'Infectious dose custom') - else: - raise ValueError(f"Invalid InfectiousDoses value {value}") - - -def evaluate_vtrr(root: typing.Dict, value: str, data_registry: DataRegistry): - if root[value] == ViableToRNARatios.DISTRIBUTION.value: - return viable_to_RNA_ratio_distribution(data_registry) - elif root[value] == "Custom": - return param_evaluation(root, 'Viable to RNA ratio custom') - else: - raise ValueError(f"Invalid ViableToRNARatios value {value}") - - sqrt2pi = np.sqrt(2.*np.pi) sqrt2 = np.sqrt(2.) @@ -275,13 +257,13 @@ def covid_overal_vl_data(data_registry): # Derived from data in doi.org/10.1016/j.ijid.2020.09.025 and # https://iosh.com/media/8432/aerosol-infection-risk-hospital-patient-care-full-report.pdf (page 60) -def viable_to_RNA_ratio_distribution(data_registry): - return Uniform(data_registry.virological_data['viable_to_RNA_ratio_distribution']['low'], data_registry.virological_data['viable_to_RNA_ratio_distribution']['high']) +def viable_to_RNA_ratio_distribution(): + return Uniform(0.01, 0.6) # From discussion with virologists -def infectious_dose_distribution(data_registry): - return Uniform(data_registry.virological_data['infectious_dose_distribution']['low'], data_registry.virological_data['infectious_dose_distribution']['high']) +def infectious_dose_distribution(): + return Uniform(10., 100.) # From https://doi.org/10.1101/2021.10.14.21264988 and references therein @@ -290,38 +272,38 @@ def virus_distributions(data_registry): return { 'SARS_CoV_2': mc.SARSCoV2( viral_load_in_sputum=evaluate_vl(vd['SARS_CoV_2'], 'viral_load_in_sputum', data_registry), - infectious_dose=evaluate_infectd(vd['SARS_CoV_2'], 'infectious_dose', data_registry), - viable_to_RNA_ratio=evaluate_vtrr(vd['SARS_CoV_2'], 'viable_to_RNA_ratio', data_registry), + infectious_dose=param_evaluation(vd['SARS_CoV_2'], 'infectious_dose'), + viable_to_RNA_ratio=param_evaluation(vd['SARS_CoV_2'], 'viable_to_RNA_ratio'), transmissibility_factor=vd['SARS_CoV_2']['transmissibility_factor'], ), 'SARS_CoV_2_ALPHA': mc.SARSCoV2( viral_load_in_sputum=evaluate_vl(vd['SARS_CoV_2_ALPHA'], 'viral_load_in_sputum', data_registry), - infectious_dose=evaluate_infectd(vd['SARS_CoV_2_ALPHA'], 'infectious_dose', data_registry), - viable_to_RNA_ratio=evaluate_vtrr(vd['SARS_CoV_2_ALPHA'], 'viable_to_RNA_ratio', data_registry), + infectious_dose=param_evaluation(vd['SARS_CoV_2_ALPHA'], 'infectious_dose'), + viable_to_RNA_ratio=param_evaluation(vd['SARS_CoV_2_ALPHA'], 'viable_to_RNA_ratio'), transmissibility_factor=vd['SARS_CoV_2_ALPHA']['transmissibility_factor'], ), 'SARS_CoV_2_BETA': mc.SARSCoV2( viral_load_in_sputum=evaluate_vl(vd['SARS_CoV_2_BETA'], 'viral_load_in_sputum', data_registry), - infectious_dose=evaluate_infectd(vd['SARS_CoV_2_BETA'], 'infectious_dose', data_registry), - viable_to_RNA_ratio=evaluate_vtrr(vd['SARS_CoV_2_BETA'], 'viable_to_RNA_ratio', data_registry), + infectious_dose=param_evaluation(vd['SARS_CoV_2_BETA'], 'infectious_dose'), + viable_to_RNA_ratio=param_evaluation(vd['SARS_CoV_2_BETA'], 'viable_to_RNA_ratio'), transmissibility_factor=vd['SARS_CoV_2_BETA']['transmissibility_factor'], ), 'SARS_CoV_2_GAMMA': mc.SARSCoV2( viral_load_in_sputum=evaluate_vl(vd['SARS_CoV_2_GAMMA'], 'viral_load_in_sputum', data_registry), - infectious_dose=evaluate_infectd(vd['SARS_CoV_2_GAMMA'], 'infectious_dose', data_registry), - viable_to_RNA_ratio=evaluate_vtrr(vd['SARS_CoV_2_GAMMA'], 'viable_to_RNA_ratio', data_registry), + infectious_dose=param_evaluation(vd['SARS_CoV_2_GAMMA'], 'infectious_dose'), + viable_to_RNA_ratio=param_evaluation(vd['SARS_CoV_2_GAMMA'], 'viable_to_RNA_ratio'), transmissibility_factor=vd['SARS_CoV_2_GAMMA']['transmissibility_factor'], ), 'SARS_CoV_2_DELTA': mc.SARSCoV2( viral_load_in_sputum=evaluate_vl(vd['SARS_CoV_2_DELTA'], 'viral_load_in_sputum', data_registry), - infectious_dose=evaluate_infectd(vd['SARS_CoV_2_DELTA'], 'infectious_dose', data_registry), - viable_to_RNA_ratio=evaluate_vtrr(vd['SARS_CoV_2_DELTA'], 'viable_to_RNA_ratio', data_registry), + infectious_dose=param_evaluation(vd['SARS_CoV_2_DELTA'], 'infectious_dose'), + viable_to_RNA_ratio=param_evaluation(vd['SARS_CoV_2_DELTA'], 'viable_to_RNA_ratio'), transmissibility_factor=vd['SARS_CoV_2_DELTA']['transmissibility_factor'], ), 'SARS_CoV_2_OMICRON': mc.SARSCoV2( viral_load_in_sputum=evaluate_vl(vd['SARS_CoV_2_OMICRON'], 'viral_load_in_sputum', data_registry), - infectious_dose=evaluate_infectd(vd['SARS_CoV_2_OMICRON'], 'infectious_dose', data_registry), - viable_to_RNA_ratio=evaluate_vtrr(vd['SARS_CoV_2_OMICRON'], 'viable_to_RNA_ratio', data_registry), + infectious_dose=param_evaluation(vd['SARS_CoV_2_OMICRON'], 'infectious_dose'), + viable_to_RNA_ratio=param_evaluation(vd['SARS_CoV_2_OMICRON'], 'viable_to_RNA_ratio'), transmissibility_factor=vd['SARS_CoV_2_OMICRON']['transmissibility_factor'], ), } diff --git a/caimira/store/data_registry.py b/caimira/store/data_registry.py index 59e4026b..429644ae 100644 --- a/caimira/store/data_registry.py +++ b/caimira/store/data_registry.py @@ -1,4 +1,4 @@ -from caimira.enums import ViralLoads, InfectiousDoses, ViableToRNARatios +from caimira.enums import ViralLoads class DataRegistry: @@ -210,54 +210,82 @@ class DataRegistry: "interpolation_fp_right": 0, "max_function": 0.2, }, - "viable_to_RNA_ratio_distribution": { - "low": 0.01, - "high": 0.6, - }, - "infectious_dose_distribution": { - "low": 10, - "high": 100, - }, "virus_distributions": { "SARS_CoV_2": { "viral_load_in_sputum": ViralLoads.COVID_OVERALL.value, - "infectious_dose": InfectiousDoses.DISTRIBUTION.value, - "viable_to_RNA_ratio": ViableToRNARatios.DISTRIBUTION.value, + "infectious_dose": { + "associated_value": "Uniform distribution", + "parameters": {"low": 10, "high": 100}, + }, + "viable_to_RNA_ratio": { + 'associated_value': 'Uniform distribution', + 'parameters': {'low': 0.01, 'high': 0.6}, + }, "transmissibility_factor": 1, "infectiousness_days": 14, }, "SARS_CoV_2_ALPHA": { "viral_load_in_sputum": ViralLoads.COVID_OVERALL.value, - "infectious_dose": InfectiousDoses.DISTRIBUTION.value, - "viable_to_RNA_ratio": ViableToRNARatios.DISTRIBUTION.value, + "infectious_dose": { + "associated_value": "Uniform distribution", + "parameters": {"low": 10, "high": 100}, + }, + "viable_to_RNA_ratio": { + 'associated_value': 'Uniform distribution', + 'parameters': {'low': 0.01, 'high': 0.6}, + }, "transmissibility_factor": 0.78, "infectiousness_days": 14, }, "SARS_CoV_2_BETA": { "viral_load_in_sputum": ViralLoads.COVID_OVERALL.value, - "infectious_dose": InfectiousDoses.DISTRIBUTION.value, - "viable_to_RNA_ratio": ViableToRNARatios.DISTRIBUTION.value, + "infectious_dose": { + "associated_value": "Uniform distribution", + "parameters": {"low": 10, "high": 100}, + }, + "viable_to_RNA_ratio": { + 'associated_value': 'Uniform distribution', + 'parameters': {'low': 0.01, 'high': 0.6}, + }, "transmissibility_factor": 0.8, "infectiousness_days": 14, }, "SARS_CoV_2_GAMMA": { "viral_load_in_sputum": ViralLoads.COVID_OVERALL.value, - "infectious_dose": InfectiousDoses.DISTRIBUTION.value, - "viable_to_RNA_ratio": ViableToRNARatios.DISTRIBUTION.value, + "infectious_dose": { + "associated_value": "Uniform distribution", + "parameters": {"low": 10, "high": 100}, + }, + "viable_to_RNA_ratio": { + 'associated_value': 'Uniform distribution', + 'parameters': {'low': 0.01, 'high': 0.6}, + }, "transmissibility_factor": 0.72, "infectiousness_days": 14, }, "SARS_CoV_2_DELTA": { "viral_load_in_sputum": ViralLoads.COVID_OVERALL.value, - "infectious_dose": InfectiousDoses.DISTRIBUTION.value, - "viable_to_RNA_ratio": ViableToRNARatios.DISTRIBUTION.value, + "infectious_dose": { + "associated_value": "Uniform distribution", + "parameters": {"low": 10, "high": 100}, + }, + "viable_to_RNA_ratio": { + 'associated_value': 'Uniform distribution', + 'parameters': {'low': 0.01, 'high': 0.6}, + }, "transmissibility_factor": 0.51, "infectiousness_days": 14, }, "SARS_CoV_2_OMICRON": { "viral_load_in_sputum": ViralLoads.COVID_OVERALL.value, - "infectious_dose": InfectiousDoses.DISTRIBUTION.value, - "viable_to_RNA_ratio": ViableToRNARatios.DISTRIBUTION.value, + "infectious_dose": { + "associated_value": "Uniform distribution", + "parameters": {"low": 10, "high": 100}, + }, + "viable_to_RNA_ratio": { + 'associated_value': 'Uniform distribution', + 'parameters': {'low': 0.01, 'high': 0.6}, + }, "transmissibility_factor": 0.2, "infectiousness_days": 14, }, diff --git a/caimira/tests/test_monte_carlo_full_models.py b/caimira/tests/test_monte_carlo_full_models.py index f5b6bc74..ec4f6496 100644 --- a/caimira/tests/test_monte_carlo_full_models.py +++ b/caimira/tests/test_monte_carlo_full_models.py @@ -186,8 +186,8 @@ def skagit_chorale_mc(data_registry): presence=models.SpecificInterval(((0, 2.5), )), virus=mc.SARSCoV2( viral_load_in_sputum=10**9, - infectious_dose=infectious_dose_distribution(data_registry), - viable_to_RNA_ratio=viable_to_RNA_ratio_distribution(data_registry), + infectious_dose=infectious_dose_distribution(), + viable_to_RNA_ratio=viable_to_RNA_ratio_distribution(), transmissibility_factor=1., ), mask=models.Mask.types['No mask'], @@ -230,8 +230,8 @@ def bus_ride_mc(data_registry): presence=models.SpecificInterval(((0, 1.67), )), virus=mc.SARSCoV2( viral_load_in_sputum=5*10**8, - infectious_dose=infectious_dose_distribution(data_registry), - viable_to_RNA_ratio=viable_to_RNA_ratio_distribution(data_registry), + infectious_dose=infectious_dose_distribution(), + viable_to_RNA_ratio=viable_to_RNA_ratio_distribution(), transmissibility_factor=1., ), mask=models.Mask.types['No mask'], From 884e1a6c43ff00b1f783279f8e8a1555abfe49a4 Mon Sep 17 00:00:00 2001 From: Luis Aleixo Date: Thu, 14 Mar 2024 16:20:01 +0100 Subject: [PATCH 08/10] added breathing cycle to short range dilution factor --- caimira/models.py | 7 +++++-- caimira/store/data_registry.py | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/caimira/models.py b/caimira/models.py index b06c9d9b..e671194b 100644 --- a/caimira/models.py +++ b/caimira/models.py @@ -1355,8 +1355,11 @@ def dilution_factor(self) -> _VectorisedFloat: # Initial velocity of the exhalation airflow (m/s) u0 = np.array(Q_exh/Am) - # Duration of the expiration period(s), assuming a 4s breath-cycle - tstar: float = _dilution_factor['tstar'] # type: ignore + # Duration of one breathing cycle + breathing_cicle: float = _dilution_factor['breathing_cycle'] # type: ignore + + # Duration of the expiration period(s) + tstar: float = breathing_cicle / 2 # Streamwise and radial penetration coefficients _df_pc = _dilution_factor['penetration_coefficients'] # type: ignore diff --git a/caimira/store/data_registry.py b/caimira/store/data_registry.py index 429644ae..498db95b 100644 --- a/caimira/store/data_registry.py +++ b/caimira/store/data_registry.py @@ -363,7 +363,7 @@ class DataRegistry: "dilution_factor": { "mouth_diameter": 0.02, "exhalation_coefficient": 2, - "tstar": 2, + "breathing_cycle": 4, "penetration_coefficients": { "𝛽r1": 0.18, "𝛽r2": 0.2, From ee965052d3388b6039f7234dadde22a25ee44ec0 Mon Sep 17 00:00:00 2001 From: Luis Aleixo Date: Mon, 27 May 2024 10:50:59 +0200 Subject: [PATCH 09/10] wrapped data service enabled env. variable in try/except block --- caimira/apps/calculator/__init__.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/caimira/apps/calculator/__init__.py b/caimira/apps/calculator/__init__.py index edfc75d1..ee69695e 100644 --- a/caimira/apps/calculator/__init__.py +++ b/caimira/apps/calculator/__init__.py @@ -573,7 +573,11 @@ def make_app( data_registry = DataRegistry() data_service = None - data_service_enabled = int(os.environ.get('DATA_SERVICE_ENABLED', 0)) + try: + data_service_enabled = int(os.environ.get('DATA_SERVICE_ENABLED', 0)) + except ValueError: + data_service_enabled = None + if data_service_enabled: data_service = DataService.create() return Application( From 6edd3753e70f8de818f2987e018fc16fdb90f609 Mon Sep 17 00:00:00 2001 From: Luis Aleixo Date: Mon, 27 May 2024 10:51:11 +0200 Subject: [PATCH 10/10] updated version (minor) --- caimira/apps/calculator/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/caimira/apps/calculator/__init__.py b/caimira/apps/calculator/__init__.py index ee69695e..487723cd 100644 --- a/caimira/apps/calculator/__init__.py +++ b/caimira/apps/calculator/__init__.py @@ -42,7 +42,7 @@ # calculator version. If the calculator needs to make breaking changes (e.g. change # form attributes) then it can also increase its MAJOR version without needing to # increase the overall CAiMIRA version (found at ``caimira.__version__``). -__version__ = "4.15.2" +__version__ = "4.15.3" LOG = logging.getLogger("Calculator")