Line data Source code
1 0 : // Distributed under the MIT License.
2 : // See LICENSE.txt for details.
3 :
4 : #pragma once
5 :
6 : #include <boost/container/static_vector.hpp>
7 : #include <cstddef>
8 : #include <iosfwd>
9 : #include <optional>
10 : #include <tuple>
11 : #include <type_traits>
12 : #include <utility>
13 :
14 : #include "DataStructures/CircularDeque.hpp"
15 : #include "DataStructures/MathWrapper.hpp"
16 : #include "Time/History.hpp"
17 : #include "Time/TimeStepId.hpp"
18 : #include "Utilities/Gsl.hpp"
19 : #include "Utilities/StlBoilerplate.hpp"
20 : #include "Utilities/TMPL.hpp"
21 :
22 : /// \cond
23 : namespace PUP {
24 : class er;
25 : } // namespace PUP
26 : /// \endcond
27 :
28 1 : namespace TimeSteppers {
29 : namespace BoundaryHistory_detail {
30 : template <typename Data>
31 : struct StepData {
32 : struct Entry {
33 : TimeStepId id;
34 : Data data;
35 :
36 : void pup(PUP::er& p);
37 : };
38 :
39 : size_t integration_order;
40 : // Unlike in History, the full step is the first entry, so we need
41 : // one more element.
42 : boost::container::static_vector<Entry, history_max_substeps + 1> substeps;
43 :
44 : void pup(PUP::er& p);
45 : };
46 : } // namespace BoundaryHistory_detail
47 :
48 : /// \ingroup TimeSteppersGroup
49 : /// Access to the list of `TimeStepId`s in a `BoundaryHistory`.
50 : ///
51 : /// For simplicity of implementation, iterable-container access is not
52 : /// provided for substeps within a step, but is instead provided
53 : /// through additional methods on this class.
54 : /// @{
55 1 : class ConstBoundaryHistoryTimes
56 : : public stl_boilerplate::RandomAccessSequence<ConstBoundaryHistoryTimes,
57 : const TimeStepId, false> {
58 : protected:
59 0 : ~ConstBoundaryHistoryTimes() = default;
60 :
61 : public:
62 0 : virtual size_t size() const = 0;
63 0 : virtual const TimeStepId& operator[](size_t n) const = 0;
64 0 : virtual const TimeStepId& operator[](
65 : const std::pair<size_t, size_t>& step_and_substep) const = 0;
66 0 : virtual size_t integration_order(size_t n) const = 0;
67 0 : virtual size_t integration_order(const TimeStepId& id) const = 0;
68 0 : virtual size_t number_of_substeps(size_t n) const = 0;
69 : /// This returns the same value for any substep of the same step.
70 1 : virtual size_t number_of_substeps(const TimeStepId& id) const = 0;
71 : };
72 :
73 0 : class MutableBoundaryHistoryTimes : public ConstBoundaryHistoryTimes {
74 : protected:
75 0 : ~MutableBoundaryHistoryTimes() = default;
76 :
77 : public:
78 : /// Remove the earliest step and its substeps.
79 1 : virtual void pop_front() const = 0;
80 0 : virtual void clear() const = 0;
81 : /// Remove all substeps for step \p n except for the step itself.
82 1 : virtual void clear_substeps(size_t n) const = 0;
83 : };
84 : /// @}
85 :
86 : /// \ingroup TimeSteppersGroup
87 : /// Type erased base class for evaluating BoundaryHistory couplings.
88 : ///
89 : /// The results are cached in the `BoundaryHistory` class.
90 : template <typename UntypedCouplingResult>
91 1 : class BoundaryHistoryEvaluator {
92 : public:
93 0 : virtual const UntypedCouplingResult& operator()(
94 : const TimeStepId& local_id, const TimeStepId& remote_id) const = 0;
95 :
96 : protected:
97 0 : ~BoundaryHistoryEvaluator() = default;
98 : };
99 :
100 : /// \ingroup TimeSteppersGroup
101 : /// History data used by a TimeStepper for boundary integration.
102 : ///
103 : /// \tparam LocalData local data passed to the boundary coupling
104 : /// \tparam RemoteData remote data passed to the boundary coupling
105 : /// \tparam UntypedCouplingResult math_wrapper_type of cached boundary couplings
106 : template <typename LocalData, typename RemoteData,
107 : typename UntypedCouplingResult>
108 1 : class BoundaryHistory {
109 : static_assert(tmpl::list_contains_v<tmpl::list<MATH_WRAPPER_TYPES>,
110 : UntypedCouplingResult>);
111 :
112 : public:
113 0 : BoundaryHistory() = default;
114 0 : BoundaryHistory(const BoundaryHistory& other) = default;
115 0 : BoundaryHistory(BoundaryHistory&&) = default;
116 0 : BoundaryHistory& operator=(const BoundaryHistory& other) = default;
117 0 : BoundaryHistory& operator=(BoundaryHistory&&) = default;
118 0 : ~BoundaryHistory() = default;
119 :
120 : // Factored out of ConstSideAccess so that the base classes of
121 : // MutableSideAccess can have protected destructors.
122 : template <bool Local, bool Mutable>
123 0 : class SideAccessCommon
124 : : public tmpl::conditional_t<Mutable, MutableBoundaryHistoryTimes,
125 : ConstBoundaryHistoryTimes> {
126 : public:
127 0 : using MutableData = tmpl::conditional_t<Local, LocalData, RemoteData>;
128 0 : using Data = tmpl::conditional_t<Mutable, MutableData, const MutableData>;
129 :
130 0 : size_t size() const override;
131 0 : static constexpr size_t max_size() {
132 : return std::remove_cvref_t<decltype(std::declval<ConstSideAccess<Local>>()
133 : .parent_data())>::max_size();
134 : }
135 :
136 0 : const TimeStepId& operator[](size_t n) const override;
137 0 : const TimeStepId& operator[](
138 : const std::pair<size_t, size_t>& step_and_substep) const override;
139 :
140 0 : size_t integration_order(size_t n) const override;
141 0 : size_t integration_order(const TimeStepId& id) const override;
142 :
143 0 : size_t number_of_substeps(size_t n) const override;
144 0 : size_t number_of_substeps(const TimeStepId& id) const override;
145 :
146 : /// Access the data stored on the side. When performed through a
147 : /// `MutableSideAccess`, these allow modification of the data.
148 : /// Performing such modifications likely invalidates the coupling
149 : /// cache for the associated `BoundaryHistory` object, which
150 : /// should be cleared.
151 : /// @{
152 1 : Data& data(size_t n) const;
153 1 : Data& data(const TimeStepId& id) const;
154 : /// @}
155 :
156 : /// Apply \p func to each entry.
157 : ///
158 : /// The function \p func must accept two arguments, one of type
159 : /// `const TimeStepId&` and a second of either type `const Data&`
160 : /// or `gsl::not_null<Data*>`, with the `not_null` version only
161 : /// available if this is a `MutableSideAccess`. If \p func takes
162 : /// a `not_null`, it must return a `bool` indicating if it
163 : /// modified the entry. If any entries are modified, the coupling
164 : /// cache of parent `BoundaryHistory` will be cleared.
165 : template <typename Func>
166 1 : void for_each(Func&& func) const;
167 :
168 : protected:
169 0 : ~SideAccessCommon() = default;
170 :
171 : tmpl::conditional_t<
172 : Mutable, CircularDeque<BoundaryHistory_detail::StepData<MutableData>>&,
173 : const CircularDeque<BoundaryHistory_detail::StepData<MutableData>>&>
174 0 : parent_data() const;
175 :
176 0 : auto& step_data(const TimeStepId& id) const;
177 :
178 0 : auto& entry(const TimeStepId& id) const;
179 :
180 0 : auto& entry(const std::pair<size_t, size_t>& step_and_substep) const;
181 :
182 0 : using StoredHistory =
183 : tmpl::conditional_t<Mutable, BoundaryHistory, const BoundaryHistory>;
184 0 : explicit SideAccessCommon(gsl::not_null<StoredHistory*> parent);
185 :
186 0 : gsl::not_null<StoredHistory*> parent_;
187 : };
188 :
189 : template <bool Local>
190 0 : class MutableSideAccess final : public SideAccessCommon<Local, true> {
191 : public:
192 0 : using Data = tmpl::conditional_t<Local, LocalData, RemoteData>;
193 :
194 0 : void pop_front() const override;
195 0 : void clear() const override;
196 0 : void clear_substeps(size_t n) const override;
197 :
198 0 : void insert(const TimeStepId& id, size_t integration_order,
199 : Data data) const;
200 :
201 0 : void insert_initial(const TimeStepId& id, size_t integration_order,
202 : Data data) const;
203 :
204 : private:
205 0 : friend class BoundaryHistory;
206 0 : explicit MutableSideAccess(gsl::not_null<BoundaryHistory*> parent);
207 : };
208 :
209 : template <bool Local>
210 0 : class ConstSideAccess final : public SideAccessCommon<Local, false> {
211 : private:
212 0 : friend class BoundaryHistory;
213 0 : explicit ConstSideAccess(gsl::not_null<const BoundaryHistory*> parent);
214 : };
215 :
216 0 : MutableSideAccess<true> local();
217 0 : ConstSideAccess<true> local() const;
218 :
219 0 : MutableSideAccess<false> remote();
220 0 : ConstSideAccess<false> remote() const;
221 :
222 : private:
223 : template <typename Coupling>
224 0 : class EvaluatorImpl final
225 : : public BoundaryHistoryEvaluator<UntypedCouplingResult> {
226 : public:
227 0 : const UntypedCouplingResult& operator()(
228 : const TimeStepId& local_id, const TimeStepId& remote_id) const override;
229 :
230 : private:
231 0 : friend class BoundaryHistory;
232 :
233 0 : EvaluatorImpl(const gsl::not_null<const BoundaryHistory*> parent,
234 : Coupling coupling)
235 : : parent_(parent), coupling_(std::move(coupling)) {}
236 :
237 0 : gsl::not_null<const BoundaryHistory*> parent_;
238 0 : Coupling coupling_;
239 : };
240 :
241 : public:
242 : /// Obtain an object that can evaluate type-erased boundary
243 : /// couplings.
244 : ///
245 : /// The passed functor must take objects of types `LocalData` and
246 : /// `RemoteData` and return an object with math_wrapper_type
247 : /// `UntypedCouplingResult`. Results are cached, so different calls
248 : /// to this function should pass equivalent couplings.
249 : template <typename Coupling>
250 1 : auto evaluator(Coupling&& coupling) const {
251 : return EvaluatorImpl<Coupling>(this, std::forward<Coupling>(coupling));
252 : }
253 :
254 : /// Clear the cached values.
255 : ///
256 : /// This is required after existing history entries that have been
257 : /// used in coupling calculations are mutated.
258 1 : void clear_coupling_cache();
259 :
260 0 : void pup(PUP::er& p);
261 :
262 : template <bool IncludeData>
263 0 : std::ostream& print(std::ostream& os, size_t padding_size = 0) const;
264 :
265 : private:
266 0 : void insert_local(const TimeStepId& id, size_t integration_order,
267 : LocalData data);
268 0 : void insert_remote(const TimeStepId& id, size_t integration_order,
269 : RemoteData data);
270 :
271 0 : void insert_initial_local(const TimeStepId& id, size_t integration_order,
272 : LocalData data);
273 0 : void insert_initial_remote(const TimeStepId& id, size_t integration_order,
274 : RemoteData data);
275 :
276 0 : void pop_local();
277 0 : void pop_remote();
278 :
279 0 : void clear_substeps_local(size_t n);
280 0 : void clear_substeps_remote(size_t n);
281 :
282 : std::tuple<std::optional<UntypedCouplingResult>&, const LocalData&,
283 : const RemoteData&>
284 0 : find_cache_entry(const TimeStepId& local_id,
285 : const TimeStepId& remote_id) const;
286 :
287 0 : CircularDeque<BoundaryHistory_detail::StepData<LocalData>> local_data_{};
288 0 : CircularDeque<BoundaryHistory_detail::StepData<RemoteData>> remote_data_{};
289 :
290 : template <typename Data>
291 0 : using CouplingSubsteps =
292 : boost::container::static_vector<Data, history_max_substeps + 1>;
293 :
294 : // NOLINTNEXTLINE(spectre-mutable)
295 : mutable CircularDeque<CouplingSubsteps<
296 : CircularDeque<CouplingSubsteps<std::optional<UntypedCouplingResult>>>>>
297 0 : couplings_;
298 : };
299 :
300 : template <typename LocalData, typename RemoteData,
301 : typename UntypedCouplingResult>
302 : template <bool Local, bool Mutable>
303 : template <typename Func>
304 : void BoundaryHistory<LocalData, RemoteData, UntypedCouplingResult>::
305 : SideAccessCommon<Local, Mutable>::for_each(Func&& func) const {
306 : bool entries_changed = false;
307 : for (auto& step : parent_data()) {
308 : for (auto& substep : step.substeps) {
309 : if constexpr (std::is_invocable_v<Func&, const TimeStepId&,
310 : const Data&>) {
311 : func(std::as_const(substep.id), std::as_const(substep.data));
312 : } else {
313 : static_assert(Mutable,
314 : "Cannot perform mutating for_each on a ConstSideAccess");
315 : if (func(std::as_const(substep.id), make_not_null(&substep.data))) {
316 : entries_changed = true;
317 : }
318 : }
319 : }
320 : }
321 : if constexpr (Mutable) {
322 : if (entries_changed) {
323 : // A minor optimization would be to only clear the cache entries
324 : // that have actually been invalidated, but most things that
325 : // modify the history modify all the entries.
326 : parent_->clear_coupling_cache();
327 : }
328 : }
329 : }
330 :
331 : template <typename LocalData, typename RemoteData,
332 : typename UntypedCouplingResult>
333 : template <typename Coupling>
334 : auto BoundaryHistory<LocalData, RemoteData, UntypedCouplingResult>::
335 : EvaluatorImpl<Coupling>::operator()(const TimeStepId& local_id,
336 : const TimeStepId& remote_id) const
337 : -> const UntypedCouplingResult& {
338 : const auto [coupling_entry, local_data, remote_data] =
339 : parent_->find_cache_entry(local_id, remote_id);
340 : if (not coupling_entry.has_value()) {
341 : auto new_entry = coupling_(local_data, remote_data);
342 : static_assert(std::is_same_v<math_wrapper_type<decltype(new_entry)>,
343 : UntypedCouplingResult>);
344 : coupling_entry.emplace(into_math_wrapper_type(std::move(new_entry)));
345 : }
346 : return *coupling_entry;
347 : }
348 :
349 : template <typename LocalData, typename RemoteData,
350 : typename UntypedCouplingResult>
351 0 : std::ostream& operator<<(std::ostream& os,
352 : const BoundaryHistory<LocalData, RemoteData,
353 : UntypedCouplingResult>& history);
354 : } // namespace TimeSteppers
355 :
356 : // Documentation for macro defined in the tpp file
357 : #ifdef SPECTRE_DOXYGEN_INVOKED
358 : /// \ingroup TimeSteppersGroup
359 : /// Explicitly instantiate BoundaryHistory and helpers. Should be
360 : /// called with the template arguments for `BoundaryHistory`.
361 1 : #define INSTANTIATE_BOUNDARY_HISTORY(...) UNSPECIFIED
362 : #endif // SPECTRE_DOXYGEN_INVOKED
|