Merge branch 'master' of https://gitlab.cern.ch/cara/cara into feature/virus_qid_and_viral_loads
This commit is contained in:
commit
19d25310dc
3 changed files with 84 additions and 65 deletions
13
README.md
13
README.md
|
|
@ -58,6 +58,19 @@ 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:
|
||||
|
||||
`'Jan': [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],
|
||||
'Feb': [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
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ v{{ calculator_version }} <span style="float:right; font-weight:bold">Please sen
|
|||
</div>
|
||||
|
||||
{% if DEBUG %}
|
||||
<form id="covid_calculator" name="covid_calculator" onsubmit="return debug_submit(this)">
|
||||
<form id="covid_calculator" name="covid_calculator" onsubmit="return debug_submit(this)" class="form-inline">
|
||||
{% else %}
|
||||
<form id="covid_calculator" name="covid_calculator" action="/calculator/report" onsubmit="return validate_form(this)" method="POST">
|
||||
{% endif %}
|
||||
|
|
@ -34,37 +34,67 @@ v{{ calculator_version }} <span style="float:right; font-weight:bold">Please sen
|
|||
|
||||
<input type="hidden" name="calculator_version" value="{{ calculator_version }}">
|
||||
|
||||
<div style="width: 33%; float:left;">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-lg-4 col-md-6">
|
||||
|
||||
<!-- General Options -->
|
||||
<b>Simulation name:</b> <input type="text" name="simulation_name" placeholder="E.g. Workshop without masks" required><br>
|
||||
Room number: <input type="text" name="room_number" placeholder="E.g. 17/R-033" required><br>
|
||||
<hr width="80%">
|
||||
|
||||
<div class="row">
|
||||
<label class="col-xl-3 col-lg-4 col-sm-3 col-form-label">Simulation name:</label>
|
||||
<input type="text" class="col-xl-5 col-lg-7 col-sm-7 col-7" name="simulation_name" placeholder="E.g. Workshop without masks" required>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<label class="col-xl-3 col-lg-4 col-sm-3 col-form-label">Room number:</label>
|
||||
<input type="text" class="col-xl-5 col-lg-7 col-sm-7 col-7" name="room_number" placeholder="E.g. 17/R-033" required>
|
||||
</div>
|
||||
|
||||
<hr width="80%">
|
||||
<b>Virus data:</b>
|
||||
|
||||
<div data-tooltip="Choose the SARS-CoV-2 Variant of Concern (VOC).">
|
||||
<span class="tooltip_text">?</span>
|
||||
</div><br>
|
||||
Variant:
|
||||
<select id="Variant" name="virus_type">
|
||||
<option value="SARS_CoV_2">SARS-CoV-2 (nominal strain)</option>
|
||||
<option value="SARS_CoV_2_B117">SARS-CoV-2 (B.1.1.7)</option>
|
||||
<option value="SARS_CoV_2_P1">SARS-CoV-2 (P.1)</option>
|
||||
</select><br>
|
||||
|
||||
<div class="row">
|
||||
<label class="col-xl-3 col-lg-4 col-sm-3 col-form-label">Variant:</label>
|
||||
<select id="Variant" name="virus_type" class="col-xl-5 col-lg-7 col-sm-7 col-7">
|
||||
<option value="SARS_CoV_2">SARS-CoV-2 (nominal strain)</option>
|
||||
<option value="SARS_CoV_2_B117">SARS-CoV-2 (B.1.1.7)</option>
|
||||
<option value="SARS_CoV_2_P1">SARS-CoV-2 (P.1)</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<hr width="80%">
|
||||
|
||||
<b>Room data:</b>
|
||||
<div data-tooltip="The area you wish to study (choose one of the 2 options). Use GIS Portal or measure.">
|
||||
<span class="tooltip_text">?</span>
|
||||
</div><br>
|
||||
<input type="radio" id="room_data_volume" name="volume_type" value="room_volume_explicit" onclick="require_fields(this)" tabindex="-1" required>
|
||||
<label for="room_data_volume">Room volume:</label>
|
||||
<input type="number" step="any" id="room_volume" class="non_zero" name="room_volume" placeholder="Room volume (m³)" min="0" data-has-radio="#room_data_volume"><br>
|
||||
<input type="radio" id="room_data_dimensions" name="volume_type" value="room_volume_from_dimensions" onclick="require_fields(this)" tabindex="-1" required>
|
||||
<label for="room_data_dimensions">Floor area:</label>
|
||||
<input type="number" step="any" id="floor_area" class="non_zero" name="floor_area" placeholder="Room floor area (m²)" min="0" data-has-radio="#room_data_dimensions"><br>
|
||||
<label for="room_data_dimensions">Ceiling height:</label>
|
||||
<input type="number" step="any" id="ceiling_height" class="non_zero" name="ceiling_height" placeholder="Room ceiling height (m²)" min="0" data-has-radio="#room_data_dimensions"><br>
|
||||
<div class="row">
|
||||
|
||||
<div class="col-xl-3 col-lg-4 col-sm-3">
|
||||
<input type="radio" id="room_data_volume" name="volume_type" value="room_volume_explicit" onclick="require_fields(this)" tabindex="-1" required>
|
||||
<label class="col-form-label">Room volume:</label>
|
||||
</div>
|
||||
<input type="number" step="any" id="room_volume" class="non_zero col-xl-3 col-lg-5 col-md-7 col-sm-3 col-3" name="room_volume" placeholder="Room volume (m³)" min="0" data-has-radio="#room_data_volume">
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xl-3 col-lg-4 col-sm-3">
|
||||
<input type="radio" id="room_data_dimensions" name="volume_type" value="room_volume_from_dimensions" onclick="require_fields(this)" tabindex="-1" required>
|
||||
<label for="room_data_dimensions">Floor area:</label>
|
||||
</div>
|
||||
<input type="number" step="any" id="floor_area" class="non_zero col-xl-3 col-lg-5 col-md-7 col-sm-3 col-3" name="floor_area" placeholder="Room floor area (m²)" min="0" data-has-radio="#room_data_dimensions">
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xl-3 col-lg-4 col-sm-3">
|
||||
<label for="room_data_dimensions">Ceiling height:</label>
|
||||
</div>
|
||||
<input type="number" step="any" id="ceiling_height" class="non_zero col-xl-3 col-lg-5 col-md-7 col-sm-3 col-3" name="ceiling_height" placeholder="Room ceiling height (m²)" min="0" data-has-radio="#room_data_dimensions">
|
||||
</div>
|
||||
|
||||
<hr width="80%">
|
||||
|
||||
<!-- Ventilation Options -->
|
||||
|
|
@ -134,15 +164,24 @@ v{{ calculator_version }} <span style="float:right; font-weight:bold">Please sen
|
|||
<hr width="80%">
|
||||
</div>
|
||||
|
||||
<div style="width: 33%; float:left;">
|
||||
<div class="col-lg-4 col-md-6">
|
||||
|
||||
<!-- Event Options -->
|
||||
<b>Event data:</b>
|
||||
<div data-tooltip="The total no. of occupants in the room and how many of them you assume are infected.">
|
||||
<span class="tooltip_text">?</span>
|
||||
</div><br>
|
||||
Total number of occupants: <input type="number" id="total_people" name="total_people" min=1 required><br>
|
||||
Number of infected people: <input type="number" id="infected_people" name="infected_people" min=1 required><br>
|
||||
|
||||
<div class="row">
|
||||
<label class="col-xl-3 col-lg-4 col-sm-3 col-form-label">Total number of occupants:</label>
|
||||
<input type="number" id="total_people" class="col-lg-4 col-md-5 col-sm-3" name="total_people" min=1 required>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<label class="col-xl-3 col-lg-4 col-sm-3 col-form-label">Number of infected people: </label>
|
||||
<input type="number" id="infected_people" class="col-lg-4 col-md-5 col-sm-3" name="infected_people" min=1 required><br>
|
||||
</div>
|
||||
|
||||
<span id="training_limit_error" class="red_text" hidden>Training activities limited to 1 infected<br></span>
|
||||
<hr width="80%">
|
||||
|
||||
|
|
@ -265,9 +304,15 @@ v{{ calculator_version }} <span style="float:right; font-weight:bold">Please sen
|
|||
<br style="clear:both;">
|
||||
<i>Coffee breaks are spread evenly throughout the day.</i><br>
|
||||
<hr width="80%">
|
||||
<div class="row">
|
||||
<div class="col-md-4 col-md-offset-4">
|
||||
<button type='submit' class="btn btn-primary" id="generate_report">Generate report</button>
|
||||
</div>
|
||||
</div>
|
||||
<br><br>
|
||||
</div>
|
||||
|
||||
<div style="width: 33%; float:left;">
|
||||
<div class="col-lg-4 col-sm-12">
|
||||
|
||||
<b>Quick Guide:</b><br>
|
||||
This tool simulates the long range airborne spread SARS-CoV-2 virus in a finite volume and estimates the risk of COVID-19 infection. It is based on current scientific data and can be used to compare the effectiveness of different mitigation measures.<br>
|
||||
|
|
@ -315,7 +360,9 @@ v{{ calculator_version }} <span style="float:right; font-weight:bold">Please sen
|
|||
for more detailed explanations on how to use this tool. <br>
|
||||
</div>
|
||||
|
||||
<button type='submit' id="generate_report">Generate report</button><br><br><br><br>
|
||||
</div>
|
||||
</div>
|
||||
<br><br><br><br>
|
||||
</form>
|
||||
|
||||
<div>
|
||||
|
|
|
|||
|
|
@ -37,9 +37,6 @@ import typing
|
|||
|
||||
import numpy as np
|
||||
from scipy.interpolate import interp1d
|
||||
import scipy.stats as sct
|
||||
from sklearn.neighbors import KernelDensity # type: ignore
|
||||
|
||||
|
||||
if not typing.TYPE_CHECKING:
|
||||
from memoization import cached
|
||||
|
|
@ -58,18 +55,6 @@ _VectorisedFloat = typing.Union[float, np.ndarray]
|
|||
_VectorisedInt = typing.Union[int, np.ndarray]
|
||||
|
||||
|
||||
def _lognormal_distribution(csi: float, lamb: float, samples: int) -> np.ndarray:
|
||||
"""
|
||||
Generates a number of samples from a specified lognormal distribution
|
||||
:param csi: parameter used to specify the lognormal distribution
|
||||
:param lamb: parameter used to specify the lognormal distribution
|
||||
:param samples: number of samples to be generated
|
||||
:return: numpy-array containing the samples
|
||||
"""
|
||||
sf_norm = sct.norm.sf(np.random.normal(size=samples))
|
||||
return sct.lognorm.isf(sf_norm, csi, loc=0, scale=np.exp(lamb))
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Room:
|
||||
#: The total volume of the room
|
||||
|
|
@ -584,9 +569,6 @@ class Activity:
|
|||
#: Pre-populated examples of activities.
|
||||
types: typing.ClassVar[typing.Dict[str, "Activity"]]
|
||||
|
||||
#: Pre-defined examples of activity distributions.
|
||||
distributions: typing.ClassVar[typing.Dict[str, typing.Callable[[int], "Activity"]]]
|
||||
|
||||
|
||||
Activity.types = {
|
||||
'Seated': Activity(0.51, 0.51),
|
||||
|
|
@ -596,29 +578,6 @@ Activity.types = {
|
|||
'Heavy exercise': Activity(3.30, 3.30),
|
||||
}
|
||||
|
||||
def _generate_activity_distribution(csi: float, lamb: float, samples: int):
|
||||
return Activity(
|
||||
_lognormal_distribution(csi, lamb, samples),
|
||||
_lognormal_distribution(csi, lamb, samples),
|
||||
)
|
||||
|
||||
Activity.distributions = {
|
||||
'Seated': lambda n: _generate_activity_distribution(
|
||||
0.10498338229297108, -0.6872121723362303, n),
|
||||
|
||||
'Standing': lambda n: _generate_activity_distribution(
|
||||
0.09373162411398223, -0.5742377578494785, n),
|
||||
|
||||
'Light activity': lambda n: _generate_activity_distribution(
|
||||
0.09435378091059601, 0.21380242785625422, n),
|
||||
|
||||
'Moderate activity': lambda n: _generate_activity_distribution(
|
||||
0.1894616357138137, 0.551771330362601, n),
|
||||
|
||||
'Heavy exercise': lambda n: _generate_activity_distribution(
|
||||
0.21744554768657565, 1.1644665696723049, n),
|
||||
}
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Population:
|
||||
|
|
|
|||
Loading…
Reference in a new issue