dynamic_npis.h Source File

CPP API: dynamic_npis.h Source File
dynamic_npis.h
Go to the documentation of this file.
1 /*
2 * Copyright (C) 2020-2026 MEmilio
3 *
4 * Authors: Martin J. Kuehn, Daniel Abele
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 MIO_EPI_DYNAMIC_LOCKDOWN_H
21 #define MIO_EPI_DYNAMIC_LOCKDOWN_H
22 
24 #include "memilio/utils/stl_util.h"
25 
26 #include <cassert>
27 
28 namespace mio
29 {
30 
35 template <typename FP>
37 {
38 public:
44  void set_threshold(FP threshold, const std::vector<DampingSampling<FP>>& dampings)
45  {
46  insert_sorted_replace(m_thresholds, std::make_pair(threshold, dampings), [](auto& t1, auto& t2) {
47  return t1.first > t2.first;
48  });
49  }
50 
58  {
59  // thresholds are sorted by value descending, so upper_bound returns the first threshold that is smaller using binary search
60  auto iter_max_exceeded_threshold =
61  std::upper_bound(m_thresholds.begin(), m_thresholds.end(), value, [](auto& val, auto& t2) {
62  return val > t2.first;
63  });
64  return iter_max_exceeded_threshold;
65  }
66 
73  auto get_thresholds() const
74  {
75  return Range(m_thresholds);
76  }
78  {
79  return Range(m_thresholds);
80  }
88  {
89  return m_duration;
90  }
91 
97  {
98  m_duration = v;
99  }
100 
110  {
111  return m_delay;
112  }
117  {
118  assert(delay >= SimulationTime<FP>(0.0) && "Implementation delay must be non-negative.");
119  m_delay = delay;
120  }
132  FP get_base_value() const
133  {
134  return m_base;
135  }
139  void set_base_value(FP v)
140  {
141  m_base = v;
142  }
154  {
155  return m_directive_begin;
156  }
161  {
162  assert(begin < m_directive_end && "Directive begin must be before directive end.");
164  }
176  {
177  return m_directive_end;
178  }
183  {
184  assert(m_directive_begin < end && "Directive end must be strictly after directive begin.");
186  }
192  void draw_sample()
193  {
194  for (auto&& t : m_thresholds) {
195  for (auto&& d : t.second) {
196  d.draw_sample();
197  }
198  }
199  }
200 
205  template <class IOContext>
206  void serialize(IOContext& io) const
207  {
208  auto obj = io.create_object("DynamicNPIs");
209  obj.add_list("Thresholds", get_thresholds().begin(), get_thresholds().end());
210  obj.add_element("Duration", get_duration());
211  obj.add_element("Delay", get_implementation_delay());
212  obj.add_element("BaseValue", get_base_value());
213  obj.add_element("DirectiveBegin", get_directive_begin());
214  obj.add_element("DirectiveEnd", get_directive_end());
215  }
216 
221  template <class IOContext>
222  static IOResult<DynamicNPIs> deserialize(IOContext& io)
223  {
224  auto obj = io.expect_object("DynamicNPIs");
225  auto t = obj.expect_list("Thresholds", Tag<std::pair<FP, std::vector<DampingSampling<FP>>>>{});
226  auto d = obj.expect_element("Duration", Tag<SimulationTime<FP>>{});
227  auto i = obj.expect_element("Delay", Tag<SimulationTime<FP>>{});
228  auto b = obj.expect_element("BaseValue", Tag<FP>{});
229  auto f = obj.expect_element("DirectiveBegin", Tag<SimulationTime<FP>>{});
230  auto l = obj.expect_element("DirectiveEnd", Tag<SimulationTime<FP>>{});
231  return apply(
232  io,
233  [](auto&& t_, auto&& d_, auto&& i_, auto&& b_, auto&& f_, auto&& l_) {
234  auto npis = DynamicNPIs();
235  npis.set_duration(d_);
236  npis.set_implementation_delay(i_);
237  npis.set_base_value(b_);
238  for (auto&& e : t_) {
239  npis.set_threshold(e.first, e.second);
240  }
241  npis.set_directive_begin(f_);
242  npis.set_directive_end(l_);
243  return npis;
244  },
245  t, d, i, b, f, l);
246  }
247 
248 private:
249  std::vector<std::pair<FP, std::vector<DampingSampling<FP>>>> m_thresholds;
252  SimulationTime<FP> m_directive_begin{SimulationTime<FP>(std::numeric_limits<FP>::lowest())};
254  FP m_base{1.0};
255 };
256 
269 template <typename FP, class DampingExpr>
270 std::vector<size_t> get_damping_indices(const DampingExpr& damping_expr, DampingLevel lvl, DampingType type,
272 {
273  std::vector<size_t> indices;
274  for (size_t i = 0; i < damping_expr.get_dampings().size(); ++i) {
275  const auto d = damping_expr.get_dampings()[i];
276  if (d.get_level() == lvl && d.get_type() == type && d.get_time() > begin && d.get_time() < end) {
277  indices.push_back(i);
278  }
279  }
280  return indices;
281 }
282 
294 template <typename FP, class DampingExpr>
295 Eigen::Ref<const typename DampingExpr::Matrix> get_active_damping(const DampingExpr& damping_expr, DampingLevel lvl,
296  DampingType type, SimulationTime<FP> t)
297 {
298  auto ub =
299  std::find_if(damping_expr.get_dampings().rbegin(), damping_expr.get_dampings().rend(), [lvl, type, t](auto& d) {
300  return d.get_level() == lvl && d.get_type() == type && d.get_time() <= t;
301  });
302  if (ub != damping_expr.get_dampings().rend()) {
303  return ub->get_coeffs();
304  }
305  return DampingExpr::Matrix::Zero(damping_expr.get_shape().rows(), damping_expr.get_shape().cols());
306 }
307 
334 template <typename FP, class DampingExprGroup, class MakeMatrix>
335 void implement_dynamic_npis(DampingExprGroup& damping_expr_group, const std::vector<DampingSampling<FP>>& npis,
336  SimulationTime<FP> begin, SimulationTime<FP> end, MakeMatrix&& make_matrix)
337 {
338  for (auto& npi : npis) {
339  for (auto& mat_idx : npi.get_matrix_indices()) {
340  auto type = npi.get_type();
341  auto level = npi.get_level();
342  auto& damping_expr = damping_expr_group[mat_idx];
343 
344  auto active = get_active_damping<FP>(damping_expr, level, type, begin);
345  auto active_end = get_active_damping<FP>(damping_expr, level, type, end)
346  .eval(); // copy because it may be removed or changed
347  auto value = make_matrix(npi.get_value().value() * npi.get_group_weights());
348 
349  auto npi_implemented = false;
350 
351  // add begin of npi if not already bigger
352  if ((active.array() < value.array()).any()) {
353  damping_expr.add_damping(max(value, active), level, type, begin);
354  npi_implemented = true;
355  }
356 
357  // replace dampings during the new npi
358  auto damping_indices = get_damping_indices<FP>(damping_expr, level, type, begin, end);
359  for (auto& i : damping_indices) {
360  auto& d = damping_expr.get_dampings()[i];
361  damping_expr.add_damping(max(d.get_coeffs(), value), level, type, d.get_time());
362  npi_implemented = true;
363  }
364 
365  // add end of npi to restore active dampings if any change was made
366  if (npi_implemented) {
367  damping_expr.add_damping(active_end, level, type, end);
368  }
369  }
370  }
371 
372  // remove duplicates that accumulated because of dampings that become active during the time span
373  // a damping is obsolete if the most recent damping of the same type and level has the same value
374  for (auto& damping_expr : damping_expr_group) {
375  // go from the back so indices aren't invalidated when dampings are removed
376  // use indices to loop instead of reverse iterators because removing invalidates the current iterator
377  for (auto i = int(0); i < int(damping_expr.get_dampings().size()) - 1; ++i) {
378  auto it = damping_expr.get_dampings().rbegin() + i;
379 
380  // look for previous damping of the same type/level
381  auto it_prev = std::find_if(it + 1, damping_expr.get_dampings().rend(), [&di = *it](auto& dj) {
382  return di.get_level() == dj.get_level() && di.get_type() == dj.get_type();
383  });
384 
385  // remove if match is found and has same value
386  if (it_prev != damping_expr.get_dampings().rend() && it->get_coeffs() == it_prev->get_coeffs()) {
387  damping_expr.remove_damping(damping_expr.get_dampings().size() - 1 - i);
388  }
389  }
390  }
391 }
392 
393 } // namespace mio
394 
395 #endif // MIO_EPI_DYNAMIC_LOCKDOWN_H
randomly sample dampings for e.g.
Definition: damping_sampling.h:38
represents non-pharmaceutical interventions (NPI) that are activated during the simulation if some va...
Definition: dynamic_npis.h:37
void draw_sample()
draw a random sample from the damping distributions
Definition: dynamic_npis.h:192
static IOResult< DynamicNPIs > deserialize(IOContext &io)
deserialize an object of this class.
Definition: dynamic_npis.h:222
FP get_base_value() const
Get/Set the base value of the thresholds.
Definition: dynamic_npis.h:132
SimulationTime< FP > get_duration() const
get the duration of the NPIs.
Definition: dynamic_npis.h:87
void set_threshold(FP threshold, const std::vector< DampingSampling< FP >> &dampings)
set a threshold and the NPIs that should be enacted.
Definition: dynamic_npis.h:44
void set_implementation_delay(SimulationTime< FP > delay)
Definition: dynamic_npis.h:116
SimulationTime< FP > get_implementation_delay() const
Get/Set the implementation delay at which the NPIs are implemented after threshold exceedance.
Definition: dynamic_npis.h:109
auto get_max_exceeded_threshold(FP value)
find the highest threshold that is smaller than the value.
Definition: dynamic_npis.h:57
SimulationTime< FP > m_directive_begin
Definition: dynamic_npis.h:252
SimulationTime< FP > m_directive_end
Definition: dynamic_npis.h:253
void serialize(IOContext &io) const
serialize this.
Definition: dynamic_npis.h:206
void set_duration(SimulationTime< FP > v)
set the duration of the NPIs.
Definition: dynamic_npis.h:96
SimulationTime< FP > m_duration
Definition: dynamic_npis.h:250
std::vector< std::pair< FP, std::vector< DampingSampling< FP > > > > m_thresholds
Definition: dynamic_npis.h:249
auto get_thresholds()
range of pairs of threshold values and NPIs.
Definition: dynamic_npis.h:77
SimulationTime< FP > m_delay
Definition: dynamic_npis.h:251
auto get_thresholds() const
range of pairs of threshold values and NPIs.
Definition: dynamic_npis.h:73
FP m_base
Definition: dynamic_npis.h:254
void set_base_value(FP v)
Definition: dynamic_npis.h:139
SimulationTime< FP > get_directive_end() const
Get/Set the last day of the simulation for which a DynamicNPI can be active.
Definition: dynamic_npis.h:175
void set_directive_begin(SimulationTime< FP > begin)
Definition: dynamic_npis.h:160
SimulationTime< FP > get_directive_begin() const
Get/Set the first day of the simulation for which a DynamicNPI can be activated.
Definition: dynamic_npis.h:153
void set_directive_end(SimulationTime< FP > end)
Definition: dynamic_npis.h:182
Typesafe wrapper for a floating-point simulation time value (in days).
Definition: damping.h:78
static min_max_return_type< ad::internal::active_type< AD_TAPE_REAL, DATA_HANDLER_1 >, ad::internal::active_type< AD_TAPE_REAL, DATA_HANDLER_1 > >::type max(const ad::internal::active_type< AD_TAPE_REAL, DATA_HANDLER_1 > &a, const ad::internal::active_type< AD_TAPE_REAL, DATA_HANDLER_1 > &b)
Definition: ad.hpp:2596
trait_value< T >::RETURN_TYPE & value(T &x)
Definition: ad.hpp:3308
int size(Comm comm)
Return the size of the given communicator.
Definition: miompi.cpp:75
A collection of classes to simplify handling of matrix shapes in meta programming.
Definition: models/abm/analyze_result.h:30
requires details::IsElementReference< M > RowMajorIterator< M, false > end(M &m)
create a non-const end iterator for the matrix m.
Definition: eigen_util.h:449
Range(std::pair< I, S > iterator_pair) -> Range< I, S >
Deduction guide to correctly deduce range type when constructed from a pair.
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
std::vector< size_t > get_damping_indices(const DampingExpr &damping_expr, DampingLevel lvl, DampingType type, SimulationTime< FP > begin, SimulationTime< FP > end)
Get a list of indices of specified dampings.
Definition: dynamic_npis.h:270
void implement_dynamic_npis(DampingExprGroup &damping_expr_group, const std::vector< DampingSampling< FP >> &npis, SimulationTime< FP > begin, SimulationTime< FP > end, MakeMatrix &&make_matrix)
implement dynamic NPIs for a time span.
Definition: dynamic_npis.h:335
auto max(const Eigen::MatrixBase< A > &a, B &&b)
coefficient wise maximum of two matrices.
Definition: eigen_util.h:171
requires details::IsElementReference< M > RowMajorIterator< M, false > begin(M &m)
create a non-const iterator to first element of the matrix m.
Definition: eigen_util.h:421
Eigen::Ref< const typename DampingExpr::Matrix > get_active_damping(const DampingExpr &damping_expr, DampingLevel lvl, DampingType type, SimulationTime< FP > t)
Get the value of the damping that matches the given type and level and that is active at the specifie...
Definition: dynamic_npis.h:295
boost::outcome_v2::unchecked< T, IOStatus > IOResult
Value-or-error type for operations that return a value but can fail.
Definition: io.h:354
void insert_sorted_replace(std::vector< T > &vec, T const &item, Pred pred)
inserts element in a sorted vector, replacing items that are equal precondition: elements in the vect...
Definition: stl_util.h:82