Merge branch 'changes/CO2_review' into 'master'
CO2 fitting algorithm post-review changes See merge request caimira/caimira!516
This commit is contained in:
commit
424f4f4804
9 changed files with 53 additions and 28 deletions
|
|
@ -1,3 +1,11 @@
|
||||||
|
# 4.17.4 (November 05, 2024)
|
||||||
|
|
||||||
|
## Features Added
|
||||||
|
- CO2 fitting algorithm post-review changes
|
||||||
|
|
||||||
|
## Bug Fixes
|
||||||
|
- N/A
|
||||||
|
|
||||||
# 4.17.3 (October 16, 2024)
|
# 4.17.3 (October 16, 2024)
|
||||||
|
|
||||||
## Features Added
|
## Features Added
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "caimira"
|
name = "caimira"
|
||||||
version = "4.17.3"
|
version = "4.17.4"
|
||||||
description = "CAiMIRA - CERN Airborne Model for Indoor Risk Assessment"
|
description = "CAiMIRA - CERN Airborne Model for Indoor Risk Assessment"
|
||||||
license = { text = "Apache-2.0" }
|
license = { text = "Apache-2.0" }
|
||||||
authors = [
|
authors = [
|
||||||
|
|
|
||||||
|
|
@ -129,8 +129,8 @@ _MODEL_CLASSES = [
|
||||||
|
|
||||||
# Inject the runtime generated MC types into this module.
|
# Inject the runtime generated MC types into this module.
|
||||||
for _model in _MODEL_CLASSES:
|
for _model in _MODEL_CLASSES:
|
||||||
setattr(sys.modules[__name__], _model.__name__, _build_mc_model(_model))
|
setattr(sys.modules[__name__], _model.__name__, _build_mc_model(_model)) # type: ignore
|
||||||
|
|
||||||
|
|
||||||
# Make sure that each of the models is imported if you do a ``import *``.
|
# Make sure that each of the models is imported if you do a ``import *``.
|
||||||
__all__ = [_model.__name__ for _model in _MODEL_CLASSES] + ["MCModelBase"]
|
__all__ = [_model.__name__ for _model in _MODEL_CLASSES] + ["MCModelBase"] # type: ignore
|
||||||
|
|
|
||||||
|
|
@ -21,14 +21,15 @@ def build_initial_plot(
|
||||||
[occupancy_transition_times[-1]] +
|
[occupancy_transition_times[-1]] +
|
||||||
ventilation_transition_times)
|
ventilation_transition_times)
|
||||||
|
|
||||||
ventilation_plot: str = form.generate_ventilation_plot(
|
vent_plot_img, vent_plot_data = form.generate_ventilation_plot(
|
||||||
ventilation_transition_times=all_vent_transition_times,
|
ventilation_transition_times=ventilation_transition_times,
|
||||||
occupancy_transition_times=occupancy_transition_times
|
occupancy_transition_times=occupancy_transition_times
|
||||||
)
|
)
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
'CO2_plot': ventilation_plot,
|
|
||||||
'transition_times': [round(el, 2) for el in all_vent_transition_times],
|
'transition_times': [round(el, 2) for el in all_vent_transition_times],
|
||||||
|
'CO2_plot_img': vent_plot_img,
|
||||||
|
'CO2_plot_data': vent_plot_data
|
||||||
}
|
}
|
||||||
|
|
||||||
return context
|
return context
|
||||||
|
|
@ -52,9 +53,12 @@ def build_fitting_results(
|
||||||
# predictive CO2 result based on the fitting results.
|
# predictive CO2 result based on the fitting results.
|
||||||
context = dict(CO2model.CO2_fit_params())
|
context = dict(CO2model.CO2_fit_params())
|
||||||
|
|
||||||
|
vent_plot_img, vent_plot_data = form.generate_ventilation_plot(ventilation_transition_times=ventilation_transition_times[:-1],
|
||||||
|
predictive_CO2=context['predictive_CO2'])
|
||||||
|
|
||||||
# Add the transition times and CO2 plot to the results.
|
# Add the transition times and CO2 plot to the results.
|
||||||
context['transition_times'] = ventilation_transition_times
|
context['transition_times'] = ventilation_transition_times
|
||||||
context['CO2_plot'] = form.generate_ventilation_plot(ventilation_transition_times=ventilation_transition_times[:-1],
|
context['CO2_plot_img'] = vent_plot_img
|
||||||
predictive_CO2=context['predictive_CO2'])
|
context['CO2_plot_data'] = vent_plot_data
|
||||||
|
|
||||||
return context
|
return context
|
||||||
|
|
|
||||||
|
|
@ -105,7 +105,7 @@ class CO2FormData(FormData):
|
||||||
"""
|
"""
|
||||||
Perform change point detection using scipy library (find_peaks method) with rolling average of data.
|
Perform change point detection using scipy library (find_peaks method) with rolling average of data.
|
||||||
Incorporate existing state change candidates and adjust the result accordingly.
|
Incorporate existing state change candidates and adjust the result accordingly.
|
||||||
Returns a list of the detected ventilation state changes, discarding any occupancy state change.
|
Returns a list of the detected ventilation transition times, discarding any occupancy state change.
|
||||||
"""
|
"""
|
||||||
times: list = self.CO2_data['times']
|
times: list = self.CO2_data['times']
|
||||||
CO2_values: list = self.CO2_data['CO2']
|
CO2_values: list = self.CO2_data['CO2']
|
||||||
|
|
@ -147,33 +147,46 @@ class CO2FormData(FormData):
|
||||||
def generate_ventilation_plot(self,
|
def generate_ventilation_plot(self,
|
||||||
ventilation_transition_times: typing.Optional[list] = None,
|
ventilation_transition_times: typing.Optional[list] = None,
|
||||||
occupancy_transition_times: typing.Optional[list] = None,
|
occupancy_transition_times: typing.Optional[list] = None,
|
||||||
predictive_CO2: typing.Optional[list] = None) -> str:
|
predictive_CO2: typing.Optional[list] = None):
|
||||||
|
|
||||||
# Plot data (x-axis: times; y-axis: CO2 concentrations)
|
# Plot data (x-axis: times; y-axis: CO2 concentrations)
|
||||||
times_values: list = self.CO2_data['times']
|
times_values: list = self.CO2_data['times']
|
||||||
CO2_values: list = self.CO2_data['CO2']
|
CO2_values: list = self.CO2_data['CO2']
|
||||||
|
|
||||||
fig = plt.figure(figsize=(7, 4), dpi=110)
|
fig = plt.figure(figsize=(7, 4), dpi=110)
|
||||||
plt.plot(times_values, CO2_values, label='Input CO₂')
|
plt.plot(times_values, CO2_values, label='CO₂ Data')
|
||||||
|
|
||||||
|
# Add predictive CO2
|
||||||
|
if (predictive_CO2):
|
||||||
|
plt.plot(times_values, predictive_CO2, label='Predictive CO₂')
|
||||||
|
|
||||||
# Add occupancy state changes:
|
# Add ventilation transition times:
|
||||||
if (occupancy_transition_times):
|
|
||||||
for i, time in enumerate(occupancy_transition_times):
|
|
||||||
plt.axvline(x = time, color = 'grey', linewidth=0.5, linestyle='--', label='Occupancy change (from input)' if i == 0 else None)
|
|
||||||
# Add ventilation state changes:
|
|
||||||
if (ventilation_transition_times):
|
if (ventilation_transition_times):
|
||||||
for i, time in enumerate(ventilation_transition_times):
|
for i, time in enumerate(ventilation_transition_times):
|
||||||
if i == 0:
|
if i == 0:
|
||||||
label = 'Ventilation change (detected)' if occupancy_transition_times else 'Ventilation state changes'
|
label = 'Ventilation transition times (suggestion)' if occupancy_transition_times else 'Ventilation transition times'
|
||||||
else: label = None
|
else: label = None
|
||||||
plt.axvline(x = time, color = 'red', linewidth=0.5, linestyle='--', label=label)
|
plt.axvline(x = time, color = 'red', linewidth=1, linestyle='--', label=label)
|
||||||
|
|
||||||
|
# Add occupancy changes (UI):
|
||||||
|
if (occupancy_transition_times):
|
||||||
|
for i, time in enumerate(occupancy_transition_times):
|
||||||
|
plt.axvline(x = time, color = 'grey', linewidth=1, linestyle='--', label='Occupancy change (from UI)' if i == 0 else None)
|
||||||
|
|
||||||
if (predictive_CO2):
|
|
||||||
plt.plot(times_values, predictive_CO2, label='Predictive CO₂')
|
|
||||||
plt.xlabel('Time of day')
|
plt.xlabel('Time of day')
|
||||||
plt.ylabel('Concentration (ppm)')
|
plt.ylabel('Concentration (ppm)')
|
||||||
plt.legend()
|
plt.legend()
|
||||||
return img2base64(_figure2bytes(fig))
|
|
||||||
|
vent_plot_data = {
|
||||||
|
'plot': img2base64(_figure2bytes(fig)),
|
||||||
|
'times': times_values,
|
||||||
|
'CO2': CO2_values,
|
||||||
|
'occ_trans_time': occupancy_transition_times,
|
||||||
|
'vent_trans_time': ventilation_transition_times,
|
||||||
|
'predictive_CO2': predictive_CO2,
|
||||||
|
}
|
||||||
|
|
||||||
|
return img2base64(_figure2bytes(fig)), vent_plot_data
|
||||||
|
|
||||||
def population_present_changes(self, infected_presence: models.Interval, exposed_presence: models.Interval) -> typing.List[float]:
|
def population_present_changes(self, infected_presence: models.Interval, exposed_presence: models.Interval) -> typing.List[float]:
|
||||||
state_change_times = set(infected_presence.transition_times())
|
state_change_times = set(infected_presence.transition_times())
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ def test_integrated_concentration(simple_co2_conc_model):
|
||||||
def test_find_change_points(scenario_data, room_volume, max_total_people, start, finish, state_changes, request):
|
def test_find_change_points(scenario_data, room_volume, max_total_people, start, finish, state_changes, request):
|
||||||
'''
|
'''
|
||||||
Specific test of the find_change_points method.
|
Specific test of the find_change_points method.
|
||||||
Testing the ventilation state changes only.
|
Testing the ventilation transition times only.
|
||||||
'''
|
'''
|
||||||
CO2_form_model: CO2FormData = CO2FormData(
|
CO2_form_model: CO2FormData = CO2FormData(
|
||||||
CO2_data=request.getfixturevalue(scenario_data),
|
CO2_data=request.getfixturevalue(scenario_data),
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "cern-caimira"
|
name = "cern-caimira"
|
||||||
version = "4.17.3"
|
version = "4.17.4"
|
||||||
description = "CAiMIRA - CERN Airborne Model for Indoor Risk Assessment"
|
description = "CAiMIRA - CERN Airborne Model for Indoor Risk Assessment"
|
||||||
license = { text = "Apache-2.0" }
|
license = { text = "Apache-2.0" }
|
||||||
authors = [
|
authors = [
|
||||||
|
|
|
||||||
|
|
@ -222,7 +222,7 @@ function validateCO2Form() {
|
||||||
if (parsedValue.length <= 1) {
|
if (parsedValue.length <= 1) {
|
||||||
insertErrorFor(
|
insertErrorFor(
|
||||||
$referenceNode,
|
$referenceNode,
|
||||||
`'${$ventilationStates.attr('name')}' must have more than one ventilation state change (at least the beggining and end of simulation time).<br />`
|
`'${$ventilationStates.attr('name')}' must have more than one ventilation transition time (at least the beginning and end of simulation time).<br />`
|
||||||
);
|
);
|
||||||
submit = false;
|
submit = false;
|
||||||
}
|
}
|
||||||
|
|
@ -297,9 +297,9 @@ function displayTransitionTimesHourFormat(start, stop) {
|
||||||
|
|
||||||
function displayFittingData(json_response) {
|
function displayFittingData(json_response) {
|
||||||
$("#DIVCO2_fitting_result").show();
|
$("#DIVCO2_fitting_result").show();
|
||||||
$("#CO2_data_plot").attr("src", json_response["CO2_plot"]);
|
$("#CO2_data_plot").attr("src", json_response["CO2_plot_img"]);
|
||||||
// Not needed for the form submission
|
// Not needed for the form submission
|
||||||
delete json_response["CO2_plot"];
|
delete json_response["CO2_plot_img"];
|
||||||
delete json_response["predictive_CO2"];
|
delete json_response["predictive_CO2"];
|
||||||
// Convert nulls to empty strings in the JSON response
|
// Convert nulls to empty strings in the JSON response
|
||||||
if (json_response["room_capacity"] === null) json_response["room_capacity"] = '';
|
if (json_response["room_capacity"] === null) json_response["room_capacity"] = '';
|
||||||
|
|
@ -381,7 +381,7 @@ function plotCO2Data(url) {
|
||||||
response
|
response
|
||||||
.json()
|
.json()
|
||||||
.then((json_response) => {
|
.then((json_response) => {
|
||||||
$("#CO2_data_plot").attr("src", json_response["CO2_plot"])
|
$("#CO2_data_plot").attr("src", json_response["CO2_plot_img"])
|
||||||
$("#fitting_ventilation_states").val(`[${json_response["transition_times"]}]`)
|
$("#fitting_ventilation_states").val(`[${json_response["transition_times"]}]`)
|
||||||
})
|
})
|
||||||
.then($("#DIVCO2_fitting_to_submit").show())
|
.then($("#DIVCO2_fitting_to_submit").show())
|
||||||
|
|
|
||||||
|
|
@ -349,7 +349,7 @@
|
||||||
The dashed lines are suggestions for the ventilation transition times<br>
|
The dashed lines are suggestions for the ventilation transition times<br>
|
||||||
</p>
|
</p>
|
||||||
<div id="DIVfitting_ventilation" class="form-group mb-0">
|
<div id="DIVfitting_ventilation" class="form-group mb-0">
|
||||||
<label for="fitting_ventilation_states">Please enter the ventilation state change times, separated by comma - e.g. [8.5, 10, 11.5, 17]. </label>
|
<label for="fitting_ventilation_states">Please enter the ventilation transition times, separated by comma - e.g. [8.5, 10, 11.5, 17]. </label>
|
||||||
<div data-tooltip="Default values indicated below correspond to the dashed lines in the above plot - these are only suggestions and can be changed.">
|
<div data-tooltip="Default values indicated below correspond to the dashed lines in the above plot - these are only suggestions and can be changed.">
|
||||||
<span class="tooltip_text">?</span>
|
<span class="tooltip_text">?</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue