Merge branch 'feature/multiple_PI' into 'master'
Report with multiple P(I) Closes #258 See merge request cara/cara!358
This commit is contained in:
commit
8fe4939757
9 changed files with 154 additions and 64 deletions
|
|
@ -256,6 +256,10 @@ def manufacture_alternative_scenarios(form: FormData) -> typing.Dict[str, mc.Exp
|
|||
without_mask_or_vent = dataclass_utils.replace(without_mask, ventilation_type='no_ventilation')
|
||||
scenarios['No ventilation with Type I masks'] = with_mask_no_vent.build_mc_model()
|
||||
scenarios['Neither ventilation nor masks'] = without_mask_or_vent.build_mc_model()
|
||||
|
||||
else:
|
||||
no_short_range_alternative = dataclass_utils.replace(form, short_range_interactions=[])
|
||||
scenarios['Base scenario without short-range interactions'] = no_short_range_alternative.build_mc_model()
|
||||
|
||||
return scenarios
|
||||
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ function draw_plot(svg_id) {
|
|||
|
||||
sr_unique_activities = [...new Set(short_range_expirations)]
|
||||
if (show_sr_legend) {
|
||||
// Long range cumulative dose line legend - line and area
|
||||
// Long-range cumulative dose line legend - line and area
|
||||
var legendLongCumulativeIcon = vis.append('line')
|
||||
.style("stroke-dasharray", "5 5") //dashed array for line
|
||||
.attr('stroke-width', '2')
|
||||
|
|
@ -113,18 +113,18 @@ function draw_plot(svg_id) {
|
|||
.text('Long-range cumulative dose')
|
||||
.style('font-size', '15px')
|
||||
.attr('opacity', 0);
|
||||
// Short range area icon
|
||||
// Short-range area icon
|
||||
var legendShortRangeAreaIcon = {};
|
||||
sr_unique_activities.forEach((b, index) => {
|
||||
legendShortRangeAreaIcon[index] = vis.append('rect')
|
||||
.attr('width', 20)
|
||||
.attr('height', 15);
|
||||
// Short range area icon colors
|
||||
// Short-range area icon colors
|
||||
if (sr_unique_activities[index] == 'Breathing') legendShortRangeAreaIcon[index].attr('fill', 'red').attr('fill-opacity', '0.2');
|
||||
else if (sr_unique_activities[index] == 'Speaking') legendShortRangeAreaIcon[index].attr('fill', 'green').attr('fill-opacity', '0.1');
|
||||
else legendShortRangeAreaIcon[index].attr('fill', 'blue').attr('fill-opacity', '0.1');
|
||||
});
|
||||
// Short range area text
|
||||
// Short-range area text
|
||||
var legendShortRangeText = {};
|
||||
sr_unique_activities.forEach((b, index) => {
|
||||
legendShortRangeText[index] = vis.append('text')
|
||||
|
|
@ -432,9 +432,9 @@ function draw_plot(svg_id) {
|
|||
if (show_sr_legend) {
|
||||
sr_unique_activities.forEach((b, index) => {
|
||||
legendShortRangeAreaIcon[index].attr('x', legend_x_start)
|
||||
.attr('y', graph_height + 4 * size - 15/2);
|
||||
.attr('y', graph_height + (4 + index) * size - 15/2);
|
||||
legendShortRangeText[index].attr('x', legend_x_start + space_between_text_icon)
|
||||
.attr('y', graph_height + 4 * size + text_height);
|
||||
.attr('y', graph_height + (4 + index) * size + text_height);
|
||||
});
|
||||
legendLongCumulativeIcon.attr("x1", legend_x_start)
|
||||
.attr("x2", legend_x_start + 20)
|
||||
|
|
@ -541,7 +541,7 @@ function draw_plot(svg_id) {
|
|||
// Generate the alternative scenarios plot using d3 library.
|
||||
// 'alternative_scenarios' is a dictionary with all the alternative scenarios
|
||||
// 'times' is a list of times for all the scenarios
|
||||
// The method is prepared to consider short range interactions if needed.
|
||||
// The method is prepared to consider short-range interactions if needed.
|
||||
function draw_alternative_scenarios_plot(concentration_plot_svg_id, alternative_plot_svg_id) {
|
||||
// H:M format
|
||||
var time_format = d3.timeFormat('%H:%M');
|
||||
|
|
|
|||
BIN
cara/apps/static/images/long_range_anim.png
Normal file
BIN
cara/apps/static/images/long_range_anim.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 88 KiB |
BIN
cara/apps/static/images/short_range_anim.png
Normal file
BIN
cara/apps/static/images/short_range_anim.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 264 KiB |
|
|
@ -46,6 +46,13 @@
|
|||
|
||||
<div class="tab-pane show active" id="results" role="tabpanel" aria-labelledby="results-tab" style="padding: 1%">
|
||||
|
||||
{% if form.short_range_option == "short_range_yes" %}
|
||||
{% set scenario = alternative_scenarios.stats.values() | first %}
|
||||
{% set long_range_prob_inf = scenario.probability_of_infection %}
|
||||
{% else %}
|
||||
{% set long_range_prob_inf = prob_inf %}
|
||||
{% endif %}
|
||||
|
||||
{% block report_results %}
|
||||
<div class="card bg-light mb-3" id="results-div">
|
||||
<div class="card-header"><strong>Results </strong>
|
||||
|
|
@ -59,36 +66,73 @@
|
|||
<div class="card-body">
|
||||
<p class="card-text">
|
||||
<div class="align-self-center">
|
||||
<div class="d-flex">
|
||||
<div class="split">
|
||||
<div class="col-md-3">
|
||||
<div style="text-align:center"><b>Probability of infection (%)</b></div>
|
||||
<div class="d-flex" style="min-height: 160px">
|
||||
{% block warning_animation %}
|
||||
<div class="intro-banner-vdo-play-btn animation-color m-auto d-flex align-items-center justify-content-center">
|
||||
<b>{{prob_inf | non_zero_percentage}}</b>
|
||||
<i class="glyphicon glyphicon-play whiteText" aria-hidden="true"></i>
|
||||
<span class="ripple animation-color"></span>
|
||||
<span class="ripple animation-color"></span>
|
||||
<span class="ripple animation-color"></span>
|
||||
</div>
|
||||
{% endblock warning_animation %}
|
||||
<div class="card card-body align-self-center" style="text-align:center; max-width: 300px">
|
||||
<h6 class="card-title">
|
||||
<b>Probability of infection (%)</b><br>
|
||||
{% if form.short_range_option == "short_range_yes" %}
|
||||
Without <b>short-range interactions</b>
|
||||
{% endif %}
|
||||
</h6>
|
||||
<br>
|
||||
<img src="/static/images/long_range_anim.png" class="align-middle mb-3">
|
||||
<div class="d-flex" style="min-height: 160px">
|
||||
{% block long_range_warning_animation %}
|
||||
<div class="intro-banner-vdo-play-btn animation-color m-auto d-flex align-items-center justify-content-center">
|
||||
<b>{{long_range_prob_inf | non_zero_percentage}}</b>
|
||||
<i class="glyphicon glyphicon-play whiteText" aria-hidden="true"></i>
|
||||
<span class="ripple animation-color"></span>
|
||||
<span class="ripple animation-color"></span>
|
||||
<span class="ripple animation-color"></span>
|
||||
</div>
|
||||
{% endblock long_range_warning_animation %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-8 pr-0 pl-0 d-flex">
|
||||
<br>
|
||||
{% if form.short_range_option == "short_range_yes" %}
|
||||
<div class="card card-body align-self-center" style="text-align:center; max-width: 300px">
|
||||
<h6 class="card-title">
|
||||
<b>Probability of infection (%)</b><br>
|
||||
With <b>short-range interactions</b>
|
||||
</h6>
|
||||
<br>
|
||||
<img src="/static/images/short_range_anim.png" class="align-middle mb-3">
|
||||
<div class="d-flex" style="min-height: 160px">
|
||||
{% block warning_animation %}
|
||||
<div class="intro-banner-vdo-play-btn animation-color m-auto d-flex align-items-center justify-content-center">
|
||||
<b>{{prob_inf | non_zero_percentage}}</b>
|
||||
<i class="glyphicon glyphicon-play whiteText" aria-hidden="true"></i>
|
||||
<span class="ripple animation-color"></span>
|
||||
<span class="ripple animation-color"></span>
|
||||
<span class="ripple animation-color"></span>
|
||||
</div>
|
||||
{% endblock warning_animation %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="d-flex">
|
||||
{% block report_summary %}
|
||||
<div class="align-self-center alert alert-dark mb-0" role="alert">
|
||||
Taking into account the uncertainties tied to the model variables, in this scenario and assuming all occupants are exposed equally, the <b>probability of one exposed occupant getting infected is {{ prob_inf | non_zero_percentage }}</b> and the <b>expected number of new cases is {{ expected_new_cases | float_format }}</b>*.
|
||||
<br>
|
||||
<div class="flex-row align-self-center">
|
||||
<br>
|
||||
<div class="align-self-center alert alert-dark mb-0" role="alert">
|
||||
Taking into account the uncertainties tied to the model variables, in this scenario and assuming all occupants are exposed equally (i.e. without short-range interactions), the <b>probability of one exposed occupant getting infected is {{ long_range_prob_inf | non_zero_percentage }}</b> and the <b>expected number of new cases is {{ expected_new_cases | float_format }}</b>*.
|
||||
</div>
|
||||
{% if form.short_range_option == "short_range_yes" %}
|
||||
<br>
|
||||
<div class="align-self-center alert alert-dark mb-0" role="alert">
|
||||
In this scenario, assuming <b>short-range interactions</b> occur, the <b>probability of one exposed occupant getting infected can go as high as {{ prob_inf | non_zero_percentage }}</b>.
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock report_summary %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
{% block report_summary_footnote %}
|
||||
{% endblock report_summary_footnote %}
|
||||
</div>
|
||||
<p id="section1">* The results are based on the parameters and assumptions published in the CARA publication: <a href="https://doi.org/10.1098/rsfs.2021.0076"> doi.org/10.1098/rsfs.2021.0076</a>.</p><br>
|
||||
<br><p id="section1">* The results are based on the parameters and assumptions published in the CARA publication: <a href="https://doi.org/10.1098/rsfs.2021.0076"> doi.org/10.1098/rsfs.2021.0076</a>.</p><br>
|
||||
{% if form.short_range_option == "short_range_yes" %}
|
||||
{% if 'Speaking' in form.short_range_interactions|string or 'Shouting' in form.short_range_interactions|string %}
|
||||
<button class="btn btn-sm btn-primary" id="button_full_exposure" disabled>Show full exposure</button>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,25 @@
|
|||
{% extends "base/calculator.report.html.j2" %}
|
||||
|
||||
{% set cern_level = 'green-1' %} <!-- green-1, yellow-2, orange-3, red-4 -->
|
||||
|
||||
{% if form.short_range_option == "short_range_yes" %}
|
||||
{% set scenario = alternative_scenarios.stats.values() | first %}
|
||||
{% set long_range_prob_inf = scenario.probability_of_infection %}
|
||||
{% else %}
|
||||
{% set long_range_prob_inf = prob_inf %}
|
||||
{% endif %}
|
||||
|
||||
{% if ((long_range_prob_inf > 10) or (expected_new_cases >= 1)) %}
|
||||
{% set long_range_scale_warning = 'red' %}
|
||||
{% set long_range_warning_color= 'bg-danger' %}
|
||||
{% elif (2 <= long_range_prob_inf <= 10) %}
|
||||
{% set long_range_scale_warning = 'orange' %}
|
||||
{% set long_range_warning_color = 'bg-warning' %}
|
||||
{% else %}
|
||||
{% set long_range_scale_warning = 'green' %}
|
||||
{% set long_range_warning_color = 'bg-success' %}
|
||||
{% endif %}
|
||||
|
||||
{% if ((prob_inf > 10) or (expected_new_cases >= 1)) %} {% set scale_warning = 'red' %}
|
||||
{% elif (2 <= prob_inf <= 10) %} {% set scale_warning = 'orange' %}
|
||||
{% else %} {% set scale_warning = 'green' %}
|
||||
|
|
@ -12,12 +31,21 @@
|
|||
</li>
|
||||
{% endblock report_preamble_navtab %}
|
||||
|
||||
{% block long_range_warning_animation %}
|
||||
<div class="intro-banner-vdo-play-btn {{long_range_warning_color}} m-auto d-flex align-items-center justify-content-center">
|
||||
<b>{{long_range_prob_inf | non_zero_percentage}}</b>
|
||||
<i class="glyphicon glyphicon-play whiteText" aria-hidden="true"></i>
|
||||
<span class="ripple {{long_range_warning_color}}"></span>
|
||||
<span class="ripple {{long_range_warning_color}}"></span>
|
||||
<span class="ripple {{long_range_warning_color}}"></span>
|
||||
</div>
|
||||
{% endblock long_range_warning_animation %}
|
||||
|
||||
{% block warning_animation %}
|
||||
{% if scale_warning == 'red' %} {% set warning_color= 'bg-danger' %}
|
||||
{% elif scale_warning == 'orange' %} {% set warning_color = 'bg-warning' %}
|
||||
{% elif scale_warning == 'green' %} {% set warning_color = 'bg-success' %}
|
||||
{% endif %}
|
||||
|
||||
<div class="intro-banner-vdo-play-btn {{warning_color}} m-auto d-flex align-items-center justify-content-center">
|
||||
<b>{{prob_inf | non_zero_percentage}}</b>
|
||||
<i class="glyphicon glyphicon-play whiteText" aria-hidden="true"></i>
|
||||
|
|
@ -28,51 +56,63 @@
|
|||
{% endblock warning_animation %}
|
||||
|
||||
{% block report_summary %}
|
||||
{% set report_message = "Taking into account the uncertainties tied to the model variables, in this scenario and assuming all occupants are exposed equally, the <b>probability of one exposed occupant getting infected is " + prob_inf | non_zero_percentage + "</b> and the <b>expected number of new cases is " + expected_new_cases | float_format + "</b>*." %}
|
||||
<div class="flex-row align-self-center">
|
||||
{% if scale_warning == 'red' %}
|
||||
<br>
|
||||
{% if long_range_scale_warning == 'red' %}
|
||||
<div class="alert alert-danger mb-0" role="alert">
|
||||
<strong>Not Acceptable:</strong>
|
||||
{{ report_message }}
|
||||
</div>
|
||||
{% elif scale_warning == 'orange' %}
|
||||
{% elif long_range_scale_warning == 'orange' %}
|
||||
<div class="alert alert-warning mb-0" role="alert">
|
||||
<strong>Attention:</strong>
|
||||
{{ report_message }}
|
||||
</div>
|
||||
{% elif scale_warning == 'green' %}
|
||||
{% elif long_range_scale_warning == 'green' %}
|
||||
<div class="alert alert-success mb-0" role="alert">
|
||||
<strong>Acceptable:</strong>
|
||||
{{ report_message }}
|
||||
{% endif %}
|
||||
Taking into account the uncertainties tied to the model variables, in this scenario and assuming all occupants are exposed equally (i.e. without short-range interactions), the <b>probability of one exposed occupant getting infected is {{long_range_prob_inf | non_zero_percentage}}</b> and the <b>expected number of new cases is {{expected_new_cases | float_format}}</b>*.
|
||||
</div>
|
||||
{% if form.short_range_option == "short_range_yes" %}
|
||||
<br>
|
||||
{% if scale_warning == 'red' %}
|
||||
<div class="alert alert-danger mb-0" role="alert">
|
||||
<strong>Not Acceptable:</strong>
|
||||
{% elif scale_warning == 'orange' %}
|
||||
<div class="alert alert-warning mb-0" role="alert">
|
||||
<strong>Attention:</strong>
|
||||
{% elif scale_warning == 'green' %}
|
||||
<div class="alert alert-success mb-0" role="alert">
|
||||
<strong>Acceptable:</strong>
|
||||
{% endif %}
|
||||
In this scenario, assuming <b>short-range interactions</b> occur, the <b>probability of one exposed occupant getting infected can go as high as {{prob_inf | non_zero_percentage}}</b>.
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if (prob_inf > 2) %}
|
||||
<br>
|
||||
{% if cern_level == "green-1" %}
|
||||
<div class="alert alert-dark mb-0" role="alert" style="height:fit-content">
|
||||
Note: the current CERN COVID Scale is <b>Green – 1</b>. The public health and safety restrictions are reduced to a mininum. Align your risk assessment with the guidance and instructions provided by the HSE Unit.
|
||||
</div>
|
||||
{% elif cern_level == "yellow-2" %}
|
||||
<div class="alert alert-dark mb-0" role="alert" style="height:fit-content">
|
||||
Note: the current CERN COVID Scale is <b>Yellow - 2</b>. There is a reduced risk that asymptomatic or pre-symptomatic infected individuals circulate within the CERN site. There may be around <b>6'500 daily on-site accesses</b> during this stage. See with your supervisor, DSO/LEXGLIMOS and space manager if this scenario is acceptable and if any additional measures can be applied (ALARA).
|
||||
</div>
|
||||
{% elif cern_level == "orange-3" %}
|
||||
<div class="alert alert-dark mb-0" role="alert" style="height:fit-content">
|
||||
Warning: the current CERN COVID Scale is <b>Orange - 3</b>. There is a medium risk that asymptomatic or pre-symptomatic infected individuals circulate within the CERN site. There may be around <b>5'000 daily on-site accesses</b> during this stage. See with your supervisor, DSO/LEXGLIMOS and space manager if this scenario is acceptable and if any additional measures can be applied (ALARA).
|
||||
</div>
|
||||
{% elif cern_level == "red-4" %}
|
||||
<div class="alert alert-dark mb-0" role="alert" style="height:fit-content">
|
||||
Warning: the current CERN COVID Scale is <b>Red - 4</b>. There is a strong risk that asymptomatic or pre-symptomatic infected individuals circulate within the CERN site. There may be around <b>4'000 daily on-site accesses</b> during this stage. Please reduce the value below the threshold of 2%. See with your supervisor, DSO/LEXGLIMOS and space manager if this scenario is acceptable and if any additional measures are required.
|
||||
</div>
|
||||
{% else %}
|
||||
<p><b>Note:</b> The CERN COVID Level is not specified.</p>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if (prob_inf > 2) %}
|
||||
<br>
|
||||
{% if cern_level == "green-1" %}
|
||||
<div class="alert alert-dark mb-0" role="alert" style="height:fit-content">
|
||||
<b>Note:</b> the current CERN COVID Scale is <b>Green – 1</b>. The public health and safety restrictions are reduced to a mininum. Align your risk assessment with the guidance and instructions provided by the HSE Unit.
|
||||
</div>
|
||||
{% elif cern_level == "yellow-2" %}
|
||||
<div class="alert alert-dark mb-0" role="alert" style="height:fit-content">
|
||||
<b>Note:</b> the current CERN COVID Scale is <b>Yellow - 2</b>. There is a reduced risk that asymptomatic or pre-symptomatic infected individuals circulate within the CERN site. There may be around <b>6'500 daily on-site accesses</b> during this stage. See with your supervisor, DSO/LEXGLIMOS and space manager if this scenario is acceptable and if any additional measures can be applied (ALARA).
|
||||
</div>
|
||||
{% elif cern_level == "orange-3" %}
|
||||
<div class="alert alert-dark mb-0" role="alert" style="height:fit-content">
|
||||
<b>Warning</b>: the current CERN COVID Scale is <b>Orange - 3</b>. There is a medium risk that asymptomatic or pre-symptomatic infected individuals circulate within the CERN site. There may be around <b>5'000 daily on-site accesses</b> during this stage. See with your supervisor, DSO/LEXGLIMOS and space manager if this scenario is acceptable and if any additional measures can be applied (ALARA).
|
||||
</div>
|
||||
{% elif cern_level == "red-4" %}
|
||||
<div class="alert alert-dark mb-0" role="alert" style="height:fit-content">
|
||||
<b>Warning</b>: the current CERN COVID Scale is <b>Red - 4</b>. There is a strong risk that asymptomatic or pre-symptomatic infected individuals circulate within the CERN site. There may be around <b>4'000 daily on-site accesses</b> during this stage. Please reduce the value below the threshold of 2%. See with your supervisor, DSO/LEXGLIMOS and space manager if this scenario is acceptable and if any additional measures are required.
|
||||
</div>
|
||||
{% else %}
|
||||
<p><b>Note:</b> The CERN COVID Level is not specified.</p>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<br>
|
||||
</div>
|
||||
{% endblock report_summary %}
|
||||
|
||||
{% block report_summary_footnote %}
|
||||
{% block report_summary_footnote %}
|
||||
<br>
|
||||
{% if scale_warning == 'red' %}
|
||||
This exceeds the authorised risk threshold or number of expected new cases.
|
||||
The risk level must be reduced before this activity can be undertaken.
|
||||
|
|
|
|||
|
|
@ -438,7 +438,7 @@ class SimpleExposureModel(SimpleConcentrationModel):
|
|||
def dose(self) -> _VectorisedFloat:
|
||||
"""
|
||||
total deposited dose (integrated over time and over particle
|
||||
diameters), including short and long range.
|
||||
diameters), including short and long-range.
|
||||
"""
|
||||
result = 0.
|
||||
for t1,t2 in self.infected_presence.boundaries():
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ python-dateutil==2.8.2
|
|||
pyzmq==22.1.0
|
||||
requests==2.26.0
|
||||
requests-unixsocket==0.2.0
|
||||
retry==0.9.2
|
||||
scikit-learn==0.24.2
|
||||
scipy==1.7.0
|
||||
Send2Trash==1.7.1
|
||||
|
|
@ -76,6 +77,7 @@ threadpoolctl==2.2.0
|
|||
timezonefinder==5.2.0
|
||||
tornado==6.1
|
||||
traitlets==5.0.5
|
||||
types-retry==0.9.7
|
||||
urllib3==1.26.6
|
||||
voila==0.2.10
|
||||
wcwidth==0.2.5
|
||||
|
|
|
|||
4
setup.py
4
setup.py
|
|
@ -30,10 +30,12 @@ REQUIREMENTS: dict = {
|
|||
'numpy',
|
||||
'psutil',
|
||||
'python-dateutil',
|
||||
'retry',
|
||||
'scipy',
|
||||
'sklearn',
|
||||
'timezonefinder',
|
||||
'tornado',
|
||||
'types-retry',
|
||||
'voila >=0.2.4',
|
||||
],
|
||||
'app': [],
|
||||
|
|
@ -42,10 +44,8 @@ REQUIREMENTS: dict = {
|
|||
'pytest-mypy',
|
||||
'pytest-tornasync',
|
||||
'numpy-stubs @ git+https://github.com/numpy/numpy-stubs.git',
|
||||
'retry',
|
||||
'types-dataclasses',
|
||||
'types-python-dateutil',
|
||||
'types-retry',
|
||||
],
|
||||
'dev': [
|
||||
'jupyterlab',
|
||||
|
|
|
|||
Loading…
Reference in a new issue