Merge branch 'master' into remotes/origin/feature/website

This commit is contained in:
Luis Aleixo 2021-11-04 15:45:41 +01:00
commit b28064d06d
9 changed files with 210 additions and 122 deletions

View file

@ -35,7 +35,7 @@ Andre Henriques<sup>1</sup>, Luis Aleixo<sup>1</sup>, Marco Andreini<sup>1</sup>
<sup>5</sup>Information Technology Department, Collaboration, Devices & Applications Group, CERN<br>
<sup>6</sup>Norwegian University of Science and Technology (NTNU)<br>
### citation
### Citation
A. Henriques, M. Andreini, G. Azzopardi, J. Devine, P. Elson, N. Mounet, M. Kongstein, N. Tarocco. CARA - COVID Airborne Risk Assessment tools. CERN (2021).

View file

@ -108,10 +108,15 @@ def calculate_report_data(model: models.ExposureModel):
er = np.array(model.concentration_model.infected.emission_rate_when_present()).mean()
exposed_occupants = model.exposed.number
expected_new_cases = np.array(model.expected_new_cases()).mean()
cumulative_doses = np.cumsum([
np.array(model.exposure_between_bounds(float(time1), float(time2))).mean()
for time1, time2 in zip(times[:-1], times[1:])
])
return {
"times": list(times),
"exposed_presence_intervals": [list(interval) for interval in model.exposed.presence.boundaries()],
"cumulative_doses": list(cumulative_doses),
"concentrations": concentrations,
"highest_const": highest_const,
"prob_inf": prob,
@ -303,11 +308,11 @@ class ReportGenerator:
context['permalink'] = generate_permalink(base_url, self.calculator_prefix, form)
context['calculator_prefix'] = self.calculator_prefix
context['scale_warning'] = {
'level': 'yellow-2',
'level': 'yellow-2',
'incidence_rate': 'lower than 25 new cases per 100 000 inhabitants',
'onsite_access': 'of about 8000',
'onsite_access': 'of about 8000',
'threshold': ''
}
}
return context
def _template_environment(self) -> jinja2.Environment:

View file

@ -1,22 +1,32 @@
/* Generate the concentration plot using d3 library. */
function draw_concentration_plot(svg_id, times, concentrations, exposed_presence_intervals) {
function draw_concentration_plot(svg_id, times, concentrations, cumulative_doses, exposed_presence_intervals) {
console.log(cumulative_doses)
var time_format = d3.timeFormat('%H:%M');
// H:M time format for x axis.
var data = []
// Prepare data
times.map((time, index) => data.push({'time': time, 'hour': new Date().setHours(Math.trunc(time), (time - Math.trunc(time)) * 60), 'concentration': concentrations[index] }))
var data_for_graphs = {
'concentrations': [],
'cumulative_doses': [],
}
times.map((time, index) => data_for_graphs.concentrations.push({ 'time': time, 'hour': new Date().setHours(Math.trunc(time), (time - Math.trunc(time)) * 60), 'concentration': concentrations[index]}));
times.map((time, index) => data_for_graphs.cumulative_doses.push({ 'time': time, 'hour': new Date().setHours(Math.trunc(time), (time - Math.trunc(time)) * 60), 'concentration': cumulative_doses[index]}));
// Add main SVG element
var plot_div = document.getElementById(svg_id);
var vis = d3.select(plot_div).append('svg');
var xRange = d3.scaleTime().domain([data[0].hour, data[data.length - 1].hour]);
var xTimeRange = d3.scaleLinear().domain([data[0].time, data[data.length - 1].time]);
var bisecHour = d3.bisector((d) => { return d.hour; }).left;
var yRange = d3.scaleLinear().domain([0., Math.max(...concentrations)]);
// H:M time format for x axis.
xRange = d3.scaleTime().domain([data_for_graphs.concentrations[0].hour, data_for_graphs.concentrations[data_for_graphs.concentrations.length - 1].hour]),
xTimeRange = d3.scaleLinear().domain([data_for_graphs.concentrations[0].time, data_for_graphs.concentrations[data_for_graphs.concentrations.length - 1].time]),
bisecHour = d3.bisector((d) => { return d.hour; }).left,
yRange = d3.scaleLinear().domain([0., Math.max(...concentrations)]),
yCumulativeRange = d3.scaleLinear().domain([0., Math.max(...cumulative_doses)*1.1]),
xAxis = d3.axisBottom(xRange).tickFormat(d => time_format(d)),
yAxis = d3.axisLeft(yRange).ticks(4),
yCumulativeAxis = d3.axisRight(yCumulativeRange).ticks(4);
// Line representing the mean concentration.
var lineFunc = d3.line();
@ -25,6 +35,13 @@ function draw_concentration_plot(svg_id, times, concentrations, exposed_presence
.attr('stroke-width', 2)
.attr('fill', 'none');
var lineCumulative = d3.line();
var draw_cumulative_line = vis.append('svg:path')
.attr('stroke', '#1f77b4')
.attr('stroke-width', 2)
.style("stroke-dasharray", "5 5")
.attr('fill', 'none');
// Area representing the presence of exposed person(s).
var exposedArea = {};
var drawArea = {};
@ -64,6 +81,19 @@ function draw_concentration_plot(svg_id, times, concentrations, exposed_presence
.attr('text-anchor', 'middle')
.text('Mean concentration (virions/m³)');
// Y cumulative concentration axis declaration.
var yAxisCumEl = vis.append('svg:g')
.attr('class', 'y axis')
.style('font-size', 14)
.style("stroke-dasharray", "5 5");
// Y cumulated concentration axis label.
var yAxisCumLabelEl = vis.append('svg:text')
.attr('class', 'y label')
.attr('fill', 'black')
.attr('text-anchor', 'middle')
.text('Mean cumulative dose (virions)');
// Legend for the plot elements - line and area.
var legendLineIcon = vis.append('rect')
.attr('width', 20)
@ -88,7 +118,7 @@ function draw_concentration_plot(svg_id, times, concentrations, exposed_presence
// Legend bounding
var legendBBox = vis.append('rect')
.attr('width', 275)
.attr('width', 255)
.attr('height', 50)
.attr('stroke', 'lightgrey')
.attr('stroke-width', '2')
@ -98,35 +128,42 @@ function draw_concentration_plot(svg_id, times, concentrations, exposed_presence
.attr('fill', 'none');
// Tooltip.
var focus = vis.append('svg:g')
.style('display', 'none');
var focus = {}, tooltip_rect = {}, tooltip_time = {}, tooltip_concentration = {}, toolBox = {};
for (const [concentration, data] of Object.entries(data_for_graphs)) {
focus.append('circle')
.attr('r', 3);
focus[concentration] = vis.append('svg:g')
.style('display', 'none');
var tooltip_rect = focus.append('rect')
.attr('fill', 'white')
.attr('stroke', '#000')
.attr('width', 80)
.attr('height', 50)
.attr('y', -22)
.attr('rx', 4)
.attr('ry', 4);
focus[concentration].append('circle')
.attr('r', 3);
var tooltip_time = focus.append('text')
.attr('id', 'tooltip-time')
.attr('y', -2);
tooltip_rect[concentration] = focus[concentration].append('rect')
.attr('fill', 'white')
.attr('stroke', '#000')
.attr('width', 85)
.attr('height', 50)
.attr('x', 10)
.attr('y', -22)
.attr('rx', 4)
.attr('ry', 4);
var tooltip_concentration = focus.append('text')
.attr('id', 'tooltip-concentration')
.attr('y', 18);
tooltip_time[concentration] = focus[concentration].append('text')
.attr('id', 'tooltip-time')
.attr('x', 18)
.attr('y', -2);
var toolBox = vis.append('rect')
.attr('fill', 'none')
.attr('pointer-events', 'all')
.on('mouseover', () => { focus.style('display', null); })
.on('mouseout', () => { focus.style('display', 'none'); })
.on('mousemove', mousemove);
tooltip_concentration[concentration] = focus[concentration].append('text')
.attr('id', 'tooltip-concentration')
.attr('x', 18)
.attr('y', 18);
toolBox[concentration] = vis.append('rect')
.attr('fill', 'none')
.attr('pointer-events', 'all')
.on('mouseover', () => { for (const [concentration, data] of Object.entries(focus)) focus[concentration].style('display', null); })
.on('mouseout', () => { for (const [concentration, data] of Object.entries(focus)) focus[concentration].style('display', 'none'); })
.on('mousemove', mousemove);
}
var graph_width;
var graph_height;
@ -148,7 +185,7 @@ function draw_concentration_plot(svg_id, times, concentrations, exposed_presence
else {
var margins = { top: 30, right: 20, bottom: 50, left: 40 };
div_width = div_width * 1.1
graph_width = div_width * .95;
graph_width = div_width * .9;
graph_height = div_height * 0.65; // On mobile screen sizes we want the legend to be on the bottom of the graph.
const svg_margins = {'margin-left': '-1rem', 'margin-top': '3rem'};
Object.entries(svg_margins).forEach(([prop,val]) => vis.style(prop,val));
@ -163,13 +200,20 @@ function draw_concentration_plot(svg_id, times, concentrations, exposed_presence
// Axis ranges.
xRange.range([margins.left, graph_width - margins.right]);
xTimeRange.range([margins.left, graph_width - margins.right]);
yRange.range([graph_height - margins.bottom, margins.top])
yRange.range([graph_height - margins.bottom, margins.top]);
yCumulativeRange.range([graph_height - margins.bottom, margins.top]);
// Line.
lineFunc.defined(d => !isNaN(d.concentration))
.x(d => xTimeRange(d.time))
.y(d => yRange(d.concentration));
draw_line.attr("d", lineFunc(data));
draw_line.attr("d", lineFunc(data_for_graphs.concentrations));
// Cumulative line
lineCumulative.defined(d => !isNaN(d.concentration))
.x(d => xTimeRange(d.time))
.y(d => yCumulativeRange(d.concentration));
draw_cumulative_line.attr("d", lineCumulative(data_for_graphs.cumulative_doses));
// Area.
exposed_presence_intervals.forEach((b, index) => {
@ -177,7 +221,7 @@ function draw_concentration_plot(svg_id, times, concentrations, exposed_presence
.y0(graph_height - margins.bottom)
.y1(d => yRange(d.concentration));
drawArea[index].attr('d', exposedArea[index](data.filter(d => {
drawArea[index].attr('d', exposedArea[index](data_for_graphs.concentrations.filter(d => {
return d.time >= b[0] && d.time <= b[1]
})));
});
@ -199,18 +243,33 @@ function draw_concentration_plot(svg_id, times, concentrations, exposed_presence
.attr('y', (graph_height + margins.left) * 0.9)
.attr('transform', 'rotate(-90, 0,' + graph_height + ')');
yAxisCumEl.attr('transform', 'translate(' + (graph_width - margins.right) + ',0)').call(yCumulativeAxis);
yAxisCumLabelEl.attr('transform', 'rotate(-90, 0,' + graph_height + ')')
.attr('x', (graph_height + margins.bottom) / 2);
if (plot_div.clientWidth >= 900) {
yAxisCumLabelEl.attr('transform', 'rotate(-90, 0,' + graph_height + ')')
.attr('x', (graph_height + margins.bottom) / 2)
.attr('y', 1.71 * graph_width);
}
else {
yAxisCumLabelEl.attr('transform', 'rotate(-90, 0,' + graph_height + ')')
.attr('x', (graph_height + margins.bottom * 0.55) / 2)
.attr('y', graph_width + 290);
}
// Legend on right side.
const size = 20;
if (plot_div.clientWidth >= 900) {
legendLineIcon.attr('x', graph_width + size)
legendLineIcon.attr('x', graph_width + size * 2.5)
.attr('y', margins.top + size);
legendLineText.attr('x', graph_width + 3 * size)
legendLineText.attr('x', graph_width + 4 * size)
.attr('y', margins.top + size);
legendAreaIcon.attr('x', graph_width + size)
legendAreaIcon.attr('x', graph_width + size * 2.5)
.attr('y', margins.top + 1.5 * size);
legendAreaText.attr('x', graph_width + 3 * size)
legendAreaText.attr('x', graph_width + 4 * size)
.attr('y', margins.top + 2 * size);
legendBBox.attr('x', graph_width * 1.005)
legendBBox.attr('x', graph_width * 1.07)
.attr('y', margins.top * 1.2);
}
// Legend on the bottom.
@ -228,32 +287,50 @@ function draw_concentration_plot(svg_id, times, concentrations, exposed_presence
}
// ToolBox.
toolBox.attr('width', graph_width - margins.right)
.attr('height', graph_height);
for (const [concentration, data] of Object.entries(data_for_graphs)) {
toolBox[concentration].attr('width', graph_width - margins.right)
.attr('height', graph_height);
}
}
// Draw for the first time to initialize.
redraw();
function mousemove() {
if (d3.pointer(event)[0] < graph_width / 2) {
tooltip_rect.attr('x', 10)
tooltip_time.attr('x', 18)
tooltip_concentration.attr('x', 18);
}
else {
tooltip_rect.attr('x', -90)
tooltip_time.attr('x', -82)
tooltip_concentration.attr('x', -82)
for (const [scenario, data] of Object.entries(data_for_graphs)) {
if (d3.pointer(event)[0] < graph_width / 2) {
tooltip_rect[scenario].attr('x', 10)
tooltip_time[scenario].attr('x', 18)
tooltip_concentration[scenario].attr('x', 18);
}
else {
tooltip_rect[scenario].attr('x', -90)
tooltip_time[scenario].attr('x', -82)
tooltip_concentration[scenario].attr('x', -82)
}
}
// Concentration line
var x0 = xRange.invert(d3.pointer(event, this)[0]),
i = bisecHour(data, x0, 1),
d0 = data[i - 1],
d1 = data[i],
d = x0 - d0.hour > d1.hour - x0 ? d1 : d0;
focus.attr('transform', 'translate(' + xRange(d.hour) + ',' + yRange(d.concentration) + ')');
focus.select('#tooltip-time').text('x = ' + time_format(d.hour));
focus.select('#tooltip-concentration').text('y = ' + d.concentration.toFixed(2));
i = bisecHour(data_for_graphs.concentrations, x0, 1),
d0 = data_for_graphs.concentrations[i - 1],
d1 = data_for_graphs.concentrations[i];
if (d1) {
var d = x0 - d0.hour > d1.hour - x0 ? d1 : d0;
focus.concentrations.attr('transform', 'translate(' + xRange(d.hour) + ',' + yRange(d.concentration) + ')');
focus.concentrations.select('#tooltip-time').text('x = ' + time_format(d.hour));
focus.concentrations.select('#tooltip-concentration').text('y = ' + d.concentration.toFixed(2));
}
// Cumulative line
var x0 = xRange.invert(d3.pointer(event, this)[0]),
i = bisecHour(data_for_graphs.cumulative_doses, x0, 1),
d0 = data_for_graphs.cumulative_doses[i - 1],
d1 = data_for_graphs.cumulative_doses[i];
if (d1 && d1.concentration) {
var d = x0 - d0.hour > d1.hour - x0 ? d1 : d0;
focus.cumulative_doses.attr('transform', 'translate(' + xRange(d.hour) + ',' + yCumulativeRange(d.concentration) + ')');
focus.cumulative_doses.select('#tooltip-time').text('x = ' + time_format(d.hour));
focus.cumulative_doses.select('#tooltip-concentration').text('y = ' + d.concentration.toFixed(2));
}
}
// Redraw based on the new size whenever the browser window is resized.

View file

@ -93,8 +93,9 @@
<script type="application/javascript">
var times = {{ times | JSONify }}
var concentrations = {{ concentrations | JSONify }}
var cumulative_doses = {{ cumulative_doses | JSONify }}
var exposed_presence_intervals = {{ exposed_presence_intervals | JSONify }}
draw_concentration_plot("concentration_plot", times, concentrations, exposed_presence_intervals);
draw_concentration_plot("concentration_plot", times, concentrations, cumulative_doses, exposed_presence_intervals);
</script>
</p>
</div>

View file

@ -1,37 +1,20 @@
import numpy as np
from cara import models
from cara.data.weather import wx_data, nearest_wx_station
# TODO: The values in this module to be removed and instead use the cara.data.weather functionality.
MONTH_NAMES = [
'January', 'February', 'March', 'April', 'May', 'June', 'July',
'August', 'September', 'October', 'November', 'December',
]
# Load the weather data (temperature in kelvin) for Geneva.
coordinates = (46.204391, 6.143158)
wx_station_id = nearest_wx_station(longitude=coordinates[1], latitude=coordinates[0])[0]
# average temperature of each month, hour per hour (from midnight to 11 pm)
Geneva_hourly_temperatures_celsius_per_hour = {
'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],
'Mar': [4.2, 3.5, 3.1, 2.5, 2.1, 1.6, 1.5, 1.6, 2.2, 4.0, 6.3, 8.4,
10.0, 11.1, 11.2, 11.1, 11.0, 10.2, 8.9, 8.3, 7.5, 6.7, 6.3, 5.6],
'Apr': [7.4, 6.7, 6.2, 5.5, 5.2, 4.7, 4.5, 4.6, 5.3, 7.2, 9.6, 11.9,
13.7, 14.8, 14.9, 14.8, 14.7, 13.8, 12.4, 11.8, 10.9, 10.1, 9.6, 8.9],
'May': [11.8, 11.1, 10.6, 9.9, 9.5, 8.9, 8.8, 8.9, 9.6, 11.6, 14.2, 16.6,
18.4, 19.6, 19.7, 19.6, 19.4, 18.6, 17.1, 16.5, 15.6, 14.6, 14.2, 13.4],
'Jun': [15.2, 14.4, 13.9, 13.2, 12.7, 12.2, 12.0, 12.1, 12.8, 15.0, 17.7,
20.2, 22.1, 23.3, 23.5, 23.4, 23.2, 22.3, 20.8, 20.1, 19.1, 18.2, 17.7, 16.9],
'Jul': [17.6, 16.7, 16.1, 15.3, 14.9, 14.3, 14.1, 14.2, 15.0, 17.3, 20.2,
23.0, 25.0, 26.3, 26.5, 26.4, 26.2, 25.2, 23.6, 22.8, 21.8, 20.8, 20.2, 19.4],
'Aug': [17.1, 16.2, 15.7, 14.9, 14.5, 13.9, 13.7, 13.8, 14.6, 16.9, 19.7,
22.4, 24.4, 25.6, 25.8, 25.7, 25.5, 24.5, 22.9, 22.2, 21.2, 20.2, 19.7, 18.9],
'Sep': [13.4, 12.7, 12.2, 11.5, 11.2, 10.7, 10.5, 10.6, 11.3, 13.2, 15.6,
17.9, 19.6, 20.8, 20.9, 20.8, 20.7, 19.8, 18.4, 17.8, 16.9, 16.1, 15.6, 14.9],
'Oct': [9.4, 8.8, 8.5, 7.9, 7.6, 7.2, 7.1, 7.2, 7.7, 9.3, 11.2, 13.0,
14.4, 15.3, 15.4, 15.3, 15.2, 14.5, 13.4, 12.9, 12.2, 11.6, 11.2, 10.6],
'Nov': [4.0, 3.6, 3.3, 2.9, 2.6, 2.3, 2.2, 2.2, 2.7, 3.9, 5.5, 6.9, 8.0,
8.7, 8.8, 8.7, 8.7, 8.1, 7.2, 6.8, 6.3, 5.7, 5.5, 5.0],
'Dec': [1.4, 1.0, 0.8, 0.4, 0.2, -0.0, -0.1, -0.1, 0.3, 1.3, 2.6, 3.8,
4.7, 5.2, 5.3, 5.2, 5.2, 4.7, 4.0, 3.7, 3.2, 2.8, 2.6, 2.2]
}
local_hourly_temperatures_celsius_per_hour = {month.replace(month, MONTH_NAMES[i][:3]):
[t - 273.15 for t in temp] for i, (month, temp)
in enumerate(wx_data()[wx_station_id].items())}
# Geneva hourly temperatures as piecewise constant function (in Kelvin).
GenevaTemperatures_hourly = {
month: models.PiecewiseConstant(
@ -40,12 +23,12 @@ GenevaTemperatures_hourly = {
tuple(float(time) for time in range(25)),
tuple(273.15 + np.array(temperatures)),
)
for month, temperatures in Geneva_hourly_temperatures_celsius_per_hour.items()
for month, temperatures in local_hourly_temperatures_celsius_per_hour.items()
}
# Same temperatures on a finer temperature mesh (every 6 minutes).
GenevaTemperatures = {
month: GenevaTemperatures_hourly[month].refine(refine_factor=10)
for month, temperatures in Geneva_hourly_temperatures_celsius_per_hour.items()
for month, temperatures in local_hourly_temperatures_celsius_per_hour.items()
}

View file

@ -102,7 +102,6 @@ class Interval:
return True
return False
@dataclass(frozen=True)
class SpecificInterval(Interval):
#: A sequence of times (start, stop), in hours, that the infected person
@ -922,9 +921,33 @@ class ExposureModel:
#: The fraction of viruses actually deposited in the respiratory tract
fraction_deposited: _VectorisedFloat = 0.6
def _normed_exposure_between_bounds(self, time1: float, time2: float) -> _VectorisedFloat:
"""The number of virions per meter^3 between any two times, normalized
by the emission rate of the infected population"""
exposure = 0.
for start, stop in self.exposed.presence.boundaries():
if stop < time1:
continue
elif start > time2:
break
elif start <= time1 and time2<= stop:
exposure += self.concentration_model.normed_integrated_concentration(time1, time2)
elif start <= time1 and stop < time2:
exposure += self.concentration_model.normed_integrated_concentration(time1, stop)
elif time1 < start and time2 <= stop:
exposure += self.concentration_model.normed_integrated_concentration(start, time2)
elif time1 <= start and stop < time2:
exposure += self.concentration_model.normed_integrated_concentration(start, stop)
return exposure
def exposure_between_bounds(self, time1: float, time2: float) -> _VectorisedFloat:
"""The number of virions per meter^3 between any two times."""
return (self._normed_exposure_between_bounds(time1, time2) *
self.concentration_model.infected.emission_rate_when_present())
def _normed_exposure(self) -> _VectorisedFloat:
"""
The number of virus per meter^3, normalized by the emission rate
The number of virions per meter^3, normalized by the emission rate
of the infected population.
"""
normed_exposure = 0.0
@ -935,7 +958,7 @@ class ExposureModel:
return normed_exposure * self.repeats
def exposure(self) -> _VectorisedFloat:
"""The number of virus per meter^3."""
"""The number of virions per meter^3."""
return (self._normed_exposure() *
self.concentration_model.infected.emission_rate_when_present())

View file

@ -55,7 +55,6 @@ populations = [
),
]
def known_concentrations(func):
dummy_room = models.Room(50, 0.5)
dummy_ventilation = models._VentilationBase()
@ -73,21 +72,21 @@ def known_concentrations(func):
@pytest.mark.parametrize(
"population, cm, f_dep, expected_exposure, expected_probability", [
[populations[1], known_concentrations(lambda t: 36.), 1.,
np.array([432, 432]), np.array([99.6803184113, 99.5181053773])],
"population, cm, f_dep, expected_exposure, expected_probability",[
[populations[1], known_concentrations(lambda t: 36.), 1.,
np.array([432, 432]), np.array([99.6803184113, 99.5181053773])],
[populations[2], known_concentrations(lambda t: 36.), 1.,
np.array([432, 432]), np.array([97.4574432074, 98.3493482895])],
[populations[2], known_concentrations(lambda t: 36.), 1.,
np.array([432, 432]), np.array([97.4574432074, 98.3493482895])],
[populations[0], known_concentrations(lambda t: np.array([36., 72.])), 1.,
np.array([432, 864]), np.array([98.3493482895, 99.9727534893])],
[populations[0], known_concentrations(lambda t: np.array([36., 72.])), 1.,
np.array([432, 864]), np.array([98.3493482895, 99.9727534893])],
[populations[1], known_concentrations(lambda t: np.array([36., 72.])), 1.,
np.array([432, 864]), np.array([99.6803184113, 99.9976777757])],
[populations[1], known_concentrations(lambda t: np.array([36., 72.])), 1.,
np.array([432, 864]), np.array([99.6803184113, 99.9976777757])],
[populations[0], known_concentrations(lambda t: 72.), np.array([0.5, 1.]),
864, np.array([98.3493482895, 99.9727534893])],
[populations[0], known_concentrations(lambda t: 72.), np.array([0.5, 1.]),
864, np.array([98.3493482895, 99.9727534893])],
])
def test_exposure_model_ndarray(population, cm, f_dep,
expected_exposure, expected_probability):

View file

@ -291,7 +291,7 @@ def build_hourly_dependent_model_multipleventilation(month, intervals_open=((7.5
@pytest.mark.parametrize(
"month, temperatures",
data.Geneva_hourly_temperatures_celsius_per_hour.items(),
data.local_hourly_temperatures_celsius_per_hour.items(),
)
@pytest.mark.parametrize(
"time",
@ -306,7 +306,7 @@ def test_concentrations_hourly_dep_temp_vs_constant(month, temperatures, time):
@pytest.mark.parametrize(
"month, temperatures",
data.Geneva_hourly_temperatures_celsius_per_hour.items(),
data.local_hourly_temperatures_celsius_per_hour.items(),
)
@pytest.mark.parametrize(
"time",
@ -330,7 +330,7 @@ def test_concentrations_hourly_dep_multipleventilation():
@pytest.mark.parametrize(
"month_temp_item",
data.Geneva_hourly_temperatures_celsius_per_hour.items(),
data.local_hourly_temperatures_celsius_per_hour.items(),
)
@pytest.mark.parametrize(
"time",
@ -378,8 +378,8 @@ def build_exposure_model(concentration_model):
@pytest.mark.parametrize(
"month, expected_exposure",
[
['Jan', 496.5427],
['Jun', 1898.1354],
['Jan', 503.254087759],
['Jun', 2294.71115639],
],
)
def test_exposure_hourly_dep(month,expected_exposure):
@ -399,8 +399,8 @@ def test_exposure_hourly_dep(month,expected_exposure):
@pytest.mark.parametrize(
"month, expected_exposure",
[
['Jan', 499.6921],
['Jun', 2007.59925],
['Jan', 511.118941481],
['Jun', 2398.90129579],
],
)
def test_exposure_hourly_dep_refined(month,expected_exposure):

View file

@ -20,7 +20,7 @@ REQUIREMENTS: dict = {
'core': [
'dataclasses; python_version < "3.7"',
'ipykernel',
'ipympl != 0.8.0',
'ipympl != 0.8.0, != 0.8.1',
'ipywidgets',
'Jinja2',
'loky',