Merge branch 'changes/schema_update' into 'master'

Data registry update (schema v2.1.1)

See merge request caimira/caimira!487
This commit is contained in:
Luis Aleixo 2024-05-27 11:10:20 +02:00
commit c8b6d882cc
11 changed files with 383 additions and 400 deletions

View file

@ -42,7 +42,7 @@ from .user import AuthenticatedUser, AnonymousUser
# calculator version. If the calculator needs to make breaking changes (e.g. change # 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 # form attributes) then it can also increase its MAJOR version without needing to
# increase the overall CAiMIRA version (found at ``caimira.__version__``). # increase the overall CAiMIRA version (found at ``caimira.__version__``).
__version__ = "4.15.2" __version__ = "4.15.3"
LOG = logging.getLogger("Calculator") LOG = logging.getLogger("Calculator")
@ -573,7 +573,11 @@ def make_app(
data_registry = DataRegistry() data_registry = DataRegistry()
data_service = None data_service = None
data_service_enabled = 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() if data_service_enabled: data_service = DataService.create()
return Application( return Application(

View file

@ -159,7 +159,7 @@ class CO2FormData(FormData):
return tuple((self.CO2_data['times'][0], self.CO2_data['times'][-1])) return tuple((self.CO2_data['times'][0], self.CO2_data['times'][-1]))
def build_model(self, size=None) -> models.CO2DataModel: # type: ignore 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 # Build a simple infected and exposed population for the case when presence
# intervals and number of people are dynamic. Activity type is not needed. # intervals and number of people are dynamic. Activity type is not needed.
infected_presence = self.infected_present_interval() infected_presence = self.infected_present_interval()

View file

@ -200,10 +200,10 @@ class VirusFormData(FormData):
if self.arve_sensors_option == False: if self.arve_sensors_option == False:
if self.room_heating_option: if self.room_heating_option:
humidity = self.data_registry.room['defaults']['humidity_with_heating'] humidity = self.data_registry.room['humidity_with_heating']
else: else:
humidity = self.data_registry.room['defaults']['humidity_without_heating'] humidity = self.data_registry.room['humidity_without_heating']
inside_temp = self.data_registry.room['defaults']['inside_temp'] inside_temp = self.data_registry.room['inside_temp']
else: else:
humidity = float(self.humidity) humidity = float(self.humidity)
inside_temp = self.inside_temp inside_temp = self.inside_temp
@ -245,11 +245,11 @@ class VirusFormData(FormData):
) )
def build_model(self, sample_size=None) -> models.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) return self.build_mc_model().build_model(size=sample_size)
def build_CO2_model(self, sample_size=None) -> models.CO2ConcentrationModel: 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) infected_population: models.InfectedPopulation = self.infected_population().build_model(sample_size)
exposed_population: models.Population = self.exposed_population().build_model(sample_size) exposed_population: models.Population = self.exposed_population().build_model(sample_size)

View file

@ -19,6 +19,7 @@ from caimira.store.data_registry import DataRegistry
from ... import monte_carlo as mc from ... import monte_carlo as mc
from .model_generator import VirusFormData from .model_generator import VirusFormData
from ... import dataclass_utils from ... import dataclass_utils
from caimira.enums import ViralLoads
def model_start_end(model: models.ExposureModel): 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_dist_count, prob_dist_bins = np.histogram(prob/100, bins=100, density=True)
prob_probabilistic_exposure = np.array(model.total_probability_rule()).mean() prob_probabilistic_exposure = np.array(model.total_probability_rule()).mean()
expected_new_cases = np.array(model.expected_new_cases()).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()] 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 { return {
"model_repr": repr(model), "model_repr": repr(model),
@ -194,7 +210,7 @@ def calculate_report_data(form: VirusFormData, model: models.ExposureModel, exec
"expected_new_cases": expected_new_cases, "expected_new_cases": expected_new_cases,
"uncertainties_plot_src": uncertainties_plot_src, "uncertainties_plot_src": uncertainties_plot_src,
"CO2_concentrations": CO2_concentrations, "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, "conditional_probability_data": conditional_probability_data,
} }
@ -233,8 +249,8 @@ def conditional_prob_inf_given_vl_dist(
for vl_log in viral_loads: 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 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()) pi_means.append(specific_prob.mean())
lower_percentiles.append(np.quantile(specific_prob, data_registry.conditional_prob_inf_given_viral_load['lower_percentile'])) lower_percentiles.append(np.quantile(specific_prob, 0.05))
upper_percentiles.append(np.quantile(specific_prob, data_registry.conditional_prob_inf_given_viral_load['upper_percentile'])) upper_percentiles.append(np.quantile(specific_prob, 0.95))
return pi_means, lower_percentiles, upper_percentiles return pi_means, lower_percentiles, upper_percentiles
@ -245,8 +261,8 @@ def manufacture_conditional_probability_data(
): ):
data_registry: DataRegistry = exposure_model.data_registry data_registry: DataRegistry = exposure_model.data_registry
min_vl = data_registry.conditional_prob_inf_given_viral_load['min_vl'] min_vl = 2
max_vl = data_registry.conditional_prob_inf_given_viral_load['max_vl'] max_vl = 10
step = (max_vl - min_vl)/100 step = (max_vl - min_vl)/100
viral_loads = np.arange(min_vl, max_vl, step) viral_loads = np.arange(min_vl, max_vl, step)
specific_vl = np.log10(exposure_model.concentration_model.virus.viral_load_in_sputum) specific_vl = np.log10(exposure_model.concentration_model.virus.viral_load_in_sputum)
@ -256,11 +272,12 @@ def manufacture_conditional_probability_data(
return list(viral_loads), list(pi_means), list(lower_percentiles), list(upper_percentiles) return list(viral_loads), list(pi_means), list(lower_percentiles), list(upper_percentiles)
def uncertainties_plot(exposure_model: models.ExposureModel, prob: models._VectorisedFloat): def uncertainties_plot(infection_probability: models._VectorisedFloat,
fig = plt.figure(figsize=(4, 7), dpi=110) viral_load_in_sputum: models._VectorisedFloat,
viral_loads: models._VectorisedFloat,
infection_probability = prob / 100 pi_means: models._VectorisedFloat,
viral_loads, pi_means, lower_percentiles, upper_percentiles = manufacture_conditional_probability_data(exposure_model, infection_probability) lower_percentiles: models._VectorisedFloat,
upper_percentiles: models._VectorisedFloat):
fig, axs = plt.subplots(2, 3, fig, axs = plt.subplots(2, 3,
gridspec_kw={'width_ratios': [5, 0.5] + [1], 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, 1].set_visible(False)
axs[0, 0].plot(viral_loads, pi_means, label='Predictive total probability') axs[0, 0].plot(viral_loads, np.array(pi_means)/100, 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].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].hist(infection_probability, bins=30, orientation='horizontal')
axs[0, 2].set_xticks([]) 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].set_xlim(0, highest_bar)
axs[0, 2].text(highest_bar * 0.5, 0.5, axs[0, 2].text(highest_bar * 0.5, 0.5,
rf"$\bf{np.round(np.mean(infection_probability) * 100, 1)}$%", ha='center', va='center') rf"$\bf{np.round(np.mean(infection_probability), 1)}$%", ha='center', va='center')
axs[1, 0].hist(np.log10(exposure_model.concentration_model.infected.virus.viral_load_in_sputum), axs[1, 0].hist(np.log10(viral_load_in_sputum),
bins=150, range=(2, 10), color='grey') bins=150, range=(2, 10), color='grey')
axs[1, 0].set_facecolor("lightgrey") axs[1, 0].set_facecolor("lightgrey")
axs[1, 0].set_yticks([]) axs[1, 0].set_yticks([])
@ -442,7 +459,7 @@ def scenario_statistics(
sample_times: typing.List[float], sample_times: typing.List[float],
compute_prob_exposure: bool 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): if (compute_prob_exposure):
# It means we have data to calculate the total_probability_rule # It means we have data to calculate the total_probability_rule
prob_probabilistic_exposure = model.total_probability_rule() prob_probabilistic_exposure = model.total_probability_rule()

View file

@ -214,11 +214,12 @@
draw_histogram("prob_inf_hist", {{ prob_inf }}, {{ prob_inf_sd }}); draw_histogram("prob_inf_hist", {{ prob_inf }}, {{ prob_inf_sd }});
</script> </script>
<br> <br>
{% if model.data_registry.virological_data['virus_distributions'][form.virus_type]['viral_load_in_sputum'] == 'Ref: Viral load - covid_overal_vl_data' %}
<div class="form-check"> <div class="form-check">
<input type="checkbox" id="conditional_probability_plot" class="tabbed form-check-input" name="conditional_probability_plot" value="1" onClick="conditional_probability_plot(this.checked, {{ form.conditional_probability_plot | int }});"> <input type="checkbox" id="conditional_probability_plot" class="tabbed form-check-input" name="conditional_probability_plot" value="1" onClick="conditional_probability_plot(this.checked, {{ form.conditional_probability_plot | int }});">
<label id="label_conditional_probability_plot" for="conditional_probability_plot" class="form-check-label col-sm-12">Generate full uncertainty data (as function of the viral load)</label> <label id="label_conditional_probability_plot" for="conditional_probability_plot" class="form-check-label col-sm-12">Generate full uncertainty data (as function of the viral load)</label>
</div> </div>
{% endif %}
{% if form.conditional_probability_plot %} {% if form.conditional_probability_plot %}
<div id="conditional_probability_div"> <div id="conditional_probability_div">
<img src= "{{ uncertainties_plot_src }}" /> <img src= "{{ uncertainties_plot_src }}" />

View file

@ -3,11 +3,3 @@ from enum import Enum
class ViralLoads(Enum): class ViralLoads(Enum):
COVID_OVERALL = "Ref: Viral load - covid_overal_vl_data" COVID_OVERALL = "Ref: Viral load - covid_overal_vl_data"
SYMPTOMATIC_FREQUENCIES = "Ref: Viral load - symptomatic_vl_frequencies" 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"

View file

@ -879,7 +879,7 @@ class _PopulationWithVirus(Population):
The fraction of infectious virus. The fraction of infectious virus.
""" """
return self.data_registry.population_with_virus['fraction_of_infectious_virus'] # type: ignore return 1
def aerosols(self): def aerosols(self):
""" """
@ -1052,7 +1052,7 @@ class _ConcentrationModelBase:
(in the same unit as the concentration). Its the value towards which (in the same unit as the concentration). Its the value towards which
the concentration will decay to. 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: def normalization_factor(self) -> _VectorisedFloat:
""" """
@ -1242,7 +1242,7 @@ class ConcentrationModel(_ConcentrationModelBase):
def __post_init__(self): def __post_init__(self):
if self.evaporation_factor is None: 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 @property
def population(self) -> InfectedPopulation: def population(self) -> InfectedPopulation:
@ -1335,7 +1335,7 @@ class ShortRangeModel:
''' '''
The dilution factor for the respective expiratory activity type. 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) # Average mouth opening diameter (m)
mouth_diameter: float = _dilution_factor['mouth_diameter'] # type: ignore mouth_diameter: float = _dilution_factor['mouth_diameter'] # type: ignore
@ -1355,11 +1355,14 @@ class ShortRangeModel:
# Initial velocity of the exhalation airflow (m/s) # Initial velocity of the exhalation airflow (m/s)
u0 = np.array(Q_exh/Am) u0 = np.array(Q_exh/Am)
# Duration of the expiration period(s), assuming a 4s breath-cycle # Duration of one breathing cycle
tstar: float = _dilution_factor['tstar'] # type: ignore 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 # 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 𝛽r1: float = _df_pc['𝛽r1'] # type: ignore
𝛽r2: float = _df_pc['𝛽r2'] # type: ignore 𝛽r2: float = _df_pc['𝛽r2'] # type: ignore
𝛽x1: float = _df_pc['𝛽x1'] # type: ignore 𝛽x1: float = _df_pc['𝛽x1'] # type: ignore
@ -1585,7 +1588,7 @@ class ExposureModel:
#: The number of times the exposure event is repeated (default 1). #: The number of times the exposure event is repeated (default 1).
@property @property
def repeats(self) -> int: def repeats(self) -> int:
return self.data_registry.exposure_model['repeats'] # type: ignore return 1
def __post_init__(self): def __post_init__(self):
""" """

View file

@ -7,84 +7,72 @@ import numpy as np
from scipy import special as sp from scipy import special as sp
from scipy.stats import weibull_min 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 import caimira.monte_carlo.models as mc
from caimira.monte_carlo.sampleable import LogCustom, LogNormal, Normal, LogCustomKernel, CustomKernel, Uniform, Custom from caimira.monte_carlo.sampleable import LogCustom, LogNormal, Normal, LogCustomKernel, CustomKernel, Uniform, Custom
from caimira.store.data_registry import DataRegistry from caimira.store.data_registry import DataRegistry
def evaluate_vl(value, data_registry: DataRegistry): def evaluate_vl(root: typing.Dict, value: str, data_registry: DataRegistry):
if value == ViralLoads.COVID_OVERALL.value: if root[value] == ViralLoads.COVID_OVERALL.value:
return covid_overal_vl_data(data_registry) return covid_overal_vl_data(data_registry)
elif value == ViralLoads.SYMPTOMATIC_FREQUENCIES.value: elif root[value] == ViralLoads.SYMPTOMATIC_FREQUENCIES.value:
return symptomatic_vl_frequencies return symptomatic_vl_frequencies
elif root[value] == 'Custom':
return param_evaluation(root, 'Viral load custom')
else: else:
raise ValueError(f"Invalid ViralLoads value {value}") raise ValueError(f"Invalid ViralLoads value {value}")
def evaluate_infectd(value, data_registry: DataRegistry):
if value == InfectiousDoses.DISTRIBUTION.value:
return infectious_dose_distribution(data_registry)
else:
raise ValueError(f"Invalid InfectiousDoses value {value}")
def evaluate_vtrr(value, data_registry: DataRegistry):
if value == ViableToRNARatios.DISTRIBUTION.value:
return viable_to_RNA_ratio_distribution(data_registry)
else:
raise ValueError(f"Invalid ViableToRNARatios value {value}")
sqrt2pi = np.sqrt(2.*np.pi) sqrt2pi = np.sqrt(2.*np.pi)
sqrt2 = np.sqrt(2.) 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: Args:
dict (dict): The root to search. dict (dict): The root to search.
key_part (str): The distribution key to match. key_part (str): The value type key to match.
Returns: Returns:
str: The associated distribution. str: The associated value.
""" """
try: try:
for key, value in dict.items(): for key, value in dict.items():
if (key_part in key): if (key_part in key):
return value['associated_distribution'] return value['associated_value']
except KeyError: except KeyError:
return f"Key '{key_part}' not found." return f"Key '{key_part}' not found."
def evaluate_custom_distribution(dist: str, params: typing.Dict) -> typing.Any: def evaluate_custom_value_type(value_type: str, params: typing.Dict) -> typing.Any:
""" """
Evaluate a custom distribution. Evaluate a custom value type.
Args: Args:
dist (str): The type of distribution. dist (str): The type of value.
params (Dict): The parameters for the distribution. params (Dict): The parameters for the value type.
Returns: Returns:
Any: The generated distribution. Any: The generated value.
Raises: Raises:
ValueError: If the distribution type is not recognized. ValueError: If the value type is not recognized.
""" """
if dist == 'Linear Space': if value_type == 'Constant value':
return np.linspace(params['start'], params['stop'], params['num']) return params
elif dist == 'Normal': elif value_type == 'Normal distribution':
return Normal(params['normal_mean_gaussian'], params['normal_standard_deviation_gaussian']) return Normal(params['normal_mean_gaussian'], params['normal_standard_deviation_gaussian'])
elif dist == 'Log-normal': elif value_type == 'Log-normal distribution':
return LogNormal(params['lognormal_mean_gaussian'], params['lognormal_standard_deviation_gaussian']) return LogNormal(params['lognormal_mean_gaussian'], params['lognormal_standard_deviation_gaussian'])
elif dist == 'Uniform': elif value_type == 'Uniform distribution':
return Uniform(params['low'], params['high']) return Uniform(params['low'], params['high'])
else: 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: def param_evaluation(root: typing.Dict, param: typing.Union[str, typing.Any]) -> typing.Any:
@ -104,17 +92,10 @@ def param_evaluation(root: typing.Dict, param: typing.Union[str, typing.Any]) ->
""" """
value = root.get(param) value = root.get(param)
if isinstance(value, str): if isinstance(value, dict):
if value == 'Custom': value_type: str = root[param]['associated_value']
custom_distribution: typing.Dict = custom_distribution_lookup(
root, 'custom distribution')
for d, p in custom_distribution.items():
return evaluate_custom_distribution(d, p)
elif isinstance(value, dict):
dist: str = root[param]['associated_distribution']
params: typing.Dict = root[param]['parameters'] params: typing.Dict = root[param]['parameters']
return evaluate_custom_distribution(dist, params) return evaluate_custom_value_type(value_type, params)
elif isinstance(value, float) or isinstance(value, int): elif isinstance(value, float) or isinstance(value, int):
return value return value
@ -148,21 +129,21 @@ class BLOmodel:
# total concentration of aerosols for each mode. # total concentration of aerosols for each mode.
@property @property
def cn(self) -> typing.Tuple[float, float, float]: 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']) return (_cn['B'],_cn['L'],_cn['O'])
# Mean of the underlying normal distributions (represents the log of a # Mean of the underlying normal distributions (represents the log of a
# diameter in microns), for resp. the B, L and O modes. # diameter in microns), for resp. the B, L and O modes.
@property @property
def mu(self) -> typing.Tuple[float, float, float]: 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']) return (_mu['B'], _mu['L'], _mu['O'])
# Std deviation of the underlying normal distribution, for resp. # Std deviation of the underlying normal distribution, for resp.
# the B, L and O modes. # the B, L and O modes.
@property @property
def sigma(self) -> typing.Tuple[float, float, float]: 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']) return (_sigma['B'],_sigma['L'],_sigma['O'])
def distribution(self, d): def distribution(self, d):
@ -229,19 +210,12 @@ def activity_distributions(data_registry):
# From https://doi.org/10.1101/2021.10.14.21264988 and references therein # From https://doi.org/10.1101/2021.10.14.21264988 and references therein
symptomatic_vl_frequencies = LogCustomKernel( def symptomatic_vl_frequencies(data_registry):
np.array((2.46032, 2.67431, 2.85434, 3.06155, 3.25856, 3.47256, 3.66957, 3.85979, 4.09927, 4.27081, return LogCustomKernel(
4.47631, 4.66653, 4.87204, 5.10302, 5.27456, 5.46478, 5.6533, 5.88428, 6.07281, 6.30549, np.array(data_registry.virological_data['symptomatic_vl_frequencies']['log_variable']),
6.48552, 6.64856, 6.85407, 7.10373, 7.30075, 7.47229, 7.66081, 7.85782, 8.05653, 8.27053, np.array(data_registry.virological_data['symptomatic_vl_frequencies']['frequencies']),
8.48453, 8.65607, 8.90573, 9.06878, 9.27429, 9.473, 9.66152, 9.87552)), kernel_bandwidth=data_registry.virological_data['symptomatic_vl_frequencies']['kernel_bandwidth']
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
)
# Weibull distribution with a shape factor of 3.47 and a scale factor of 7.01. # Weibull distribution with a shape factor of 3.47 and a scale factor of 7.01.
@ -250,86 +224,86 @@ symptomatic_vl_frequencies = LogCustomKernel(
def viral_load(data_registry): def viral_load(data_registry):
return np.linspace( return np.linspace(
weibull_min.ppf( weibull_min.ppf(
data_registry.covid_overal_vl_data['start'], data_registry.virological_data['covid_overal_vl_data']['start'],
c=data_registry.covid_overal_vl_data['shape_factor'], c=data_registry.virological_data['covid_overal_vl_data']['shape_factor'],
scale=data_registry.covid_overal_vl_data['scale_factor'] scale=data_registry.virological_data['covid_overal_vl_data']['scale_factor']
), ),
weibull_min.ppf( weibull_min.ppf(
data_registry.covid_overal_vl_data['stop'], data_registry.virological_data['covid_overal_vl_data']['stop'],
c=data_registry.covid_overal_vl_data['shape_factor'], c=data_registry.virological_data['covid_overal_vl_data']['shape_factor'],
scale=data_registry.covid_overal_vl_data['scale_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): def frequencies_pdf(data_registry):
return weibull_min.pdf( return weibull_min.pdf(
viral_load(data_registry), viral_load(data_registry),
c=data_registry.covid_overal_vl_data['shape_factor'], c=data_registry.virological_data['covid_overal_vl_data']['shape_factor'],
scale=data_registry.covid_overal_vl_data['scale_factor'] scale=data_registry.virological_data['covid_overal_vl_data']['scale_factor']
) )
def covid_overal_vl_data(data_registry): def covid_overal_vl_data(data_registry):
return LogCustom( 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( function=lambda d: np.interp(
d, d,
viral_load(data_registry), viral_load(data_registry),
frequencies_pdf(data_registry), frequencies_pdf(data_registry),
data_registry.covid_overal_vl_data['interpolation_fp_left'], data_registry.virological_data['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_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 # 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) # https://iosh.com/media/8432/aerosol-infection-risk-hospital-patient-care-full-report.pdf (page 60)
def viable_to_RNA_ratio_distribution(data_registry): def viable_to_RNA_ratio_distribution():
return Uniform(data_registry.viable_to_RNA_ratio_distribution['low'], data_registry.viable_to_RNA_ratio_distribution['high']) return Uniform(0.01, 0.6)
# From discussion with virologists # From discussion with virologists
def infectious_dose_distribution(data_registry): def infectious_dose_distribution():
return Uniform(data_registry.infectious_dose_distribution['low'], data_registry.infectious_dose_distribution['high']) return Uniform(10., 100.)
# From https://doi.org/10.1101/2021.10.14.21264988 and references therein # From https://doi.org/10.1101/2021.10.14.21264988 and references therein
def virus_distributions(data_registry): def virus_distributions(data_registry):
vd = data_registry.virus_distributions vd = data_registry.virological_data['virus_distributions']
return { return {
'SARS_CoV_2': mc.SARSCoV2( 'SARS_CoV_2': mc.SARSCoV2(
viral_load_in_sputum=evaluate_vl(vd['SARS_CoV_2']['viral_load_in_sputum'], 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), infectious_dose=param_evaluation(vd['SARS_CoV_2'], 'infectious_dose'),
viable_to_RNA_ratio=evaluate_vtrr(vd['SARS_CoV_2']['viable_to_RNA_ratio'], data_registry), viable_to_RNA_ratio=param_evaluation(vd['SARS_CoV_2'], 'viable_to_RNA_ratio'),
transmissibility_factor=vd['SARS_CoV_2']['transmissibility_factor'], transmissibility_factor=vd['SARS_CoV_2']['transmissibility_factor'],
), ),
'SARS_CoV_2_ALPHA': mc.SARSCoV2( 'SARS_CoV_2_ALPHA': mc.SARSCoV2(
viral_load_in_sputum=evaluate_vl(vd['SARS_CoV_2_ALPHA']['viral_load_in_sputum'], 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), infectious_dose=param_evaluation(vd['SARS_CoV_2_ALPHA'], 'infectious_dose'),
viable_to_RNA_ratio=evaluate_vtrr(vd['SARS_CoV_2_ALPHA']['viable_to_RNA_ratio'], data_registry), viable_to_RNA_ratio=param_evaluation(vd['SARS_CoV_2_ALPHA'], 'viable_to_RNA_ratio'),
transmissibility_factor=vd['SARS_CoV_2_ALPHA']['transmissibility_factor'], transmissibility_factor=vd['SARS_CoV_2_ALPHA']['transmissibility_factor'],
), ),
'SARS_CoV_2_BETA': mc.SARSCoV2( 'SARS_CoV_2_BETA': mc.SARSCoV2(
viral_load_in_sputum=evaluate_vl(vd['SARS_CoV_2_BETA']['viral_load_in_sputum'], 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), infectious_dose=param_evaluation(vd['SARS_CoV_2_BETA'], 'infectious_dose'),
viable_to_RNA_ratio=evaluate_vtrr(vd['SARS_CoV_2_BETA']['viable_to_RNA_ratio'], data_registry), viable_to_RNA_ratio=param_evaluation(vd['SARS_CoV_2_BETA'], 'viable_to_RNA_ratio'),
transmissibility_factor=vd['SARS_CoV_2_BETA']['transmissibility_factor'], transmissibility_factor=vd['SARS_CoV_2_BETA']['transmissibility_factor'],
), ),
'SARS_CoV_2_GAMMA': mc.SARSCoV2( 'SARS_CoV_2_GAMMA': mc.SARSCoV2(
viral_load_in_sputum=evaluate_vl(vd['SARS_CoV_2_GAMMA']['viral_load_in_sputum'], 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), infectious_dose=param_evaluation(vd['SARS_CoV_2_GAMMA'], 'infectious_dose'),
viable_to_RNA_ratio=evaluate_vtrr(vd['SARS_CoV_2_GAMMA']['viable_to_RNA_ratio'], data_registry), viable_to_RNA_ratio=param_evaluation(vd['SARS_CoV_2_GAMMA'], 'viable_to_RNA_ratio'),
transmissibility_factor=vd['SARS_CoV_2_GAMMA']['transmissibility_factor'], transmissibility_factor=vd['SARS_CoV_2_GAMMA']['transmissibility_factor'],
), ),
'SARS_CoV_2_DELTA': mc.SARSCoV2( 'SARS_CoV_2_DELTA': mc.SARSCoV2(
viral_load_in_sputum=evaluate_vl(vd['SARS_CoV_2_DELTA']['viral_load_in_sputum'], 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), infectious_dose=param_evaluation(vd['SARS_CoV_2_DELTA'], 'infectious_dose'),
viable_to_RNA_ratio=evaluate_vtrr(vd['SARS_CoV_2_DELTA']['viable_to_RNA_ratio'], data_registry), viable_to_RNA_ratio=param_evaluation(vd['SARS_CoV_2_DELTA'], 'viable_to_RNA_ratio'),
transmissibility_factor=vd['SARS_CoV_2_DELTA']['transmissibility_factor'], transmissibility_factor=vd['SARS_CoV_2_DELTA']['transmissibility_factor'],
), ),
'SARS_CoV_2_OMICRON': mc.SARSCoV2( 'SARS_CoV_2_OMICRON': mc.SARSCoV2(
viral_load_in_sputum=evaluate_vl(vd['SARS_CoV_2_OMICRON']['viral_load_in_sputum'], 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), infectious_dose=param_evaluation(vd['SARS_CoV_2_OMICRON'], 'infectious_dose'),
viable_to_RNA_ratio=evaluate_vtrr(vd['SARS_CoV_2_OMICRON']['viable_to_RNA_ratio'], data_registry), viable_to_RNA_ratio=param_evaluation(vd['SARS_CoV_2_OMICRON'], 'viable_to_RNA_ratio'),
transmissibility_factor=vd['SARS_CoV_2_OMICRON']['transmissibility_factor'], transmissibility_factor=vd['SARS_CoV_2_OMICRON']['transmissibility_factor'],
), ),
} }
@ -347,21 +321,21 @@ def mask_distributions(data_registry):
data_registry.mask_distributions['Type I'], 'η_inhale'), data_registry.mask_distributions['Type I'], 'η_inhale'),
η_exhale=param_evaluation( η_exhale=param_evaluation(
data_registry.mask_distributions['Type I'], 'η_exhale') 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( 'FFP2': mc.Mask(
η_inhale=param_evaluation( η_inhale=param_evaluation(
data_registry.mask_distributions['FFP2'], 'η_inhale'), data_registry.mask_distributions['FFP2'], 'η_inhale'),
η_exhale=param_evaluation( η_exhale=param_evaluation(
data_registry.mask_distributions['FFP2'], 'η_exhale') 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( 'Cloth': mc.Mask(
η_inhale=param_evaluation( η_inhale=param_evaluation(
data_registry.mask_distributions['Cloth'], 'η_inhale'), data_registry.mask_distributions['Cloth'], 'η_inhale'),
η_exhale=param_evaluation( η_exhale=param_evaluation(
data_registry.mask_distributions['Cloth'], 'η_exhale') 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
), ),
} }
@ -392,10 +366,10 @@ def expiration_distribution(
def expiration_BLO_factors(data_registry): def expiration_BLO_factors(data_registry):
breathing = data_registry.expiration_BLO_factors['Breathing'] breathing = data_registry.expiration_particle['expiration_BLO_factors']['Breathing']
speaking = data_registry.expiration_BLO_factors['Speaking'] speaking = data_registry.expiration_particle['expiration_BLO_factors']['Speaking']
singing = data_registry.expiration_BLO_factors['Singing'] singing = data_registry.expiration_particle['expiration_BLO_factors']['Singing']
shouting = data_registry.expiration_BLO_factors['Shouting'] shouting = data_registry.expiration_particle['expiration_BLO_factors']['Shouting']
return { return {
'Breathing': ( 'Breathing': (
param_evaluation(breathing, 'B'), param_evaluation(breathing, 'B'),
@ -425,8 +399,8 @@ def expiration_distributions(data_registry):
exp_type: expiration_distribution( exp_type: expiration_distribution(
data_registry=data_registry, data_registry=data_registry,
BLO_factors=BLO_factors, BLO_factors=BLO_factors,
d_min=param_evaluation(data_registry.long_range_expiration_distributions, 'minimum_diameter'), d_min=param_evaluation(data_registry.expiration_particle['long_range_expiration_distributions'], 'minimum_diameter'),
d_max=param_evaluation(data_registry.long_range_expiration_distributions, 'maximum_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() for exp_type, BLO_factors in expiration_BLO_factors(data_registry).items()
} }
@ -437,8 +411,8 @@ def short_range_expiration_distributions(data_registry):
exp_type: expiration_distribution( exp_type: expiration_distribution(
data_registry=data_registry, data_registry=data_registry,
BLO_factors=BLO_factors, BLO_factors=BLO_factors,
d_min=param_evaluation(data_registry.short_range_expiration_distributions, 'minimum_diameter'), d_min=param_evaluation(data_registry.expiration_particle['short_range_expiration_distributions'], 'minimum_diameter'),
d_max=param_evaluation(data_registry.short_range_expiration_distributions, 'maximum_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() for exp_type, BLO_factors in expiration_BLO_factors(data_registry).items()
} }
@ -452,8 +426,8 @@ frequencies = np.array((0.0598036, 0.0946154, 0.1299152, 0.1064905, 0.1099066, 0
def short_range_distances(data_registry): def short_range_distances(data_registry):
return Custom( return Custom(
bounds=( bounds=(
param_evaluation(data_registry.short_range_distances, 'minimum_distance'), param_evaluation(data_registry.short_range_model['conversational_distance'], 'minimum_distance'),
param_evaluation(data_registry.short_range_distances, 'maximum_distance') param_evaluation(data_registry.short_range_model['conversational_distance'], 'maximum_distance')
), ),
function=lambda x: np.interp(x, distances, frequencies, left=0., right=0.), function=lambda x: np.interp(x, distances, frequencies, left=0., right=0.),
max_function=0.13 max_function=0.13

View file

@ -1,4 +1,4 @@
from caimira.enums import ViralLoads, InfectiousDoses, ViableToRNARatios from caimira.enums import ViralLoads
class DataRegistry: class DataRegistry:
@ -6,34 +6,42 @@ class DataRegistry:
version = None version = None
BLOmodel = { expiration_particle = {
"cn": { "long_range_expiration_distributions": {
"B": 0.06, "minimum_diameter": 0.1,
"L": 0.2, "maximum_diameter": 30,
"O": 0.0010008,
}, },
"mu": { "short_range_expiration_distributions": {
"B": 0.989541, "minimum_diameter": 0.1,
"L": 1.38629, "maximum_diameter": 100,
"O": 4.97673,
}, },
"sigma": { "BLOmodel": {
"B": 0.262364, "cn": {"B": 0.06, "L": 0.2, "O": 0.0010008},
"L": 0.506818, "mu": {"B": 0.989541, "L": 1.38629, "O": 4.97673},
"O": 0.585005, "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 = { activity_distributions = {
"Seated": { "Seated": {
"inhalation_rate": { "inhalation_rate": {
"associated_distribution": "Log-normal", "associated_value": "Log-normal distribution",
"parameters": { "parameters": {
"lognormal_mean_gaussian": -0.6872121723362303, "lognormal_mean_gaussian": -0.6872121723362303,
"lognormal_standard_deviation_gaussian": 0.10498338229297108, "lognormal_standard_deviation_gaussian": 0.10498338229297108,
}, },
}, },
"exhalation_rate": { "exhalation_rate": {
"associated_distribution": "Log-normal", "associated_value": "Log-normal distribution",
"parameters": { "parameters": {
"lognormal_mean_gaussian": -0.6872121723362303, "lognormal_mean_gaussian": -0.6872121723362303,
"lognormal_standard_deviation_gaussian": 0.10498338229297108, "lognormal_standard_deviation_gaussian": 0.10498338229297108,
@ -42,14 +50,14 @@ class DataRegistry:
}, },
"Standing": { "Standing": {
"inhalation_rate": { "inhalation_rate": {
"associated_distribution": "Log-normal", "associated_value": "Log-normal distribution",
"parameters": { "parameters": {
"lognormal_mean_gaussian": -0.5742377578494785, "lognormal_mean_gaussian": -0.5742377578494785,
"lognormal_standard_deviation_gaussian": 0.09373162411398223, "lognormal_standard_deviation_gaussian": 0.09373162411398223,
}, },
}, },
"exhalation_rate": { "exhalation_rate": {
"associated_distribution": "Log-normal", "associated_value": "Log-normal distribution",
"parameters": { "parameters": {
"lognormal_mean_gaussian": -0.5742377578494785, "lognormal_mean_gaussian": -0.5742377578494785,
"lognormal_standard_deviation_gaussian": 0.09373162411398223, "lognormal_standard_deviation_gaussian": 0.09373162411398223,
@ -58,14 +66,14 @@ class DataRegistry:
}, },
"Light activity": { "Light activity": {
"inhalation_rate": { "inhalation_rate": {
"associated_distribution": "Log-normal", "associated_value": "Log-normal distribution",
"parameters": { "parameters": {
"lognormal_mean_gaussian": 0.21380242785625422, "lognormal_mean_gaussian": 0.21380242785625422,
"lognormal_standard_deviation_gaussian": 0.09435378091059601, "lognormal_standard_deviation_gaussian": 0.09435378091059601,
}, },
}, },
"exhalation_rate": { "exhalation_rate": {
"associated_distribution": "Log-normal", "associated_value": "Log-normal distribution",
"parameters": { "parameters": {
"lognormal_mean_gaussian": 0.21380242785625422, "lognormal_mean_gaussian": 0.21380242785625422,
"lognormal_standard_deviation_gaussian": 0.09435378091059601, "lognormal_standard_deviation_gaussian": 0.09435378091059601,
@ -74,14 +82,14 @@ class DataRegistry:
}, },
"Moderate activity": { "Moderate activity": {
"inhalation_rate": { "inhalation_rate": {
"associated_distribution": "Log-normal", "associated_value": "Log-normal distribution",
"parameters": { "parameters": {
"lognormal_mean_gaussian": 0.551771330362601, "lognormal_mean_gaussian": 0.551771330362601,
"lognormal_standard_deviation_gaussian": 0.1894616357138137, "lognormal_standard_deviation_gaussian": 0.1894616357138137,
}, },
}, },
"exhalation_rate": { "exhalation_rate": {
"associated_distribution": "Log-normal", "associated_value": "Log-normal distribution",
"parameters": { "parameters": {
"lognormal_mean_gaussian": 0.551771330362601, "lognormal_mean_gaussian": 0.551771330362601,
"lognormal_standard_deviation_gaussian": 0.1894616357138137, "lognormal_standard_deviation_gaussian": 0.1894616357138137,
@ -90,14 +98,14 @@ class DataRegistry:
}, },
"Heavy exercise": { "Heavy exercise": {
"inhalation_rate": { "inhalation_rate": {
"associated_distribution": "Log-normal", "associated_value": "Log-normal distribution",
"parameters": { "parameters": {
"lognormal_mean_gaussian": 1.1644665696723049, "lognormal_mean_gaussian": 1.1644665696723049,
"lognormal_standard_deviation_gaussian": 0.21744554768657565, "lognormal_standard_deviation_gaussian": 0.21744554768657565,
}, },
}, },
"exhalation_rate": { "exhalation_rate": {
"associated_distribution": "Log-normal", "associated_value": "Log-normal distribution",
"parameters": { "parameters": {
"lognormal_mean_gaussian": 1.1644665696723049, "lognormal_mean_gaussian": 1.1644665696723049,
"lognormal_standard_deviation_gaussian": 0.21744554768657565, "lognormal_standard_deviation_gaussian": 0.21744554768657565,
@ -105,194 +113,216 @@ class DataRegistry:
}, },
}, },
} }
symptomatic_vl_frequencies = {
"log_variable": [ virological_data = {
2.46032, "symptomatic_vl_frequencies": {
2.67431, "log_variable": [
2.85434, 2.46032,
3.06155, 2.67431,
3.25856, 2.85434,
3.47256, 3.06155,
3.66957, 3.25856,
3.85979, 3.47256,
4.09927, 3.66957,
4.27081, 3.85979,
4.47631, 4.09927,
4.66653, 4.27081,
4.87204, 4.47631,
5.10302, 4.66653,
5.27456, 4.87204,
5.46478, 5.10302,
5.6533, 5.27456,
5.88428, 5.46478,
6.07281, 5.6533,
6.30549, 5.88428,
6.48552, 6.07281,
6.64856, 6.30549,
6.85407, 6.48552,
7.10373, 6.64856,
7.30075, 6.85407,
7.47229, 7.10373,
7.66081, 7.30075,
7.85782, 7.47229,
8.05653, 7.66081,
8.27053, 7.85782,
8.48453, 8.05653,
8.65607, 8.27053,
8.90573, 8.48453,
9.06878, 8.65607,
9.27429, 8.90573,
9.473, 9.06878,
9.66152, 9.27429,
9.87552, 9.473,
], 9.66152,
"frequencies": [ 9.87552,
0.001206885, ],
0.007851618, "frequencies": [
0.008078144, 0.001206885,
0.01502491, 0.007851618,
0.013258014, 0.008078144,
0.018528495, 0.01502491,
0.020053765, 0.013258014,
0.021896167, 0.018528495,
0.022047184, 0.020053765,
0.018604005, 0.021896167,
0.01547796, 0.022047184,
0.018075445, 0.018604005,
0.021503523, 0.01547796,
0.022349217, 0.018075445,
0.025097721, 0.021503523,
0.032875078, 0.022349217,
0.030594727, 0.025097721,
0.032573045, 0.032875078,
0.034717482, 0.030594727,
0.034792991, 0.032573045,
0.033267721, 0.034717482,
0.042887485, 0.034792991,
0.036846816, 0.033267721,
0.03876473, 0.042887485,
0.045016819, 0.036846816,
0.040063473, 0.03876473,
0.04883754, 0.045016819,
0.043944602, 0.040063473,
0.048142864, 0.04883754,
0.041588741, 0.043944602,
0.048762031, 0.048142864,
0.027921732, 0.041588741,
0.033871788, 0.048762031,
0.022122693, 0.027921732,
0.016927718, 0.033871788,
0.008833228, 0.022122693,
0.00478598, 0.016927718,
0.002807662, 0.008833228,
], 0.00478598,
"kernel_bandwidth": 0.1, 0.002807662,
} ],
covid_overal_vl_data = { "kernel_bandwidth": 0.1,
"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": { 'covid_overal_vl_data': {
"viral_load_in_sputum": ViralLoads.COVID_OVERALL.value, "shape_factor": 3.47,
"infectious_dose": InfectiousDoses.DISTRIBUTION.value, "scale_factor": 7.01,
"viable_to_RNA_ratio": ViableToRNARatios.DISTRIBUTION.value, "start": 0.01,
"transmissibility_factor": 0.78, "stop": 0.99,
"infectiousness_days": 14, "num": 30.0,
"min_bound": 2,
"max_bound": 10,
"interpolation_fp_left": 0,
"interpolation_fp_right": 0,
"max_function": 0.2,
}, },
"SARS_CoV_2_BETA": { "virus_distributions": {
"viral_load_in_sputum": ViralLoads.COVID_OVERALL.value, "SARS_CoV_2": {
"infectious_dose": InfectiousDoses.DISTRIBUTION.value, "viral_load_in_sputum": ViralLoads.COVID_OVERALL.value,
"viable_to_RNA_ratio": ViableToRNARatios.DISTRIBUTION.value, "infectious_dose": {
"transmissibility_factor": 0.8, "associated_value": "Uniform distribution",
"infectiousness_days": 14, "parameters": {"low": 10, "high": 100},
}, },
"SARS_CoV_2_GAMMA": { "viable_to_RNA_ratio": {
"viral_load_in_sputum": ViralLoads.COVID_OVERALL.value, 'associated_value': 'Uniform distribution',
"infectious_dose": InfectiousDoses.DISTRIBUTION.value, 'parameters': {'low': 0.01, 'high': 0.6},
"viable_to_RNA_ratio": ViableToRNARatios.DISTRIBUTION.value, },
"transmissibility_factor": 0.72, "transmissibility_factor": 1,
"infectiousness_days": 14, "infectiousness_days": 14,
}, },
"SARS_CoV_2_DELTA": { "SARS_CoV_2_ALPHA": {
"viral_load_in_sputum": ViralLoads.COVID_OVERALL.value, "viral_load_in_sputum": ViralLoads.COVID_OVERALL.value,
"infectious_dose": InfectiousDoses.DISTRIBUTION.value, "infectious_dose": {
"viable_to_RNA_ratio": ViableToRNARatios.DISTRIBUTION.value, "associated_value": "Uniform distribution",
"transmissibility_factor": 0.51, "parameters": {"low": 10, "high": 100},
"infectiousness_days": 14, },
}, "viable_to_RNA_ratio": {
"SARS_CoV_2_OMICRON": { 'associated_value': 'Uniform distribution',
"viral_load_in_sputum": ViralLoads.COVID_OVERALL.value, 'parameters': {'low': 0.01, 'high': 0.6},
"infectious_dose": InfectiousDoses.DISTRIBUTION.value, },
"viable_to_RNA_ratio": ViableToRNARatios.DISTRIBUTION.value, "transmissibility_factor": 0.78,
"transmissibility_factor": 0.2, "infectiousness_days": 14,
"infectiousness_days": 14, },
}, "SARS_CoV_2_BETA": {
"SARS_CoV_2_Other": { "viral_load_in_sputum": ViralLoads.COVID_OVERALL.value,
"viral_load_in_sputum": ViralLoads.COVID_OVERALL.value, "infectious_dose": {
"infectious_dose": InfectiousDoses.DISTRIBUTION.value, "associated_value": "Uniform distribution",
"viable_to_RNA_ratio": ViableToRNARatios.DISTRIBUTION.value, "parameters": {"low": 10, "high": 100},
"transmissibility_factor": 0.1, },
"infectiousness_days": 14, "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": {
"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": {
"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": {
"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,
},
}, },
} }
mask_distributions = { mask_distributions = {
"Type I": { "Type I": {
"η_inhale": { "η_inhale": {
"associated_distribution": "Uniform", "associated_value": "Uniform distribution",
"parameters": { "parameters": {
"low": 0.25, "low": 0.25,
"high": 0.80, "high": 0.80,
}, },
}, },
"Known filtration efficiency of masks when exhaling?": "No",
"factor_exhale": 1, "factor_exhale": 1,
}, },
"FFP2": { "FFP2": {
"η_inhale": { "η_inhale": {
"associated_distribution": "Uniform", "associated_value": "Uniform distribution",
"parameters": { "parameters": {
"low": 0.83, "low": 0.83,
"high": 0.91, "high": 0.91,
}, },
}, },
"Known filtration efficiency of masks when exhaling?": "No",
"factor_exhale": 1, "factor_exhale": 1,
}, },
"Cloth": { "Cloth": {
"η_inhale": { "η_inhale": {
"associated_distribution": "Uniform", "associated_value": "Uniform distribution",
"parameters": { "parameters": {
"low": 0.05, "low": 0.05,
"high": 0.40, "high": 0.40,
}, },
}, },
"Known filtration efficiency of masks when exhaling?": "Yes",
"η_exhale": { "η_exhale": {
"associated_distribution": "Uniform", "associated_value": "Uniform distribution",
"parameters": { "parameters": {
"low": 0.20, "low": 0.20,
"high": 0.50, "high": 0.50,
@ -301,50 +331,15 @@ class DataRegistry:
"factor_exhale": 1, "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 = { room = {
"defaults": { "inside_temp": 293,
"inside_temp": 293, "humidity_with_heating": 0.3,
"humidity_with_heating": 0.3, "humidity_without_heating": 0.5,
"humidity_without_heating": 0.5,
},
} }
ventilation = { ventilation = {
"natural": { "natural": {
"discharge_factor": { "discharge_factor": {
@ -353,41 +348,38 @@ class DataRegistry:
}, },
"infiltration_ventilation": 0.25, "infiltration_ventilation": 0.25,
} }
particle = {
"evaporation_factor": 0.3,
}
population_with_virus = {
"fraction_of_infectious_virus": 1,
}
concentration_model = { concentration_model = {
"min_background_concentration": 0.0, "virus_concentration_model": {
"min_background_concentration": 0.0,
},
"CO2_concentration_model": { "CO2_concentration_model": {
"CO2_atmosphere_concentration": 440.44, "CO2_atmosphere_concentration": 440.44,
"CO2_fraction_exhaled": 0.042, "CO2_fraction_exhaled": 0.042,
}, },
} }
short_range_model = { short_range_model = {
"dilution_factor": { "dilution_factor": {
"mouth_diameter": 0.02, "mouth_diameter": 0.02,
"exhalation_coefficient": 2, "exhalation_coefficient": 2,
"tstar": 2, "breathing_cycle": 4,
"penetration_coefficients": { "penetration_coefficients": {
"𝛽r1": 0.18, "𝛽r1": 0.18,
"𝛽r2": 0.2, "𝛽r2": 0.2,
"𝛽x1": 2.4, "𝛽x1": 2.4,
}, },
}, },
"conversational_distance": {
"minimum_distance": 0.5,
"maximum_distance": 2.0,
},
} }
exposure_model = {
"repeats": 1, monte_carlo = {
"sample_size": 250000,
} }
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
population_scenario_activity = { population_scenario_activity = {
"office": {"placeholder": "Office", "activity": "Seated", "expiration": {"Speaking": 1, "Breathing": 2}}, "office": {"placeholder": "Office", "activity": "Seated", "expiration": {"Speaking": 1, "Breathing": 2}},
"smallmeeting": { "smallmeeting": {

View file

@ -20,7 +20,7 @@ class DataService:
self._host = host self._host = host
@classmethod @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.""" """Factory."""
return cls(host) return cls(host)

View file

@ -186,8 +186,8 @@ def skagit_chorale_mc(data_registry):
presence=models.SpecificInterval(((0, 2.5), )), presence=models.SpecificInterval(((0, 2.5), )),
virus=mc.SARSCoV2( virus=mc.SARSCoV2(
viral_load_in_sputum=10**9, viral_load_in_sputum=10**9,
infectious_dose=infectious_dose_distribution(data_registry), infectious_dose=infectious_dose_distribution(),
viable_to_RNA_ratio=viable_to_RNA_ratio_distribution(data_registry), viable_to_RNA_ratio=viable_to_RNA_ratio_distribution(),
transmissibility_factor=1., transmissibility_factor=1.,
), ),
mask=models.Mask.types['No mask'], mask=models.Mask.types['No mask'],
@ -230,8 +230,8 @@ def bus_ride_mc(data_registry):
presence=models.SpecificInterval(((0, 1.67), )), presence=models.SpecificInterval(((0, 1.67), )),
virus=mc.SARSCoV2( virus=mc.SARSCoV2(
viral_load_in_sputum=5*10**8, viral_load_in_sputum=5*10**8,
infectious_dose=infectious_dose_distribution(data_registry), infectious_dose=infectious_dose_distribution(),
viable_to_RNA_ratio=viable_to_RNA_ratio_distribution(data_registry), viable_to_RNA_ratio=viable_to_RNA_ratio_distribution(),
transmissibility_factor=1., transmissibility_factor=1.,
), ),
mask=models.Mask.types['No mask'], mask=models.Mask.types['No mask'],