Skip to content

Commit 65f0b3f

Browse files
977 allow flexible start times in IDE model (#979)
-The start time of the simulation is not assumed to be zero anymore but is determined by the initial flows given as input to the model. Co-authored-by: lenaploetzke <70579874+lenaploetzke@users.noreply.github.com>
1 parent 579ea34 commit 65f0b3f

File tree

4 files changed

+176
-108
lines changed

4 files changed

+176
-108
lines changed

cpp/models/ide_secir/model.cpp

Lines changed: 37 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,10 @@ Model::Model(TimeSeries<ScalarType>&& init, ScalarType N_init, ScalarType deaths
3939
{
4040
m_deaths_before =
4141
deaths - m_transitions.get_last_value()[Eigen::Index(InfectionTransition::InfectedCriticalToDead)];
42+
// Add first time point in m_populations according to last time point in m_transitions which is where we start the simulation.
4243
m_populations.add_time_point<Eigen::VectorXd>(
43-
0, TimeSeries<ScalarType>::Vector::Constant((int)InfectionState::Count, 0));
44+
m_transitions.get_last_time(), TimeSeries<ScalarType>::Vector::Constant((int)InfectionState::Count, 0));
45+
// Set deaths at simulation start time t0.
4446
m_populations[Eigen::Index(0)][Eigen::Index(InfectionState::Dead)] = deaths;
4547
}
4648

@@ -69,7 +71,7 @@ void Model::initialize(ScalarType dt)
6971
else if (m_populations[Eigen::Index(0)][Eigen::Index(InfectionState::Susceptible)] > 1e-12) {
7072
// Take initialized value for Susceptibles if value can't be calculated via the standard formula.
7173
m_initialization_method = 2;
72-
// Calculate other compartment sizes for t=0.
74+
// Calculate other compartment sizes for start time t0.
7375
other_compartments_current_timestep(dt);
7476

7577
// R; need an initial value for R, therefore do not calculate via compute_recovered()
@@ -84,8 +86,8 @@ void Model::initialize(ScalarType dt)
8486
}
8587
else if (m_populations[Eigen::Index(0)][Eigen::Index(InfectionState::Recovered)] > 1e-12) {
8688
// If value for Recovered is initialized and standard method is not applicable (i.e., no value for total infections
87-
// or suceptibles given directly), calculate Susceptibles via other compartments.
88-
// The calculation of other compartments' values is not dependent on Susceptibles at time 0, i.e., S(0), but only on the transitions of the past.
89+
// or Susceptibles given directly), calculate Susceptibles via other compartments.
90+
// The calculation of other compartments' values is not dependent on Susceptibles at time t0, i.e., S(t0), but only on the transitions of the past.
8991
m_initialization_method = 3;
9092
other_compartments_current_timestep(dt);
9193

@@ -99,9 +101,9 @@ void Model::initialize(ScalarType dt)
99101
m_populations[Eigen::Index(0)][Eigen::Index(InfectionState::Dead)];
100102
}
101103
else {
102-
// compute Susceptibles at time 0 and m_forceofinfection at time -dt as initial values for discretization scheme
103-
// use m_forceofinfection at -dt to be consistent with further calculations of S (see compute_susceptibles()),
104-
// where also the value of m_forceofinfection for the previous timestep is used
104+
// Compute Susceptibles at t0 and m_forceofinfection at time t0-dt as initial values for discretization scheme.
105+
// Use m_forceofinfection at t0-dt to be consistent with further calculations of S (see compute_susceptibles()),
106+
// where also the value of m_forceofinfection for the previous timestep is used.
105107
update_forceofinfection(dt, true);
106108
if (m_forceofinfection > 1e-12) {
107109
m_initialization_method = 4;
@@ -115,10 +117,10 @@ void Model::initialize(ScalarType dt)
115117
m_transitions.get_last_value()[Eigen::Index(InfectionTransition::SusceptibleToExposed)] /
116118
(dt * m_forceofinfection);
117119

118-
//calculate other compartment sizes for t=0
120+
// Calculate other compartment sizes for t0.
119121
other_compartments_current_timestep(dt);
120122

121-
//R; need an initial value for R, therefore do not calculate via compute_recovered()
123+
// R; need an initial value for R, therefore do not calculate via compute_recovered().
122124
m_populations[Eigen::Index(0)][Eigen::Index(InfectionState::Recovered)] =
123125
m_N - m_populations[Eigen::Index(0)][Eigen::Index(InfectionState::Susceptible)] -
124126
m_populations[Eigen::Index(0)][Eigen::Index(InfectionState::Exposed)] -
@@ -148,15 +150,15 @@ void Model::initialize(ScalarType dt)
148150
}
149151
}
150152

151-
// Compute m_forceofinfection at time 0 needed for further simulation.
153+
// Compute m_forceofinfection at time t0 needed for further simulation.
152154
update_forceofinfection(dt);
153155
}
154156

155157
void Model::compute_susceptibles(ScalarType dt)
156158
{
157159
Eigen::Index num_time_points = m_populations.get_num_time_points();
158-
// using number of susceptibles from previous time step and force of infection from previous time step:
159-
// compute current number of susceptibles and store susceptibles in m_populations
160+
// Using number of Susceptibles from previous time step and force of infection from previous time step:
161+
// compute current number of Susceptibles and store Susceptibles in m_populations
160162
m_populations.get_last_value()[Eigen::Index(InfectionState::Susceptible)] =
161163
m_populations[num_time_points - 2][Eigen::Index(InfectionState::Susceptible)] / (1 + dt * m_forceofinfection);
162164
}
@@ -181,7 +183,7 @@ void Model::compute_flow(int idx_InfectionTransitions, Eigen::Index idx_Incoming
181183

182184
ScalarType state_age = (num_time_points - 1 - i) * dt;
183185

184-
// backward difference scheme to approximate first derivative
186+
// Use backward difference scheme to approximate first derivative.
185187
sum += (parameters.get<TransitionDistributions>()[idx_InfectionTransitions].eval(state_age) -
186188
parameters.get<TransitionDistributions>()[idx_InfectionTransitions].eval(state_age - dt)) /
187189
dt * m_transitions[i + 1][idx_IncomingFlow];
@@ -193,35 +195,35 @@ void Model::compute_flow(int idx_InfectionTransitions, Eigen::Index idx_Incoming
193195

194196
void Model::flows_current_timestep(ScalarType dt)
195197
{
196-
// calculate flow from S to E with force of infection from previous time step und susceptibles from current time step
198+
// Calculate flow from S to E with force of infection from previous time step und susceptibles from current time step.
197199
m_transitions.get_last_value()[Eigen::Index(InfectionTransition::SusceptibleToExposed)] =
198200
dt * m_forceofinfection * m_populations.get_last_value()[Eigen::Index(InfectionState::Susceptible)];
199-
// calculate all other flows with compute_flow
200-
// flow from E to C
201+
// Calculate all other flows with compute_flow.
202+
// Flow from E to C
201203
compute_flow((int)InfectionTransition::ExposedToInfectedNoSymptoms,
202204
Eigen::Index(InfectionTransition::SusceptibleToExposed), dt);
203-
// flow from C to I
205+
// Flow from C to I
204206
compute_flow((int)InfectionTransition::InfectedNoSymptomsToInfectedSymptoms,
205207
Eigen::Index(InfectionTransition::ExposedToInfectedNoSymptoms), dt);
206-
// flow from C to R
208+
// Flow from C to R
207209
compute_flow((int)InfectionTransition::InfectedNoSymptomsToRecovered,
208210
Eigen::Index(InfectionTransition::ExposedToInfectedNoSymptoms), dt);
209-
// flow from I to H
211+
// Flow from I to H
210212
compute_flow((int)InfectionTransition::InfectedSymptomsToInfectedSevere,
211213
Eigen::Index(InfectionTransition::InfectedNoSymptomsToInfectedSymptoms), dt);
212-
// flow from I to R
214+
// Flow from I to R
213215
compute_flow((int)InfectionTransition::InfectedSymptomsToRecovered,
214216
Eigen::Index(InfectionTransition::InfectedNoSymptomsToInfectedSymptoms), dt);
215-
// flow from H to U
217+
// Flow from H to U
216218
compute_flow((int)InfectionTransition::InfectedSevereToInfectedCritical,
217219
Eigen::Index(InfectionTransition::InfectedSymptomsToInfectedSevere), dt);
218-
// flow from to H to R
220+
// Flow from to H to R
219221
compute_flow((int)InfectionTransition::InfectedSevereToRecovered,
220222
Eigen::Index(InfectionTransition::InfectedSymptomsToInfectedSevere), dt);
221-
// flow from U to D
223+
// Flow from U to D
222224
compute_flow((int)InfectionTransition::InfectedCriticalToDead,
223225
Eigen::Index(InfectionTransition::InfectedSevereToInfectedCritical), dt);
224-
// flow from U to R
226+
// Flow from U to R
225227
compute_flow((int)InfectionTransition::InfectedCriticalToRecovered,
226228
Eigen::Index(InfectionTransition::InfectedSevereToInfectedCritical), dt);
227229
}
@@ -239,7 +241,7 @@ void Model::update_forceofinfection(ScalarType dt, bool initialization)
239241
{
240242
m_forceofinfection = 0;
241243

242-
// determine the relevant calculation area = union of the supports of the relevant transition distributions
244+
// Determine the relevant calculation area = union of the supports of the relevant transition distributions.
243245
ScalarType calc_time = std::max(
244246
{parameters.get<TransitionDistributions>()[(int)InfectionTransition::InfectedNoSymptomsToInfectedSymptoms]
245247
.get_support_max(dt, m_tol),
@@ -250,7 +252,7 @@ void Model::update_forceofinfection(ScalarType dt, bool initialization)
250252
parameters.get<TransitionDistributions>()[(int)InfectionTransition::InfectedSymptomsToRecovered]
251253
.get_support_max(dt, m_tol)});
252254

253-
// corresponding index
255+
// Corresponding index.
254256
/* need calc_time_index timesteps in sum,
255257
subtract 1 because in the last summand all TransitionDistributions evaluate to 0 (by definition of support_max)*/
256258
Eigen::Index calc_time_index = (Eigen::Index)std::ceil(calc_time / dt) - 1;
@@ -260,13 +262,14 @@ void Model::update_forceofinfection(ScalarType dt, bool initialization)
260262
ScalarType deaths;
261263

262264
if (initialization) {
263-
// determine m_forceofinfection at time -dt which is the penultimate timepoint in m_transitions
265+
// Determine m_forceofinfection at time t0-dt which is the penultimate timepoint in m_transitions.
264266
num_time_points = m_transitions.get_num_time_points() - 1;
265-
current_time = -dt;
266-
deaths = m_deaths_before;
267+
// Get time of penultimate timepoint in m_transitions.
268+
current_time = m_transitions.get_time(num_time_points - 1);
269+
deaths = m_deaths_before;
267270
}
268271
else {
269-
// determine m_forceofinfection for current last time in m_transitions.
272+
// Determine m_forceofinfection for current last time in m_transitions.
270273
num_time_points = m_transitions.get_num_time_points();
271274
current_time = m_transitions.get_last_time();
272275
deaths = m_populations.get_last_value()[Eigen::Index(InfectionState::Dead)];
@@ -309,7 +312,7 @@ void Model::compute_compartment(Eigen::Index idx_InfectionState, Eigen::Index id
309312
{
310313
ScalarType sum = 0;
311314

312-
// determine relevant calculation area and corresponding index
315+
// Determine relevant calculation area and corresponding index.
313316
ScalarType calc_time =
314317
std::max(parameters.get<TransitionDistributions>()[idx_TransitionDistribution1].get_support_max(dt, m_tol),
315318
parameters.get<TransitionDistributions>()[idx_TransitionDistribution2].get_support_max(dt, m_tol));
@@ -335,10 +338,10 @@ void Model::compute_compartment(Eigen::Index idx_InfectionState, Eigen::Index id
335338
void Model::other_compartments_current_timestep(ScalarType dt)
336339
{
337340
// E
341+
// idx_TransitionDistribution2 is a dummy index as there is no transition from E to R in our model,
342+
// write any transition here as probability of going from E to R is 0.
338343
compute_compartment(Eigen::Index(InfectionState::Exposed), Eigen::Index(InfectionTransition::SusceptibleToExposed),
339-
(int)InfectionTransition::ExposedToInfectedNoSymptoms, 0,
340-
dt); // this is a dummy index as there is no transition from E to R in our model,
341-
// write any transition here as probability from E to R is 0
344+
(int)InfectionTransition::ExposedToInfectedNoSymptoms, 0, dt);
342345
// C
343346
compute_compartment(Eigen::Index(InfectionState::InfectedNoSymptoms),
344347
Eigen::Index(InfectionTransition::ExposedToInfectedNoSymptoms),

0 commit comments

Comments
 (0)