From b6051778eb8260a2ce35d8cadb775cdd8b99ada5 Mon Sep 17 00:00:00 2001 From: Luis Aleixo Date: Tue, 20 Jun 2023 17:19:58 +0200 Subject: [PATCH 01/15] modified method to calculate conditional probability --- caimira/apps/calculator/report_generator.py | 32 +++++++++------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/caimira/apps/calculator/report_generator.py b/caimira/apps/calculator/report_generator.py index b64b64b4..cf81bb4a 100644 --- a/caimira/apps/calculator/report_generator.py +++ b/caimira/apps/calculator/report_generator.py @@ -143,7 +143,7 @@ def calculate_report_data(form: FormData, model: models.ExposureModel) -> typing 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))) if form.conditional_probability_plot else None + 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()] return { @@ -188,24 +188,20 @@ def generate_permalink(base_url, get_root_url, get_root_calculator_url, form: F } -def uncertainties_plot(exposure_model: models.ExposureModel): +def uncertainties_plot(exposure_model: models.ExposureModel, prob: typing.Union[float, np.ndarray]): fig = plt.figure(figsize=(4, 7), dpi=110) - viral_loads = np.linspace(2, 10, 600) + infection_probability = prob / 100 + vl = np.log10(exposure_model.concentration_model.infected.virus.viral_load_in_sputum) + + min_vl, max_vl, step = 2, 10, 8/100. + viral_loads = np.arange(min_vl, max_vl, step) pi_means, lower_percentiles, upper_percentiles = [], [], [] - for vl in viral_loads: - model_vl = dataclass_utils.nested_replace( - exposure_model, { - 'concentration_model.infected.virus.viral_load_in_sputum' : 10**vl, - } - ) - pi = model_vl.infection_probability()/100 - - pi_means.append(np.mean(pi)) - lower_percentiles.append(np.quantile(pi, 0.05)) - upper_percentiles.append(np.quantile(pi, 0.95)) - - histogram_data = exposure_model.infection_probability() / 100 + for vl_log_min in viral_loads: + specific_prob = infection_probability[np.where((vl_log_min-vl)*(vl_log_min+step-vl)<0)[0]] + pi_means.append(specific_prob.mean()) + lower_percentiles.append(np.quantile(specific_prob, 0.05)) + upper_percentiles.append(np.quantile(specific_prob, 0.95)) fig, axs = plt.subplots(2, 3, gridspec_kw={'width_ratios': [5, 0.5] + [1], @@ -221,7 +217,7 @@ def uncertainties_plot(exposure_model: models.ExposureModel): 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, 2].hist(histogram_data, bins=30, orientation='horizontal') + axs[0, 2].hist(infection_probability, bins=30, orientation='horizontal') axs[0, 2].set_xticks([]) axs[0, 2].set_xticklabels([]) axs[0, 2].set_facecolor("lightgrey") @@ -230,7 +226,7 @@ def uncertainties_plot(exposure_model: models.ExposureModel): axs[0, 2].set_xlim(0, highest_bar) axs[0, 2].text(highest_bar * 0.5, 0.5, - rf"$\bf{np.round(np.mean(histogram_data) * 100, 1)}$%", ha='center', va='center') + 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), bins=150, range=(2, 10), color='grey') axs[1, 0].set_facecolor("lightgrey") From fe60e1703f7db93d9abcb1bc09141c5eaf3d8c52 Mon Sep 17 00:00:00 2001 From: Luis Aleixo Date: Wed, 21 Jun 2023 12:16:47 +0200 Subject: [PATCH 02/15] added mypy typing check --- caimira/apps/calculator/report_generator.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/caimira/apps/calculator/report_generator.py b/caimira/apps/calculator/report_generator.py index cf81bb4a..572b804e 100644 --- a/caimira/apps/calculator/report_generator.py +++ b/caimira/apps/calculator/report_generator.py @@ -197,8 +197,8 @@ def uncertainties_plot(exposure_model: models.ExposureModel, prob: typing.Union[ min_vl, max_vl, step = 2, 10, 8/100. viral_loads = np.arange(min_vl, max_vl, step) pi_means, lower_percentiles, upper_percentiles = [], [], [] - for vl_log_min in viral_loads: - specific_prob = infection_probability[np.where((vl_log_min-vl)*(vl_log_min+step-vl)<0)[0]] + for vl_log in viral_loads: + specific_prob = infection_probability[np.where((vl_log-vl)*(vl_log+step-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)) From 7d338d3e2d902b80d0322864338d758c0641bc45 Mon Sep 17 00:00:00 2001 From: Luis Aleixo Date: Thu, 20 Jul 2023 12:05:45 +0200 Subject: [PATCH 03/15] separated vl method and added a test file --- caimira/apps/calculator/report_generator.py | 29 ++++--- caimira/tests/test_conditional_probability.py | 75 +++++++++++++++++++ 2 files changed, 95 insertions(+), 9 deletions(-) create mode 100644 caimira/tests/test_conditional_probability.py diff --git a/caimira/apps/calculator/report_generator.py b/caimira/apps/calculator/report_generator.py index 572b804e..bd0121bc 100644 --- a/caimira/apps/calculator/report_generator.py +++ b/caimira/apps/calculator/report_generator.py @@ -188,20 +188,31 @@ def generate_permalink(base_url, get_root_url, get_root_calculator_url, form: F } -def uncertainties_plot(exposure_model: models.ExposureModel, prob: typing.Union[float, np.ndarray]): - fig = plt.figure(figsize=(4, 7), dpi=110) - - infection_probability = prob / 100 - vl = np.log10(exposure_model.concentration_model.infected.virus.viral_load_in_sputum) +def conditional_prob_inf_given_vl_dist(infection_probability: models._VectorisedFloat, + viral_loads: models._VectorisedFloat, specific_vl: float, step: models._VectorisedFloat): + pi_means = [] + lower_percentiles = [] + upper_percentiles = [] - min_vl, max_vl, step = 2, 10, 8/100. - viral_loads = np.arange(min_vl, max_vl, step) - pi_means, lower_percentiles, upper_percentiles = [], [], [] for vl_log in viral_loads: - specific_prob = infection_probability[np.where((vl_log-vl)*(vl_log+step-vl)<0)[0]] #type: ignore + specific_prob = infection_probability[np.where((vl_log-specific_vl)*(vl_log+step-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)) + + return pi_means, lower_percentiles, upper_percentiles + + +def uncertainties_plot(exposure_model: models.ExposureModel, prob: models._VectorisedFloat): + fig = plt.figure(figsize=(4, 7), dpi=110) + + infection_probability = prob / 100 + specific_vl = np.log10(exposure_model.concentration_model.infected.virus.viral_load_in_sputum) + + min_vl, max_vl, step = 2, 10, 8/100. + viral_loads = np.arange(min_vl, max_vl, step) + pi_means, lower_percentiles, upper_percentiles = conditional_prob_inf_given_vl_dist(infection_probability, + viral_loads, specific_vl, step) fig, axs = plt.subplots(2, 3, gridspec_kw={'width_ratios': [5, 0.5] + [1], diff --git a/caimira/tests/test_conditional_probability.py b/caimira/tests/test_conditional_probability.py new file mode 100644 index 00000000..f9da0c9a --- /dev/null +++ b/caimira/tests/test_conditional_probability.py @@ -0,0 +1,75 @@ +import numpy as np +import pytest + +import caimira.monte_carlo as mc +from caimira import models +from caimira.dataclass_utils import nested_replace +from caimira.apps.calculator import report_generator +from caimira.monte_carlo.data import activity_distributions, virus_distributions, expiration_distributions + + +@pytest.fixture +def baseline_exposure_model(): + concentration_mc = mc.ConcentrationModel( + room=models.Room(volume=50, inside_temp=models.PiecewiseConstant((0., 24.), (298,)), humidity=0.5), + ventilation=models.MultipleVentilation( + ventilations=( + models.AirChange(active=models.PeriodicInterval(period=120, duration=120), air_exch=0.25), + ) + ), + infected=mc.InfectedPopulation( + number=1, + presence=mc.SpecificInterval(present_times=((0, 3.5), (4.5, 9))), + virus=virus_distributions['SARS_CoV_2_DELTA'], + mask=models.Mask.types['No mask'], + activity=activity_distributions['Seated'], + expiration=expiration_distributions['Breathing'], + host_immunity=0., + ), + evaporation_factor=0.3, + ) + return mc.ExposureModel( + concentration_model=concentration_mc, + short_range=(), + exposed=mc.Population( + number=3, + presence=mc.SpecificInterval(present_times=((0, 3.5), (4.5, 9))), + activity=activity_distributions['Seated'], + mask=models.Mask.types['No mask'], + host_immunity=0., + ), + geographical_data=models.Cases(), + ) + + +def test_conditional_prob_inf_given_vl_dist(baseline_exposure_model): + + viral_loads = np.array([3., 5., 7., 9.,]) + mc_model = baseline_exposure_model.build_model(250_000) + + expected_pi_means = [] + expected_lower_percentiles = [] + expected_upper_percentiles = [] + + for vl in viral_loads: + model_vl: mc.ExposureModel = nested_replace( + mc_model, { + 'concentration_model.infected.virus.viral_load_in_sputum' : 10**vl, + } + ) + pi = model_vl.infection_probability()/100 + + expected_pi_means.append(np.mean(pi)) + expected_lower_percentiles.append(np.quantile(pi, 0.05)) + expected_upper_percentiles.append(np.quantile(pi, 0.95)) + + infection_probability = mc_model.infection_probability() / 100 + specific_vl = np.log10(mc_model.concentration_model.infected.virus.viral_load_in_sputum) + step = (max(viral_loads) - min(viral_loads))/100 + actual_pi_means, actual_lower_percentiles, actual_upper_percentiles = ( + report_generator.conditional_prob_inf_given_vl_dist(infection_probability, viral_loads, specific_vl, step) + ) + + assert np.allclose(actual_pi_means, expected_pi_means, rtol=0.1) + assert np.allclose(actual_lower_percentiles, expected_lower_percentiles, rtol=0.1) + assert np.allclose(actual_upper_percentiles, expected_upper_percentiles, rtol=0.1) From 88c23da96268c51ae20d5cfb11045fe6307a5f3e Mon Sep 17 00:00:00 2001 From: Luis Aleixo Date: Fri, 28 Jul 2023 17:39:13 +0200 Subject: [PATCH 04/15] increased sample size --- caimira/tests/test_conditional_probability.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/caimira/tests/test_conditional_probability.py b/caimira/tests/test_conditional_probability.py index f9da0c9a..cae5f057 100644 --- a/caimira/tests/test_conditional_probability.py +++ b/caimira/tests/test_conditional_probability.py @@ -45,14 +45,14 @@ def baseline_exposure_model(): def test_conditional_prob_inf_given_vl_dist(baseline_exposure_model): viral_loads = np.array([3., 5., 7., 9.,]) - mc_model = baseline_exposure_model.build_model(250_000) + mc_model: models.ExposureModel = baseline_exposure_model.build_model(2_000_000) expected_pi_means = [] expected_lower_percentiles = [] expected_upper_percentiles = [] for vl in viral_loads: - model_vl: mc.ExposureModel = nested_replace( + model_vl: models.ExposureModel = nested_replace( mc_model, { 'concentration_model.infected.virus.viral_load_in_sputum' : 10**vl, } From b6e1afc2a6b277d2c815d037fcc75fc54e015659 Mon Sep 17 00:00:00 2001 From: Luis Aleixo Date: Mon, 31 Jul 2023 17:05:48 +0200 Subject: [PATCH 05/15] Increased test sample size --- caimira/tests/test_conditional_probability.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/caimira/tests/test_conditional_probability.py b/caimira/tests/test_conditional_probability.py index cae5f057..b6c5e9ff 100644 --- a/caimira/tests/test_conditional_probability.py +++ b/caimira/tests/test_conditional_probability.py @@ -45,7 +45,7 @@ def baseline_exposure_model(): def test_conditional_prob_inf_given_vl_dist(baseline_exposure_model): viral_loads = np.array([3., 5., 7., 9.,]) - mc_model: models.ExposureModel = baseline_exposure_model.build_model(2_000_000) + mc_model: models.ExposureModel = baseline_exposure_model.build_model(3_000_000) expected_pi_means = [] expected_lower_percentiles = [] From 6622315dd9a80cf0a7b934a72c7db2c811c23ba1 Mon Sep 17 00:00:00 2001 From: Luis Aleixo Date: Mon, 31 Jul 2023 17:33:44 +0200 Subject: [PATCH 06/15] changed mypy type --- caimira/apps/calculator/report_generator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/caimira/apps/calculator/report_generator.py b/caimira/apps/calculator/report_generator.py index bd0121bc..7f31893d 100644 --- a/caimira/apps/calculator/report_generator.py +++ b/caimira/apps/calculator/report_generator.py @@ -189,7 +189,7 @@ def generate_permalink(base_url, get_root_url, get_root_calculator_url, form: F def conditional_prob_inf_given_vl_dist(infection_probability: models._VectorisedFloat, - viral_loads: models._VectorisedFloat, specific_vl: float, step: models._VectorisedFloat): + viral_loads: np.ndarray, specific_vl: float, step: models._VectorisedFloat): pi_means = [] lower_percentiles = [] upper_percentiles = [] From c4d527f1a5af0ff99dcb075dce941ce49d21da0a Mon Sep 17 00:00:00 2001 From: Luis Aleixo Date: Wed, 2 Aug 2023 14:49:03 +0200 Subject: [PATCH 07/15] solved seed problem with conditional prob test --- caimira/tests/test_conditional_probability.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/caimira/tests/test_conditional_probability.py b/caimira/tests/test_conditional_probability.py index b6c5e9ff..2adf4f9c 100644 --- a/caimira/tests/test_conditional_probability.py +++ b/caimira/tests/test_conditional_probability.py @@ -1,5 +1,6 @@ import numpy as np import pytest +from retry import retry import caimira.monte_carlo as mc from caimira import models @@ -42,10 +43,11 @@ def baseline_exposure_model(): ) +@retry(tries=10) def test_conditional_prob_inf_given_vl_dist(baseline_exposure_model): viral_loads = np.array([3., 5., 7., 9.,]) - mc_model: models.ExposureModel = baseline_exposure_model.build_model(3_000_000) + mc_model: models.ExposureModel = baseline_exposure_model.build_model(1_000_000) expected_pi_means = [] expected_lower_percentiles = [] From 96e9aead441326e3111931f86e52f9fadafb1ac2 Mon Sep 17 00:00:00 2001 From: Luis Aleixo Date: Thu, 3 Aug 2023 15:01:06 +0200 Subject: [PATCH 08/15] removed manufacture viral load scenarios method and related references --- caimira/apps/calculator/defaults.py | 1 - caimira/apps/calculator/model_generator.py | 2 -- caimira/apps/calculator/report_generator.py | 13 ------------- caimira/apps/templates/base/calculator.form.html.j2 | 1 - 4 files changed, 17 deletions(-) diff --git a/caimira/apps/calculator/defaults.py b/caimira/apps/calculator/defaults.py index 22b157aa..a9e9d0f9 100644 --- a/caimira/apps/calculator/defaults.py +++ b/caimira/apps/calculator/defaults.py @@ -19,7 +19,6 @@ DEFAULTS = { 'calculator_version': NO_DEFAULT, 'ceiling_height': 0., 'conditional_probability_plot': False, - 'conditional_probability_viral_loads': False, 'exposed_coffee_break_option': 'coffee_break_0', 'exposed_coffee_duration': 5, 'exposed_finish': '17:30', diff --git a/caimira/apps/calculator/model_generator.py b/caimira/apps/calculator/model_generator.py index 664eef2b..ae473ea5 100644 --- a/caimira/apps/calculator/model_generator.py +++ b/caimira/apps/calculator/model_generator.py @@ -35,7 +35,6 @@ class FormData: precise_activity: dict ceiling_height: float conditional_probability_plot: bool - conditional_probability_viral_loads: bool exposed_coffee_break_option: str exposed_coffee_duration: int exposed_finish: minutes_since_midnight @@ -803,7 +802,6 @@ def baseline_raw_form_data() -> typing.Dict[str, typing.Union[str, float]]: 'air_supply': '', 'ceiling_height': '', 'conditional_probability_plot': '0', - 'conditional_probability_viral_loads': '0', 'exposed_coffee_break_option': 'coffee_break_4', 'exposed_coffee_duration': '10', 'exposed_finish': '18:00', diff --git a/caimira/apps/calculator/report_generator.py b/caimira/apps/calculator/report_generator.py index 7f31893d..0e4f38ec 100644 --- a/caimira/apps/calculator/report_generator.py +++ b/caimira/apps/calculator/report_generator.py @@ -317,18 +317,6 @@ def non_zero_percentage(percentage: int) -> str: return "99.9%" else: return "{:0.1f}%".format(percentage) - - -def manufacture_viral_load_scenarios(model: mc.ExposureModel) -> typing.Dict[str, mc.ExposureModel]: - viral_load = model.concentration_model.infected.virus.viral_load_in_sputum - scenarios = {} - for percentil in (0.01, 0.05, 0.25, 0.5, 0.75, 0.95, 0.99): - vl = np.quantile(viral_load, percentil) - specific_vl_scenario = dataclass_utils.nested_replace(model, - {'concentration_model.infected.virus.viral_load_in_sputum': vl} - ) - scenarios[round(np.log10(vl))] = np.mean(specific_vl_scenario.infection_probability()) - return scenarios def manufacture_alternative_scenarios(form: FormData) -> typing.Dict[str, mc.ExposureModel]: @@ -478,7 +466,6 @@ class ReportGenerator: context.update(report_data) alternative_scenarios = manufacture_alternative_scenarios(form) - context['alternative_viral_load'] = manufacture_viral_load_scenarios(model) if form.conditional_probability_viral_loads else None context['alternative_scenarios'] = comparison_report( form, report_data, alternative_scenarios, scenario_sample_times, executor_factory=executor_factory, ) diff --git a/caimira/apps/templates/base/calculator.form.html.j2 b/caimira/apps/templates/base/calculator.form.html.j2 index 2ab873b3..8277f829 100644 --- a/caimira/apps/templates/base/calculator.form.html.j2 +++ b/caimira/apps/templates/base/calculator.form.html.j2 @@ -408,7 +408,6 @@
-
From 1abfe3cfe59550359bfe3d71cd0a082bf7d85168 Mon Sep 17 00:00:00 2001 From: Luis Aleixo Date: Thu, 3 Aug 2023 15:01:31 +0200 Subject: [PATCH 09/15] added a method to manufacture conditional probability and send it in the report --- caimira/apps/calculator/report_generator.py | 23 +++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/caimira/apps/calculator/report_generator.py b/caimira/apps/calculator/report_generator.py index 0e4f38ec..8297e132 100644 --- a/caimira/apps/calculator/report_generator.py +++ b/caimira/apps/calculator/report_generator.py @@ -145,6 +145,9 @@ def calculate_report_data(form: FormData, model: models.ExposureModel) -> typing 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))} return { "model_repr": repr(model), @@ -166,6 +169,7 @@ def calculate_report_data(form: FormData, model: models.ExposureModel) -> typing "uncertainties_plot_src": uncertainties_plot_src, "CO2_concentrations": CO2_concentrations, "vl_dist": list(np.log10(model.concentration_model.virus.viral_load_in_sputum)), + "conditional_probability_data": conditional_probability_data, } @@ -203,16 +207,23 @@ def conditional_prob_inf_given_vl_dist(infection_probability: models._Vectorised 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 + 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, + specific_vl, step) + + 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 - specific_vl = np.log10(exposure_model.concentration_model.infected.virus.viral_load_in_sputum) - - min_vl, max_vl, step = 2, 10, 8/100. - viral_loads = np.arange(min_vl, max_vl, step) - pi_means, lower_percentiles, upper_percentiles = conditional_prob_inf_given_vl_dist(infection_probability, - viral_loads, specific_vl, step) + viral_loads, pi_means, lower_percentiles, upper_percentiles = manufacture_conditional_probability_data(exposure_model, infection_probability) fig, axs = plt.subplots(2, 3, gridspec_kw={'width_ratios': [5, 0.5] + [1], From 77143c13c06ff7fb5052de2bce94de97163062b7 Mon Sep 17 00:00:00 2001 From: Luis Aleixo Date: Thu, 3 Aug 2023 16:16:26 +0200 Subject: [PATCH 10/15] added vl_data on percentiles --- caimira/apps/calculator/defaults.py | 1 + caimira/apps/calculator/model_generator.py | 2 ++ caimira/apps/calculator/report_generator.py | 13 +++++++++++++ 3 files changed, 16 insertions(+) diff --git a/caimira/apps/calculator/defaults.py b/caimira/apps/calculator/defaults.py index a9e9d0f9..22b157aa 100644 --- a/caimira/apps/calculator/defaults.py +++ b/caimira/apps/calculator/defaults.py @@ -19,6 +19,7 @@ DEFAULTS = { 'calculator_version': NO_DEFAULT, 'ceiling_height': 0., 'conditional_probability_plot': False, + 'conditional_probability_viral_loads': False, 'exposed_coffee_break_option': 'coffee_break_0', 'exposed_coffee_duration': 5, 'exposed_finish': '17:30', diff --git a/caimira/apps/calculator/model_generator.py b/caimira/apps/calculator/model_generator.py index ae473ea5..664eef2b 100644 --- a/caimira/apps/calculator/model_generator.py +++ b/caimira/apps/calculator/model_generator.py @@ -35,6 +35,7 @@ class FormData: precise_activity: dict ceiling_height: float conditional_probability_plot: bool + conditional_probability_viral_loads: bool exposed_coffee_break_option: str exposed_coffee_duration: int exposed_finish: minutes_since_midnight @@ -802,6 +803,7 @@ def baseline_raw_form_data() -> typing.Dict[str, typing.Union[str, float]]: 'air_supply': '', 'ceiling_height': '', 'conditional_probability_plot': '0', + 'conditional_probability_viral_loads': '0', 'exposed_coffee_break_option': 'coffee_break_4', 'exposed_coffee_duration': '10', 'exposed_finish': '18:00', diff --git a/caimira/apps/calculator/report_generator.py b/caimira/apps/calculator/report_generator.py index 8297e132..6429729c 100644 --- a/caimira/apps/calculator/report_generator.py +++ b/caimira/apps/calculator/report_generator.py @@ -328,6 +328,18 @@ def non_zero_percentage(percentage: int) -> str: return "99.9%" else: return "{:0.1f}%".format(percentage) + + +def manufacture_viral_load_scenarios_percentiles(model: mc.ExposureModel) -> typing.Dict[str, mc.ExposureModel]: + viral_load = model.concentration_model.infected.virus.viral_load_in_sputum + scenarios = {} + for percentil in (0.01, 0.05, 0.25, 0.5, 0.75, 0.95, 0.99): + vl = np.quantile(viral_load, percentil) + specific_vl_scenario = dataclass_utils.nested_replace(model, + {'concentration_model.infected.virus.viral_load_in_sputum': vl} + ) + scenarios[round(np.log10(vl))] = np.mean(specific_vl_scenario.infection_probability()) + return scenarios def manufacture_alternative_scenarios(form: FormData) -> typing.Dict[str, mc.ExposureModel]: @@ -477,6 +489,7 @@ class ReportGenerator: context.update(report_data) alternative_scenarios = manufacture_alternative_scenarios(form) + context['alternative_viral_load'] = manufacture_viral_load_scenarios_percentiles(model) if form.conditional_probability_viral_loads else None context['alternative_scenarios'] = comparison_report( form, report_data, alternative_scenarios, scenario_sample_times, executor_factory=executor_factory, ) From 27da077493bab1430e1ef3e2ffed5e2c61f38fe8 Mon Sep 17 00:00:00 2001 From: Luis Aleixo Date: Thu, 3 Aug 2023 16:24:59 +0200 Subject: [PATCH 11/15] added full vl value on key of viral load --- caimira/apps/calculator/report_generator.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/caimira/apps/calculator/report_generator.py b/caimira/apps/calculator/report_generator.py index 6429729c..bdcdf7c2 100644 --- a/caimira/apps/calculator/report_generator.py +++ b/caimira/apps/calculator/report_generator.py @@ -328,7 +328,7 @@ def non_zero_percentage(percentage: int) -> str: return "99.9%" else: return "{:0.1f}%".format(percentage) - + def manufacture_viral_load_scenarios_percentiles(model: mc.ExposureModel) -> typing.Dict[str, mc.ExposureModel]: viral_load = model.concentration_model.infected.virus.viral_load_in_sputum @@ -338,7 +338,7 @@ def manufacture_viral_load_scenarios_percentiles(model: mc.ExposureModel) -> typ specific_vl_scenario = dataclass_utils.nested_replace(model, {'concentration_model.infected.virus.viral_load_in_sputum': vl} ) - scenarios[round(np.log10(vl))] = np.mean(specific_vl_scenario.infection_probability()) + scenarios[vl] = np.mean(specific_vl_scenario.infection_probability()) return scenarios From f8530d47245235883f793156f6ebaf7ad7fc1fc2 Mon Sep 17 00:00:00 2001 From: Nicolas Mounet Date: Fri, 1 Sep 2023 15:21:04 +0200 Subject: [PATCH 12/15] Centering the interval for vl uncertainty computation in report generator -> improving test accuracy --- caimira/apps/calculator/report_generator.py | 2 +- caimira/tests/test_conditional_probability.py | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/caimira/apps/calculator/report_generator.py b/caimira/apps/calculator/report_generator.py index bdcdf7c2..b4510b3f 100644 --- a/caimira/apps/calculator/report_generator.py +++ b/caimira/apps/calculator/report_generator.py @@ -199,7 +199,7 @@ def conditional_prob_inf_given_vl_dist(infection_probability: models._Vectorised upper_percentiles = [] for vl_log in viral_loads: - specific_prob = infection_probability[np.where((vl_log-specific_vl)*(vl_log+step-specific_vl)<0)[0]] #type: ignore + 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)) diff --git a/caimira/tests/test_conditional_probability.py b/caimira/tests/test_conditional_probability.py index 2adf4f9c..0a76899c 100644 --- a/caimira/tests/test_conditional_probability.py +++ b/caimira/tests/test_conditional_probability.py @@ -43,7 +43,7 @@ def baseline_exposure_model(): ) -@retry(tries=10) +@retry(tries=2) def test_conditional_prob_inf_given_vl_dist(baseline_exposure_model): viral_loads = np.array([3., 5., 7., 9.,]) @@ -67,11 +67,11 @@ def test_conditional_prob_inf_given_vl_dist(baseline_exposure_model): infection_probability = mc_model.infection_probability() / 100 specific_vl = np.log10(mc_model.concentration_model.infected.virus.viral_load_in_sputum) - step = (max(viral_loads) - min(viral_loads))/100 + step = 8/100 actual_pi_means, actual_lower_percentiles, actual_upper_percentiles = ( report_generator.conditional_prob_inf_given_vl_dist(infection_probability, viral_loads, specific_vl, step) ) - assert np.allclose(actual_pi_means, expected_pi_means, rtol=0.1) - assert np.allclose(actual_lower_percentiles, expected_lower_percentiles, rtol=0.1) - assert np.allclose(actual_upper_percentiles, expected_upper_percentiles, rtol=0.1) + assert np.allclose(actual_pi_means, expected_pi_means, atol=0.001) + assert np.allclose(actual_lower_percentiles, expected_lower_percentiles, atol=0.001) + assert np.allclose(actual_upper_percentiles, expected_upper_percentiles, atol=0.001) From 82f4e149055709a96b70ecde392fac90d9b0251a Mon Sep 17 00:00:00 2001 From: Nicolas Mounet Date: Fri, 1 Sep 2023 16:18:10 +0200 Subject: [PATCH 13/15] relaxing tolerances on conditional prob. test --- caimira/tests/test_conditional_probability.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/caimira/tests/test_conditional_probability.py b/caimira/tests/test_conditional_probability.py index 0a76899c..ec5fcbdd 100644 --- a/caimira/tests/test_conditional_probability.py +++ b/caimira/tests/test_conditional_probability.py @@ -43,7 +43,7 @@ def baseline_exposure_model(): ) -@retry(tries=2) +@retry(tries=3) def test_conditional_prob_inf_given_vl_dist(baseline_exposure_model): viral_loads = np.array([3., 5., 7., 9.,]) @@ -72,6 +72,6 @@ def test_conditional_prob_inf_given_vl_dist(baseline_exposure_model): report_generator.conditional_prob_inf_given_vl_dist(infection_probability, viral_loads, specific_vl, step) ) - assert np.allclose(actual_pi_means, expected_pi_means, atol=0.001) - assert np.allclose(actual_lower_percentiles, expected_lower_percentiles, atol=0.001) - assert np.allclose(actual_upper_percentiles, expected_upper_percentiles, atol=0.001) + assert np.allclose(actual_pi_means, expected_pi_means, atol=0.002) + assert np.allclose(actual_lower_percentiles, expected_lower_percentiles, atol=0.002) + assert np.allclose(actual_upper_percentiles, expected_upper_percentiles, atol=0.002) From 878e4b26cd8b25f2aba31670b116a30b7b162a81 Mon Sep 17 00:00:00 2001 From: Nicolas Mounet Date: Sat, 2 Sep 2023 11:21:17 +0200 Subject: [PATCH 14/15] Increasing number of samples in test_conditional_probability.py --- caimira/tests/test_conditional_probability.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/caimira/tests/test_conditional_probability.py b/caimira/tests/test_conditional_probability.py index ec5fcbdd..85f1c666 100644 --- a/caimira/tests/test_conditional_probability.py +++ b/caimira/tests/test_conditional_probability.py @@ -47,7 +47,7 @@ def baseline_exposure_model(): def test_conditional_prob_inf_given_vl_dist(baseline_exposure_model): viral_loads = np.array([3., 5., 7., 9.,]) - mc_model: models.ExposureModel = baseline_exposure_model.build_model(1_000_000) + mc_model: models.ExposureModel = baseline_exposure_model.build_model(2_000_000) expected_pi_means = [] expected_lower_percentiles = [] From 1d72ef7aa7f57f9b6fef3e6d328b0cb19cc59b7e Mon Sep 17 00:00:00 2001 From: Luis Aleixo Date: Tue, 5 Sep 2023 10:17:43 +0200 Subject: [PATCH 15/15] increased patch version --- 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 0b014d59..e0a2fd78 100644 --- a/caimira/apps/calculator/__init__.py +++ b/caimira/apps/calculator/__init__.py @@ -38,7 +38,7 @@ from .user import AuthenticatedUser, AnonymousUser # 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.12" +__version__ = "4.12.1" LOG = logging.getLogger(__name__)