Skip to content

Commit 5bbecd0

Browse files
committed
Finish relative time of first state inclusion
1 parent 2e2d7f6 commit 5bbecd0

File tree

8 files changed

+157
-85
lines changed

8 files changed

+157
-85
lines changed

cpp/models/abm/analyze_result.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ std::vector<Model> ensemble_params_percentile(const std::vector<std::vector<Mode
188188
return model.parameters.template get<ViralShedParameters>()[{virus_variant, age_group}];
189189
},
190190
[](auto& dist1, auto& dist2) {
191-
return dist1.virus_shed_alpha < dist2.virus_shed_alpha;
191+
return dist1.viral_shed_alpha < dist2.viral_shed_alpha;
192192
});
193193
param_percentile_dist(
194194
node, std::vector<mio::AbstractParameterDistribution>(num_runs),

cpp/models/abm/infection.cpp

Lines changed: 59 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,14 @@ namespace abm
2929
{
3030

3131
void Infection::initialize_viral_load(PersonalRandomNumberGenerator& rng, VirusVariant virus, AgeGroup age,
32-
const Parameters& params, TimePoint init_date, ProtectionEvent latest_protection)
32+
const Parameters& params, ProtectionEvent latest_protection)
3333
{
3434
auto& vl_params = params.get<ViralLoadDistributions>()[{virus, age}];
3535
ScalarType high_viral_load_factor = 1;
3636

3737
if (latest_protection.type != ProtectionType::NoProtection) {
3838
high_viral_load_factor -= params.get<HighViralLoadProtectionFactor>()[{latest_protection.type, age, virus}](
39-
init_date.days() - latest_protection.time.days());
39+
m_viral_load.start_date.days() - latest_protection.time.days());
4040
}
4141

4242
m_viral_load.peak = vl_params.viral_load_peak.get(rng) * high_viral_load_factor;
@@ -57,15 +57,56 @@ void Infection::initialize_viral_shed(PersonalRandomNumberGenerator& rng, VirusV
5757
m_individual_viral_shed_factor = shedfactor_param.get(rng);
5858
}
5959

60+
TimePoint Infection::shift_init_date(const InitialInfectionStateDistribution& init_state_dist,
61+
PersonalRandomNumberGenerator& rng, VirusVariant virus, AgeGroup age,
62+
TimePoint init_date)
63+
{
64+
auto dist = init_state_dist[{virus, age}];
65+
TimeSpan time_in_state = days(dist.get(rng));
66+
return init_date - time_in_state;
67+
}
68+
6069
Infection::Infection(PersonalRandomNumberGenerator& rng, VirusVariant virus, AgeGroup age, const Parameters& params,
6170
TimePoint init_date, InfectionState init_state, ProtectionEvent latest_protection, bool detected)
6271
: m_virus_variant(virus)
6372
, m_detected(detected)
6473
{
6574
assert(age.get() < params.get_num_groups());
66-
m_viral_load.start_date = draw_infection_course(rng, age, params, init_date, init_state, latest_protection);
75+
assert(init_state != InfectionState::Susceptible &&
76+
"Initializatin of an Infection must happen with an InfectionState that is not Suscpetible.");
77+
78+
draw_infection_course_forward(rng, age, params, init_date, init_state, latest_protection);
79+
m_viral_load.start_date = draw_infection_course_backward(rng, age, params, init_date, init_state);
80+
81+
initialize_viral_load(rng, virus, age, params, latest_protection);
82+
initialize_viral_shed(rng, virus, age, params);
83+
}
84+
85+
Infection::Infection(PersonalRandomNumberGenerator& rng, VirusVariant virus, AgeGroup age, const Parameters& params,
86+
TimePoint init_date, InfectionState init_state,
87+
const InitialInfectionStateDistribution& init_state_dist, ProtectionEvent latest_protection,
88+
bool detected)
89+
: m_virus_variant(virus)
90+
, m_detected(detected)
91+
{
92+
assert(age.get() < params.get_num_groups());
93+
assert(init_state != InfectionState::Susceptible &&
94+
"Initializatin of an Infection must happen with an InfectionState that is not Suscpetible.");
95+
96+
// Draw the first transition and time that the agent has already spent in that state
97+
StateTransition first_transition =
98+
get_forward_transition(rng, age, params, init_state, init_date, latest_protection);
99+
ScalarType relative_time_in_first_state = init_state_dist[{virus, age}].get(rng);
100+
101+
init_date -= first_transition.duration.multiply(relative_time_in_first_state);
102+
m_infection_course.push_back({init_date, first_transition.from_state});
67103

68-
initialize_viral_load(rng, virus, age, params, init_date, latest_protection);
104+
// Draw the rest of the infection course
105+
draw_infection_course_forward(rng, age, params, init_date + first_transition.duration, first_transition.to_state,
106+
latest_protection);
107+
m_viral_load.start_date = draw_infection_course_backward(rng, age, params, init_date, init_state);
108+
109+
initialize_viral_load(rng, virus, age, params, latest_protection);
69110
initialize_viral_shed(rng, virus, age, params);
70111
}
71112

@@ -131,9 +172,8 @@ TimePoint Infection::draw_infection_course(PersonalRandomNumberGenerator& rng, A
131172
TimePoint init_date, InfectionState init_state,
132173
ProtectionEvent latest_protection)
133174
{
134-
TimePoint start_of_init_state =
135-
draw_infection_course_forward(rng, age, params, init_date, init_state, latest_protection);
136-
TimePoint start_date = draw_infection_course_backward(rng, age, params, start_of_init_state, init_state);
175+
draw_infection_course_forward(rng, age, params, init_date, init_state, latest_protection);
176+
TimePoint start_date = draw_infection_course_backward(rng, age, params, init_date, init_state);
137177
return start_date;
138178
}
139179

@@ -145,10 +185,6 @@ StateTransition Infection::get_forward_transition(PersonalRandomNumberGenerator&
145185
StateTransition transition{current_state, current_state, TimeSpan{}};
146186

147187
switch (current_state) {
148-
case InfectionState::Susceptible:
149-
transition.to_state = InfectionState::Exposed;
150-
transition.duration = mio::abm::hours(0);
151-
break;
152188

153189
case InfectionState::Exposed:
154190
transition.to_state = InfectionState::InfectedNoSymptoms;
@@ -192,14 +228,14 @@ StateTransition Infection::get_forward_transition(PersonalRandomNumberGenerator&
192228
ScalarType critical_prob = params.get<CriticalPerInfectedSevere>()[{m_virus_variant, age}];
193229
ScalarType death_prob = params.get<DeathsPerInfectedSevere>()[{m_virus_variant, age}];
194230

195-
if (p < critical_prob) {
196-
transition.to_state = InfectionState::InfectedCritical;
197-
transition.duration = days(params.get<TimeInfectedSevereToCritical>()[{m_virus_variant, age}].get(rng));
198-
}
199-
else if (p < critical_prob + death_prob) {
231+
if (p < death_prob) {
200232
transition.to_state = InfectionState::Dead;
201233
transition.duration = days(params.get<TimeInfectedSevereToDead>()[{m_virus_variant, age}].get(rng));
202234
}
235+
else if (p < critical_prob + death_prob) {
236+
transition.to_state = InfectionState::InfectedCritical;
237+
transition.duration = days(params.get<TimeInfectedSevereToCritical>()[{m_virus_variant, age}].get(rng));
238+
}
203239
else {
204240
transition.to_state = InfectionState::Recovered;
205241
transition.duration = days(params.get<TimeInfectedSevereToRecovered>()[{m_virus_variant, age}].get(rng));
@@ -276,7 +312,7 @@ StateTransition Infection::get_recovered_backward_transition(PersonalRandomNumbe
276312

277313
// Compute death probability to factor it out
278314
ScalarType p_death = calculate_death_probability(age, params);
279-
assert(p_death == 1 && "Trying to create a recovered agent although the chance to die is 100%.");
315+
assert(p_death < 1 && "Trying to create a recovered agent although the chance to die is 100%.");
280316
ScalarType inv_death = 1 / (1 - p_death);
281317

282318
ScalarType symptoms_prob = params.get<SymptomsPerInfectedNoSymptoms>()[{m_virus_variant, age}];
@@ -343,40 +379,29 @@ ScalarType Infection::get_severity_protection_factor(const Parameters& params, P
343379
current_time.days() - latest_protection.time.days());
344380
}
345381

346-
TimePoint Infection::draw_infection_course_forward(PersonalRandomNumberGenerator& rng, AgeGroup age,
347-
const Parameters& params, TimePoint init_date,
348-
InfectionState start_state, ProtectionEvent latest_protection)
382+
void Infection::draw_infection_course_forward(PersonalRandomNumberGenerator& rng, AgeGroup age,
383+
const Parameters& params, TimePoint init_date, InfectionState start_state,
384+
ProtectionEvent latest_protection)
349385
{
350-
TimePoint start_of_init_state = init_date; // since there is no state transition from Recovered or Dead
351-
bool init = true; // the random start time of the first state cannot be drawn
352-
// thus, just use the init_date as the start for this
353-
auto& init_state_dist = params.get<InitialInfectionStateDistributions>()[{m_virus_variant, age, start_state}];
354-
355386
TimePoint current_time = init_date;
356387
InfectionState current_state = start_state;
357388
m_infection_course.push_back({current_time, current_state});
358389

359390
while (current_state != InfectionState::Recovered && current_state != InfectionState::Dead) {
360391
StateTransition transition =
361392
get_forward_transition(rng, age, params, current_state, current_time, latest_protection);
362-
if (init && current_state != InfectionState::Susceptible) { // random init within first time period
363-
ScalarType p = init_state_dist.get(rng);
364-
start_of_init_state -= transition.duration.multiply(1 - p);
365-
transition.duration = transition.duration.multiply(p);
366-
init = false;
367-
}
393+
368394
current_time += transition.duration;
369395
current_state = transition.to_state;
370396
m_infection_course.push_back({current_time, current_state});
371397
}
372-
return start_of_init_state;
373398
}
374399

375400
TimePoint Infection::draw_infection_course_backward(PersonalRandomNumberGenerator& rng, AgeGroup age,
376-
const Parameters& params, TimePoint start_of_init_state,
401+
const Parameters& params, TimePoint init_date,
377402
InfectionState init_state)
378403
{
379-
TimePoint current_time = start_of_init_state;
404+
TimePoint current_time = init_date;
380405
InfectionState current_state = init_state;
381406

382407
while (current_state != InfectionState::Exposed) {

cpp/models/abm/infection.h

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,13 @@ struct ViralLoad {
6767
}
6868
};
6969

70+
/**
71+
* @brief Distributions of the relative time that people have been in their initial infection state at the beginning of the simulation.
72+
* Values have to be within [0, 1].
73+
* This makes it possible to draw from a user-defined distribution instead of drawing from a uniform distribution.
74+
*/
75+
using InitialInfectionStateDistribution = CustomIndexArray<AbstractParameterDistribution, VirusVariant, AgeGroup>;
76+
7077
class Infection
7178
{
7279
public:
@@ -83,7 +90,23 @@ class Infection
8390
* @param[in] detected [Default: false] If the Infection is detected.
8491
*/
8592
Infection(PersonalRandomNumberGenerator& rng, VirusVariant virus, AgeGroup age, const Parameters& params,
86-
TimePoint start_date, InfectionState start_state = InfectionState::Susceptible,
93+
TimePoint start_date, InfectionState start_state = InfectionState::Exposed,
94+
ProtectionEvent latest_protection = {ProtectionType::NoProtection, TimePoint(0)}, bool detected = false);
95+
96+
/**
97+
* @brief Create an Infection for a single Person with a time spent in the given initial state that is drawn from the given distribution.
98+
* @param[inout] rng PersonalRandomNumberGenerator of the Person.
99+
* @param[in] virus Virus type of the Infection.
100+
* @param[in] age AgeGroup to determine the ViralLoad course.
101+
* @param[in] params Parameters of the Model.
102+
* @param[in] init_date Date of initializing the Infection.
103+
* @param[in] init_state #InfectionState at time of initializing the Infection.
104+
* @param[in] init_state_dist Distribution to draw the relative time spent in the initial state from. Values have to be within [0, 1].
105+
* @param[in] latest_protection The pair value of last ProtectionType (previous Infection/Vaccination) and TimePoint of that protection.
106+
* @param[in] detected If the Infection is detected.
107+
*/
108+
Infection(PersonalRandomNumberGenerator& rng, VirusVariant virus, AgeGroup age, const Parameters& params,
109+
TimePoint init_date, InfectionState init_state, const InitialInfectionStateDistribution& init_state_dist,
87110
ProtectionEvent latest_protection = {ProtectionType::NoProtection, TimePoint(0)}, bool detected = false);
88111

89112
/**
@@ -178,20 +201,16 @@ class Infection
178201
* InfectedCritical -> Recovered or InfectedCritical -> Dead,
179202
* until either Recoverd or Dead is reached.
180203
* The duration in each #InfectionState is taken from the respective parameter.
181-
* The first transition has a random, uniformly distributed length from 0 to the respective parameter
182-
* for persons that are initialized somewhere in the middle of the infection, i.e. not a newly infected
183-
* person nor a Recovered/Dead person, to reflect uncertainty in the current state.
184204
* @param[inout] rng PersonalRandomNumberGenerator of the Person.
185205
* @param[in] age AgeGroup of the Person.
186206
* @param[in] params Parameters of the Model.
187207
* @param[in] init_date Date of initializing the Infection.
188208
* @param[in] init_state #InfectionState at time of initializing the Infection.
189209
* @param[in] latest_protection Latest protection against Infection, has an influence on transition probabilities.
190-
* @return The starting date of the init_state.
191210
*/
192-
TimePoint draw_infection_course_forward(PersonalRandomNumberGenerator& rng, AgeGroup age, const Parameters& params,
193-
TimePoint init_date, InfectionState init_state,
194-
ProtectionEvent latest_protection);
211+
void draw_infection_course_forward(PersonalRandomNumberGenerator& rng, AgeGroup age, const Parameters& params,
212+
TimePoint init_date, InfectionState init_state,
213+
ProtectionEvent latest_protection);
195214

196215
/**
197216
* @brief Determine Infection course prior to the given #InfectionState start_state.
@@ -213,11 +232,10 @@ class Infection
213232
* @param[in] virus Virus type of the Infection.
214233
* @param[in] age AgeGroup of the Person.
215234
* @param[in] params Parameters of the Model.
216-
* @param[in] init_date Date of initializing the Infection.
217235
* @param[in] latest_protection Latest protection against Infection.
218236
*/
219237
void initialize_viral_load(PersonalRandomNumberGenerator& rng, VirusVariant virus, AgeGroup age,
220-
const Parameters& params, TimePoint init_date, ProtectionEvent latest_protection);
238+
const Parameters& params, ProtectionEvent latest_protection);
221239

222240
/**
223241
* @brief Initialize the viral shed parameters and individual factor for the infection.
@@ -229,6 +247,18 @@ class Infection
229247
void initialize_viral_shed(PersonalRandomNumberGenerator& rng, VirusVariant virus, AgeGroup age,
230248
const Parameters& params);
231249

250+
/**
251+
* @brief Adjust start date based on initial infection state distribution
252+
* @param[in] init_state_dist Distribution to draw the time spent in the initial state from.
253+
* @param[inout] rng PersonalRandomNumberGenerator of the Person.
254+
* @param[in] virus Virus type of the Infection.
255+
* @param[in] age AgeGroup of the Person.
256+
* @param[in] init_date Date of initializing the Infection.
257+
*/
258+
TimePoint shift_init_date(const InitialInfectionStateDistribution& init_state_dist,
259+
PersonalRandomNumberGenerator& rng, VirusVariant virus, AgeGroup age,
260+
TimePoint init_date);
261+
232262
/**
233263
* @brief Get the forward transition from a given infection state.
234264
* @param[inout] rng PersonalRandomNumberGenerator of the Person.

cpp/models/abm/model_functions.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ void interact(PersonalRandomNumberGenerator& personal_rng, Person& person, const
101101
local_indiv_expected_trans); // use VirusVariant::Count for no virus submission
102102
if (virus != VirusVariant::Count) {
103103
person.add_new_infection(Infection(personal_rng, virus, age_receiver, global_parameters, t + dt / 2,
104-
mio::abm::InfectionState::Susceptible,
104+
mio::abm::InfectionState::Exposed,
105105
person.get_latest_protection(t + dt / 2),
106106
false)); // Starting time in second order approximation
107107
}

cpp/models/abm/parameters.h

Lines changed: 6 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -277,23 +277,6 @@ struct DeathsPerInfectedCritical {
277277
}
278278
};
279279

280-
/**
281-
* @brief Distributions of the time that people have been in their initial infection state at the beginning of the simulation.
282-
* This makes it possible to draw from a user-defined distribution instead of drawing from a uniform distribution.
283-
*/
284-
struct InitialInfectionStateDistributions {
285-
using Type = CustomIndexArray<AbstractParameterDistribution, VirusVariant, AgeGroup, InfectionState>;
286-
static Type get_default(AgeGroup size)
287-
{
288-
return Type({VirusVariant::Count, size, InfectionState::Count},
289-
AbstractParameterDistribution(ParameterDistributionUniform(0., 1.)));
290-
}
291-
static std::string name()
292-
{
293-
return "InitialInfectionStateDistributions";
294-
}
295-
};
296-
297280
/**
298281
* @brief Parameters for the ViralLoad course. Default values taken as constant values from the average from
299282
* https://github.com/VirologyCharite/SARS-CoV-2-VL-paper/tree/main
@@ -718,13 +701,12 @@ using ParametersBase =
718701
TimeInfectedSymptomsToSevere, TimeInfectedSymptomsToRecovered, TimeInfectedSevereToCritical,
719702
TimeInfectedSevereToRecovered, TimeInfectedSevereToDead, TimeInfectedCriticalToDead,
720703
TimeInfectedCriticalToRecovered, SymptomsPerInfectedNoSymptoms, SeverePerInfectedSymptoms,
721-
CriticalPerInfectedSevere, DeathsPerInfectedSevere, DeathsPerInfectedCritical,
722-
InitialInfectionStateDistributions, ViralLoadDistributions, ViralShedParameters, ViralShedFactor,
723-
InfectionRateFromViralShed, MaskProtection, AerosolTransmissionRates, LockdownDate, QuarantineDuration,
724-
QuarantineEffectiveness, SocialEventRate, BasicShoppingRate, WorkRatio, SchoolRatio,
725-
GotoWorkTimeMinimum, GotoWorkTimeMaximum, GotoSchoolTimeMinimum, GotoSchoolTimeMaximum,
726-
AgeGroupGotoSchool, AgeGroupGotoWork, InfectionProtectionFactor, SeverityProtectionFactor,
727-
HighViralLoadProtectionFactor, TestData>;
704+
CriticalPerInfectedSevere, DeathsPerInfectedSevere, DeathsPerInfectedCritical, ViralLoadDistributions,
705+
ViralShedParameters, ViralShedFactor, InfectionRateFromViralShed, MaskProtection,
706+
AerosolTransmissionRates, LockdownDate, QuarantineDuration, QuarantineEffectiveness, SocialEventRate,
707+
BasicShoppingRate, WorkRatio, SchoolRatio, GotoWorkTimeMinimum, GotoWorkTimeMaximum,
708+
GotoSchoolTimeMinimum, GotoSchoolTimeMaximum, AgeGroupGotoSchool, AgeGroupGotoWork,
709+
InfectionProtectionFactor, SeverityProtectionFactor, HighViralLoadProtectionFactor, TestData>;
728710

729711
/**
730712
* @brief Maximum number of Person%s an infectious Person can infect at the respective Location.

0 commit comments

Comments
 (0)