Line data Source code
1 0 : // Distributed under the MIT License.
2 : // See LICENSE.txt for details.
3 :
4 : #pragma once
5 :
6 : #include <bit>
7 : #include <cmath>
8 : #include <cstddef>
9 : #include <optional>
10 : #include <type_traits>
11 :
12 : #include "DataStructures/DataBox/DataBox.hpp"
13 : #include "Time/AdaptiveSteppingDiagnostics.hpp"
14 : #include "Time/ChooseLtsStepSize.hpp"
15 : #include "Time/LtsMode.hpp"
16 : #include "Time/Tags/HistoryEvolvedVariables.hpp"
17 : #include "Time/Tags/LtsStepChoosers.hpp"
18 : #include "Time/Tags/MinimumTimeStep.hpp"
19 : #include "Time/Time.hpp"
20 : #include "Time/TimeStepId.hpp"
21 : #include "Time/TimeStepRequest.hpp"
22 : #include "Time/TimeStepRequestProcessor.hpp"
23 : #include "Time/TimeSteppers/LtsTimeStepper.hpp"
24 : #include "Utilities/ErrorHandling/Assert.hpp"
25 : #include "Utilities/ErrorHandling/Error.hpp"
26 : #include "Utilities/Gsl.hpp"
27 : #include "Utilities/TMPL.hpp"
28 :
29 : /// \cond
30 : struct AllStepChoosers;
31 : namespace Tags {
32 : struct AdaptiveSteppingDiagnostics;
33 : struct DataBox;
34 : struct FixedLtsRatio;
35 : struct LtsMode;
36 : template <typename Tag>
37 : struct Next;
38 : struct TimeStep;
39 : struct TimeStepId;
40 : template <typename StepperInterface>
41 : struct TimeStepper;
42 : } // namespace Tags
43 : /// \endcond
44 :
45 : /// \brief Adjust the step size for local time stepping.
46 : ///
47 : /// \details
48 : /// Usually, the new step size is chosen by calling the StepChoosers from
49 : /// `Tags::LtsStepChoosers`, restricted based on the allowed step sizes at the
50 : /// current time, and limits from history initialization.
51 : ///
52 : /// If `Tags::FixedLtsRatio` is present in the DataBox and not empty, the
53 : /// StepChoosers are not called and instead the desired step is taken to be the
54 : /// slab size over that value. Early in the evolution, the actual chosen step
55 : /// may differ from this because of restrictions on the allowed step, but all
56 : /// such restrictions are global and will not result in different decisions for
57 : /// different elements with the same desired fixed ratio.
58 : ///
59 : /// The optional template parameter `StepChoosersToUse` may be used to
60 : /// indicate a subset of the constructable step choosers to use for the current
61 : /// application of `ChangeStepSize`. Passing `AllStepChoosers` (default)
62 : /// indicates that any constructible step chooser may be used. This option is
63 : /// used when multiple components need to invoke `ChangeStepSize` with step
64 : /// choosers that may not be compatible with all components.
65 : template <typename StepChoosersToUse = AllStepChoosers,
66 : template <typename> typename CacheTagPrefix = std::type_identity_t>
67 1 : struct ChangeStepSize {
68 0 : using const_global_cache_tags =
69 : tmpl::list<CacheTagPrefix<Tags::MinimumTimeStep>,
70 : CacheTagPrefix<Tags::LtsStepChoosers>>;
71 :
72 0 : using return_tags = tmpl::list<Tags::DataBox>;
73 0 : using argument_tags = tmpl::list<>;
74 :
75 : template <typename DbTags>
76 0 : static void apply(const gsl::not_null<db::DataBox<DbTags>*> box) {
77 : if (db::get<CacheTagPrefix<Tags::LtsMode>>(*box) == LtsMode::Off) {
78 : return;
79 : }
80 :
81 : const auto& time_step_id = db::get<Tags::TimeStepId>(*box);
82 : if (time_step_id.substep() != 0) {
83 : return;
84 : }
85 :
86 : const LtsTimeStepper& time_stepper =
87 : db::get<CacheTagPrefix<Tags::TimeStepper<LtsTimeStepper>>>(*box);
88 : const auto& step_choosers =
89 : db::get<CacheTagPrefix<Tags::LtsStepChoosers>>(*box);
90 :
91 : using history_tags = ::Tags::get_all_history_tags<DbTags>;
92 : bool can_change_step_size = true;
93 : tmpl::for_each<history_tags>([&box, &can_change_step_size, &time_stepper,
94 : &time_step_id](auto tag_v) {
95 : if (not can_change_step_size) {
96 : return;
97 : }
98 : using tag = typename decltype(tag_v)::type;
99 : const auto& history = db::get<tag>(*box);
100 : can_change_step_size =
101 : time_stepper.can_change_step_size(time_step_id, history);
102 : });
103 :
104 : const auto current_step = db::get<Tags::TimeStep>(*box);
105 :
106 : std::optional<size_t> fixed_lts_ratio{};
107 : if constexpr (db::tag_is_retrievable_v<Tags::FixedLtsRatio,
108 : db::DataBox<DbTags>>) {
109 : fixed_lts_ratio = db::get<Tags::FixedLtsRatio>(*box);
110 : }
111 :
112 : TimeStepRequestProcessor step_requests(time_step_id.time_runs_forward());
113 : if (fixed_lts_ratio.has_value()) {
114 : ASSERT(std::popcount(*fixed_lts_ratio) == 1,
115 : "fixed_lts_ratio must be a power of 2, not " << *fixed_lts_ratio);
116 : step_requests.process(TimeStepRequest{
117 : .size_goal =
118 : (current_step.slab().duration() / *fixed_lts_ratio).value()});
119 : } else {
120 : const double last_step_size = current_step.value();
121 : for (const auto& step_chooser : step_choosers) {
122 : const auto step_request =
123 : step_chooser->template desired_step<StepChoosersToUse>(
124 : last_step_size, *box);
125 : step_requests.process(step_request);
126 : }
127 : }
128 :
129 : if (not can_change_step_size) {
130 : step_requests.error_on_hard_limit(
131 : current_step.value(),
132 : (time_step_id.step_time() + current_step).value());
133 : return;
134 : }
135 :
136 : const double desired_step = step_requests.step_size(
137 : time_step_id.step_time().value(), current_step.value());
138 :
139 : const auto& minimum_time_step =
140 : db::get<CacheTagPrefix<::Tags::MinimumTimeStep>>(*box);
141 :
142 : // We do this check twice, first on the desired value, and then on
143 : // the actual chosen value, which is probably slightly smaller.
144 : if (std::abs(desired_step) < minimum_time_step) {
145 : ERROR_NO_TRACE(
146 : "Chosen step size "
147 : << desired_step << " is smaller than the MinimumTimeStep of "
148 : << minimum_time_step
149 : << ".\n"
150 : "\n"
151 : "This can indicate a flaw in the step chooser, the grid, or a "
152 : "simulation instability that an error-based stepper is naively "
153 : "attempting to resolve. A possible issue is an aliasing-driven "
154 : "instability that could be cured by more aggressive filtering if "
155 : "you are using DG.");
156 : }
157 :
158 : const auto new_step =
159 : choose_lts_step_size(time_step_id.step_time(), desired_step);
160 : step_requests.error_on_hard_limit(
161 : new_step.value(), (time_step_id.step_time() + new_step).value());
162 :
163 : if (new_step == current_step) {
164 : return;
165 : }
166 :
167 : if (std::abs(new_step.value()) < minimum_time_step) {
168 : ERROR_NO_TRACE(
169 : "Chosen step size after conversion to a fraction of a slab "
170 : << new_step << " is smaller than the MinimumTimeStep of "
171 : << minimum_time_step
172 : << ".\n"
173 : "\n"
174 : "This can indicate a flaw in the step chooser, the grid, or a "
175 : "simulation instability that an error-based stepper is naively "
176 : "attempting to resolve. A possible issue is an aliasing-driven "
177 : "instability that could be cured by more aggressive filtering if "
178 : "you are using DG.");
179 : }
180 :
181 : db::mutate<Tags::Next<Tags::TimeStepId>, Tags::TimeStep,
182 : Tags::AdaptiveSteppingDiagnostics>(
183 : [&](const gsl::not_null<TimeStepId*> local_next_time_id,
184 : const gsl::not_null<TimeDelta*> time_step,
185 : const gsl::not_null<AdaptiveSteppingDiagnostics*> diags) {
186 : *time_step = new_step;
187 : *local_next_time_id =
188 : time_stepper.next_time_id(time_step_id, *time_step);
189 : ++diags->number_of_step_fraction_changes;
190 : },
191 : box);
192 : }
193 : };
|