Skip to content

Commit

Permalink
incidence rate for dynamic exposed occupancy
Browse files Browse the repository at this point in the history
  • Loading branch information
lrdossan committed Jun 30, 2023
1 parent a59463c commit bacc6ba
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 22 deletions.
36 changes: 24 additions & 12 deletions caimira/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1670,33 +1670,45 @@ def _infection_probability_list(self):
@method_cache
def infection_probability(self) -> _VectorisedFloat:
return (1 - np.prod([1 - prob for prob in self._infection_probability_list()], axis = 0)) * 100

def total_probability_rule(self) -> _VectorisedFloat:
if (isinstance(self.concentration_model.infected.number, IntPiecewiseConstant) or
isinstance(self.exposed.number, IntPiecewiseConstant)):
raise NotImplementedError("Cannot compute total probability "
"(including incidence rate) with dynamic occupancy")

if (self.geographical_data.geographic_population != 0 and self.geographical_data.geographic_cases != 0):
def _total_probability_rule_list(self):
dynamic_sum_probability = []
population_change_times = self.population_state_change_times()
for start, stop in zip(population_change_times[:-1], population_change_times[1:]):
sum_probability = 0.0

# Create an equivalent exposure model but changing the number of infected cases.
total_people = self.concentration_model.infected.number + self.exposed.number
total_people = self.concentration_model.infected.people_present(stop) + self.exposed.people_present(stop)
max_num_infected = (total_people if total_people < 10 else 10)
# The influence of a higher number of simultainious infected people (> 4 - 5) yields an almost negligible contirbution to the total probability.
# The influence of a higher number of simultainious infected people (> 4 - 5) yields an almost negligible contribution to the total probability.
# To be on the safe side, a hard coded limit with a safety margin of 2x was set.
# Therefore we decided a hard limit of 10 infected people.
for num_infected in range(1, max_num_infected + 1):
exposure_model = nested_replace(
self, {'concentration_model.infected.number': num_infected}
self, {
'concentration_model.infected': InfectedPopulation(
number=num_infected,
presence=SpecificInterval(((start, stop),)),
mask=self.concentration_model.infected.mask,
activity=self.concentration_model.infected.activity,
host_immunity=self.concentration_model.infected.host_immunity,
virus=self.concentration_model.infected.virus,
expiration=self.concentration_model.infected.expiration,
),
},
)
prob_ind = exposure_model.infection_probability().mean() / 100
n = total_people - num_infected
# By means of the total probability rule
prob_at_least_one_infected = 1 - (1 - prob_ind)**n
sum_probability += (prob_at_least_one_infected *
self.geographical_data.probability_meet_infected_person(self.concentration_model.infected.virus, num_infected, total_people))
return sum_probability * 100
dynamic_sum_probability.append(sum_probability)
return dynamic_sum_probability

@method_cache
def total_probability_rule(self) -> _VectorisedFloat:
if (self.geographical_data.geographic_population != 0 and self.geographical_data.geographic_cases != 0):
return (1 - np.prod([1 - prob for prob in self._total_probability_rule_list()], axis = 0)) * 100
else:
return 0

Expand Down
21 changes: 11 additions & 10 deletions caimira/tests/models/test_dynamic_population.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,11 @@ def full_exposure_model():
activity=models.Activity.types['Seated'],
host_immunity=0.
),
geographical_data=(),
geographical_data=models.Cases(
geographic_population=100000,
geographic_cases=68,
ascertainment_bias=5,
),
)


Expand Down Expand Up @@ -210,19 +214,15 @@ def test_infection_probability(


def test_dynamic_total_probability_rule(
full_exposure_model: models.ExposureModel,
dynamic_infected_single_exposure_model: models.ExposureModel,
dynamic_exposed_single_exposure_model: models.ExposureModel,
dynamic_population_exposure_model: models.ExposureModel):

with pytest.raises(NotImplementedError, match=re.escape("Cannot compute total probability "
"(including incidence rate) with dynamic occupancy")):
dynamic_infected_single_exposure_model.total_probability_rule()
with pytest.raises(NotImplementedError, match=re.escape("Cannot compute total probability "
"(including incidence rate) with dynamic occupancy")):
dynamic_exposed_single_exposure_model.total_probability_rule()
with pytest.raises(NotImplementedError, match=re.escape("Cannot compute total probability "
"(including incidence rate) with dynamic occupancy")):
dynamic_population_exposure_model.total_probability_rule()
assert full_exposure_model.total_probability_rule() == dynamic_population_exposure_model.total_probability_rule()
assert full_exposure_model.total_probability_rule() == dynamic_infected_single_exposure_model.total_probability_rule()
assert full_exposure_model.total_probability_rule() == dynamic_exposed_single_exposure_model.total_probability_rule()


def test_dynamic_expected_new_cases(
dynamic_infected_single_exposure_model: models.ExposureModel,
Expand All @@ -239,6 +239,7 @@ def test_dynamic_expected_new_cases(
"with dynamic occupancy")):
dynamic_population_exposure_model.expected_new_cases()


def test_dynamic_reproduction_number(
dynamic_infected_single_exposure_model: models.ExposureModel,
dynamic_exposed_single_exposure_model: models.ExposureModel,
Expand Down

0 comments on commit bacc6ba

Please sign in to comment.