BoundaryHistory.hpp
1 // Distributed under the MIT License.
2 // See LICENSE.txt for details.
3 
4 #pragma once
5 
6 #include <algorithm>
7 #include <boost/iterator/transform_iterator.hpp>
8 #include <cstddef>
9 #include <deque>
10 #include <map>
11 #include <pup.h>
12 #include <pup_stl.h> // IWYU pragma: keep
13 #include <tuple>
14 #include <utility>
15 
16 #include "ErrorHandling/Assert.hpp"
17 #include "Parallel/PupStlCpp11.hpp" // IWYU pragma: keep
18 #include "Time/Time.hpp" // IWYU pragma: keep
19 #include "Time/TimeId.hpp"
21 #include "Utilities/Gsl.hpp"
22 
23 namespace TimeSteppers {
24 
25 /// \ingroup TimeSteppersGroup
26 /// History data used by a TimeStepper for boundary integration.
27 /// \tparam LocalVars local variables passed to the boundary coupling
28 /// \tparam RemoteVars remote variables passed to the boundary coupling
29 /// \tparam CouplingResult result of the coupling function
30 template <typename LocalVars, typename RemoteVars, typename CouplingResult>
32  template <typename Vars>
33  using IteratorType = boost::transform_iterator<
34  const Time& (*)(const std::tuple<Time, Vars>&),
35  typename std::deque<std::tuple<Time, Vars>>::const_iterator>;
36  public:
37  using local_iterator = IteratorType<LocalVars>;
38  using remote_iterator = IteratorType<RemoteVars>;
39 
40  // No copying because of the pointers in the cache. Moving is fine
41  // because we also move the container being pointed into and maps
42  // guarantee that this doesn't invalidate pointers.
43  BoundaryHistory() = default;
44  BoundaryHistory(const BoundaryHistory&) = delete;
45  BoundaryHistory(BoundaryHistory&&) = default;
46  BoundaryHistory& operator=(const BoundaryHistory&) = delete;
47  BoundaryHistory& operator=(BoundaryHistory&&) = default;
48  ~BoundaryHistory() = default;
49 
50  /// Add a new value to the end of the history of the indicated side.
51  //@{
52  void local_insert(const TimeId& time_id, LocalVars vars) noexcept {
53  local_data_.emplace_back(time_id.time(), std::move(vars));
54  }
55  void remote_insert(const TimeId& time_id, RemoteVars vars) noexcept {
56  remote_data_.emplace_back(time_id.time(), std::move(vars));
57  }
58  //@}
59 
60  /// Add a new value to the front of the history of the indicated
61  /// side. This is often convenient for setting initial data.
62  //@{
63  void local_insert_initial(const TimeId& time_id, LocalVars vars) noexcept {
64  local_data_.emplace_front(time_id.time(), std::move(vars));
65  }
66  void remote_insert_initial(const TimeId& time_id, RemoteVars vars) noexcept {
67  remote_data_.emplace_front(time_id.time(), std::move(vars));
68  }
69  //@}
70 
71  /// Mark all data before the passed point in history on the
72  /// indicated side as unneeded so it can be removed. Calling this
73  /// directly should not often be necessary, as it is handled
74  /// internally by the time steppers.
75  //@{
76  void local_mark_unneeded(const local_iterator& first_needed) noexcept {
77  mark_unneeded<0>(make_not_null(&local_data_), first_needed);
78  }
79  void remote_mark_unneeded(const remote_iterator& first_needed) noexcept {
80  mark_unneeded<1>(make_not_null(&remote_data_), first_needed);
81  }
82  //@}
83 
84  /// Access to the sequence of times on the indicated side.
85  //@{
86  local_iterator local_begin() const noexcept {
87  return local_iterator(local_data_.begin(), std::get<0>);
88  }
89  local_iterator local_end() const noexcept {
90  return local_iterator(local_data_.end(), std::get<0>);
91  }
92 
93  remote_iterator remote_begin() const noexcept {
94  return remote_iterator(remote_data_.begin(), std::get<0>);
95  }
96  remote_iterator remote_end() const noexcept {
97  return remote_iterator(remote_data_.end(), std::get<0>);
98  }
99 
100  size_t local_size() const noexcept { return local_data_.size(); }
101  size_t remote_size() const noexcept { return remote_data_.size(); }
102  //@}
103 
104  /// Evaluate the coupling function at the given local and remote
105  /// history entries. The coupling function will be passed the local
106  /// and remote FluxVars and should return a CouplingResult. Values are
107  /// cached internally, so callers should ensure that the coupling
108  /// functions provided on separate calls produce the same result.
109  template <typename Coupling>
110  const CouplingResult& coupling(Coupling&& c, const local_iterator& local,
111  const remote_iterator& remote) const noexcept;
112 
113  // clang-tidy: google-runtime-references
114  void pup(PUP::er& p) noexcept; // NOLINT
115 
116  private:
117  template <size_t Side, typename DataType, typename Iterator>
118  void mark_unneeded(gsl::not_null<DataType*> data,
119  const Iterator& first_needed) noexcept;
120 
123  // We use pointers instead of iterators because deque invalidates
124  // iterators when elements are inserted or removed at the ends, but
125  // not pointers.
126  mutable std::map<std::pair<const Time*, const Time*>, CouplingResult>
127  coupling_cache_;
128 };
129 
130 template <typename LocalVars, typename RemoteVars, typename CouplingResult>
131 template <size_t Side, typename DataType, typename Iterator>
133  gsl::not_null<DataType*> data, const Iterator& first_needed) noexcept {
134  for (auto it = data->begin(); it != first_needed.base(); ++it) {
135  // Clean out cache entries referring to the entry we are removing.
136  for (auto cache_entry = coupling_cache_.begin();
137  cache_entry != coupling_cache_.end();) {
138  if (std::get<Side>(cache_entry->first) == &std::get<0>(*it)) {
139  cache_entry = coupling_cache_.erase(cache_entry);
140  } else {
141  ++cache_entry;
142  }
143  }
144  }
145  data->erase(data->begin(), first_needed.base());
146 }
147 
148 template <typename LocalVars, typename RemoteVars, typename CouplingResult>
149 template <typename Coupling>
150 const CouplingResult&
152  Coupling&& c, const local_iterator& local,
153  const remote_iterator& remote) const noexcept {
154  const auto insert_result = coupling_cache_.insert(
155  std::make_pair(std::make_pair(&*local, &*remote), CouplingResult{}));
156  CouplingResult& inserted_value = insert_result.first->second;
157  const bool is_new_value = insert_result.second;
158  if (is_new_value) {
159  inserted_value =
160  std::forward<Coupling>(c)(cpp17::as_const(std::get<1>(*local.base())),
161  cpp17::as_const(std::get<1>(*remote.base())));
162  }
163  return inserted_value;
164 }
165 
166 template <typename LocalVars, typename RemoteVars, typename CouplingResult>
168  PUP::er& p) noexcept {
169  p | local_data_;
170  p | remote_data_;
171 
172  const size_t cache_size = PUP_stl_container_size(p, coupling_cache_);
173  if (p.isUnpacking()) {
174  for (size_t entry_num = 0; entry_num < cache_size; ++entry_num) {
175  size_t local_index, remote_index;
176  CouplingResult cache_value;
177  p | local_index;
178  p | remote_index;
179  p | cache_value;
180  const auto cache_key =
181  std::make_pair(&std::get<0>(local_data_[local_index]),
182  &std::get<0>(remote_data_[remote_index]));
183  coupling_cache_.insert(std::make_pair(cache_key, cache_value));
184  }
185  } else {
186  for (auto& cache_entry : coupling_cache_) {
187  // clang-tidy: modernize-use-auto - Ensuring the correct type is
188  // important here to prevent undefined behavior in charm. I
189  // want to be explicit.
190  size_t local_index = static_cast<size_t>( // NOLINT
191  std::find_if(
192  local_data_.begin(), local_data_.end(),
193  [goal = cache_entry.first.first](const auto& entry) noexcept {
194  return &std::get<0>(entry) == goal;
195  }) -
196  local_data_.begin());
197  ASSERT(local_index < local_data_.size(),
198  "Failed to find local history entry for cache entry");
199 
200  // clang-tidy: modernize-use-auto - Ensuring the correct type is
201  // important here to prevent undefined behavior in charm. I
202  // want to be explicit.
203  size_t remote_index = static_cast<size_t>( // NOLINT
204  std::find_if(
205  remote_data_.begin(), remote_data_.end(),
206  [goal = cache_entry.first.second](const auto& entry) noexcept {
207  return &std::get<0>(entry) == goal;
208  }) -
209  remote_data_.begin());
210  ASSERT(remote_index < remote_data_.size(),
211  "Failed to find remote history entry for cache entry");
212 
213  p | local_index;
214  p | remote_index;
215  p | cache_entry.second;
216  }
217  }
218 }
219 } // namespace TimeSteppers
local_iterator local_begin() const noexcept
Access to the sequence of times on the indicated side.
Definition: BoundaryHistory.hpp:86
remote_iterator remote_begin() const noexcept
Access to the sequence of times on the indicated side.
Definition: BoundaryHistory.hpp:93
size_t remote_size() const noexcept
Access to the sequence of times on the indicated side.
Definition: BoundaryHistory.hpp:101
void remote_insert(const TimeId &time_id, RemoteVars vars) noexcept
Add a new value to the end of the history of the indicated side.
Definition: BoundaryHistory.hpp:55
The time in a simulation. Times can be safely compared for exact equality as long as they do not belo...
Definition: Time.hpp:31
remote_iterator remote_end() const noexcept
Access to the sequence of times on the indicated side.
Definition: BoundaryHistory.hpp:96
A unique identifier for the temporal state of an integrated system.
Definition: TimeId.hpp:25
constexpr const T & as_const(const T &t) noexcept
Returns a const reference to its argument.
Definition: ConstantExpressions.hpp:408
Defines class TimeId.
#define ASSERT(a, m)
Assert that an expression should be true.
Definition: Assert.hpp:49
size_t local_size() const noexcept
Access to the sequence of times on the indicated side.
Definition: BoundaryHistory.hpp:100
void remote_insert_initial(const TimeId &time_id, RemoteVars vars) noexcept
Add a new value to the front of the history of the indicated side. This is often convenient for setti...
Definition: BoundaryHistory.hpp:66
void local_insert(const TimeId &time_id, LocalVars vars) noexcept
Add a new value to the end of the history of the indicated side.
Definition: BoundaryHistory.hpp:52
Defines Time and TimeDelta.
History data used by a TimeStepper for boundary integration.
Definition: BoundaryHistory.hpp:31
PUP routines for new C+11 STL containers and other standard library objects Charm does not provide im...
local_iterator local_end() const noexcept
Access to the sequence of times on the indicated side.
Definition: BoundaryHistory.hpp:89
void remote_mark_unneeded(const remote_iterator &first_needed) noexcept
Mark all data before the passed point in history on the indicated side as unneeded so it can be remov...
Definition: BoundaryHistory.hpp:79
Define simple functions for constant expressions.
Defines macro ASSERT.
void local_insert_initial(const TimeId &time_id, LocalVars vars) noexcept
Add a new value to the front of the history of the indicated side. This is often convenient for setti...
Definition: BoundaryHistory.hpp:63
const CouplingResult & coupling(Coupling &&c, const local_iterator &local, const remote_iterator &remote) const noexcept
Evaluate the coupling function at the given local and remote history entries. The coupling function w...
Definition: BoundaryHistory.hpp:151
void local_mark_unneeded(const local_iterator &first_needed) noexcept
Mark all data before the passed point in history on the indicated side as unneeded so it can be remov...
Definition: BoundaryHistory.hpp:76
Defines functions and classes from the GSL.
gsl::not_null< T * > make_not_null(T *ptr) noexcept
Construct a not_null from a pointer. Often this will be done as an implicit conversion, but it may be necessary to perform the conversion explicitly when type deduction is desired.
Definition: Gsl.hpp:863
Holds classes that take time steps.
Definition: BoundaryHistory.hpp:23
Require a pointer to not be a nullptr
Definition: ConservativeFromPrimitive.hpp:12