Merge branch 'feature/virus_variant' into 'master'

Feature: virus variant

Closes #133

See merge request cara/cara!130
This commit is contained in:
James Devine 2021-03-09 08:38:48 +00:00
commit 12c3721996
6 changed files with 111 additions and 21 deletions

View file

@ -31,6 +31,20 @@ The simulation name has no bearing on the calculation.
A room number is included, if you do not wish to use a formal room number any reference will do - for example "57/2-004"
### Virus Data
Please choose the correct virus strain or any reported Variant of Concern (VOC) from the list.
Changing this setting alters the properties of the virus which are used for the simulation.
This has a significant effect on the probability of infection.
The choices are:
* `SARS-CoV-2 (nominal strain)`, covering typical strains and varaints which are not of concern from an epidemiologic point of view of the virus;
* `SARS-CoV-2 (B.1.1.7)`, the VOC first identified in the UK at the end of 2020 which is found to be approximately 1.5x more transmissible compared to the non-VOCs;
* `SARS-CoV-2 (P.1)`, the VOC first identified in Brazil in January 2021 which is found to be approximately 2.25x more transmissible compared to the non-VOCs.
The user can base their choice according to the prevalence of the different variants in the local area. Access to this information can be found here:
* Geneva: https://www.covid19.admin.ch/fr/epidemiologic/virus-variants?detGeo=GE
* Ain (France): https://www.santepubliquefrance.fr/dossiers/coronavirus-covid-19/covid-19-cartographie-des-variants-en-france-donnees-par-region-et-par-departement
### Room Data
Please enter either the room volume (in m³) or both the floor area (m²) and the room height (m).

View file

@ -49,6 +49,7 @@ class FormData:
simulation_name: str
total_people: int
ventilation_type: str
virus_type: str
volume_type: str
windows_duration: float
windows_frequency: float
@ -130,6 +131,7 @@ class FormData:
('mask_type', MASK_TYPES),
('mask_wearing_option', MASK_WEARING_OPTIONS),
('ventilation_type', VENTILATION_TYPES),
('virus_type', VIRUS_TYPES),
('volume_type', VOLUME_TYPES),
('window_opening_regime', WINDOWS_OPENING_REGIMES),
('window_type', WINDOWS_TYPES)]
@ -204,8 +206,8 @@ class FormData:
return mask
def infected_population(self) -> models.InfectedPopulation:
# Initializes the virus as SARS_Cov_2
virus = models.Virus.types['SARS_CoV_2']
# Initializes the virus
virus = models.Virus.types[self.virus_type]
scenario_activity_and_expiration = {
'office': (
@ -545,6 +547,7 @@ def baseline_raw_form_data():
'simulation_name': 'Test',
'total_people': '10',
'ventilation_type': 'natural_ventilation',
'virus_type': 'SARS_CoV_2',
'volume_type': 'room_volume_explicit',
'windows_duration': '',
'windows_frequency': '',
@ -561,6 +564,7 @@ MECHANICAL_VENTILATION_TYPES = {'mech_type_air_changes', 'mech_type_air_supply',
MASK_TYPES = {'Type I', 'FFP2'}
MASK_WEARING_OPTIONS = {'mask_on', 'mask_off'}
VENTILATION_TYPES = {'natural_ventilation', 'mechanical_ventilation', 'no_ventilation'}
VIRUS_TYPES = {'SARS_CoV_2', 'SARS_CoV_2_B117', 'SARS_CoV_2_P1'}
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'}

View file

@ -1,6 +1,6 @@
{% extends "layout.html.j2" %}
{% set MODEL_VERSION="v1.4.1" %}
{% set MODEL_VERSION="v1.5.0" %}
{% set DEBUG=False %}
{% set active_page="calculator/" %}
@ -41,6 +41,19 @@
<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%">
<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>
<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>
@ -257,35 +270,46 @@
<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>
<b>Virus data:</b> <br>
SARS-CoV-2 covers typical strains of the virus and two variants of concern (VOC):<br>
<ul>
<li>B.1.1.7 (first identified in UK, Dec 2020),</li>
<li>P.1 (first identified in Brazil/Japan, Jan 2021).</li>
</ul>
Choose variant according to local area prevalence, e.g. for <a href="https://www.covid19.admin.ch/fr/epidemiologic/virus-variants?detGeo=GE">Geneva</a>
or <a href="https://www.santepubliquefrance.fr/dossiers/coronavirus-covid-19/covid-19-cartographie-des-variants-en-france-donnees-par-region-et-par-departement">Ain (France)</a>.<br>
<b>Ventilation data:</b> <br>
<ul>
<li>Mechanical ventilation = the HVAC supply of fresh air. Check the flow rates with the concerned technical department.</li>
<li>Natural ventilation = the type of window opening. The opening distance is between the fixed frame and movable part when open (commonly used values are window height of 1.6m and window opening between 0.15m and 0.6m). In case of periodic opening, specify the duration (e.g. for 10 min) and frequency (e.g. every 60 min).</li>
<li>HEPA filtration = the air flow of the device. The following values are based on the different fan velocities of a specific commercial device proposed by the HSE Unit:</li>
<ul>
<li>Level 6 (max) = 430 m<sup>3</sup>/h (noisy)</li>
<li>Level 5 = 250 m<sup>3</sup>/h (ok w.r.t. noise, recommended)</li>
<li>Level 4 = 130 m<sup>3</sup>/h (silent)</li>
<li>Level 3 = 95 m<sup>3</sup>/h (silent)</li>
<li>Level 6 (max) = 430 m<sup>3</sup>/h (noisy),</li>
<li>Level 5 = 250 m<sup>3</sup>/h (ok w.r.t. noise, recommended),</li>
<li>Level 4 = 130 m<sup>3</sup>/h (silent),</li>
<li>Level 3 = 95 m<sup>3</sup>/h (silent).</li>
</ul>
</ul>
<b>Activity types:</b><br>
The type of activity applies to both the infected and exposed persons:
<ul>
<li>Office = Typical office scenario with all persons seated, in conversation (talking 33% of the time, otherwise breathing normally).</li>
<li>Meeting = Typical meeting scenario with all persons seated, in conversation (talking time is shared equally between all persons).</li>
<li>Call Centre = A conservative assumption for office spaces, assumes all occupants are seated and talking continuously.</li>
<li>Library = assumes all occupants are seated, breathing and not talking.</li>
<li>Laboratory = Typical lab scenario with all persons doing light physical activity and talking 50% of the time.</li>
<li>Workshop = Typical mechanical assembly workshop or equipment installation scenario with all persons doing moderate activity and talking 50% of the time.</li>
<li>Training = Training course scenario. One person (Trainer) standing, talking, all others seated, talking quietly (whispering). It is assumed the Trainer is the infected person, for the worst case scenario.</li>
<li>Gym = Included for comparison purposes only, all persons are doing heavy exercise and breathing (no talking).</li>
<li>Office = all seated, talking 33% of the time,</li>
<li>Meeting = all seated, talking time shared between all persons,</li>
<li>Call Centre = all seated, continuous talking,</li>
<li>Library = all seated, no talking, just breathing,</li>
<li>Laboratory = light physical activity, talking 50% of the time,</li>
<li>Workshop = moderate physical activity, talking 50% of the time,</li>
<li>Training = trainer standing and talking, rest seated and talking quietly.
Trainer assumed infected (worst case scenario),</li>
<li>Gym = heavy exercise, no talking, just breathing.</li>
</ul>
<b>Activity breaks:</b><br>
<ul>
<li>If coffee breaks are included, they are spread out evenly throughout the day, in addition to any lunch break (if applicable).</li>
<li>If coffee breaks are included, they are spread out evenly throughout the day,
in addition to any lunch break (if applicable).</li>
</ul>
Refer to <a href="/calculator/user-guide"> COVID Calculator App user guide </a> for more detailed explanations on how to use this tool. <br>
Refer to <a href="/calculator/user-guide"> COVID Calculator App user guide </a>
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>

View file

@ -20,7 +20,7 @@
<p class=subtitle> Created {{ creation_date }} using model version {{ form.model_version }}</p><br>
<p><strong>Applicable rules: <br>
Please ensure that this scenario conforms to current <a href="https://hse.cern/covid-19-information"> CERN HSE rules<a> (minimum ventilation requirements, mask wearing and the maximum number of people permitted in a space).</strong> <br>
Please ensure that this scenario conforms to current <a href="https://hse.cern/covid-19-information"> CERN HSE rules</a> (minimum ventilation requirements, mask wearing and the maximum number of people permitted in a space).</strong> <br>
The results of this simulation are colour coded according to the risk values authorized at CERN (approved in December 2020):
<ul><li>Events with a <span class="green_bkg">P(i) less than 5%</span> may go ahead without further mitigation measures.</li>
<li>Events with a <span class="yellow_bkg">P(i) between 5% and 15%</span> shall be subject to ALARA principles (see footnote) to minimise the risk before proceeding.</li>
@ -28,13 +28,22 @@
</ul>
</p>
<p><strong>Simulation:</strong><p>
<p><strong>Simulation:</strong></p>
<p>Simulation Name: {{ form.simulation_name }}</p>
<p>Room Number: {{ form.room_number }}</p>
<p class="data_title">Input data:</p>
<ul>
<li><p class="data_text">Virus variant:
{% if form.virus_type == "SARS_CoV_2" %}
SARS-CoV-2 (nominal strain)
{% elif form.virus_type == "SARS_CoV_2_B117" %}
<a href="https://www.ecdc.europa.eu/en/publications-data/covid-19-risk-assessment-spread-new-sars-cov-2-variants-eueea">SARS-CoV-2 (B.1.1.7)</a>
{% elif form.virus_type == "SARS_CoV_2_P1" %}
<a href="https://github.com/CADDE-CENTRE/Novel-SARS-CoV-2-P1-Lineage-in-Brazil/blob/main/manuscript/FINAL_P1_MANUSCRIPT_25-02-2021_combined.pdf">SARS-CoV-2 (P.1)</a>
{% endif %}
</p></li>
<li><p class="data_text">Room Volume: {{ model.concentration_model.room.volume }} m³</p></li>
</ul>
@ -210,7 +219,7 @@ In this scenario, the estimated probability of one exposed occupant getting infe
See the footnotes for more details on the ALARA principles.
{% elif (prob_inf < 5) %}
This level of risk is within acceptable parameters, no further actions are required.
{% endif %}
{% endif %}</p>
<p class="data_title">Exposure graph:</p>
<img id="scenario_concentration_plot" src="{{ scenario_plot_src }}">

View file

@ -226,6 +226,7 @@ class ModelWidgets(View):
self.widget.children += (self._build_ventilation(node.concentration_model.ventilation),)
self.widget.children += (self._build_infected(node.concentration_model.infected),)
self.widget.children += (self._build_exposed(node),)
self.widget.children += (self._build_infectivity(node.concentration_model.infected),)
def _build_exposed(self, node):
return collapsible([widgets.HBox([
@ -457,6 +458,27 @@ class ModelWidgets(View):
)
return w
def _build_infectivity(self,node):
return collapsible([widgets.HBox([
self._build_virus(node.virus),
])], title="Virus variant")
def _build_virus(self, node):
virus = node.dcs_instance()
for name, virus_ in models.Virus.types.items():
if virus == virus_:
break
virus_choice = widgets.Select(options=list(models.Virus.types.keys()), value=name)
def on_virus_change(change):
node.dcs_select(change['new'])
virus_choice.observe(on_virus_change, names=['value'])
return widget_group(
[[widgets.Label("Virus"), virus_choice]]
)
def present(self):
return self.widget
@ -499,6 +521,12 @@ class CARAStateBuilder(state.StateBuilder):
choices=models.Mask.types,
)
def build_type_Virus(self, _: dataclasses.Field):
return state.DataclassStatePredefined(
models.Virus,
choices=models.Virus.types,
)
def build_type__VentilationBase(self, _: dataclasses.Field):
s: state.DataclassStateNamed = state.DataclassStateNamed(
states={

View file

@ -384,12 +384,23 @@ class Virus:
Virus.types = {
'SARS_CoV_2': Virus(
halflife=1.1,
viral_load_in_sputum=10e8,
viral_load_in_sputum=1e9,
# No data on coefficient for SARS-CoV-2 yet.
# It is somewhere between 0.001 and 0.01 to have a 50% chance
# to cause infection. i.e. 1000 or 100 SARS-CoV viruses to cause infection.
coefficient_of_infectivity=0.02,
),
'SARS_CoV_2_B117': Virus(
# also called VOC-202012/01
halflife=1.1,
viral_load_in_sputum=1e9,
coefficient_of_infectivity=1/30.,
),
'SARS_CoV_2_P1': Virus(
halflife=1.1,
viral_load_in_sputum=1e9,
coefficient_of_infectivity=0.045,
),
}