Merge branch 'feature/climate_function' of https://gitlab.cern.ch/cara/cara into feature/climate_function
This commit is contained in:
commit
fef8773034
4 changed files with 26 additions and 31 deletions
13
README.md
13
README.md
|
|
@ -58,19 +58,6 @@ CARA has not undergone review, approval or certification by competent authoritie
|
|||
The software is provided "as is", without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and non-infringement.
|
||||
In no event shall the authors or copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with the software or the use or other dealings in the software.
|
||||
|
||||
## Adapting CARA to your location
|
||||
|
||||
The default weather data (average hourly outdoor temperature in Celcius for each month of the year) used in CARA is for Geneva, Switzerland.
|
||||
In order for the natural ventilation option to work correctly for other geographic locations, the outdoor temperatures must be updated.
|
||||
There are some scripts to help download and process the temperature data from your nearest weather station in the https://gitlab.cern.ch/cara/climatology-data repository.
|
||||
Once you have used the scripts, the hourly temperature data for your location should be added to the file `data.py` in place of the default values for Geneva. The temperature values for your locations should be pasted into the `Geneva_hourly_temperatures_celsius_per_hour` variable, **without changing the variable name** in the following format:
|
||||
|
||||
`'1': [0.2, -0.3, -0.5, -0.9, -1.1, -1.4, -1.5, -1.5, -1.1, 0.1, 1.5,
|
||||
2.8, 3.8, 4.4, 4.5, 4.4, 4.4, 3.9, 3.1, 2.7, 2.2, 1.7, 1.5, 1.1],
|
||||
'2': [0.9, 0.3, 0.0, -0.5, -0.7, -1.1, -1.2, -1.1, -0.7, 0.8, 2.5,
|
||||
4.2, 5.4, 6.2, 6.3, 6.2, 6.1, 5.5, 4.5, 4.1, 3.5, 2.8, 2.5, 2.0],...`
|
||||
|
||||
CARA currently supports **only one geographic location for weather data per instance**.
|
||||
|
||||
## Running CARA locally
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ from dataclasses import dataclass
|
|||
import html
|
||||
import logging
|
||||
import typing
|
||||
import datetime
|
||||
|
||||
import numpy as np
|
||||
|
||||
|
|
@ -105,9 +104,9 @@ class FormData:
|
|||
'infected_lunch_start': '12:30',
|
||||
'infected_people': _NO_DEFAULT,
|
||||
'infected_start': '08:30',
|
||||
'location_latitude': 46.20833,
|
||||
'location_longitude': 6.14275,
|
||||
'location_name': 'Geneva',
|
||||
'location_latitude': _NO_DEFAULT,
|
||||
'location_longitude': _NO_DEFAULT,
|
||||
'location_name': _NO_DEFAULT,
|
||||
'mask_type': 'Type I',
|
||||
'mask_wearing_option': 'mask_off',
|
||||
'mechanical_ventilation_type': 'not-applicable',
|
||||
|
|
@ -205,7 +204,8 @@ class FormData:
|
|||
('virus_type', VIRUS_TYPES),
|
||||
('volume_type', VOLUME_TYPES),
|
||||
('window_opening_regime', WINDOWS_OPENING_REGIMES),
|
||||
('window_type', WINDOWS_TYPES)]
|
||||
('window_type', WINDOWS_TYPES),
|
||||
('event_month', MONTH_NAMES)]
|
||||
for attr_name, valid_set in validation_tuples:
|
||||
if getattr(self, attr_name) not in valid_set:
|
||||
raise ValueError(f"{getattr(self, attr_name)} is not a valid value for {attr_name}")
|
||||
|
|
@ -261,11 +261,8 @@ class FormData:
|
|||
else:
|
||||
window_interval = always_on
|
||||
|
||||
#month = self.event_month[:3]
|
||||
datetime_object = datetime.datetime.strptime(self.event_month[:3], "%b")
|
||||
month = datetime_object.month
|
||||
month = MONTH_NAMES.index(self.event_month) + 1
|
||||
|
||||
# set location
|
||||
wx_station = self.nearest_weather_station()
|
||||
temp_profile = cara.data.weather.mean_hourly_temperatures(wx_station[0], month)
|
||||
|
||||
|
|
@ -314,10 +311,10 @@ class FormData:
|
|||
return models.MultipleVentilation((ventilation, infiltration_ventilation))
|
||||
|
||||
def nearest_weather_station(self) -> cara.data.weather.WxStationRecordType:
|
||||
wx_station = cara.data.weather.nearest_wx_station(
|
||||
"""Return the nearest weather station (which has valid data) for this form"""
|
||||
return cara.data.weather.nearest_wx_station(
|
||||
longitude=self.location_longitude, latitude=self.location_latitude
|
||||
)
|
||||
return wx_station
|
||||
|
||||
def mask(self) -> models.Mask:
|
||||
# Initializes the mask type if mask wearing is "continuous", otherwise instantiates the mask attribute as
|
||||
|
|
@ -622,6 +619,9 @@ def baseline_raw_form_data():
|
|||
'infected_lunch_start': '12:30',
|
||||
'infected_people': '1',
|
||||
'infected_start': '09:00',
|
||||
'location_latitude': 46.20833,
|
||||
'location_longitude': 6.14275,
|
||||
'location_name': 'Geneva',
|
||||
'mask_type': 'Type I',
|
||||
'mask_wearing_option': 'mask_off',
|
||||
'mechanical_ventilation_type': '',
|
||||
|
|
@ -658,6 +658,11 @@ 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}
|
||||
|
||||
MONTH_NAMES = [
|
||||
'January', 'February', 'March', 'April', 'May', 'June', 'July',
|
||||
'August', 'September', 'October', 'November', 'December',
|
||||
]
|
||||
|
||||
|
||||
def _hours2timestring(hours: float):
|
||||
# Convert times like 14.5 to strings, like "14:30"
|
||||
|
|
@ -713,5 +718,3 @@ for _field in dataclasses.fields(FormData):
|
|||
elif _field.type is bool:
|
||||
_CAST_RULES_FORM_ARG_TO_NATIVE[_field.name] = lambda v: v == '1'
|
||||
_CAST_RULES_NATIVE_TO_FORM_ARG[_field.name] = int
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -9,9 +9,7 @@ from scipy.spatial import cKDTree
|
|||
from cara import models
|
||||
|
||||
|
||||
weather_debug = False
|
||||
|
||||
DATA_LOCATION = Path(__file__).absolute().parent
|
||||
WX_DATA_LOCATION = Path(__file__).absolute().parent
|
||||
|
||||
|
||||
WxStationIdType = str
|
||||
|
|
@ -28,7 +26,7 @@ def wx_data() -> typing.Dict[WxStationIdType, typing.Dict[MonthType, HourlyTempT
|
|||
The data is structured by station location, and for each station location, by month.
|
||||
|
||||
"""
|
||||
with (DATA_LOCATION / 'global_weather_set.json').open("r") as json_file:
|
||||
with (WX_DATA_LOCATION / 'global_weather_set.json').open("r") as json_file:
|
||||
data = json.load(json_file)
|
||||
|
||||
for station in list(data.keys()):
|
||||
|
|
@ -54,7 +52,7 @@ def wx_station_data() -> typing.Dict[WxStationIdType, WxStationRecordType]:
|
|||
weather_data = wx_data()
|
||||
station_data = {}
|
||||
fixed_delimits = [0, 12, 13, 44, 51, 60, 69, 90, 91]
|
||||
station_file = DATA_LOCATION / 'hadisd_station_fullinfo_v311_202001p.txt'
|
||||
station_file = WX_DATA_LOCATION / 'hadisd_station_fullinfo_v311_202001p.txt'
|
||||
|
||||
for line in station_file.open('rt'):
|
||||
start_end_positions = zip(fixed_delimits[:-1], fixed_delimits[1:])
|
||||
|
|
@ -89,6 +87,7 @@ def mean_hourly_temperatures(wx_station: str, month: int) -> HourlyTempType:
|
|||
Index 0 of the result corresponds to hour 00:00-01:00, and index 23 (the last) to 23:00-00:00.
|
||||
|
||||
"""
|
||||
# Note that the current dataset encodes month number as a string.
|
||||
return wx_data()[wx_station][str(month)]
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -434,6 +434,12 @@ def test_key_validation_mech_ventilation_type_na(baseline_form_data):
|
|||
model_generator.FormData.from_dict(baseline_form_data)
|
||||
|
||||
|
||||
def test_key_validation_event_month(baseline_form_data):
|
||||
baseline_form_data['event_month'] = 'invalid month'
|
||||
with pytest.raises(ValueError, match='invalid month is not a valid value for event_month'):
|
||||
model_generator.FormData.from_dict(baseline_form_data)
|
||||
|
||||
|
||||
def test_default_types():
|
||||
# Validate that FormData._DEFAULTS are complete and of the correct type.
|
||||
# Validate that we have the right types and matching attributes to the DEFAULTS.
|
||||
|
|
|
|||
Loading…
Reference in a new issue