flow_model.h Source File

CPP API: flow_model.h Source File
flow_model.h
Go to the documentation of this file.
1 /*
2 * Copyright (C) 2020-2026 MEmilio
3 *
4 * Authors: Rene Schmieding, 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 
21 #ifndef MIO_COMPARTMENTS_FLOW_MODEL_H
22 #define MIO_COMPARTMENTS_FLOW_MODEL_H
23 
26 #include "memilio/utils/flow.h"
27 #include "memilio/utils/type_list.h" // IWYU pragma: keep for easier flow definitions
28 
29 namespace mio
30 {
31 
32 namespace details
33 {
34 // The following functions are not defined anywhere. Their use is to provide type conversions via their return type.
35 
36 // Function declaration used to remove OmittedTag from the type list of a tuple.
37 // First a list of tuples is generated for each Tag in Tags, where the tuple is either of type tuple<Tag>, or if
38 // Tag == OmittedTag, of type tuple<>. This list is then concatenated, effectively removing OmittedTag.
39 template <class OmittedTag, class... Tags>
40 decltype(std::tuple_cat(
41  std::declval<std::conditional_t<std::is_same_v<OmittedTag, Tags>, std::tuple<>, std::tuple<Tags>>>()...))
42  filter_tuple(std::tuple<Tags...>);
43 
44 // Function declaration used to replace type T by std::tuple.
45 template <template <class...> class T, class... Args>
46 std::tuple<Args...> as_tuple(T<Args...>);
47 
48 // Function declaration used to replace std::tuple by type T.
49 template <template <class...> class T, class... Args>
50 T<Args...> as_index(std::tuple<Args...>);
51 
52 // Remove all occurrences of OmittedTag from the types in a std::tuple<types...>.
53 template <class OmittedTag, class Tuple>
54 using filtered_tuple_t = decltype(filter_tuple<OmittedTag>(std::declval<Tuple>()));
55 
56 // Remove all occurrences of OmittedTag from the types in an Index = IndexTemplate<types...>.
57 template <class OmittedTag, template <class...> class IndexTemplate, class Index>
58 using filtered_index_t = decltype(as_index<IndexTemplate>(
59  std::declval<filtered_tuple_t<OmittedTag, decltype(as_tuple(std::declval<Index>()))>>()));
60 
61 } //namespace details
62 
72 template <typename FP, class Comp, class Pop, class Params, class Flows>
73 class FlowModel : public CompartmentalModel<FP, Comp, Pop, Params>
74 {
75  using PopIndex = typename Pop::Index;
76  // FlowIndex is the same as PopIndex without the category Comp. It is used as argument type for
77  // get_flat_flow_index, since a flow is used to determine only the compartment (i.e. Index<Comp>) for the
78  // population of the model.
79  // The remaining indices (those contained in FlowIndex) must still be provided to get an entry of population.
80  // This approach only works with exactly one category of type Comp in PopIndex (hence the assertion below).
82  // Enforce that Comp is a unique Category of PopIndex, since we use Flows (via their source/target) to provide
83  // Comp indices for the population.
84  static_assert(FlowIndex::size == PopIndex::size - 1, "Compartments must be used exactly once as population index.");
85  static_assert(!Flows::has_duplicates, "Flow duplicates detected. Flows must be unique.");
86 
87 public:
92  template <class... Args>
93  FlowModel(Args... args)
94  : CompartmentalModel<FP, Comp, Pop, Params>(args...)
95  , m_flow_values((this->populations.numel() / static_cast<size_t>(Comp::Count)) * Flows::size())
96  {
97  }
98 
99  // Note: use get_flat_flow_index when accessing flows
100  // Note: by convention, we compute incoming flows, thus entries in flows must be non-negative
101  virtual void get_flows(Eigen::Ref<const Eigen::VectorX<FP>> /*pop*/, Eigen::Ref<const Eigen::VectorX<FP>> /*y*/,
102  FP /*t*/, Eigen::Ref<Eigen::VectorX<FP>> /*flows*/) const = 0;
103 
112  void get_derivatives(Eigen::Ref<const Eigen::VectorX<FP>> flows, Eigen::Ref<Eigen::VectorX<FP>> dydt) const
113  {
114  // set dydt to 0, then iteratively add all flow contributions
115  dydt.setZero();
116  if constexpr (std::is_same_v<FlowIndex, Index<>>) {
117  // special case where PopIndex only contains Comp, hence FlowIndex has no dimensions to iterate over
118  get_rhs_impl(flows, dydt, Index<>{});
119  }
120  else {
121  for (FlowIndex I : reduce_index<FlowIndex>(this->populations.size())) {
122  get_rhs_impl(flows, dydt, I);
123  }
124  }
125  }
126 
138  void get_derivatives(Eigen::Ref<const Eigen::VectorX<FP>> pop, Eigen::Ref<const Eigen::VectorX<FP>> y, FP t,
139  Eigen::Ref<Eigen::VectorX<FP>> dydt) const final
140  {
141  m_flow_values.setZero();
142  get_flows(pop, y, t, m_flow_values);
143  get_derivatives(m_flow_values, dydt);
144  }
145 
151  Eigen::VectorX<FP> get_initial_flows() const
152  {
153  return Eigen::VectorX<FP>::Zero((this->populations.numel() / static_cast<size_t>(Comp::Count)) * Flows::size());
154  }
155 
181  template <Comp Source, Comp Target>
182  size_t get_flat_flow_index(const FlowIndex& indices) const
183  {
184  if constexpr (std::is_same_v<FlowIndex, Index<>>) {
185  return get_flat_flow_index<Source, Target>();
186  }
187  else {
188  const FlowIndex flow_index_dimensions = reduce_index<FlowIndex>(this->populations.size());
189  return flatten_index(indices, flow_index_dimensions) * Flows::size() +
190  index_of_type_v<Flow<Source, Target>, Flows>;
191  }
192  }
193 
200  template <Comp Source, Comp Target>
201  constexpr size_t get_flat_flow_index() const
202  {
203  static_assert(std::is_same_v<FlowIndex, Index<>>, "Other indices must be specified");
204  return index_of_type_v<Flow<Source, Target>, Flows>;
205  }
206 
207 private:
208  mutable Eigen::VectorX<FP> m_flow_values;
209 
219  template <size_t I = 0>
220  inline void get_rhs_impl(Eigen::Ref<const Eigen::VectorX<FP>> flows, Eigen::Ref<Eigen::VectorX<FP>> rhs,
221  const FlowIndex& index) const
222  {
224  const auto flat_flow_index = get_flat_flow_index<Flow::source, Flow::target>(index);
225  const auto flat_source_population =
226  this->populations.get_flat_index(extend_index<PopIndex>(index, (size_t)Flow::source));
227  const auto flat_target_population =
228  this->populations.get_flat_index(extend_index<PopIndex>(index, (size_t)Flow::target));
229  rhs[flat_source_population] -= flows[flat_flow_index]; // subtract outflow from source compartment
230  rhs[flat_target_population] += flows[flat_flow_index]; // add outflow to target compartment
231  // handle next flow (if there is one)
232  if constexpr (I + 1 < Flows::size()) {
233  get_rhs_impl<I + 1>(flows, rhs, index);
234  }
235  }
236 };
237 
244 template <class Model, typename FP>
245 concept IsFlowModel =
246  requires(Model m, Eigen::Ref<const Eigen::VectorX<FP>> const_vref, Eigen::Ref<Eigen::VectorX<FP>> vref, FP t) {
247  requires IsCompartmentalModel<Model, FP>;
248  { m.get_initial_flows() } -> std::convertible_to<Eigen::VectorX<FP>>;
249  m.get_flows(const_vref, const_vref, t, vref);
250  m.get_derivatives(const_vref, vref);
251  };
252 
253 } // namespace mio
254 
255 #endif // MIO_COMPARTMENTS_FLOW_MODEL_H
A FlowModel is a CompartmentalModel defined by the flows between compartments.
Definition: flow_model.h:74
void get_rhs_impl(Eigen::Ref< const Eigen::VectorX< FP >> flows, Eigen::Ref< Eigen::VectorX< FP >> rhs, const FlowIndex &index) const
Compute the derivatives of the compartments.
Definition: flow_model.h:220
void get_derivatives(Eigen::Ref< const Eigen::VectorX< FP >> flows, Eigen::Ref< Eigen::VectorX< FP >> dydt) const
Compute the right-hand-side of the ODE dydt = f(y, t) from flow values.
Definition: flow_model.h:112
constexpr size_t get_flat_flow_index() const
A flat index into an array of flows (as computed by get_flows), if the only used category in Pop is C...
Definition: flow_model.h:201
FlowModel(Args... args)
Default constructor, forwarding args to Base constructor.
Definition: flow_model.h:93
Eigen::VectorX< FP > get_initial_flows() const
Initial values for flows.
Definition: flow_model.h:151
size_t get_flat_flow_index(const FlowIndex &indices) const
A flat index into an array of flows (as computed by get_flows), given the indices of each category.
Definition: flow_model.h:182
Eigen::VectorX< FP > m_flow_values
Cache to avoid allocation in get_derivatives (using get_flows).
Definition: flow_model.h:208
typename Pop::Index PopIndex
Definition: flow_model.h:75
virtual void get_flows(Eigen::Ref< const Eigen::VectorX< FP >>, Eigen::Ref< const Eigen::VectorX< FP >>, FP, Eigen::Ref< Eigen::VectorX< FP >>) const =0
details::filtered_index_t< Comp, Index, PopIndex > FlowIndex
Definition: flow_model.h:81
void get_derivatives(Eigen::Ref< const Eigen::VectorX< FP >> pop, Eigen::Ref< const Eigen::VectorX< FP >> y, FP t, Eigen::Ref< Eigen::VectorX< FP >> dydt) const final
Compute the right-hand-side f(y, t) of the ODE and store it in dydt.
Definition: flow_model.h:138
An Index with more than one template parameter combines several Index objects.
Definition: index.h:181
The Model of the Simulation.
Definition: abm/model.h:54
decltype(as_index< IndexTemplate >(std::declval< filtered_tuple_t< OmittedTag, decltype(as_tuple(std::declval< Index >()))> >())) filtered_index_t
Definition: flow_model.h:59
std::pair< size_t, size_t > flatten_index(Index const &indices, Index const &dimensions)
Definition: custom_index_array.h:61
decltype(std::tuple_cat(std::declval< std::conditional_t< std::is_same_v< OmittedTag, Tags >, std::tuple<>, std::tuple< Tags >>>()...)) filter_tuple(std::tuple< Tags... >)
std::tuple< Args... > as_tuple(T< Args... >)
T< Args... > as_index(std::tuple< Args... >)
decltype(filter_tuple< OmittedTag >(std::declval< Tuple >())) filtered_tuple_t
Definition: flow_model.h:54
int size(Comm comm)
Return the size of the given communicator.
Definition: miompi.cpp:75
TypeList< Flow< InfectionState::Susceptible, InfectionState::Exposed >, Flow< InfectionState::Exposed, InfectionState::InfectedNoSymptoms >, Flow< InfectionState::InfectedNoSymptoms, InfectionState::InfectedSymptoms >, Flow< InfectionState::InfectedNoSymptoms, InfectionState::Recovered >, Flow< InfectionState::InfectedNoSymptomsConfirmed, InfectionState::InfectedSymptomsConfirmed >, Flow< InfectionState::InfectedNoSymptomsConfirmed, InfectionState::Recovered >, Flow< InfectionState::InfectedSymptoms, InfectionState::InfectedSevere >, Flow< InfectionState::InfectedSymptoms, InfectionState::Recovered >, Flow< InfectionState::InfectedSymptomsConfirmed, InfectionState::InfectedSevere >, Flow< InfectionState::InfectedSymptomsConfirmed, InfectionState::Recovered >, Flow< InfectionState::InfectedSevere, InfectionState::InfectedCritical >, Flow< InfectionState::InfectedSevere, InfectionState::Recovered >, Flow< InfectionState::InfectedSevere, InfectionState::Dead >, Flow< InfectionState::InfectedCritical, InfectionState::Dead >, Flow< InfectionState::InfectedCritical, InfectionState::Recovered > > Flows
Definition: ode_secir/model.h:59
A collection of classes to simplify handling of matrix shapes in meta programming.
Definition: models/abm/analyze_result.h:30
concept IsFlowModel
Concept to check if a type is a valid FlowModel.
Definition: flow_model.h:245
typename type_at_index< Index, Types... >::type type_at_index_t
The type at the Index-th position in the list Types.
Definition: metaprogramming.h:118
requires(!std::is_trivial_v< T >) void BinarySerializerObject
Definition: binary_serializer.h:333
Definition: io.h:94
CompartmentalModel is a template for a compartmental model for an array of initial populations and a ...
Definition: compartmental_model.h:59
A Flow defines a possible transition between two Compartments in a FlowModel.
Definition: flow.h:33
static const Type target
Definition: flow.h:38
static const Type source
Definition: flow.h:37