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:
Andre Henriques 2022-05-31 17:01:27 +02:00
commit 8fe4939757
9 changed files with 154 additions and 64 deletions

View file

@ -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

View file

@ -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');

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 264 KiB

View file

@ -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>

View file

@ -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.

View file

@ -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():

View file

@ -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

View file

@ -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',