Skip to content

Commit ddca0fb

Browse files
author
mheie
committed
Merge https://gitlab.cern.ch/caimira/caimira into feature/no_normed_lr_concentration_in_ShortRangeModel
2 parents 605e25d + e948cbe commit ddca0fb

3 files changed

Lines changed: 38 additions & 49 deletions

File tree

caimira/src/caimira/calculator/models/models.py

Lines changed: 25 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1615,9 +1615,19 @@ def __post_init__(self):
16151615
# the short range interaction is with does not have to be defined (?)
16161616
raise NotImplementedError("Short range interactions with multiple infected populations not yet implemented.")
16171617

1618-
_ = self.virus
1619-
_ = self.room
1620-
_ = self.ventilation
1618+
viruses = [c_model.virus for c_model in self.concentration_model_list]
1619+
virus = viruses[0]
1620+
if any(v != virus for v in viruses):
1621+
raise ValueError("All infected must be infected with the same virus.")
1622+
rooms = [c_model.room for c_model in self.concentration_model_list]
1623+
room = rooms[0]
1624+
if any(r != room for r in rooms):
1625+
raise ValueError("All concentration models must describe the same room.")
1626+
1627+
# TODO: test that all concentration models have overlapping ventilations
1628+
# NOTE: Different concentration models can have different occupancy times, and therefore different
1629+
# start and end times for which the ventilation is defined. Therefore, the ventilation objects
1630+
# must overlapp, but not neccecarily be identical.
16211631

16221632
for c_model in self.concentration_model_list:
16231633
# Check if the diameter is vectorised.
@@ -1644,27 +1654,11 @@ def concentration_model_list(self):
16441654

16451655
@property
16461656
def virus(self):
1647-
viruses = [c_model.virus for c_model in self.concentration_model_list]
1648-
virus = viruses[0]
1649-
if any(v != virus for v in viruses[1:]):
1650-
raise ValueError("All infected must be infected with the same virus.")
1651-
return virus
1657+
return self.concentration_model_list[0].virus
16521658

16531659
@property
16541660
def room(self):
1655-
rooms = [c_model.room for c_model in self.concentration_model_list]
1656-
room = rooms[0]
1657-
if any(r != room for r in rooms[1:]):
1658-
raise ValueError("All concentration models must describe the same room.")
1659-
return room
1660-
1661-
@property
1662-
def ventilation(self):
1663-
ventilations = [c_model.ventilation for c_model in self.concentration_model_list]
1664-
ventilation = ventilations[0]
1665-
if any(v != ventilation for v in ventilations[1:]):
1666-
raise ValueError("All concentration models must have the same ventilation.")
1667-
return ventilation
1661+
return self.concentration_model_list[0].room
16681662

16691663
@method_cache
16701664
def population_state_change_times(self) -> typing.List[float]:
@@ -1709,17 +1703,19 @@ def _long_range_normed_exposure_between_bounds(self, c_model, time1: float, time
17091703

17101704
def long_range_concentration(self, time: float) -> float:
17111705
"""
1712-
Integrated virus exposure concentration, as a function of time.
1706+
Total virus concentration in the room, as a function of time.
17131707
17141708
It only considers the long-range concentration without the
17151709
contribution of the short-range concentration.
17161710
1717-
Since the different ConcentrationModel objects may have different diameter bases drawn
1718-
from different distributions, the concentrations from different ConcentrationModel
1719-
objects must be averaged over the diameter before summed together.
1711+
Since the different ConcentrationModel objects may have different infected with different expirations,
1712+
they can have different particle diameters bases drawn from different probability distributions.
1713+
To Monte Carlo integrate correctly over the particle diameter, we must therefore average the concentration
1714+
of each ConcentrationModel over the particle diameter before adding together all the contributions from all
1715+
the ConcentrationModels.
17201716
"""
1721-
return sum([np.array(c_model.concentration(time)).mean() for c_model in self.concentration_model_list])
1722-
1717+
return sum([np.array(c_model.concentration(time)).mean() for c_model in self.concentration_model_list]) # Average over particle diameter
1718+
17231719
def concentration(self, time: float) -> float:
17241720
"""
17251721
Integrated virus exposure concentration, as a function of time.
@@ -1770,7 +1766,7 @@ def long_range_deposited_exposure_between_bounds(self, time1: float, time2: floa
17701766

17711767
return deposited_exposure
17721768

1773-
def deposited_exposure_between_bounds(self, time1: float, time2: float) -> _VectorisedFloat:###
1769+
def deposited_exposure_between_bounds(self, time1: float, time2: float) -> _VectorisedFloat:
17741770
"""
17751771
The number of virus per m^3 deposited on the respiratory tract
17761772
between any two times.
@@ -1910,7 +1906,7 @@ def reproduction_number(self) -> _VectorisedFloat:
19101906
cases directly generated by one infected case in a population.
19111907
"""
19121908
if isinstance(self.concentration_model, list):
1913-
raise NotImplementedError("yet to implement dynamic infected for SR interactions")
1909+
raise NotImplementedError("yet to implement dynamic infected for the reproduction number")
19141910
infected_population: InfectedPopulation = self.concentration_model.infected
19151911
if isinstance(infected_population.number, int) and infected_population.number == 1:
19161912
return self.expected_new_cases()

caimira/src/caimira/calculator/report/virus_report_data.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import base64
33
import dataclasses
44
import io
5+
import copy
56
import typing
67
import numpy as np
78
import matplotlib.pyplot as plt
@@ -447,7 +448,15 @@ def calculate_vl_scenarios_percentiles(model: mc.ExposureModel) -> typing.Dict[s
447448
scenarios = {}
448449
for percentil in (0.01, 0.05, 0.25, 0.5, 0.75, 0.95, 0.99):
449450
vl = np.quantile(viral_load, percentil)
450-
specific_vl_scenario = dataclass_utils.nested_replace(model, {'model.virus.viral_load_in_sputum': vl})
451+
452+
new_conc_model = copy.deepcopy(model.concentration_model)
453+
if isinstance(model.concentration_model, list):
454+
new_conc_model = [dataclass_utils.nested_replace(cm, {'infected.virus.viral_load_in_sputum': vl}) for cm in model.concentration_model]
455+
else:
456+
cm = model.concentration_model
457+
new_conc_model = dataclass_utils.nested_replace(cm, {'infected.virus.viral_load_in_sputum': vl})
458+
459+
specific_vl_scenario = dataclass_utils.nested_replace(model, {'concentration_model': new_conc_model})
451460
scenarios[str(vl)] = np.mean(
452461
specific_vl_scenario.infection_probability())
453462
return {

caimira/tests/test_dynamic_infected.py

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -139,19 +139,6 @@ def invalid_rooms_conc_model_list(all_infected_populations):
139139
evaporation_factor=0.3,
140140
) for vol in [75,100]]
141141

142-
@pytest.fixture
143-
def invalid_vent_conc_model_list(all_infected_populations):
144-
"""
145-
Invalid list of concentration models because the ventilations are different.
146-
"""
147-
return [models.ConcentrationModel(
148-
data_registry=data_registry,
149-
room = models.Room(75, models.PiecewiseConstant((0., 24.), (293,))),
150-
ventilation = models.AirChange(interesting_times, air_exch),
151-
infected = all_infected_populations[0],
152-
evaporation_factor=0.3,
153-
) for air_exch in [10,100]]
154-
155142
@pytest.fixture
156143
def valid_conc_model_list(all_infected_populations):
157144
return [models.ConcentrationModel(
@@ -177,22 +164,19 @@ def get_exposure_model(concentration_model_list) -> mc.ExposureModel:
177164
geographical_data=models.Cases(),
178165
)
179166

180-
def test_common_params(valid_conc_model_list, invalid_viruses_conc_model_list, invalid_rooms_conc_model_list, invalid_vent_conc_model_list):
167+
def test_common_params(valid_conc_model_list, invalid_viruses_conc_model_list, invalid_rooms_conc_model_list):
181168
"""
182-
Check that one cannot initialize an ExposureModel with ConcentrationModels
183-
with different viruses, rooms, and ventilations.
169+
Check that an error is raised when initializing an ExposureModel with ConcentrationModels
170+
with different viruses and rooms.
184171
"""
185172
with pytest.raises(ValueError):
186173
get_exposure_model(invalid_viruses_conc_model_list).build_model(1)
187174
with pytest.raises(ValueError):
188175
get_exposure_model(invalid_rooms_conc_model_list).build_model(1)
189-
with pytest.raises(ValueError):
190-
get_exposure_model(invalid_vent_conc_model_list).build_model(1)
191176

192177
valid_model = get_exposure_model(valid_conc_model_list).build_model(1)
193178
assert isinstance(valid_model.virus, models.Virus)
194179
assert isinstance(valid_model.room, models.Room)
195-
assert isinstance(valid_model.ventilation, models._VentilationBase)
196180

197181
def test_population_state_change_times(valid_conc_model_list):
198182
expected_state_changes = [0.5, 1., 1.1, 2., 3., 5., 8.5, 12, 13., 17.5]

0 commit comments

Comments
 (0)