Line data Source code
1 0 : // Distributed under the MIT License.
2 : // See LICENSE.txt for details.
3 :
4 : #pragma once
5 :
6 : #include <cmath>
7 : #include <cstddef>
8 : #include <cstdint>
9 : #include <limits>
10 : #include <map>
11 : #include <memory>
12 : #include <optional>
13 : #include <pup.h>
14 : #include <pup_stl.h>
15 : #include <string>
16 : #include <typeindex>
17 : #include <unordered_map>
18 : #include <utility>
19 : #include <vector>
20 :
21 : #include "DataStructures/DataBox/DataBox.hpp"
22 : #include "Evolution/DiscontinuousGalerkin/EqualRateLts/EqualRateRegions.hpp"
23 : #include "Options/Auto.hpp"
24 : #include "Options/Context.hpp"
25 : #include "Options/ParseError.hpp"
26 : #include "Options/String.hpp"
27 : #include "Parallel/ArrayCollection/IsDgElementCollection.hpp"
28 : #include "Parallel/GlobalCache.hpp"
29 : #include "Parallel/Reduction.hpp"
30 : #include "ParallelAlgorithms/EventsAndTriggers/Event.hpp"
31 : #include "Time/CollectStepperErrorTolerances.hpp"
32 : #include "Time/RequestsStepperErrorTolerances.hpp"
33 : #include "Time/StepChoosers/StepChooser.hpp"
34 : #include "Time/StepperErrorTolerances.hpp"
35 : #include "Time/TimeStepRequestProcessor.hpp"
36 : #include "Utilities/ErrorHandling/Assert.hpp"
37 : #include "Utilities/ErrorHandling/Error.hpp"
38 : #include "Utilities/Functional.hpp"
39 : #include "Utilities/Gsl.hpp"
40 : #include "Utilities/PrettyType.hpp"
41 : #include "Utilities/Serialization/CharmPupable.hpp"
42 : #include "Utilities/Serialization/PupStlCpp17.hpp"
43 : #include "Utilities/TMPL.hpp"
44 :
45 : /// \cond
46 : namespace Parallel::Tags {
47 : template <typename ParallelComponent, typename SectionIdTag>
48 : struct Section;
49 : } // namespace Parallel::Tags
50 : namespace Tags {
51 : struct DataBox;
52 : struct StepNumberWithinSlab;
53 : struct TimeStep;
54 : struct TimeStepId;
55 : } // namespace Tags
56 : namespace domain::Tags {
57 : template <size_t VolumeDim>
58 : struct Element;
59 : } // namespace domain::Tags
60 : namespace evolution::dg::Tags {
61 : struct EqualRateRegionId;
62 : template <size_t Dim>
63 : struct EqualRateRegions;
64 : } // namespace evolution::dg::Tags
65 : namespace evolution::dg::Tags::ChangeFixedLtsRatio {
66 : struct NewStepSize;
67 : struct NumberOfExpectedMessages;
68 : } // namespace evolution::dg::Tags::ChangeFixedLtsRatio
69 : /// \endcond
70 :
71 0 : namespace dg::Events {
72 : namespace ChangeFixedLtsRatio_detail {
73 : template <size_t Dim>
74 : struct StoreNewStep {
75 : using StepId = std::pair<int64_t, uint64_t>;
76 :
77 : template <typename ParallelComponent, typename DbTags, typename Metavariables,
78 : typename ArrayIndex>
79 : static void apply(db::DataBox<DbTags>& box,
80 : Parallel::GlobalCache<Metavariables>& /*cache*/,
81 : const ArrayIndex& /*array_index*/,
82 : const evolution::dg::EqualRateRegionId& region,
83 : const StepId& step_to_change, const double new_step_size) {
84 : const auto& equal_rate_regions =
85 : db::get<evolution::dg::Tags::EqualRateRegions<Dim>>(box);
86 : const auto& element = db::get<domain::Tags::Element<Dim>>(box);
87 : if (equal_rate_regions.is_in_region(region, element.id())) {
88 : db::mutate<evolution::dg::Tags::ChangeFixedLtsRatio::NewStepSize>(
89 : [&](const gsl::not_null<std::map<StepId, std::vector<double>>*>
90 : step_sizes) {
91 : (*step_sizes)[step_to_change].push_back(new_step_size);
92 : },
93 : make_not_null(&box));
94 : }
95 : }
96 : };
97 : } // namespace ChangeFixedLtsRatio_detail
98 :
99 : template <size_t Dim, typename StepChoosersToUse = AllStepChoosers>
100 0 : class ChangeFixedLtsRatio : public Event,
101 : public RequestsStepperErrorTolerances {
102 0 : using StepId = std::pair<int64_t, uint64_t>;
103 :
104 0 : using ReductionData = Parallel::ReductionData<
105 : Parallel::ReductionDatum<evolution::dg::EqualRateRegionId,
106 : funcl::AssertEqual<>>,
107 : Parallel::ReductionDatum<StepId, funcl::AssertEqual<>>,
108 : Parallel::ReductionDatum<double, funcl::Min<>>>;
109 :
110 : public:
111 0 : ChangeFixedLtsRatio() = default;
112 :
113 : /// \cond
114 : explicit ChangeFixedLtsRatio(CkMigrateMessage* /*unused*/) {}
115 : using PUP::able::register_constructor;
116 : WRAPPED_PUPable_decl_template(ChangeFixedLtsRatio); // NOLINT
117 : /// \endcond
118 :
119 0 : struct StepChoosers {
120 0 : static constexpr Options::String help = "Limits on step size";
121 0 : using type =
122 : std::vector<std::unique_ptr<StepChooser<StepChooserUse::LtsStep>>>;
123 0 : static size_t lower_bound_on_size() { return 1; }
124 : };
125 :
126 0 : struct DelayChange {
127 0 : static constexpr Options::String help = "Steps to wait before changing";
128 0 : using type = uint64_t;
129 : };
130 :
131 0 : struct Regions {
132 0 : using type =
133 : Options::Auto<std::vector<std::string>, Options::AutoLabel::All>;
134 0 : static constexpr Options::String help{
135 : "Regions to adjust. Adjustments for each region are independent."};
136 : };
137 :
138 0 : using options = tmpl::list<StepChoosers, DelayChange, Regions>;
139 0 : static constexpr Options::String help =
140 : "Change the number of steps per slab in regions with synchronized steps.";
141 :
142 0 : ChangeFixedLtsRatio(
143 : std::vector<std::unique_ptr<StepChooser<StepChooserUse::LtsStep>>>
144 : step_choosers,
145 : const uint64_t delay_change,
146 : std::optional<std::vector<std::string>> regions,
147 : const Options::Context& context = {})
148 : : step_choosers_(std::move(step_choosers)),
149 : delay_change_(delay_change),
150 : regions_(std::move(regions)) {
151 : if (delay_change != 0) {
152 : for (const auto& chooser : step_choosers_) {
153 : if (not chooser->can_be_delayed()) {
154 : // The runtime name might not be exactly the same as the one
155 : // used by the factory, but hopefully it's close enough that
156 : // the user can figure it out.
157 : PARSE_ERROR(context,
158 : "The " << pretty_type::get_runtime_type_name(*chooser)
159 : << " StepChooser cannot be applied with a delay.");
160 : }
161 : }
162 : }
163 : }
164 :
165 0 : using compute_tags_for_observation_box = tmpl::list<>;
166 :
167 : // Need a const version of the full box for the step choosers, but
168 : // can't get a const version while mutating other tags, so request a
169 : // mutable version.
170 0 : using return_tags = tmpl::list<::Tags::DataBox>;
171 0 : using argument_tags = tmpl::list<>;
172 :
173 : template <typename DbTags, typename Metavariables, typename ArrayIndex,
174 : typename ParallelComponent>
175 0 : void operator()(const gsl::not_null<db::DataBox<DbTags>*> box,
176 : Parallel::GlobalCache<Metavariables>& cache,
177 : const ArrayIndex& array_index,
178 : const ParallelComponent* const /*meta*/,
179 : const ObservationValue& /*observation_value*/) const {
180 : const auto& time_step_id = db::get<::Tags::TimeStepId>(*box);
181 : if (time_step_id.substep() != 0) {
182 : ERROR("Changing step ratio on a substep not implemented.");
183 : }
184 :
185 : auto& section = db::get_mutable_reference<Parallel::Tags::Section<
186 : ParallelComponent, evolution::dg::Tags::EqualRateRegionId>>(box);
187 :
188 : if (not section.has_value()) {
189 : return;
190 : }
191 :
192 : if (regions_.has_value()) {
193 : const auto& equal_rate_regions =
194 : db::get<evolution::dg::Tags::EqualRateRegions<Dim>>(*box);
195 : const auto& region_map = equal_rate_regions.regions();
196 : bool active = false;
197 : // Iterate over the regions instead of looking up our region's
198 : // name so we catch typos in the input file option.
199 : for (const auto& region_name : *regions_) {
200 : const auto region_it = region_map.find(region_name);
201 : if (region_it == region_map.end()) {
202 : ERROR_NO_TRACE("Unknown region name in ChangeFixedLtsRatio: "
203 : << region_name
204 : << ". Known regions: " << keys_of(region_map));
205 : }
206 : if (region_it->second == section->id()) {
207 : active = true;
208 : // Don't break here so we check the remaining names for validity.
209 : }
210 : }
211 : if (not active) {
212 : return;
213 : }
214 : }
215 :
216 : const double current_step = db::get<::Tags::TimeStep>(*box).value();
217 :
218 : TimeStepRequestProcessor step_requests(time_step_id.time_runs_forward());
219 : bool synchronization_required = false;
220 : for (const auto& step_chooser : step_choosers_) {
221 : step_requests.process(
222 : step_chooser->template desired_step<StepChoosersToUse>(current_step,
223 : *box));
224 : // We must synchronize if any step chooser requires it, not just
225 : // the limiting one, because choosers requiring synchronization
226 : // can be limiting on some processors and not others.
227 : if (not synchronization_required) {
228 : synchronization_required = step_chooser->uses_local_data();
229 : }
230 : }
231 :
232 : const double desired_step_size = std::abs(step_requests.step_size(
233 : time_step_id.step_time().value(), current_step));
234 :
235 : // The processing action will consider anything past the end of
236 : // the slab as at the start of the next slab. The delay could be
237 : // off by a bit because of that, but we don't have to worry about
238 : // figuring out when the slab will end.
239 : const StepId step_to_change{
240 : time_step_id.slab_number(),
241 : db::get<::Tags::StepNumberWithinSlab>(*box) + delay_change_};
242 :
243 : db::mutate<
244 : evolution::dg::Tags::ChangeFixedLtsRatio::NumberOfExpectedMessages>(
245 : [&](const gsl::not_null<std::map<StepId, size_t>*> expected) {
246 : ++(*expected)[step_to_change];
247 : },
248 : box);
249 :
250 : if constexpr (Parallel::is_dg_element_collection_v<ParallelComponent>) {
251 : (void)cache, (void)array_index;
252 : ERROR("Reductions not implemented for DgElementCollection.");
253 : } else {
254 : if (synchronization_required) {
255 : const auto& component_proxy =
256 : Parallel::get_parallel_component<ParallelComponent>(cache);
257 : const auto& self_proxy = component_proxy[array_index];
258 : // As usual, Charm section stuff doesn't work very well, so we
259 : // can't send the result to just the section.
260 : Parallel::contribute_to_reduction<
261 : ChangeFixedLtsRatio_detail::StoreNewStep<Dim>>(
262 : ReductionData(section->id(), step_to_change, desired_step_size),
263 : self_proxy, component_proxy, make_not_null(&*section));
264 : } else {
265 : db::mutate<evolution::dg::Tags::ChangeFixedLtsRatio::NewStepSize>(
266 : [&](const gsl::not_null<std::map<StepId, std::vector<double>>*>
267 : step_sizes) {
268 : (*step_sizes)[step_to_change].push_back(desired_step_size);
269 : },
270 : box);
271 : }
272 : }
273 : }
274 :
275 0 : using is_ready_argument_tags = tmpl::list<>;
276 :
277 : template <typename Metavariables, typename ArrayIndex, typename Component>
278 0 : bool is_ready(Parallel::GlobalCache<Metavariables>& /*cache*/,
279 : const ArrayIndex& /*array_index*/,
280 : const Component* const /*meta*/) const {
281 : return true;
282 : }
283 :
284 1 : bool needs_evolved_variables() const override {
285 : // This depends on the chosen StepChoosers, but they don't have a
286 : // way to report this information so we just return true to be
287 : // safe.
288 : return true;
289 : }
290 :
291 0 : void pup(PUP::er& p) override {
292 : p | step_choosers_;
293 : p | delay_change_;
294 : p | regions_;
295 : }
296 :
297 1 : std::unordered_map<std::type_index, StepperErrorTolerances> tolerances()
298 : const override {
299 : std::unordered_map<std::type_index, StepperErrorTolerances> tolerances{};
300 : for (const auto& step_chooser : step_choosers_) {
301 : collect_stepper_error_tolerances(&tolerances, *step_chooser);
302 : }
303 : return tolerances;
304 : }
305 :
306 : private:
307 : std::vector<std::unique_ptr<StepChooser<StepChooserUse::LtsStep>>>
308 0 : step_choosers_;
309 0 : uint64_t delay_change_ = std::numeric_limits<uint64_t>::max();
310 0 : std::optional<std::vector<std::string>> regions_{};
311 : };
312 :
313 : template <size_t Dim, typename StepChoosersToUse>
314 : // NOLINTNEXTLINE
315 0 : PUP::able::PUP_ID ChangeFixedLtsRatio<Dim, StepChoosersToUse>::my_PUP_ID = 0;
316 : } // namespace dg::Events
|