StepToTimes.hpp
1 // Distributed under the MIT License.
2 // See LICENSE.txt for details.
3 
4 #pragma once
5 
6 #include <algorithm>
7 #include <cmath>
8 #include <functional>
9 #include <limits>
10 #include <pup.h>
11 #include <pup_stl.h> // IWYU pragma: keep
12 #include <utility>
13 #include <vector>
14 
15 #include "Options/Options.hpp"
17 #include "Time/Slab.hpp"
18 #include "Time/StepChoosers/StepChooser.hpp"
19 #include "Time/Time.hpp"
20 #include "Time/TimeStepId.hpp"
21 #include "Time/Utilities.hpp"
22 #include "Utilities/Registration.hpp"
23 #include "Utilities/TMPL.hpp"
24 
25 /// \cond
26 namespace Parallel {
27 template <typename Metavariables>
28 class ConstGlobalCache;
29 } // namespace Parallel
30 namespace Tags {
31 struct TimeStepId;
32 } // namespace Tags
33 /// \endcond
34 
35 namespace StepChoosers {
36 template <typename StepChooserRegistrars>
38 
39 namespace Registrars {
41 } // namespace Registrars
42 
43 /// Suggests step sizes to place steps at specific times.
44 ///
45 /// The suggestion provided depends on the current time, so it should
46 /// be applied immediately, rather than delayed several slabs. As
47 /// changing immediately is inefficient, it may be best to use
48 /// triggers to only activate this check near (within a few slabs of)
49 /// the desired time.
50 template <typename StepChooserRegistrars = tmpl::list<Registrars::StepToTimes>>
51 class StepToTimes : public StepChooser<StepChooserRegistrars> {
52  public:
53  /// \cond
54  StepToTimes() = default;
55  explicit StepToTimes(CkMigrateMessage* /*unused*/) noexcept {}
56  using PUP::able::register_constructor;
57  WRAPPED_PUPable_decl_template(StepToTimes); // NOLINT
58  /// \endcond
59 
60  struct Times {
61  using type = std::vector<double>;
62  static constexpr OptionString help{"Times to force steps at"};
63  };
64 
65  static constexpr OptionString help =
66  "Suggests step sizes to place steps at specific times.\n"
67  "\n"
68  "The suggestion provided depends on the current time, so it should\n"
69  "be applied immediately, rather than delayed several slabs. As\n"
70  "changing immediately is inefficient, it may be best to use\n"
71  "triggers to only activate this check near (within a few slabs of)\n"
72  "the desired time.\n";
73  using options = tmpl::list<Times>;
74 
75  explicit StepToTimes(std::vector<double> times) noexcept
76  : times_(std::move(times)) {
77  std::sort(times_.begin(), times_.end());
78  }
79 
80  using argument_tags = tmpl::list<Tags::TimeStepId>;
81 
82  template <typename Metavariables>
83  double operator()(
84  const TimeStepId& time_step_id, const double last_step_magnitude,
85  const Parallel::ConstGlobalCache<Metavariables>& /*cache*/) const
86  noexcept {
87  const auto& substep_time = time_step_id.substep_time();
88  const double now = substep_time.value();
89  // Trying to step to a given time might not get us exactly there
90  // because of rounding errors. Avoid taking an extra tiny step if
91  // we undershoot.
92  const double sloppiness = slab_rounding_error(substep_time);
93 
94  double distance_to_next_goal;
95  if (time_step_id.time_runs_forward()) {
96  const auto next_time =
97  std::upper_bound(times_.begin(), times_.end(), now + sloppiness);
98  if (next_time == times_.end()) {
99  // We've passed all the times. No restriction.
101  }
102  distance_to_next_goal = *next_time - now;
103  } else {
104  const auto next_time =
105  std::upper_bound(times_.rbegin(), times_.rend(), now - sloppiness,
107  if (next_time == times_.rend()) {
108  // We've passed all the times. No restriction.
110  }
111  distance_to_next_goal = now - *next_time;
112  }
113 
114  if (distance_to_next_goal < 2.0 / 3.0 * last_step_magnitude) {
115  // Our goal is well within range of the expected allowed step
116  // size.
117  return distance_to_next_goal;
118  } else {
119  // We can't reach our goal in one step, or at least might not be
120  // able to if the step adjusts a relatively small amount for
121  // other reasons. Prevent the step from bringing us too close
122  // to the goal so that the step following this one will not be
123  // too small.
124  return 2.0 / 3.0 * distance_to_next_goal;
125  }
126  }
127 
128  // NOLINTNEXTLINE(google-runtime-references)
129  void pup(PUP::er& p) noexcept override { p | times_; }
130 
131  private:
132  std::vector<double> times_;
133 };
134 
135 /// \cond
136 template <typename StepChooserRegistrars>
137 PUP::able::PUP_ID StepToTimes<StepChooserRegistrars>::my_PUP_ID = 0; // NOLINT
138 /// \endcond
139 } // namespace StepChoosers
Parallel::ConstGlobalCache
Definition: ElementReceiveInterpPoints.hpp:16
CharmPupable.hpp
StepChoosers
Definition: ByBlock.hpp:33
utility
functional
Options.hpp
vector
slab_rounding_error
double slab_rounding_error(const Time &time) noexcept
Definition: Utilities.cpp:12
cmath
algorithm
Registration::Registrar
A template for defining a registrar.
Definition: Registration.hpp:42
WRAPPED_PUPable_decl_template
#define WRAPPED_PUPable_decl_template(className)
Mark derived classes as serializable.
Definition: CharmPupable.hpp:22
std::numeric_limits::infinity
T infinity(T... args)
Time.hpp
TimeStepId
Definition: TimeStepId.hpp:25
TimeStepId.hpp
limits
std::greater
StepChoosers::StepToTimes
Suggests step sizes to place steps at specific times.
Definition: StepToTimes.hpp:37
Slab.hpp
StepChooser
Definition: StepChooser.hpp:43
StepChoosers::StepToTimes::Times
Definition: StepToTimes.hpp:60
OptionString
const char *const OptionString
The string used in option structs.
Definition: Options.hpp:30
Parallel
Contains functions that forward to Charm++ parallel functions.
Definition: ElementReceiveInterpPoints.hpp:14
TMPL.hpp