ElementActions.hpp
1 // Distributed under the MIT License.
2 // See LICENSE.txt for details.
3 
4 #pragma once
5 
6 #include <cstddef>
7 #include <map>
8 #include <string>
9 #include <tuple>
10 #include <unordered_map>
11 #include <utility>
12 #include <vector>
13 
15 #include "DataStructures/DataBox/PrefixHelpers.hpp"
16 #include "DataStructures/DataBox/Tag.hpp"
17 #include "DataStructures/Index.hpp"
18 #include "Domain/InterfaceComputeTags.hpp"
19 #include "Domain/InterfaceHelpers.hpp"
22 #include "Domain/Structure/OrientationMapHelpers.hpp"
23 #include "Domain/Tags.hpp"
24 #include "IO/Observer/Actions/RegisterWithObservers.hpp"
25 #include "IO/Observer/ArrayComponentId.hpp"
26 #include "IO/Observer/ObservationId.hpp"
27 #include "IO/Observer/ReductionActions.hpp"
28 #include "IO/Observer/TypeOfObservation.hpp"
29 #include "Informer/Tags.hpp"
30 #include "Informer/Verbosity.hpp"
31 #include "NumericalAlgorithms/Convergence/Tags.hpp"
32 #include "NumericalAlgorithms/LinearSolver/Gmres.hpp"
35 #include "Options/Options.hpp"
36 #include "Parallel/GlobalCache.hpp"
37 #include "Parallel/InboxInserters.hpp"
38 #include "Parallel/Invoke.hpp"
39 #include "Parallel/ParallelComponentHelpers.hpp"
40 #include "Parallel/Printf.hpp"
41 #include "Parallel/Reduction.hpp"
42 #include "ParallelAlgorithms/DiscontinuousGalerkin/HasReceivedFromAllMortars.hpp"
43 #include "ParallelAlgorithms/Initialization/MergeIntoDataBox.hpp"
44 #include "ParallelAlgorithms/Initialization/MutateAssign.hpp"
46 #include "ParallelAlgorithms/LinearSolver/Schwarz/ComputeTags.hpp"
47 #include "ParallelAlgorithms/LinearSolver/Schwarz/ElementCenteredSubdomainData.hpp"
48 #include "ParallelAlgorithms/LinearSolver/Schwarz/OverlapHelpers.hpp"
49 #include "ParallelAlgorithms/LinearSolver/Schwarz/Protocols.hpp"
50 #include "ParallelAlgorithms/LinearSolver/Schwarz/Tags.hpp"
52 #include "Utilities/Gsl.hpp"
53 #include "Utilities/PrettyType.hpp"
54 #include "Utilities/ProtocolHelpers.hpp"
55 #include "Utilities/TMPL.hpp"
56 #include "Utilities/TaggedTuple.hpp"
57 
58 namespace LinearSolver::Schwarz::detail {
59 
60 using reduction_data = Parallel::ReductionData<
61  // Iteration
63  // Number of subdomains (= number of elements)
65  // Average number of subdomain solver iterations
68  // Minimum number of subdomain solver iterations
70  // Maximum number of subdomain solver iterations
72 
73 template <typename OptionsGroup>
74 struct RegisterObservers {
75  template <typename ParallelComponent, typename DbTagsList,
76  typename ArrayIndex>
78  register_info(const db::DataBox<DbTagsList>& /*box*/,
79  const ArrayIndex& /*array_index*/) noexcept {
81  observers::ObservationKey{pretty_type::get_name<OptionsGroup>() +
82  "SubdomainSolves"}};
83  }
84 };
85 
86 template <typename FieldsTag, typename OptionsGroup, typename SourceTag>
87 using RegisterElement =
89 
90 template <typename OptionsGroup, typename ParallelComponent,
91  typename Metavariables, typename ArrayIndex>
92 void contribute_to_subdomain_stats_observation(
93  const size_t iteration_id, const size_t subdomain_solve_num_iterations,
95  const ArrayIndex& array_index) noexcept {
96  auto& local_observer =
97  *Parallel::get_parallel_component<observers::Observer<Metavariables>>(
98  cache)
99  .ckLocalBranch();
100  Parallel::simple_action<observers::Actions::ContributeReductionData>(
101  local_observer,
103  iteration_id,
104  pretty_type::get_name<OptionsGroup>() + "SubdomainSolves"),
107  Parallel::ArrayIndex<ArrayIndex>(array_index)},
108  std::string{"/" + Options::name<OptionsGroup>() + "SubdomainSolves"},
109  std::vector<std::string>{"Iteration", "NumSubdomains", "AvgNumIterations",
110  "MinNumIterations", "MaxNumIterations"},
111  reduction_data{iteration_id, 1, subdomain_solve_num_iterations,
112  subdomain_solve_num_iterations,
113  subdomain_solve_num_iterations});
114 }
115 
116 template <typename SubdomainDataType, typename OptionsGroup>
117 struct SubdomainDataBufferTag : db::SimpleTag {
118  static std::string name() noexcept {
119  return "SubdomainData(" + Options::name<OptionsGroup>() + ")";
120  }
121  using type = SubdomainDataType;
122 };
123 
124 template <typename FieldsTag, typename OptionsGroup, typename SubdomainOperator>
125 struct InitializeElement {
126  private:
127  using fields_tag = FieldsTag;
128  using residual_tag =
130  static constexpr size_t Dim = SubdomainOperator::volume_dim;
131  using SubdomainData =
132  ElementCenteredSubdomainData<Dim, typename residual_tag::tags_list>;
133  // Here we choose a serial GMRES linear solver to solve subdomain problems.
134  // This can be generalized to allow the user to make this choice once that
135  // becomes necessary.
136  using subdomain_solver_tag =
137  Tags::SubdomainSolver<LinearSolver::Serial::Gmres<SubdomainData>,
138  OptionsGroup>;
139 
140  public:
141  using initialization_tags =
142  tmpl::list<domain::Tags::InitialExtents<Dim>, subdomain_solver_tag>;
143  using initialization_tags_to_keep = tmpl::list<subdomain_solver_tag>;
144  using const_global_cache_tags = tmpl::list<Tags::MaxOverlap<OptionsGroup>>;
145 
146  using simple_tags =
147  tmpl::list<Tags::Overlaps<residual_tag, Dim, OptionsGroup>,
148  SubdomainDataBufferTag<SubdomainData, OptionsGroup>>;
149  using compute_tags = tmpl::list<
157  Tags::IntrudingExtentsCompute<Dim, OptionsGroup>,
158  Tags::IntrudingOverlapWidthsCompute<Dim, OptionsGroup>,
160  Tags::ElementWeightCompute<Dim, OptionsGroup>,
163  Tags::IntrudingOverlapWeightCompute<Dim, OptionsGroup>>>;
164  template <typename DbTagsList, typename... InboxTags, typename Metavariables,
165  typename ActionList, typename ParallelComponent>
166  static auto apply(db::DataBox<DbTagsList>& box,
167  const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,
168  const Parallel::GlobalCache<Metavariables>& /*cache*/,
169  const ElementId<Dim>& /*element_id*/,
170  const ActionList /*meta*/,
171  const ParallelComponent* const /*meta*/) noexcept {
172  const auto& element_mesh = db::get<domain::Tags::Mesh<Dim>>(box);
173  const size_t element_num_points = element_mesh.number_of_grid_points();
175  tmpl::list<SubdomainDataBufferTag<SubdomainData, OptionsGroup>>>(
176  make_not_null(&box), SubdomainData{element_num_points});
177  return std::make_tuple(std::move(box));
178  }
179 };
180 
181 // Restrict the residual to neighboring subdomains that overlap with this
182 // element and send the data to those elements
183 template <typename FieldsTag, typename OptionsGroup, typename SubdomainOperator>
185  tmpl::list<db::add_tag_prefix<LinearSolver::Tags::Residual, FieldsTag>>,
186  OptionsGroup, true>;
187 
188 // Wait for the residual data on regions of this element's subdomain that
189 // overlap with other elements.
190 template <typename FieldsTag, typename OptionsGroup, typename SubdomainOperator>
192  SubdomainOperator::volume_dim,
193  tmpl::list<db::add_tag_prefix<LinearSolver::Tags::Residual, FieldsTag>>,
194  OptionsGroup>;
195 
196 template <size_t Dim, typename OptionsGroup, typename OverlapSolution>
197 struct OverlapSolutionInboxTag
199  OverlapSolutionInboxTag<Dim, OptionsGroup, OverlapSolution>> {
200  using temporal_id = size_t;
202 };
203 
204 // Once the residual data is available on all overlaps, solve the restricted
205 // problem for this element-centered subdomain. Apply the weighted solution on
206 // this element directly and send the solution on overlap regions to the
207 // neighbors that they overlap with.
208 template <typename FieldsTag, typename OptionsGroup, typename SubdomainOperator>
209 struct SolveSubdomain {
210  private:
211  using fields_tag = FieldsTag;
212  using residual_tag =
214  static constexpr size_t Dim = SubdomainOperator::volume_dim;
215  using SubdomainData =
216  ElementCenteredSubdomainData<Dim, typename residual_tag::tags_list>;
217  using OverlapData = typename SubdomainData::OverlapData;
218  using overlap_solution_inbox_tag =
219  OverlapSolutionInboxTag<Dim, OptionsGroup, OverlapData>;
220 
221  public:
222  using const_global_cache_tags =
223  tmpl::list<Tags::MaxOverlap<OptionsGroup>,
225 
226  template <typename DbTagsList, typename... InboxTags, typename Metavariables,
227  typename ActionList, typename ParallelComponent>
229  db::DataBox<DbTagsList>& box,
230  const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,
232  const ElementId<Dim>& element_id, const ActionList /*meta*/,
233  const ParallelComponent* const /*meta*/) noexcept {
234  const size_t iteration_id =
235  get<Convergence::Tags::IterationId<OptionsGroup>>(box);
236 
237  // Do some logging
239  ::Verbosity::Debug)) {
241  "%s " + Options::name<OptionsGroup>() + "(%zu): Solve subdomain\n",
242  element_id, iteration_id);
243  }
244 
245  const auto& element = db::get<domain::Tags::Element<Dim>>(box);
246  const size_t max_overlap = db::get<Tags::MaxOverlap<OptionsGroup>>(box);
247 
248  // Assemble the subdomain data from the data on the element and the
249  // communicated overlap data
250  db::mutate<SubdomainDataBufferTag<SubdomainData, OptionsGroup>,
251  Tags::Overlaps<residual_tag, Dim, OptionsGroup>>(
252  make_not_null(&box),
253  [max_overlap, &element](
254  const gsl::not_null<SubdomainData*> subdomain_data,
255  const auto overlap_residuals, const auto& residual) noexcept {
256  subdomain_data->element_data = residual;
257  // Nothing was communicated if the overlaps are empty
258  if (LIKELY(max_overlap > 0 and element.number_of_neighbors() > 0)) {
259  subdomain_data->overlap_data = std::move(*overlap_residuals);
260  }
261  },
262  db::get<residual_tag>(box));
263  const auto& subdomain_residual =
264  db::get<SubdomainDataBufferTag<SubdomainData, OptionsGroup>>(box);
265 
266  // Allocate workspace memory for repeatedly applying the subdomain operator
267  const size_t num_points =
268  db::get<domain::Tags::Mesh<Dim>>(box).number_of_grid_points();
269  SubdomainOperator subdomain_operator{num_points};
270 
271  // Construct the subdomain operator
272  const auto apply_subdomain_operator =
273  [&box, &subdomain_operator](const gsl::not_null<SubdomainData*> result,
274  const SubdomainData& operand) noexcept {
275  // The subdomain operator can retrieve any information on the subdomain
276  // geometry that is available through the DataBox. The user is responsible
277  // for communicating this information across neighbors if necessary.
278  db::apply<typename SubdomainOperator::element_operator>(
279  box, operand, result, make_not_null(&subdomain_operator));
280  tmpl::for_each<tmpl::list<domain::Tags::InternalDirections<Dim>,
282  [&box, &operand, &result,
283  &subdomain_operator](auto directions_v) noexcept {
284  using directions = tmpl::type_from<decltype(directions_v)>;
285  using face_operator =
286  typename SubdomainOperator::template face_operator<directions>;
287  interface_apply<directions, face_operator>(
288  box, operand, result, make_not_null(&subdomain_operator));
289  });
290  };
291 
292  // Solve the subdomain problem
293  const auto& subdomain_solver =
294  get<Tags::SubdomainSolverBase<OptionsGroup>>(box);
295  auto subdomain_solve_initial_guess_in_solution_out =
296  make_with_value<SubdomainData>(subdomain_residual, 0.);
297  const auto subdomain_solve_has_converged = subdomain_solver.solve(
298  make_not_null(&subdomain_solve_initial_guess_in_solution_out),
299  apply_subdomain_operator, subdomain_residual);
300  // Re-naming the solution buffer for the code below
301  auto& subdomain_solution = subdomain_solve_initial_guess_in_solution_out;
302 
303  // Do some logging and observing
305  ::Verbosity::Quiet)) {
306  if (not subdomain_solve_has_converged or
307  subdomain_solve_has_converged.reason() ==
310  "%s WARNING: Subdomain solver did not converge in %zu iterations: "
311  "%e -> %e\n",
312  element_id, subdomain_solve_has_converged.num_iterations(),
313  subdomain_solve_has_converged.initial_residual_magnitude(),
314  subdomain_solve_has_converged.residual_magnitude());
316  ::Verbosity::Debug)) {
318  "%s Subdomain solver converged in %zu iterations (%s): %e -> %e\n",
319  element_id, subdomain_solve_has_converged.num_iterations(),
320  subdomain_solve_has_converged.reason(),
321  subdomain_solve_has_converged.initial_residual_magnitude(),
322  subdomain_solve_has_converged.residual_magnitude());
323  }
324  }
325  contribute_to_subdomain_stats_observation<OptionsGroup, ParallelComponent>(
326  iteration_id + 1, subdomain_solve_has_converged.num_iterations(), cache,
327  element_id);
328 
329  // Apply weighting
330  if (LIKELY(max_overlap > 0)) {
331  subdomain_solution.element_data *=
332  get(db::get<Tags::Weight<OptionsGroup>>(box));
333  }
334 
335  // Apply solution to central element
336  db::mutate<fields_tag>(make_not_null(&box),
337  [&subdomain_solution](const auto fields) noexcept {
338  *fields += subdomain_solution.element_data;
339  });
340 
341  // Send overlap solutions back to the neighbors that they are on
342  if (LIKELY(max_overlap > 0)) {
343  auto& receiver_proxy =
344  Parallel::get_parallel_component<ParallelComponent>(cache);
345  for (auto& [overlap_id, overlap_solution] :
346  subdomain_solution.overlap_data) {
347  const auto& direction = overlap_id.first;
348  const auto& neighbor_id = overlap_id.second;
349  const auto& orientation =
350  element.neighbors().at(direction).orientation();
351  const auto direction_from_neighbor = orientation(direction.opposite());
352  Parallel::receive_data<overlap_solution_inbox_tag>(
353  receiver_proxy[neighbor_id], iteration_id,
354  std::make_pair(
355  OverlapId<Dim>{direction_from_neighbor, element.id()},
356  std::move(overlap_solution)));
357  }
358  }
359  return {std::move(box)};
360  }
361 };
362 
363 // Wait for the subdomain solutions on regions within this element that overlap
364 // with neighboring element-centered subdomains. Combine the solutions as a
365 // weighted sum.
366 template <typename FieldsTag, typename OptionsGroup, typename SubdomainOperator>
367 struct ReceiveOverlapSolution {
368  private:
369  using fields_tag = FieldsTag;
370  using residual_tag =
372  static constexpr size_t Dim = SubdomainOperator::volume_dim;
373  using SubdomainData =
374  ElementCenteredSubdomainData<Dim, typename residual_tag::tags_list>;
375  using OverlapSolution = typename SubdomainData::OverlapData;
376  using overlap_solution_inbox_tag =
377  OverlapSolutionInboxTag<Dim, OptionsGroup, OverlapSolution>;
378 
379  public:
380  using const_global_cache_tags = tmpl::list<Tags::MaxOverlap<OptionsGroup>>;
381  using inbox_tags = tmpl::list<overlap_solution_inbox_tag>;
382 
383  template <typename DbTagsList, typename... InboxTags, typename Metavariables,
384  size_t Dim>
385  static bool is_ready(const db::DataBox<DbTagsList>& box,
386  const tuples::TaggedTuple<InboxTags...>& inboxes,
387  const Parallel::GlobalCache<Metavariables>& /*cache*/,
388  const ElementId<Dim>& /*element_id*/) noexcept {
389  if (UNLIKELY(db::get<Tags::MaxOverlap<OptionsGroup>>(box) == 0)) {
390  return true;
391  }
392  return dg::has_received_from_all_mortars<overlap_solution_inbox_tag>(
394  get<domain::Tags::Element<Dim>>(box), inboxes);
395  }
396 
397  template <typename DbTagsList, typename... InboxTags, typename Metavariables,
398  typename ActionList, typename ParallelComponent>
400  db::DataBox<DbTagsList>& box, tuples::TaggedTuple<InboxTags...>& inboxes,
401  const Parallel::GlobalCache<Metavariables>& /*cache*/,
402  const ElementId<Dim>& element_id, const ActionList /*meta*/,
403  const ParallelComponent* const /*meta*/) noexcept {
404  const size_t iteration_id =
405  get<Convergence::Tags::IterationId<OptionsGroup>>(box);
406  const auto& element = db::get<domain::Tags::Element<Dim>>(box);
407 
408  // Nothing to do if overlap is empty
409  if (UNLIKELY(db::get<Tags::MaxOverlap<OptionsGroup>>(box) == 0 or
410  element.number_of_neighbors() == 0)) {
411  return {std::move(box)};
412  }
413 
414  // Do some logging
416  ::Verbosity::Debug)) {
417  Parallel::printf("%s " + Options::name<OptionsGroup>() +
418  "(%zu): Receive overlap solution\n",
419  element_id, iteration_id);
420  }
421 
422  // Add solutions on overlaps to this element's solution in a weighted sum
423  const auto received_overlap_solutions =
424  std::move(tuples::get<overlap_solution_inbox_tag>(inboxes)
425  .extract(iteration_id)
426  .mapped());
427  db::mutate<fields_tag>(
428  make_not_null(&box),
429  [&received_overlap_solutions](
430  const auto fields, const Index<Dim>& full_extents,
431  const std::array<size_t, Dim>& all_intruding_extents,
433  all_intruding_overlap_weights) noexcept {
434  for (const auto& [overlap_id, overlap_solution] :
435  received_overlap_solutions) {
436  const auto& direction = overlap_id.first;
437  const auto& intruding_extents =
438  gsl::at(all_intruding_extents, direction.dimension());
439  const auto& overlap_weight =
440  all_intruding_overlap_weights.at(direction);
442  fields, overlap_solution * get(overlap_weight), full_extents,
443  intruding_extents, direction);
444  }
445  },
446  db::get<domain::Tags::Mesh<Dim>>(box).extents(),
447  db::get<Tags::IntrudingExtents<Dim, OptionsGroup>>(box),
449  Tags::Weight<OptionsGroup>>>(box));
450  return {std::move(box)};
451  }
452 };
453 
454 } // namespace LinearSolver::Schwarz::detail
gsl::at
constexpr T & at(std::array< T, N > &arr, Size index)
Retrieve a entry from a container, with checks in Debug mode that the index being retrieved is valid.
Definition: Gsl.hpp:125
observers::ObservationId
A unique identifier for an observation representing the type of observation and the instance (e....
Definition: ObservationId.hpp:71
std::apply
T apply(T... args)
funcl::Divides
Functional for computing / of two objects.
Definition: Functional.hpp:235
std::string
utility
UNLIKELY
#define UNLIKELY(x)
Definition: Gsl.hpp:73
get
constexpr Tag::type & get(Variables< TagList > &v) noexcept
Return Tag::type pointing into the contiguous array.
Definition: Variables.hpp:638
Parallel::GlobalCache
Definition: ElementReceiveInterpPoints.hpp:15
Parallel::ReductionDatum
The data to be reduced, and invokables to be called whenever two reduction messages are combined and ...
Definition: Reduction.hpp:63
std::pair
GlobalCache.hpp
std::index_sequence
Options.hpp
CommunicateOverlapFields.hpp
Tags.hpp
vector
domain::Tags::Element
Definition: Tags.hpp:97
LinearSolver::Schwarz::Actions::ReceiveOverlapFields
Receive data from regions of this element's subdomain that overlap with other elements.
Definition: CommunicateOverlapFields.hpp:160
PrettyType.hpp
Parallel::printf
void printf(const std::string &format, Args &&... args)
Print an atomic message to stdout with C printf usage.
Definition: Printf.hpp:103
db::add_tag_prefix
typename detail::add_tag_prefix_impl< Prefix, Tag, Args... >::type add_tag_prefix
Definition: PrefixHelpers.hpp:51
observers::ObservationKey
Used as a key in maps to keep track of how many elements have registered.
Definition: ObservationId.hpp:28
tuple
db::get
const auto & get(const DataBox< TagList > &box) noexcept
Retrieve the item with tag Tag from the DataBox.
Definition: DataBox.hpp:969
Index
Definition: Index.hpp:31
db::SimpleTag
Mark a struct as a simple tag by inheriting from this.
Definition: Tag.hpp:36
domain::Tags::InternalDirectionsCompute
Definition: Tags.hpp:275
domain::Tags::BoundaryDirectionsInterior
Definition: Tags.hpp:296
Spectral.hpp
Tags.hpp
Direction
Definition: Direction.hpp:23
LinearSolver::Schwarz::Actions::SendOverlapFields
Send data on regions that overlap with other subdomains to their corresponding elements.
Definition: CommunicateOverlapFields.hpp:68
Initialization::mutate_assign
constexpr void mutate_assign(const gsl::not_null< db::DataBox< BoxTags > * > box, Args &&... args) noexcept
Perform a mutation to the DataBox box, assigning the args to the tags in MutateTagList in order.
Definition: MutateAssign.hpp:40
ElementId< Dim >
ElementId.hpp
std::add_pointer_t
LinearSolver::Schwarz::add_overlap_data
void add_overlap_data(const gsl::not_null< Variables< VolumeTagsList > * > volume_data, const Variables< OverlapTagsList > &overlap_data, const Index< Dim > &volume_extents, const size_t overlap_extent, const Direction< Dim > &direction) noexcept
Add the overlap_data to the volume_data
Definition: OverlapHelpers.hpp:226
ActionTesting::is_ready
bool is_ready(MockRuntimeSystem< Metavariables > &runner, const typename Component::array_index &array_index) noexcept
Runs the is_ready function and returns the result for the next action in the current phase on the arr...
Definition: MockRuntimeSystemFreeFunctions.hpp:155
Printf.hpp
DataBox.hpp
cstddef
logging::Tags::Verbosity
Tag for putting Verbosity in a DataBox.
Definition: Tags.hpp:33
std::array< size_t, Dim >
domain::Tags::LogicalCoordinates
Definition: LogicalCoordinates.hpp:78
Index.hpp
tuples::TaggedTuple
An associative container that is indexed by structs.
Definition: TaggedTuple.hpp:271
map
Element.hpp
Convergence::Tags::IterationId
Identifies a step in an iterative algorithm.
Definition: Tags.hpp:74
Scalar
Tensor< T, Symmetry<>, index_list<> > Scalar
Definition: TypeAliases.hpp:21
Gsl.hpp
domain::Tags::Interface
Tag which is either a SimpleTag for quantities on an interface, base tag to a compute item which acts...
Definition: Tags.hpp:365
domain::Tags::InterfaceCompute
Compute tag for representing items computed on a set of interfaces. Can be retrieved using Tags::Inte...
Definition: InterfaceComputeTags.hpp:66
domain::Tags::Direction
Definition: Tags.hpp:381
domain::Tags::InternalDirections
Definition: Tags.hpp:269
observers::TypeOfObservation::Reduction
@ Reduction
The sender will only perform reduction observations.
LIKELY
#define LIKELY(x)
Definition: Gsl.hpp:67
observers::ArrayComponentId
An ID type that identifies both the parallel component and the index in the parallel component.
Definition: ArrayComponentId.hpp:27
make_not_null
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,...
Definition: Gsl.hpp:880
Parallel::ArrayIndex
The array index used for indexing Chare Arrays, mostly an implementation detail.
Definition: ArrayIndex.hpp:28
Mesh.hpp
domain::Tags::BoundaryDirectionsInteriorCompute
Definition: Tags.hpp:302
unordered_map
Convergence::Reason::MaxIterations
@ MaxIterations
Reached the maximum number of iterations. Can be interpreted as an error condition.
TMPL.hpp
gsl::not_null
Require a pointer to not be a nullptr
Definition: ReadSpecThirdOrderPiecewisePolynomial.hpp:13
Parallel::InboxInserters::Map
Inserter for inserting data that is received as the value_type (with non-const key_type) of a map dat...
Definition: InboxInserters.hpp:36
observers::Actions::RegisterWithObservers
Register an observation ID with the observers.
Definition: RegisterWithObservers.hpp:39
string