From 98f6e3446b617f686314912719ffbbc677d7c492 Mon Sep 17 00:00:00 2001 From: Luis Aleixo Date: Thu, 20 Apr 2023 16:39:08 +0200 Subject: [PATCH 1/4] added default data file --- caimira/apps/calculator/DEFAULT_DATA.py | 125 ++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 caimira/apps/calculator/DEFAULT_DATA.py diff --git a/caimira/apps/calculator/DEFAULT_DATA.py b/caimira/apps/calculator/DEFAULT_DATA.py new file mode 100644 index 00000000..f9157b71 --- /dev/null +++ b/caimira/apps/calculator/DEFAULT_DATA.py @@ -0,0 +1,125 @@ +import typing + +# ------------------ Default form values ---------------------- + +# Used to declare when an attribute of a class must have a value provided, and +# there should be no default value used. +NO_DEFAULT = object() +DEFAULT_MC_SAMPLE_SIZE = 250_000 + +#: The default values for undefined fields. Note that the defaults here +#: and the defaults in the html form must not be contradictory. +DEFAULTS = { + 'activity_type': 'office', + 'air_changes': 0., + 'air_supply': 0., + 'arve_sensors_option': False, + 'specific_breaks': '{}', + 'precise_activity': '{}', + 'calculator_version': NO_DEFAULT, + 'ceiling_height': 0., + 'conditional_probability_plot': False, + 'exposed_coffee_break_option': 'coffee_break_0', + 'exposed_coffee_duration': 5, + 'exposed_finish': '17:30', + 'exposed_lunch_finish': '13:30', + 'exposed_lunch_option': True, + 'exposed_lunch_start': '12:30', + 'exposed_start': '08:30', + 'event_month': 'January', + 'floor_area': 0., + 'hepa_amount': 0., + 'hepa_option': False, + 'humidity': '', + 'infected_coffee_break_option': 'coffee_break_0', + 'infected_coffee_duration': 5, + 'infected_dont_have_breaks_with_exposed': False, + 'infected_finish': '17:30', + 'infected_lunch_finish': '13:30', + 'infected_lunch_option': True, + 'infected_lunch_start': '12:30', + 'infected_people': 1, + 'infected_start': '08:30', + 'inside_temp': NO_DEFAULT, + 'location_latitude': NO_DEFAULT, + 'location_longitude': NO_DEFAULT, + 'location_name': NO_DEFAULT, + 'geographic_population': 0, + 'geographic_cases': 0, + 'ascertainment_bias': 'confidence_low', + 'exposure_option': 'p_deterministic_exposure', + 'mask_type': 'Type I', + 'mask_wearing_option': 'mask_off', + 'mechanical_ventilation_type': 'not-applicable', + 'opening_distance': 0., + 'room_heating_option': False, + 'room_number': NO_DEFAULT, + 'room_volume': 0., + 'simulation_name': NO_DEFAULT, + 'total_people': NO_DEFAULT, + 'vaccine_option': False, + 'vaccine_booster_option': False, + 'vaccine_type': 'AZD1222_(AstraZeneca)', + 'vaccine_booster_type': 'AZD1222_(AstraZeneca)', + 'ventilation_type': 'no_ventilation', + 'virus_type': 'SARS_CoV_2', + 'volume_type': NO_DEFAULT, + 'window_type': 'window_sliding', + 'window_height': 0., + 'window_width': 0., + 'windows_duration': 10., + 'windows_frequency': 60., + 'windows_number': 0, + 'window_opening_regime': 'windows_open_permanently', + 'sensor_in_use': '', + 'short_range_option': 'short_range_no', + 'short_range_interactions': '[]', +} + +# ------------------ Activities ---------------------- + +ACTIVITIES: typing.List[typing.Dict[str, typing.Any]] = [ + {'name': 'office', 'activity': 'Seated', 'expiration': {'Speaking': 1, 'Breathing': 2}}, # Mostly silent in the office, but 1/3rd of time speaking. + {'name': 'smallmeeting', 'activity': 'Seated', 'expiration': {'Speaking': 1, 'Breathing': None}}, + {'name': 'largemeeting', 'activity': 'Standing', 'expiration': {'Speaking': 1, 'Breathing': 2}}, # Each infected person spends 1/3 of time speaking. + {'name': 'callcentre', 'activity': 'Seated', 'expiration': 'Speaking'}, + {'name': 'controlroom-day', 'activity': 'Seated', 'expiration': {'Speaking': 1, 'Breathing': 1}}, # Daytime control room shift, 50% speaking. + {'name': 'controlroom-night', 'activity': 'Seated', 'expiration': {'Speaking': 1, 'Breathing': 9}}, # Nightshift control room, 10% speaking. + {'name': 'library', 'activity': 'Seated', 'expiration': 'Breathing'}, + {'name': 'lab', 'activity': 'Light activity', 'expiration': {'Speaking': 1, 'Breathing': 1}}, # Model 1/2 of time spent speaking in a lab. + {'name': 'workshop', 'activity': 'Moderate activity', 'expiration': {'Speaking': 1, 'Breathing': 1}}, # Model 1/2 of time spent speaking in a workshop. + {'name': 'training', 'activity': 'Standing', 'expiration': 'Speaking'}, + {'name': 'training_attendee', 'activity': 'Seated', 'expiration': 'Breathing'}, + {'name': 'gym', 'activity': 'Heavy exercise', 'expiration': 'Breathing'}, + {'name': 'household-day', 'activity': 'Light activity', 'expiration': {'Breathing': 5, 'Speaking': 5}}, + {'name': 'household-night', 'activity': 'Seated', 'expiration': {'Breathing': 7, 'Speaking': 3}}, + {'name': 'primary-school', 'activity': 'Light activity', 'expiration': {'Breathing': 5, 'Speaking': 5}}, + {'name': 'secondary-school', 'activity': 'Light activity', 'expiration': {'Breathing': 7, 'Speaking': 3}}, + {'name': 'university', 'activity': 'Seated', 'expiration': {'Breathing': 9, 'Speaking': 1}}, + {'name': 'restaurant', 'activity': 'Seated', 'expiration': {'Breathing': 1, 'Speaking': 9}}, + {'name': 'precise', 'activity': None, 'expiration': None}, +] + +# ------------------ Validation ---------------------- + +ACTIVITY_TYPES = [ activity['name'] for activity in ACTIVITIES ] +COFFEE_OPTIONS_INT = {'coffee_break_0': 0, 'coffee_break_1': 1, 'coffee_break_2': 2, 'coffee_break_4': 4} +CONFIDENCE_LEVEL_OPTIONS = {'confidence_low': 10, 'confidence_medium': 5, 'confidence_high': 2} +MECHANICAL_VENTILATION_TYPES = {'mech_type_air_changes', 'mech_type_air_supply', 'not-applicable'} +MASK_TYPES = {'Type I', 'FFP2', 'Cloth'} +MASK_WEARING_OPTIONS = {'mask_on', 'mask_off'} +MONTH_NAMES = [ + 'January', 'February', 'March', 'April', 'May', 'June', 'July', + 'August', 'September', 'October', 'November', 'December', +] +VACCINE_BOOSTER_TYPE = ['AZD1222_(AstraZeneca)', 'Ad26.COV2.S_(Janssen)', 'BNT162b2_(Pfizer)', 'BNT162b2_(Pfizer)_(4th_dose)', 'BNT162b2_(Pfizer)_and_mRNA-1273_(Moderna)', + 'BNT162b2_(Pfizer)_or_mRNA-1273_(Moderna)', 'BNT162b2_(Pfizer)_or_mRNA-1273_(Moderna)_(4th_dose)', 'CoronaVac_(Sinovac)', 'Coronavac_(Sinovac)', 'Sinopharm', + 'mRNA-1273_(Moderna)', 'mRNA-1273_(Moderna)_(4th_dose)', 'Other'] +VACCINE_TYPE = ['Ad26.COV2.S_(Janssen)', 'Any_mRNA_-_heterologous', 'AZD1222_(AstraZeneca)', 'AZD1222_(AstraZeneca)_and_any_mRNA_-_heterologous', 'AZD1222_(AstraZeneca)_and_BNT162b2_(Pfizer)', + 'BBIBP-CorV_(Beijing_CNBG)', 'BNT162b2_(Pfizer)', 'BNT162b2_(Pfizer)_and_mRNA-1273_(Moderna)', 'CoronaVac_(Sinovac)', 'CoronaVac_(Sinovac)_and_AZD1222_(AstraZeneca)', 'Covishield', + 'mRNA-1273_(Moderna)', 'Sputnik_V_(Gamaleya)', 'CoronaVac_(Sinovac)_and_BNT162b2_(Pfizer)'] +VENTILATION_TYPES = {'natural_ventilation', 'mechanical_ventilation', 'no_ventilation'} +VIRUS_TYPES = {'SARS_CoV_2', 'SARS_CoV_2_ALPHA', 'SARS_CoV_2_BETA','SARS_CoV_2_GAMMA', 'SARS_CoV_2_DELTA', 'SARS_CoV_2_OMICRON'} +VOLUME_TYPES = {'room_volume_explicit', 'room_volume_from_dimensions'} +WINDOWS_OPENING_REGIMES = {'windows_open_permanently', 'windows_open_periodically', 'not-applicable'} +WINDOWS_TYPES = {'window_sliding', 'window_hinged', 'not-applicable'} From 14a6fb73a737167a70ec6097c927c8950e0309b2 Mon Sep 17 00:00:00 2001 From: Luis Aleixo Date: Thu, 20 Apr 2023 16:39:29 +0200 Subject: [PATCH 2/4] adapted model_generator validation references --- caimira/apps/calculator/model_generator.py | 190 ++------------------- 1 file changed, 18 insertions(+), 172 deletions(-) diff --git a/caimira/apps/calculator/model_generator.py b/caimira/apps/calculator/model_generator.py index 5c0fe232..ce130790 100644 --- a/caimira/apps/calculator/model_generator.py +++ b/caimira/apps/calculator/model_generator.py @@ -16,16 +16,12 @@ import caimira.monte_carlo as mc from .. import calculator from caimira.monte_carlo.data import activity_distributions, virus_distributions, mask_distributions, short_range_distances from caimira.monte_carlo.data import expiration_distribution, expiration_BLO_factors, expiration_distributions, short_range_expiration_distributions +from .DEFAULT_DATA import * LOG = logging.getLogger(__name__) minutes_since_midnight = typing.NewType('minutes_since_midnight', int) -# Used to declare when an attribute of a class must have a value provided, and -# there should be no default value used. -_NO_DEFAULT = object() -_DEFAULT_MC_SAMPLE_SIZE = 250000 - @dataclasses.dataclass class FormData: @@ -94,74 +90,7 @@ class FormData: short_range_option: str short_range_interactions: list - #: The default values for undefined fields. Note that the defaults here - #: and the defaults in the html form must not be contradictory. - _DEFAULTS: typing.ClassVar[typing.Dict[str, typing.Any]] = { - 'activity_type': 'office', - 'air_changes': 0., - 'air_supply': 0., - 'arve_sensors_option': False, - 'specific_breaks': '{}', - 'precise_activity': '{}', - 'calculator_version': _NO_DEFAULT, - 'ceiling_height': 0., - 'conditional_probability_plot': False, - 'exposed_coffee_break_option': 'coffee_break_0', - 'exposed_coffee_duration': 5, - 'exposed_finish': '17:30', - 'exposed_lunch_finish': '13:30', - 'exposed_lunch_option': True, - 'exposed_lunch_start': '12:30', - 'exposed_start': '08:30', - 'event_month': 'January', - 'floor_area': 0., - 'hepa_amount': 0., - 'hepa_option': False, - 'humidity': '', - 'infected_coffee_break_option': 'coffee_break_0', - 'infected_coffee_duration': 5, - 'infected_dont_have_breaks_with_exposed': False, - 'infected_finish': '17:30', - 'infected_lunch_finish': '13:30', - 'infected_lunch_option': True, - 'infected_lunch_start': '12:30', - 'infected_people': 1, - 'infected_start': '08:30', - 'inside_temp': _NO_DEFAULT, - 'location_latitude': _NO_DEFAULT, - 'location_longitude': _NO_DEFAULT, - 'location_name': _NO_DEFAULT, - 'geographic_population': 0, - 'geographic_cases': 0, - 'ascertainment_bias': 'confidence_low', - 'exposure_option': 'p_deterministic_exposure', - 'mask_type': 'Type I', - 'mask_wearing_option': 'mask_off', - 'mechanical_ventilation_type': 'not-applicable', - 'opening_distance': 0., - 'room_heating_option': False, - 'room_number': _NO_DEFAULT, - 'room_volume': 0., - 'simulation_name': _NO_DEFAULT, - 'total_people': _NO_DEFAULT, - 'vaccine_option': False, - 'vaccine_booster_option': False, - 'vaccine_type': 'AZD1222_(AstraZeneca)', - 'vaccine_booster_type': 'AZD1222_(AstraZeneca)', - 'ventilation_type': 'no_ventilation', - 'virus_type': 'SARS_CoV_2', - 'volume_type': _NO_DEFAULT, - 'window_type': 'window_sliding', - 'window_height': 0., - 'window_width': 0., - 'windows_duration': 10., - 'windows_frequency': 60., - 'windows_number': 0, - 'window_opening_regime': 'windows_open_permanently', - 'sensor_in_use': '', - 'short_range_option': 'short_range_no', - 'short_range_interactions': '[]', - } + _DEFAULTS: typing.ClassVar[typing.Dict[str, typing.Any]] = DEFAULTS @classmethod def from_dict(cls, form_data: typing.Dict) -> "FormData": @@ -176,7 +105,7 @@ class FormData: for key, default_value in cls._DEFAULTS.items(): if form_data.get(key, '') == '': - if default_value is _NO_DEFAULT: + if default_value is NO_DEFAULT: raise ValueError(f"{key} must be specified") form_data[key] = default_value @@ -206,8 +135,8 @@ class FormData: del form_dict['calculator_version'] for attr, value in list(form_dict.items()): - default = cls._DEFAULTS.get(attr, _NO_DEFAULT) - if default is not _NO_DEFAULT and value in [default, 'not-applicable']: + default = cls._DEFAULTS.get(attr, NO_DEFAULT) + if default is not NO_DEFAULT and value in [default, 'not-applicable']: form_dict.pop(attr) return form_dict @@ -273,7 +202,7 @@ class FormData: f"Length of breaks >= Length of {population} presence." ) - validation_tuples = [('activity_type', ACTIVITY_TYPES), + validation_tuples = [('activity_type', ACTIVITY_TYPES), ('exposed_coffee_break_option', COFFEE_OPTIONS_INT), ('infected_coffee_break_option', COFFEE_OPTIONS_INT), ('mechanical_ventilation_type', MECHANICAL_VENTILATION_TYPES), @@ -422,7 +351,7 @@ class FormData: ), ) - def build_model(self, sample_size=_DEFAULT_MC_SAMPLE_SIZE) -> models.ExposureModel: + def build_model(self, sample_size=DEFAULT_MC_SAMPLE_SIZE) -> models.ExposureModel: return self.build_mc_model().build_model(size=sample_size) def tz_name_and_utc_offset(self) -> typing.Tuple[str, float]: @@ -453,7 +382,7 @@ class FormData: month = MONTH_NAMES.index(self.event_month) + 1 wx_station = self.nearest_weather_station() - temp_profile = caimira.data.weather.mean_hourly_temperatures(wx_station[0], month) + temp_profile = caimira.data.weather.mean_hourly_temperatures(wx_station = wx_station[0], month = MONTH_NAMES.index(self.event_month) + 1) _, utc_offset = self.tz_name_and_utc_offset() @@ -548,74 +477,16 @@ class FormData: # Initializes the virus virus = virus_distributions[self.virus_type] - scenario_activity_and_expiration = { - 'office': ( - 'Seated', - # Mostly silent in the office, but 1/3rd of time speaking. - {'Speaking': 1, 'Breathing': 2} - ), - 'controlroom-day': ( - 'Seated', - # Daytime control room shift, 50% speaking. - {'Speaking': 1, 'Breathing': 1} - ), - 'controlroom-night': ( - 'Seated', - # Nightshift control room, 10% speaking. - {'Speaking': 1, 'Breathing': 9} - ), - 'smallmeeting': ( - 'Seated', - # Conversation of N people is approximately 1/N% of the time speaking. - {'Speaking': 1, 'Breathing': self.total_people - 1} - ), - 'largemeeting': ( - 'Standing', - # each infected person spends 1/3 of time speaking. - {'Speaking': 1, 'Breathing': 2} - ), - 'callcentre': ('Seated', 'Speaking'), - 'library': ('Seated', 'Breathing'), - 'training': ('Standing', 'Speaking'), - 'training_attendee': ('Seated', 'Breathing'), - 'lab': ( - 'Light activity', - #Model 1/2 of time spent speaking in a lab. - {'Speaking': 1, 'Breathing': 1}), - 'workshop': ( - 'Moderate activity', - #Model 1/2 of time spent speaking in a workshop. - {'Speaking': 1, 'Breathing': 1}), - 'gym':('Heavy exercise', 'Breathing'), - # Other activity types - 'household-day': ( - 'Light activity', - {'Breathing': 5, 'Speaking': 5} - ), - 'household-night': ( - 'Seated', - {'Breathing': 7, 'Speaking': 3} - ), - 'primary-school': ( - 'Light activity', - {'Breathing': 5, 'Speaking': 5} - ), - 'secondary-school': ( - 'Light activity', - {'Breathing': 7, 'Speaking': 3} - ), - 'university': ( - 'Seated', - {'Breathing': 9, 'Speaking': 1} - ), - 'restaurant': ( - 'Seated', - {'Breathing': 1, 'Speaking': 9} - ), - 'precise': self.generate_precise_activity_expiration(), - } - - [activity_defn, expiration_defn] = scenario_activity_and_expiration[self.activity_type] + activity_index = ACTIVITY_TYPES.index(self.activity_type) + activity_defn = ACTIVITIES[activity_index]['activity'] + expiration_defn = ACTIVITIES[activity_index]['expiration'] + + if (self.activity_type == 'smallmeeting'): + # Conversation of N people is approximately 1/N% of the time speaking. + expiration_defn = {'Speaking': 1, 'Breathing': self.total_people - 1} + elif (self.activity_type == 'precise'): + activity_defn, expiration_defn = self.generate_precise_activity_expiration() + activity = activity_distributions[activity_defn] expiration = build_expiration(expiration_defn) @@ -960,31 +831,6 @@ def baseline_raw_form_data() -> typing.Dict[str, typing.Union[str, float]]: } -ACTIVITY_TYPES = { - 'office', 'smallmeeting', 'largemeeting', 'callcentre', 'controlroom-day', 'controlroom-night', 'library', 'lab', 'workshop', 'training', - 'training_attendee', 'gym', 'household-day', 'household-night', 'primary-school', 'secondary-school', 'university', 'restaurant', 'precise', -} -MECHANICAL_VENTILATION_TYPES = {'mech_type_air_changes', 'mech_type_air_supply', 'not-applicable'} -MASK_TYPES = {'Type I', 'FFP2', 'Cloth'} -MASK_WEARING_OPTIONS = {'mask_on', 'mask_off'} -VENTILATION_TYPES = {'natural_ventilation', 'mechanical_ventilation', 'no_ventilation'} -VIRUS_TYPES = {'SARS_CoV_2', 'SARS_CoV_2_ALPHA', 'SARS_CoV_2_BETA','SARS_CoV_2_GAMMA', 'SARS_CoV_2_DELTA', 'SARS_CoV_2_OMICRON'} -VOLUME_TYPES = {'room_volume_explicit', 'room_volume_from_dimensions'} -WINDOWS_OPENING_REGIMES = {'windows_open_permanently', 'windows_open_periodically', 'not-applicable'} -WINDOWS_TYPES = {'window_sliding', 'window_hinged', 'not-applicable'} -COFFEE_OPTIONS_INT = {'coffee_break_0': 0, 'coffee_break_1': 1, 'coffee_break_2': 2, 'coffee_break_4': 4} -CONFIDENCE_LEVEL_OPTIONS = {'confidence_low': 10, 'confidence_medium': 5, 'confidence_high': 2} -MONTH_NAMES = [ - 'January', 'February', 'March', 'April', 'May', 'June', 'July', - 'August', 'September', 'October', 'November', 'December', -] -VACCINE_TYPE = ['Ad26.COV2.S_(Janssen)', 'Any_mRNA_-_heterologous', 'AZD1222_(AstraZeneca)', 'AZD1222_(AstraZeneca)_and_any_mRNA_-_heterologous', 'AZD1222_(AstraZeneca)_and_BNT162b2_(Pfizer)', - 'BBIBP-CorV_(Beijing_CNBG)', 'BNT162b2_(Pfizer)', 'BNT162b2_(Pfizer)_and_mRNA-1273_(Moderna)', 'CoronaVac_(Sinovac)', 'CoronaVac_(Sinovac)_and_AZD1222_(AstraZeneca)', 'Covishield', - 'mRNA-1273_(Moderna)', 'Sputnik_V_(Gamaleya)', 'CoronaVac_(Sinovac)_and_BNT162b2_(Pfizer)'] -VACCINE_BOOSTER_TYPE = ['AZD1222_(AstraZeneca)', 'Ad26.COV2.S_(Janssen)', 'BNT162b2_(Pfizer)', 'BNT162b2_(Pfizer)_(4th_dose)', 'BNT162b2_(Pfizer)_and_mRNA-1273_(Moderna)', - 'BNT162b2_(Pfizer)_or_mRNA-1273_(Moderna)', 'BNT162b2_(Pfizer)_or_mRNA-1273_(Moderna)_(4th_dose)', 'CoronaVac_(Sinovac)', 'Coronavac_(Sinovac)', 'Sinopharm', - 'mRNA-1273_(Moderna)', 'mRNA-1273_(Moderna)_(4th_dose)', 'Other'] - def _hours2timestring(hours: float): # Convert times like 14.5 to strings, like "14:30" return f"{int(np.floor(hours)):02d}:{int(np.round((hours % 1) * 60)):02d}" From 1171e51f907927fc9fe108b631bd0122f81ea7f2 Mon Sep 17 00:00:00 2001 From: Luis Aleixo Date: Thu, 20 Apr 2023 16:39:47 +0200 Subject: [PATCH 3/4] removed private reference for DEFAULT_DATA variables --- caimira/apps/calculator/report_generator.py | 4 ++-- caimira/tests/apps/calculator/test_model_generator.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/caimira/apps/calculator/report_generator.py b/caimira/apps/calculator/report_generator.py index 432cf286..885c2e8d 100644 --- a/caimira/apps/calculator/report_generator.py +++ b/caimira/apps/calculator/report_generator.py @@ -15,7 +15,7 @@ import matplotlib.pyplot as plt from caimira import models from caimira.apps.calculator import markdown_tools from ... import monte_carlo as mc -from .model_generator import FormData, _DEFAULT_MC_SAMPLE_SIZE +from .model_generator import FormData, DEFAULT_MC_SAMPLE_SIZE from ... import dataclass_utils @@ -362,7 +362,7 @@ def manufacture_alternative_scenarios(form: FormData) -> typing.Dict[str, mc.Exp def scenario_statistics(mc_model: mc.ExposureModel, sample_times: typing.List[float], compute_prob_exposure: bool): - model = mc_model.build_model(size=_DEFAULT_MC_SAMPLE_SIZE) + model = mc_model.build_model(size=DEFAULT_MC_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/tests/apps/calculator/test_model_generator.py b/caimira/tests/apps/calculator/test_model_generator.py index 787e7a8e..099a00d2 100644 --- a/caimira/tests/apps/calculator/test_model_generator.py +++ b/caimira/tests/apps/calculator/test_model_generator.py @@ -540,7 +540,7 @@ def test_default_types(): # Handle typing.NewType definitions. field_type = field_type.__supertype__ - if value is model_generator._NO_DEFAULT: + if value is model_generator.NO_DEFAULT: continue if field in model_generator._CAST_RULES_FORM_ARG_TO_NATIVE: From 0cb6477cb2b235f7f8e65195c4104964b120460a Mon Sep 17 00:00:00 2001 From: Luis Aleixo Date: Fri, 26 May 2023 09:44:47 +0200 Subject: [PATCH 4/4] modified file name and references. removed unused variables --- .../{DEFAULT_DATA.py => defaults.py} | 73 +++++++++++++------ caimira/apps/calculator/model_generator.py | 4 +- caimira/apps/calculator/report_generator.py | 6 -- 3 files changed, 52 insertions(+), 31 deletions(-) rename caimira/apps/calculator/{DEFAULT_DATA.py => defaults.py} (56%) diff --git a/caimira/apps/calculator/DEFAULT_DATA.py b/caimira/apps/calculator/defaults.py similarity index 56% rename from caimira/apps/calculator/DEFAULT_DATA.py rename to caimira/apps/calculator/defaults.py index f9157b71..a9e9d0f9 100644 --- a/caimira/apps/calculator/DEFAULT_DATA.py +++ b/caimira/apps/calculator/defaults.py @@ -79,33 +79,55 @@ DEFAULTS = { # ------------------ Activities ---------------------- ACTIVITIES: typing.List[typing.Dict[str, typing.Any]] = [ - {'name': 'office', 'activity': 'Seated', 'expiration': {'Speaking': 1, 'Breathing': 2}}, # Mostly silent in the office, but 1/3rd of time speaking. - {'name': 'smallmeeting', 'activity': 'Seated', 'expiration': {'Speaking': 1, 'Breathing': None}}, - {'name': 'largemeeting', 'activity': 'Standing', 'expiration': {'Speaking': 1, 'Breathing': 2}}, # Each infected person spends 1/3 of time speaking. + # Mostly silent in the office, but 1/3rd of time speaking. + {'name': 'office', 'activity': 'Seated', + 'expiration': {'Speaking': 1, 'Breathing': 2}}, + {'name': 'smallmeeting', 'activity': 'Seated', + 'expiration': {'Speaking': 1, 'Breathing': None}}, + # Each infected person spends 1/3 of time speaking. + {'name': 'largemeeting', 'activity': 'Standing', + 'expiration': {'Speaking': 1, 'Breathing': 2}}, {'name': 'callcentre', 'activity': 'Seated', 'expiration': 'Speaking'}, - {'name': 'controlroom-day', 'activity': 'Seated', 'expiration': {'Speaking': 1, 'Breathing': 1}}, # Daytime control room shift, 50% speaking. - {'name': 'controlroom-night', 'activity': 'Seated', 'expiration': {'Speaking': 1, 'Breathing': 9}}, # Nightshift control room, 10% speaking. + # Daytime control room shift, 50% speaking. + {'name': 'controlroom-day', 'activity': 'Seated', + 'expiration': {'Speaking': 1, 'Breathing': 1}}, + # Nightshift control room, 10% speaking. + {'name': 'controlroom-night', 'activity': 'Seated', + 'expiration': {'Speaking': 1, 'Breathing': 9}}, {'name': 'library', 'activity': 'Seated', 'expiration': 'Breathing'}, - {'name': 'lab', 'activity': 'Light activity', 'expiration': {'Speaking': 1, 'Breathing': 1}}, # Model 1/2 of time spent speaking in a lab. - {'name': 'workshop', 'activity': 'Moderate activity', 'expiration': {'Speaking': 1, 'Breathing': 1}}, # Model 1/2 of time spent speaking in a workshop. + # Model 1/2 of time spent speaking in a lab. + {'name': 'lab', 'activity': 'Light activity', + 'expiration': {'Speaking': 1, 'Breathing': 1}}, + # Model 1/2 of time spent speaking in a workshop. + {'name': 'workshop', 'activity': 'Moderate activity', + 'expiration': {'Speaking': 1, 'Breathing': 1}}, {'name': 'training', 'activity': 'Standing', 'expiration': 'Speaking'}, {'name': 'training_attendee', 'activity': 'Seated', 'expiration': 'Breathing'}, {'name': 'gym', 'activity': 'Heavy exercise', 'expiration': 'Breathing'}, - {'name': 'household-day', 'activity': 'Light activity', 'expiration': {'Breathing': 5, 'Speaking': 5}}, - {'name': 'household-night', 'activity': 'Seated', 'expiration': {'Breathing': 7, 'Speaking': 3}}, - {'name': 'primary-school', 'activity': 'Light activity', 'expiration': {'Breathing': 5, 'Speaking': 5}}, - {'name': 'secondary-school', 'activity': 'Light activity', 'expiration': {'Breathing': 7, 'Speaking': 3}}, - {'name': 'university', 'activity': 'Seated', 'expiration': {'Breathing': 9, 'Speaking': 1}}, - {'name': 'restaurant', 'activity': 'Seated', 'expiration': {'Breathing': 1, 'Speaking': 9}}, + {'name': 'household-day', 'activity': 'Light activity', + 'expiration': {'Breathing': 5, 'Speaking': 5}}, + {'name': 'household-night', 'activity': 'Seated', + 'expiration': {'Breathing': 7, 'Speaking': 3}}, + {'name': 'primary-school', 'activity': 'Light activity', + 'expiration': {'Breathing': 5, 'Speaking': 5}}, + {'name': 'secondary-school', 'activity': 'Light activity', + 'expiration': {'Breathing': 7, 'Speaking': 3}}, + {'name': 'university', 'activity': 'Seated', + 'expiration': {'Breathing': 9, 'Speaking': 1}}, + {'name': 'restaurant', 'activity': 'Seated', + 'expiration': {'Breathing': 1, 'Speaking': 9}}, {'name': 'precise', 'activity': None, 'expiration': None}, ] # ------------------ Validation ---------------------- -ACTIVITY_TYPES = [ activity['name'] for activity in ACTIVITIES ] -COFFEE_OPTIONS_INT = {'coffee_break_0': 0, 'coffee_break_1': 1, 'coffee_break_2': 2, 'coffee_break_4': 4} -CONFIDENCE_LEVEL_OPTIONS = {'confidence_low': 10, 'confidence_medium': 5, 'confidence_high': 2} -MECHANICAL_VENTILATION_TYPES = {'mech_type_air_changes', 'mech_type_air_supply', 'not-applicable'} +ACTIVITY_TYPES = [activity['name'] for activity in ACTIVITIES] +COFFEE_OPTIONS_INT = {'coffee_break_0': 0, 'coffee_break_1': 1, + 'coffee_break_2': 2, 'coffee_break_4': 4} +CONFIDENCE_LEVEL_OPTIONS = {'confidence_low': 10, + 'confidence_medium': 5, 'confidence_high': 2} +MECHANICAL_VENTILATION_TYPES = { + 'mech_type_air_changes', 'mech_type_air_supply', 'not-applicable'} MASK_TYPES = {'Type I', 'FFP2', 'Cloth'} MASK_WEARING_OPTIONS = {'mask_on', 'mask_off'} MONTH_NAMES = [ @@ -113,13 +135,16 @@ MONTH_NAMES = [ 'August', 'September', 'October', 'November', 'December', ] VACCINE_BOOSTER_TYPE = ['AZD1222_(AstraZeneca)', 'Ad26.COV2.S_(Janssen)', 'BNT162b2_(Pfizer)', 'BNT162b2_(Pfizer)_(4th_dose)', 'BNT162b2_(Pfizer)_and_mRNA-1273_(Moderna)', - 'BNT162b2_(Pfizer)_or_mRNA-1273_(Moderna)', 'BNT162b2_(Pfizer)_or_mRNA-1273_(Moderna)_(4th_dose)', 'CoronaVac_(Sinovac)', 'Coronavac_(Sinovac)', 'Sinopharm', - 'mRNA-1273_(Moderna)', 'mRNA-1273_(Moderna)_(4th_dose)', 'Other'] + 'BNT162b2_(Pfizer)_or_mRNA-1273_(Moderna)', 'BNT162b2_(Pfizer)_or_mRNA-1273_(Moderna)_(4th_dose)', 'CoronaVac_(Sinovac)', 'Coronavac_(Sinovac)', 'Sinopharm', + 'mRNA-1273_(Moderna)', 'mRNA-1273_(Moderna)_(4th_dose)', 'Other'] VACCINE_TYPE = ['Ad26.COV2.S_(Janssen)', 'Any_mRNA_-_heterologous', 'AZD1222_(AstraZeneca)', 'AZD1222_(AstraZeneca)_and_any_mRNA_-_heterologous', 'AZD1222_(AstraZeneca)_and_BNT162b2_(Pfizer)', - 'BBIBP-CorV_(Beijing_CNBG)', 'BNT162b2_(Pfizer)', 'BNT162b2_(Pfizer)_and_mRNA-1273_(Moderna)', 'CoronaVac_(Sinovac)', 'CoronaVac_(Sinovac)_and_AZD1222_(AstraZeneca)', 'Covishield', - 'mRNA-1273_(Moderna)', 'Sputnik_V_(Gamaleya)', 'CoronaVac_(Sinovac)_and_BNT162b2_(Pfizer)'] -VENTILATION_TYPES = {'natural_ventilation', 'mechanical_ventilation', 'no_ventilation'} -VIRUS_TYPES = {'SARS_CoV_2', 'SARS_CoV_2_ALPHA', 'SARS_CoV_2_BETA','SARS_CoV_2_GAMMA', 'SARS_CoV_2_DELTA', 'SARS_CoV_2_OMICRON'} + 'BBIBP-CorV_(Beijing_CNBG)', 'BNT162b2_(Pfizer)', 'BNT162b2_(Pfizer)_and_mRNA-1273_(Moderna)', 'CoronaVac_(Sinovac)', 'CoronaVac_(Sinovac)_and_AZD1222_(AstraZeneca)', 'Covishield', + 'mRNA-1273_(Moderna)', 'Sputnik_V_(Gamaleya)', 'CoronaVac_(Sinovac)_and_BNT162b2_(Pfizer)'] +VENTILATION_TYPES = {'natural_ventilation', + 'mechanical_ventilation', 'no_ventilation'} +VIRUS_TYPES = {'SARS_CoV_2', 'SARS_CoV_2_ALPHA', 'SARS_CoV_2_BETA', + 'SARS_CoV_2_GAMMA', 'SARS_CoV_2_DELTA', 'SARS_CoV_2_OMICRON'} VOLUME_TYPES = {'room_volume_explicit', 'room_volume_from_dimensions'} -WINDOWS_OPENING_REGIMES = {'windows_open_permanently', 'windows_open_periodically', 'not-applicable'} +WINDOWS_OPENING_REGIMES = {'windows_open_permanently', + 'windows_open_periodically', 'not-applicable'} WINDOWS_TYPES = {'window_sliding', 'window_hinged', 'not-applicable'} diff --git a/caimira/apps/calculator/model_generator.py b/caimira/apps/calculator/model_generator.py index ce130790..86e098e2 100644 --- a/caimira/apps/calculator/model_generator.py +++ b/caimira/apps/calculator/model_generator.py @@ -16,7 +16,9 @@ import caimira.monte_carlo as mc from .. import calculator from caimira.monte_carlo.data import activity_distributions, virus_distributions, mask_distributions, short_range_distances from caimira.monte_carlo.data import expiration_distribution, expiration_BLO_factors, expiration_distributions, short_range_expiration_distributions -from .DEFAULT_DATA import * +from .defaults import (NO_DEFAULT, DEFAULT_MC_SAMPLE_SIZE, DEFAULTS, ACTIVITIES, ACTIVITY_TYPES, COFFEE_OPTIONS_INT, CONFIDENCE_LEVEL_OPTIONS, + MECHANICAL_VENTILATION_TYPES, MASK_TYPES, MASK_WEARING_OPTIONS, MONTH_NAMES, VACCINE_BOOSTER_TYPE, VACCINE_TYPE, + VENTILATION_TYPES, VIRUS_TYPES, VOLUME_TYPES, WINDOWS_OPENING_REGIMES, WINDOWS_TYPES) LOG = logging.getLogger(__name__) diff --git a/caimira/apps/calculator/report_generator.py b/caimira/apps/calculator/report_generator.py index 885c2e8d..7beb5fee 100644 --- a/caimira/apps/calculator/report_generator.py +++ b/caimira/apps/calculator/report_generator.py @@ -123,7 +123,6 @@ def calculate_report_data(form: FormData, model: models.ExposureModel) -> typing for time in times ] lower_concentrations = concentrations_with_sr_breathing(form, model, times, short_range_intervals) - highest_const = max(concentrations) cumulative_doses = np.cumsum([ np.array(model.deposited_exposure_between_bounds(float(time1), float(time2))).mean() @@ -137,8 +136,6 @@ def calculate_report_data(form: FormData, model: models.ExposureModel) -> typing prob = np.array(model.infection_probability()) prob_dist_count, prob_dist_bins = np.histogram(prob/100, bins=100, density=True) prob_probabilistic_exposure = np.array(model.total_probability_rule()).mean() - er = np.array(model.concentration_model.infected.emission_rate_per_person_when_present()).mean() - exposed_occupants = model.exposed.number 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 exposed_presence_intervals = [list(interval) for interval in model.exposed.presence_interval().boundaries()] @@ -151,7 +148,6 @@ def calculate_report_data(form: FormData, model: models.ExposureModel) -> typing "short_range_expirations": short_range_expirations, "concentrations": concentrations, "concentrations_zoomed": lower_concentrations, - "highest_const": highest_const, "cumulative_doses": list(cumulative_doses), "long_range_cumulative_doses": list(long_range_cumulative_doses), "prob_inf": prob.mean(), @@ -160,8 +156,6 @@ def calculate_report_data(form: FormData, model: models.ExposureModel) -> typing "prob_hist_count": list(prob_dist_count), "prob_hist_bins": list(prob_dist_bins), "prob_probabilistic_exposure": prob_probabilistic_exposure, - "emission_rate_per_person": er, - "exposed_occupants": exposed_occupants, "expected_new_cases": expected_new_cases, "uncertainties_plot_src": uncertainties_plot_src, }