model.h Source File

CPP API: model.h Source File
ode_seir_metapop/model.h
Go to the documentation of this file.
1 /*
2 * Copyright (C) 2020-2026 MEmilio
3 *
4 * Authors: Carlotta Gerstein, Martin J. Kuehn, Henrik Zunker
5 *
6 * Contact: Martin J. Kuehn <Martin.Kuehn@DLR.de>
7 *
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 */
20 #ifndef ODESEIRMETAPOP_MODEL_H
21 #define ODESEIRMETAPOP_MODEL_H
22 
23 #include <Eigen/Dense>
24 
31 
32 namespace mio
33 {
34 namespace oseirmetapop
35 {
36 
37 /********************
38 * define the model *
39 ********************/
40 
44 
48 template <typename FP = ScalarType>
49 class Model : public FlowModel<FP, InfectionState, mio::Populations<FP, mio::regions::Region, AgeGroup, InfectionState>,
50  Parameters<FP>, Flows>
51 {
52 
55 
56 public:
57  using typename Base::ParameterSet;
58  using typename Base::Populations;
59 
65  Model(int num_regions, int num_agegroups)
66  : Base(Populations({Region(num_regions), AgeGroup(num_agegroups), InfectionState::Count}),
67  ParameterSet(Region(num_regions), AgeGroup(num_agegroups)))
68  {
69  }
70 
78  void get_flows(Eigen::Ref<const Eigen::VectorX<FP>> pop, Eigen::Ref<const Eigen::VectorX<FP>> y, FP t,
79  Eigen::Ref<Eigen::VectorX<FP>> flows) const override
80  {
81  using enum InfectionState;
82  const auto& params = this->parameters;
83  const auto& population = this->populations;
84  const Eigen::MatrixXd commuting_strengths =
85  params.template get<CommutingStrengths<>>().get_cont_freq_mat().get_matrix_at(SimulationTime<FP>(t));
86  const size_t n_age_groups = (size_t)params.get_num_agegroups();
87  const size_t n_regions = (size_t)params.get_num_regions();
88 
89  Eigen::MatrixXd infected_pop(n_regions, n_age_groups);
90  for (size_t region_n = 0; region_n < n_regions; region_n++) {
91  for (size_t age_i = 0; age_i < n_age_groups; age_i++) {
92  infected_pop(region_n, age_i) =
93  pop[population.get_flat_index({Region(region_n), AgeGroup(age_i), Infected})];
94  }
95  }
96  Eigen::MatrixXd infectious_share_per_region = commuting_strengths.transpose() * infected_pop;
97  for (size_t region_n = 0; region_n < n_regions; region_n++) {
98  for (size_t age_i = 0; age_i < n_age_groups; age_i++) {
99  infectious_share_per_region(region_n, age_i) /=
100  params.template get<PopulationAfterCommuting<FP>>()[{Region(region_n), AgeGroup(age_i)}];
101  }
102  }
103  Eigen::MatrixXd infections_due_commuting = commuting_strengths * infectious_share_per_region;
104  for (size_t region = 0; region < n_regions; region++) {
105  for (size_t age_i = 0; age_i < n_age_groups; age_i++) {
106  for (size_t age_j = 0; age_j < n_age_groups; age_j++) {
107  const size_t Ejn = population.get_flat_index({Region(region), AgeGroup(age_j), Exposed});
108  const size_t Ijn = population.get_flat_index({Region(region), AgeGroup(age_j), Infected});
109  const size_t Rjn = population.get_flat_index({Region(region), AgeGroup(age_j), Recovered});
110  const size_t Sjn = population.get_flat_index({Region(region), AgeGroup(age_j), Susceptible});
111 
112  const FP Njn = pop[Sjn] + pop[Ejn] + pop[Ijn] + pop[Rjn];
113  const FP divNjn = (Njn < Limits<FP>::zero_tolerance()) ? FP(0.0) : FP(1.0 / Njn);
114  FP coeffStoE =
115  0.5 *
116  params.template get<ContactPatterns<FP>>().get_cont_freq_mat().get_matrix_at(
117  SimulationTime<FP>(t))(age_i, age_j) *
118  params.template get<TransmissionProbabilityOnContact<FP>>()[{Region(region), AgeGroup(age_i)}];
119 
120  flows[Base::template get_flat_flow_index<Susceptible, Exposed>(
121  {Region(region), AgeGroup(age_i)})] +=
122  (pop[Ijn] * divNjn + infections_due_commuting(region, age_j)) * coeffStoE *
123  y[population.get_flat_index({Region(region), AgeGroup(age_i), Susceptible})];
124  }
125  flows[Base::template get_flat_flow_index<Exposed, Infected>({Region(region), AgeGroup(age_i)})] =
126  y[population.get_flat_index({Region(region), AgeGroup(age_i), Exposed})] /
127  params.template get<TimeExposed<FP>>()[{Region(region), AgeGroup(age_i)}];
128  flows[Base::template get_flat_flow_index<Infected, Recovered>({Region(region), AgeGroup(age_i)})] =
129  y[population.get_flat_index({Region(region), AgeGroup(age_i), Infected})] /
130  params.template get<TimeInfected<FP>>()[{Region(region), AgeGroup(age_i)}];
131  }
132  }
133  }
134 
142  {
143  if (!(t_idx < static_cast<size_t>(y.get_num_time_points()))) {
144  return mio::failure(mio::StatusCode::OutOfRange, "t_idx is not a valid index for the TimeSeries");
145  }
146 
147  auto const& params = this->parameters;
148  auto const& pop = this->populations;
149 
150  const size_t num_age_groups = (size_t)params.get_num_agegroups();
151  const size_t num_regions = (size_t)params.get_num_regions();
152  constexpr size_t num_infected_compartments = 2;
153  const size_t total_infected_compartments = num_infected_compartments * num_age_groups * num_regions;
154 
155  ContactMatrixGroup<ScalarType> const& contact_matrix = params.template get<ContactPatterns<ScalarType>>();
156  ContactMatrixGroup<ScalarType> const& commuting_strengths =
157  params.template get<CommutingStrengths<ScalarType>>();
158  auto const& population_after_commuting = params.template get<PopulationAfterCommuting<ScalarType>>();
159 
160  Eigen::MatrixXd F = Eigen::MatrixXd::Zero(total_infected_compartments, total_infected_compartments);
161  Eigen::MatrixXd V = Eigen::MatrixXd::Zero(total_infected_compartments, total_infected_compartments);
162 
163  for (auto i = AgeGroup(0); i < AgeGroup(num_age_groups); i++) {
164  for (auto n = Region(0); n < Region(num_regions); n++) {
165  size_t Si = pop.get_flat_index({n, i, InfectionState::Susceptible});
166  for (auto j = AgeGroup(0); j < AgeGroup(num_age_groups); j++) {
167  for (auto m = Region(0); m < Region(num_regions); m++) {
168  auto const population_region = pop.template slice<Region>({m.get(), 1});
169  auto const population_region_age = population_region.template slice<AgeGroup>({j.get(), 1});
170  auto Njm = std::accumulate(population_region_age.begin(), population_region_age.end(), 0.);
171 
172  double coeffStoE =
173  0.5 *
174  contact_matrix.get_matrix_at(SimulationTime<FP>(y.get_time(t_idx)))(i.get(), j.get()) *
175  params.template get<TransmissionProbabilityOnContact<ScalarType>>()[{n, i}];
176  if (n == m) {
177  F(i.get() * num_regions + n.get(), num_age_groups * num_regions + j.get() * num_regions +
178  m.get()) += coeffStoE * y.get_value(t_idx)[Si] / Njm;
179  }
180  for (auto k = Region(0); k < Region(num_regions); k++) {
181  F(i.get() * num_regions + n.get(),
182  num_age_groups * num_regions + j.get() * num_regions + m.get()) +=
183  coeffStoE * y.get_value(t_idx)[Si] *
184  commuting_strengths.get_matrix_at(SimulationTime<FP>(y.get_time(t_idx)))(n.get(),
185  k.get()) *
186  commuting_strengths.get_matrix_at(SimulationTime<FP>(y.get_time(t_idx)))(m.get(),
187  k.get()) /
188  population_after_commuting[{k, j}];
189  }
190  }
191  }
192 
193  double T_Ei = params.template get<TimeExposed<ScalarType>>()[{n, i}];
194  double T_Ii = params.template get<TimeInfected<ScalarType>>()[{n, i}];
195  V(i.get() * num_regions + n.get(), i.get() * num_regions + n.get()) = 1.0 / T_Ei;
196  V(num_age_groups * num_regions + i.get() * num_regions + n.get(), i.get() * num_regions + n.get()) =
197  -1.0 / T_Ei;
198  V(num_age_groups * num_regions + i.get() * num_regions + n.get(),
199  num_age_groups * num_regions + i.get() * num_regions + n.get()) = 1.0 / T_Ii;
200  }
201  }
202 
203  V = V.inverse();
204 
205  Eigen::MatrixXd NextGenMatrix = Eigen::MatrixXd::Zero(total_infected_compartments, total_infected_compartments);
206  NextGenMatrix = F * V;
207 
208  //Compute the largest eigenvalue in absolute value
209  Eigen::ComplexEigenSolver<Eigen::MatrixX<ScalarType>> ces;
210  ces.compute(NextGenMatrix);
211  FP rho = ces.eigenvalues().cwiseAbs().maxCoeff();
212 
213  return mio::success(rho);
214  }
215 
222  {
223  auto num_time_points = y.get_num_time_points();
224  Eigen::VectorXd temp(num_time_points);
225  for (size_t i = 0; i < static_cast<size_t>(num_time_points); i++) {
226  temp[i] = get_reproduction_number(i, y).value();
227  }
228  return temp;
229  }
230 
235  void set_commuting_strengths(const Eigen::MatrixXd& commuting_strengths)
236  {
237  auto& commuting_strengths_param =
238  this->parameters.template get<CommutingStrengths<FP>>().get_cont_freq_mat()[0].get_baseline();
239  commuting_strengths_param = commuting_strengths;
240 
241  auto number_regions = (size_t)this->parameters.get_num_regions();
242  auto number_age_groups = (size_t)this->parameters.get_num_agegroups();
243  auto& population = this->populations;
244  mio::Populations<FP, Region, AgeGroup> population_after_commuting(
245  {Region(number_regions), AgeGroup(number_age_groups)}, 0.);
246 
247  for (size_t region_n = 0; region_n < number_regions; ++region_n) {
248  for (size_t age = 0; age < number_age_groups; ++age) {
249  double population_n = 0;
250  for (size_t state = 0; state < (size_t)InfectionState::Count; state++) {
251  population_n += population[{Region(region_n), mio::AgeGroup(age), InfectionState(state)}];
252  }
253  population_after_commuting[{Region(region_n), mio::AgeGroup(age)}] += population_n;
254  for (size_t region_m = 0; region_m < number_regions; ++region_m) {
255  population_after_commuting[{Region(region_n), mio::AgeGroup(age)}] -=
256  commuting_strengths(region_n, region_m) * population_n;
257  population_after_commuting[{Region(region_m), mio::AgeGroup(age)}] +=
258  commuting_strengths(region_n, region_m) * population_n;
259  }
260  }
261  }
262  this->parameters.template get<PopulationAfterCommuting<FP>>() = population_after_commuting;
263  }
264 
271  {
272  auto number_regions = (size_t)this->parameters.get_num_regions();
273  set_commuting_strengths(Eigen::MatrixXd::Identity(number_regions, number_regions));
274  }
275 
280  template <class IOContext>
281  void serialize(IOContext& io) const
282  {
283  auto obj = io.create_object("Model");
284  obj.add_element("Parameters", this->parameters);
285  obj.add_element("Populations", this->populations);
286  }
287 
292  template <class IOContext>
293  static IOResult<Model> deserialize(IOContext& io)
294  {
295  auto obj = io.expect_object("Model");
296  auto par = obj.expect_element("Parameters", Tag<ParameterSet>{});
297  auto pop = obj.expect_element("Populations", Tag<Populations>{});
298  return apply(
299  io,
300  [](auto&& par_, auto&& pop_) {
301  return Model{pop_, par_};
302  },
303  par, pop);
304  }
305 };
306 
307 } // namespace oseirmetapop
308 } // namespace mio
309 
310 #endif // SEIRMETAPOP_MODEL_H
represents a collection of contact frequency matrices that whose sum is the total number of contacts.
Definition: contact_matrix.h:536
A FlowModel is a CompartmentalModel defined by the flows between compartments.
Definition: flow_model.h:74
A class template for compartment populations.
Definition: populations.h:55
Typesafe wrapper for a floating-point simulation time value (in days).
Definition: damping.h:78
Eigen::Ref< const Vector > get_value(Eigen::Index i) const
reference to value vector at time point i
Definition: time_series.h:298
Eigen::Index get_num_time_points() const
number of time points in the series
Definition: time_series.h:197
FP & get_time(Eigen::Index i)
time of time point at index i
Definition: time_series.h:272
The Model holds the Parameters and the initial Populations for every region and defines the ordinary ...
Definition: ode_seir_metapop/model.h:51
void set_commuting_strengths(const Eigen::MatrixXd &commuting_strengths)
Set the CommutingStrengths matrix and update the PopulationAfterCommuting.
Definition: ode_seir_metapop/model.h:235
void serialize(IOContext &io) const
serialize this.
Definition: ode_seir_metapop/model.h:281
void get_flows(Eigen::Ref< const Eigen::VectorX< FP >> pop, Eigen::Ref< const Eigen::VectorX< FP >> y, FP t, Eigen::Ref< Eigen::VectorX< FP >> flows) const override
Compute the values of the flows between compartments at a given time.
Definition: ode_seir_metapop/model.h:78
Eigen::VectorXd get_reproduction_numbers(const mio::TimeSeries< ScalarType > &y)
Computes the reproduction number for all time points of the Model output obtained by the Simulation.
Definition: ode_seir_metapop/model.h:221
Model(int num_regions, int num_agegroups)
Create a Model with the given number of Regions and AgeGroups.
Definition: ode_seir_metapop/model.h:65
static IOResult< Model > deserialize(IOContext &io)
deserialize an object of this class.
Definition: ode_seir_metapop/model.h:293
void set_commuting_strengths()
Set the CommutingStrengths matrix without data.
Definition: ode_seir_metapop/model.h:270
IOResult< ScalarType > get_reproduction_number(size_t t_idx, const mio::TimeSeries< ScalarType > &y)
Computes the reproduction number at a given index time of the Model output obtained by the Simulation...
Definition: ode_seir_metapop/model.h:141
Parameters of the SEIR metapopulation model.
Definition: ode_seir_metapop/parameters.h:153
InfectionState
InfectionState in ABM.
Definition: abm/infection_state.h:35
IOResult< FP > get_reproduction_number(size_t t_idx, const Simulation< FP, Base > &sim)
Computes the reproduction number at a given index time of the Model output obtained by the Simulation...
Definition: ode_secir/model.h:434
mio::regions::Region Region
Definition: ode_seir_metapop/parameters.h:37
A collection of classes to simplify handling of matrix shapes in meta programming.
Definition: models/abm/analyze_result.h:30
auto failure(const IOStatus &s)
Create an object that is implicitly convertible to an error IOResult<T>.
Definition: io.h:381
boost::outcome_v2::in_place_type_t< T > Tag
Type that is used for overload resolution.
Definition: io.h:408
auto i
Definition: io.h:810
details::ApplyResultT< F, T... > apply(IOContext &io, F f, const IOResult< T > &... rs)
Evaluate a function with zero or more unpacked IOResults as arguments.
Definition: io.h:482
auto success()
Create an object that is implicitly convertible to a succesful IOResult<void>.
Definition: io.h:360
constexpr std::tuple_element< I, std::tuple< Index< CategoryTags >... > >::type & get(Index< CategoryTags... > &i) noexcept
Retrieves the Index (by reference) at the Ith position of a MultiIndex.
Definition: index.h:304
boost::outcome_v2::unchecked< T, IOStatus > IOResult
Value-or-error type for operations that return a value but can fail.
Definition: io.h:354
Typesafe index representing an age group.
Definition: age_group.h:40
Populations populations
Definition: compartmental_model.h:156
Pop Populations
Definition: compartmental_model.h:62
Params ParameterSet
Definition: compartmental_model.h:63
ParameterSet parameters
Definition: compartmental_model.h:157
A Flow defines a possible transition between two Compartments in a FlowModel.
Definition: flow.h:33
Collection of types. Each type is mapped to an index of type size_t.
Definition: type_list.h:32
The latent time in day unit.
Definition: ode_seir_metapop/parameters.h:63
The infectious time in day unit.
Definition: ode_seir_metapop/parameters.h:79
Probability of getting infected from a contact.
Definition: ode_seir_metapop/parameters.h:47
Index for enumerating subregions (cities, counties, etc.) of the modelled area.
Definition: regions.h:37