Weno.hpp
1 // Distributed under the MIT License.
2 // See LICENSE.txt for details.
3 
4 #pragma once
5 
6 #include <array>
7 #include <boost/functional/hash.hpp> // IWYU pragma: keep
8 #include <cstddef>
9 #include <limits>
10 #include <string>
11 #include <type_traits>
12 #include <unordered_map>
13 #include <utility>
14 
18 #include "Domain/SizeOfElement.hpp"
19 #include "Domain/Structure/Element.hpp" // IWYU pragma: keep
20 #include "Domain/Structure/OrientationMapHelpers.hpp"
21 #include "Domain/Tags.hpp" // IWYU pragma: keep
22 #include "Evolution/DiscontinuousGalerkin/Limiters/HwenoImpl.hpp"
23 #include "Evolution/DiscontinuousGalerkin/Limiters/MinmodTci.hpp"
24 #include "Evolution/DiscontinuousGalerkin/Limiters/MinmodType.hpp"
25 #include "Evolution/DiscontinuousGalerkin/Limiters/SimpleWenoImpl.hpp"
26 #include "Evolution/DiscontinuousGalerkin/Limiters/WenoGridHelpers.hpp"
27 #include "Evolution/DiscontinuousGalerkin/Limiters/WenoHelpers.hpp"
28 #include "Evolution/DiscontinuousGalerkin/Limiters/WenoType.hpp"
29 #include "NumericalAlgorithms/Interpolation/RegularGridInterpolant.hpp"
32 #include "Options/Options.hpp"
33 #include "Utilities/Algorithm.hpp"
35 #include "Utilities/Gsl.hpp"
36 #include "Utilities/MakeArray.hpp"
37 #include "Utilities/TMPL.hpp"
38 #include "Utilities/TaggedTuple.hpp"
39 
40 /// \cond
41 template <size_t VolumeDim>
42 class Direction;
43 template <size_t VolumeDim>
44 class ElementId;
45 
46 namespace PUP {
47 class er;
48 } // namespace PUP
49 /// \endcond
50 
51 namespace Limiters {
52 /// \ingroup LimitersGroup
53 /// \brief A compact-stencil WENO limiter for DG
54 ///
55 /// Implements the simple WENO limiter of \cite Zhong2013 and the Hermite WENO
56 /// (HWENO) limiter of \cite Zhu2016. The implementation is system-agnostic and
57 /// can act on an arbitrary set of tensors.
58 ///
59 /// #### Summary of the compact-stencil WENO algorithms:
60 //
61 /// The compact-stencil WENO limiters require communication only between
62 /// nearest-neighbor elements, but aim to preserve the full order of the DG
63 /// solution when the solution is smooth. To achieve this, full volume data is
64 /// communicated between neighbors.
65 //
66 /// For each tensor component to limit, the new solution is obtained by a
67 /// standard WENO procedure --- the new solution is a linear combination of
68 /// different polynomials, with weights chosen so that the smoother (i.e., less
69 /// oscillatory) polynomials contribute the most to the sum.
70 ///
71 /// For the simple WENO and HWENO limiters, the polynomials used are the local
72 /// DG solution as well as a "modified" solution from each neighbor element. For
73 /// the simple WENO limiter, the modified solution is obtained by simply
74 /// extrapolating the neighbor solution onto the troubled element. For the HWENO
75 /// limiter, the modified solution is obtained by a least-squares fit to the
76 /// solution across multiple neighboring elements.
77 ///
78 /// #### Notes on the SpECTRE implemention of the WENO limiters:
79 ///
80 /// There are a few differences between the limiters as implemented in SpECTRE
81 /// and as presented in the references. We list them here and discuss them
82 /// further below.
83 /// 1. The choice of basis to represent the DG solution
84 /// 2. The system-agnostic implementation
85 /// 3. The oscillation indicator
86 ///
87 /// Finally, in 4., we will discuss the geometric limitations of the
88 /// implementation (which are not a deviation from the references).
89 ///
90 /// ##### 1. The choice of basis
91 ///
92 /// SpECTRE uses a Legendre basis, rather than the polynomial basis that we
93 /// understand to be used in the references. Because the construction of the
94 /// modified neighbor solutions and the WENO sum is geometrically motivated, the
95 /// overall algorithm should work similarly. However, the precise numerics may
96 /// differ.
97 ///
98 /// ##### 2. The system-agnostic implementation
99 //
100 /// This implementation can act on an arbitrary set of tensors. To reach this
101 /// generality, our HWENO implementation uses a different troubled-cell
102 /// indicator (TCI) than the reference, which instead specializes the TCI to the
103 /// Newtonian Euler system of equations.
104 ///
105 /// This implementation uses the minmod-based TVB TCI of \cite Cockburn1999 to
106 /// identify elements that need limiting. The simple WENO implementation follows
107 /// its reference: it checks the TCI independently for each tensor component, so
108 /// that only certain tensor components may be limited. The HWENO implementation
109 /// checks the TVB TCI for all tensor components, and if any single component is
110 /// troubled, then all components of all tensors are limited.
111 ///
112 /// When the evolution system has multiple evolved variables, the recommendation
113 /// of the references is to apply the limiter to the system's characteristic
114 /// variables to reduce spurious post-limiting oscillations. In SpECTRE,
115 /// applying the limiter to the characteristic variables requires specializing
116 /// the limiter to each evolution system. The system-specific limiter can also
117 /// implement a system-specific TCI (as the HWENO reference does) to more
118 /// precisely trigger the limiter.
119 ///
120 /// ##### 3. The oscillation indicator
121 ///
122 /// We use the oscillation indicator of \cite Dumbser2007, modified for use on
123 /// the square/cube grids of SpECTRE. We favor this indicator because portions
124 /// of the work can be precomputed, leading to an oscillation measure that is
125 /// efficient to evaluate.
126 ///
127 /// ##### 4. The geometric limitations
128 ///
129 /// Does not support non-Legendre bases; this is checked in DEBUG mode. In
130 /// principle other bases could be supported, but this would require
131 /// generalizing the many internal algorithms that assume a Legendre basis.
132 ///
133 /// Does not support h- or p-refinement; this is checked always. In principle
134 /// this could be supported. The modified neighbor solution algorithm would
135 /// need to be generalized (reasonable for simple WENO, extremely tedious for
136 /// HWENO), and the sum of neighbor solutions may need to be updated as well.
137 ///
138 /// Does not support curved elements; this is not enforced. The code will run
139 /// but we make no guarantees about the results. Specifically, the limiter acts
140 /// in the `Frame::Logical` coordinates, because in these coordinates it is
141 /// straightforward to formulate the algorithm. This means the limiter can
142 /// operate on generic deformed grids --- however, some things can start to
143 /// break down, especially on strongly deformed grids:
144 /// 1. When the Jacobian (from `Frame::Logical` to `Frame::Inertial`) varies
145 /// across the element, then the limiter fails to be conservative. This is
146 /// because the integral of a tensor `u` over the element will change after
147 /// the limiter activates on `u`.
148 /// 2. When computing the modified neighbor solution for the WENO sum, the
149 /// extrapolation or fitting procedure may not properly account for the
150 /// coordinates of the source data. If the coordinate map of the neighbor
151 /// differs from that of the local element, then the logical-coordinate
152 /// representation of the neighbor data may be incorrect. This may be a
153 /// large error at Block boundaries with discontinuous map changes, and may
154 /// be a small error from smoothly-varying maps that are not sufficiently
155 /// resolved from one element to the next.
156 template <size_t VolumeDim, typename TagsToLimit>
157 class Weno;
158 
159 template <size_t VolumeDim, typename... Tags>
160 class Weno<VolumeDim, tmpl::list<Tags...>> {
161  public:
162  /// \brief The WenoType
163  ///
164  /// One of `Limiters::WenoType`. See the `Limiters::Weno`
165  /// documentation for details.
166  struct Type {
167  using type = WenoType;
168  static constexpr Options::String help = {"Type of WENO limiter"};
169  };
170  /// \brief The linear weight given to each neighbor
171  ///
172  /// This linear weight gets combined with the oscillation indicator to
173  /// compute the weight for each WENO estimated solution. The standard value
174  /// in the literature is 0.001; larger values may be better suited for
175  /// problems with strong shocks, and smaller values may be better suited to
176  /// smooth problems.
177  struct NeighborWeight {
178  using type = double;
179  static type lower_bound() noexcept { return 1e-6; }
180  static type upper_bound() noexcept { return 0.1; }
181  static constexpr Options::String help = {
182  "Linear weight for each neighbor element's solution"};
183  };
184  /// \brief The TVB constant for the minmod TCI
185  ///
186  /// See `Limiters::Minmod` documentation for details.
187  struct TvbConstant {
188  using type = double;
189  static type lower_bound() noexcept { return 0.0; }
190  static constexpr Options::String help = {"TVB constant 'm'"};
191  };
192  /// \brief Turn the limiter off
193  ///
194  /// This option exists to temporarily disable the limiter for debugging
195  /// purposes. For problems where limiting is not needed, the preferred
196  /// approach is to not compile the limiter into the executable.
197  struct DisableForDebugging {
198  using type = bool;
199  static type suggested_value() noexcept { return false; }
200  static constexpr Options::String help = {"Disable the limiter"};
201  };
202  using options =
203  tmpl::list<Type, NeighborWeight, TvbConstant, DisableForDebugging>;
204  static constexpr Options::String help = {"A WENO limiter for DG"};
205 
206  Weno(WenoType weno_type, double neighbor_linear_weight, double tvb_constant,
207  bool disable_for_debugging = false) noexcept;
208 
209  Weno() noexcept = default;
210  Weno(const Weno& /*rhs*/) = default;
211  Weno& operator=(const Weno& /*rhs*/) = default;
212  Weno(Weno&& /*rhs*/) noexcept = default;
213  Weno& operator=(Weno&& /*rhs*/) noexcept = default;
214  ~Weno() = default;
215 
216  // NOLINTNEXTLINE(google-runtime-references)
217  void pup(PUP::er& p) noexcept;
218 
219  /// \brief Data to send to neighbor elements
220  struct PackagedData {
221  Variables<tmpl::list<Tags...>> volume_data;
223  Mesh<VolumeDim> mesh;
224  std::array<double, VolumeDim> element_size =
225  make_array<VolumeDim>(std::numeric_limits<double>::signaling_NaN());
226 
227  // NOLINTNEXTLINE(google-runtime-references)
228  void pup(PUP::er& p) noexcept {
229  p | volume_data;
230  p | means;
231  p | mesh;
232  p | element_size;
233  }
234  };
235 
236  using package_argument_tags =
237  tmpl::list<Tags..., domain::Tags::Mesh<VolumeDim>,
239 
240  /// \brief Package data for sending to neighbor elements
241  void package_data(gsl::not_null<PackagedData*> packaged_data,
242  const typename Tags::type&... tensors,
243  const Mesh<VolumeDim>& mesh,
244  const std::array<double, VolumeDim>& element_size,
245  const OrientationMap<VolumeDim>& orientation_map) const
246  noexcept;
247 
248  using limit_tags = tmpl::list<Tags...>;
249  using limit_argument_tags =
250  tmpl::list<domain::Tags::Mesh<VolumeDim>,
253 
254  /// \brief Limit the solution on the element
255  bool operator()(
257  const Mesh<VolumeDim>& mesh, const Element<VolumeDim>& element,
258  const std::array<double, VolumeDim>& element_size,
259  const std::unordered_map<
262  neighbor_data) const noexcept;
263 
264  private:
265  template <size_t LocalDim, typename LocalTagList>
266  // NOLINTNEXTLINE(readability-redundant-declaration) false positive
267  friend bool operator==(const Weno<LocalDim, LocalTagList>& lhs,
268  const Weno<LocalDim, LocalTagList>& rhs) noexcept;
269 
270  WenoType weno_type_;
271  double neighbor_linear_weight_;
272  double tvb_constant_;
273  bool disable_for_debugging_;
274 };
275 
276 template <size_t VolumeDim, typename... Tags>
277 Weno<VolumeDim, tmpl::list<Tags...>>::Weno(
278  const WenoType weno_type, const double neighbor_linear_weight,
279  const double tvb_constant, const bool disable_for_debugging) noexcept
280  : weno_type_(weno_type),
281  neighbor_linear_weight_(neighbor_linear_weight),
282  tvb_constant_(tvb_constant),
283  disable_for_debugging_(disable_for_debugging) {}
284 
285 template <size_t VolumeDim, typename... Tags>
286 // NOLINTNEXTLINE(google-runtime-references)
287 void Weno<VolumeDim, tmpl::list<Tags...>>::pup(PUP::er& p) noexcept {
288  p | weno_type_;
289  p | neighbor_linear_weight_;
290  p | tvb_constant_;
291  p | disable_for_debugging_;
292 }
293 
294 template <size_t VolumeDim, typename... Tags>
295 void Weno<VolumeDim, tmpl::list<Tags...>>::package_data(
296  const gsl::not_null<PackagedData*> packaged_data,
297  const typename Tags::type&... tensors, const Mesh<VolumeDim>& mesh,
298  const std::array<double, VolumeDim>& element_size,
299  const OrientationMap<VolumeDim>& orientation_map) const noexcept {
300  // By always initializing the PackagedData Variables member, we avoid an
301  // assertion that arises from having a default-constructed Variables in a
302  // disabled limiter. There is a performance cost, because the package_data()
303  // function does non-zero work even for a disabled limiter... but since the
304  // limiter should never be disabled in a production simulation, this cost
305  // should never matter.
306  (packaged_data->volume_data).initialize(mesh.number_of_grid_points());
307 
308  if (UNLIKELY(disable_for_debugging_)) {
309  // Do not initialize packaged_data
310  // (except for the Variables member "volume_data", see above)
311  return;
312  }
313 
314  const auto wrap_compute_means = [&mesh, &packaged_data](
315  auto tag, const auto tensor) noexcept {
316  for (size_t i = 0; i < tensor.size(); ++i) {
317  // Compute the mean using the local orientation of the tensor and mesh.
318  get<::Tags::Mean<decltype(tag)>>(packaged_data->means)[i] =
319  mean_value(tensor[i], mesh);
320  }
321  return '0';
322  };
323  expand_pack(wrap_compute_means(Tags{}, tensors)...);
324 
325  packaged_data->element_size =
326  orientation_map.permute_from_neighbor(element_size);
327 
328  const auto wrap_copy_tensor = [&packaged_data](auto tag,
329  const auto tensor) noexcept {
330  get<decltype(tag)>(packaged_data->volume_data) = tensor;
331  return '0';
332  };
333  expand_pack(wrap_copy_tensor(Tags{}, tensors)...);
334  packaged_data->volume_data = orient_variables(
335  packaged_data->volume_data, mesh.extents(), orientation_map);
336 
337  packaged_data->mesh = orientation_map(mesh);
338 }
339 
340 template <size_t VolumeDim, typename... Tags>
341 bool Weno<VolumeDim, tmpl::list<Tags...>>::operator()(
343  const Mesh<VolumeDim>& mesh, const Element<VolumeDim>& element,
344  const std::array<double, VolumeDim>& element_size,
345  const std::unordered_map<
347  boost::hash<std::pair<Direction<VolumeDim>, ElementId<VolumeDim>>>>&
348  neighbor_data) const noexcept {
349  if (UNLIKELY(disable_for_debugging_)) {
350  // Do not modify input tensors
351  return false;
352  }
353 
354  // Check that basis is LGL or LG
355  // A Legendre basis is assumed for the oscillation indicator (used in both
356  // SimpleWeno and Hweno) and in the Hweno reconstruction.
357  ASSERT(mesh.basis() == make_array<VolumeDim>(Spectral::Basis::Legendre),
358  "Unsupported basis: " << mesh);
359  ASSERT(mesh.quadrature() ==
360  make_array<VolumeDim>(Spectral::Quadrature::GaussLobatto) or
361  mesh.quadrature() ==
362  make_array<VolumeDim>(Spectral::Quadrature::Gauss),
363  "Unsupported quadrature: " << mesh);
364 
365  // Enforce restrictions on h-refinement, p-refinement
366  if (UNLIKELY(alg::any_of(element.neighbors(),
367  [](const auto& direction_neighbors) noexcept {
368  return direction_neighbors.second.size() != 1;
369  }))) {
370  ERROR("The Weno limiter does not yet support h-refinement");
371  // Removing this limitation will require:
372  // - Generalizing the computation of the modified neighbor solutions.
373  // - Generalizing the WENO weighted sum for multiple neighbors in each
374  // direction.
375  }
376  alg::for_each(neighbor_data, [&mesh](const auto& neighbor_and_data) noexcept {
377  if (UNLIKELY(neighbor_and_data.second.mesh != mesh)) {
378  ERROR("The Weno limiter does not yet support p-refinement");
379  // Removing this limitation will require generalizing the
380  // computation of the modified neighbor solutions.
381  }
382  });
383 
384  if (weno_type_ == WenoType::Hweno) {
385  // Troubled-cell detection for HWENO flags the element for limiting if any
386  // component of any tensor needs limiting.
387  const bool cell_is_troubled =
388  Tci::tvb_minmod_indicator<VolumeDim, PackagedData, Tags...>(
389  tvb_constant_, (*tensors)..., mesh, element, element_size,
390  neighbor_data);
391  if (not cell_is_troubled) {
392  // No limiting is needed
393  return false;
394  }
395 
398  boost::hash<std::pair<Direction<VolumeDim>, ElementId<VolumeDim>>>>
399  modified_neighbor_solution_buffer{};
400  for (const auto& neighbor_and_data : neighbor_data) {
401  const auto& neighbor = neighbor_and_data.first;
402  modified_neighbor_solution_buffer.insert(
403  make_pair(neighbor, DataVector(mesh.number_of_grid_points())));
404  }
405 
406  EXPAND_PACK_LEFT_TO_RIGHT(Weno_detail::hweno_impl<Tags>(
407  make_not_null(&modified_neighbor_solution_buffer), tensors,
408  neighbor_linear_weight_, mesh, element, neighbor_data));
409  return true; // cell_is_troubled
410 
411  } else if (weno_type_ == WenoType::SimpleWeno) {
412  // Buffers and pre-computations for TCI
413  Minmod_detail::BufferWrapper<VolumeDim> tci_buffer(mesh);
414  const auto effective_neighbor_sizes =
415  Minmod_detail::compute_effective_neighbor_sizes(element, neighbor_data);
416 
417  // Buffers for simple WENO implementation
421  boost::hash<std::pair<Direction<VolumeDim>, ElementId<VolumeDim>>>>
422  interpolator_buffer{};
425  boost::hash<std::pair<Direction<VolumeDim>, ElementId<VolumeDim>>>>
426  modified_neighbor_solution_buffer{};
427 
428  bool some_component_was_limited = false;
429 
430  const auto wrap_minmod_tci_and_simple_weno_impl =
431  [this, &some_component_was_limited, &tci_buffer, &interpolator_buffer,
432  &modified_neighbor_solution_buffer, &mesh, &element, &element_size,
433  &neighbor_data,
434  &effective_neighbor_sizes](auto tag, const auto tensor) noexcept {
435  for (size_t tensor_storage_index = 0;
436  tensor_storage_index < tensor->size(); ++tensor_storage_index) {
437  // Check TCI
438  const auto effective_neighbor_means =
439  Minmod_detail::compute_effective_neighbor_means<decltype(tag)>(
440  tensor_storage_index, element, neighbor_data);
441  const bool component_needs_limiting = Tci::tvb_minmod_indicator(
442  make_not_null(&tci_buffer), tvb_constant_,
443  (*tensor)[tensor_storage_index], mesh, element, element_size,
444  effective_neighbor_means, effective_neighbor_sizes);
445 
446  if (component_needs_limiting) {
447  if (modified_neighbor_solution_buffer.empty()) {
448  // Allocate the neighbor solution buffers only if the limiter is
449  // triggered. This reduces allocation when no limiting occurs.
450  for (const auto& neighbor_and_data : neighbor_data) {
451  const auto& neighbor = neighbor_and_data.first;
452  modified_neighbor_solution_buffer.insert(make_pair(
453  neighbor, DataVector(mesh.number_of_grid_points())));
454  }
455  }
456  Weno_detail::simple_weno_impl<decltype(tag)>(
457  make_not_null(&interpolator_buffer),
458  make_not_null(&modified_neighbor_solution_buffer), tensor,
459  neighbor_linear_weight_, tensor_storage_index, mesh, element,
460  neighbor_data);
461  some_component_was_limited = true;
462  }
463  }
464  return '0';
465  };
466  expand_pack(wrap_minmod_tci_and_simple_weno_impl(Tags{}, tensors)...);
467  return some_component_was_limited; // cell_is_troubled
468  } else {
469  ERROR("WENO limiter not implemented for WenoType: " << weno_type_);
470  }
471 
472  return false; // cell_is_troubled
473 }
474 
475 template <size_t LocalDim, typename LocalTagList>
476 bool operator==(const Weno<LocalDim, LocalTagList>& lhs,
477  const Weno<LocalDim, LocalTagList>& rhs) noexcept {
478  return lhs.weno_type_ == rhs.weno_type_ and
479  lhs.neighbor_linear_weight_ == rhs.neighbor_linear_weight_ and
480  lhs.tvb_constant_ == rhs.tvb_constant_ and
481  lhs.disable_for_debugging_ == rhs.disable_for_debugging_;
482 }
483 
484 template <size_t VolumeDim, typename TagList>
485 bool operator!=(const Weno<VolumeDim, TagList>& lhs,
486  const Weno<VolumeDim, TagList>& rhs) noexcept {
487  return not(lhs == rhs);
488 }
489 
490 } // namespace Limiters
expand_pack
constexpr void expand_pack(Ts &&...) noexcept
Allows zero-cost unordered expansion of a parameter.
Definition: TMPL.hpp:585
Limiters
Things relating to limiting.
Definition: HwenoImpl.hpp:42
alg::any_of
decltype(auto) any_of(const Container &c, UnaryPredicate &&unary_predicate)
Convenience wrapper around std::any_of.
Definition: Algorithm.hpp:190
orient_variables
Variables< TagsList > orient_variables(const Variables< TagsList > &variables, const Index< VolumeDim > &extents, const OrientationMap< VolumeDim > &orientation_of_neighbor) noexcept
Definition: OrientationMapHelpers.hpp:55
EXPAND_PACK_LEFT_TO_RIGHT
#define EXPAND_PACK_LEFT_TO_RIGHT(...)
Expand a parameter pack evaluating the terms from left to right.
Definition: TMPL.hpp:601
utility
UNLIKELY
#define UNLIKELY(x)
Definition: Gsl.hpp:73
std::pair
Options.hpp
Tags.hpp
MakeArray.hpp
domain::Tags::Element
Definition: Tags.hpp:97
Limiters::Weno
A compact-stencil WENO limiter for DG.
Definition: Weno.hpp:157
Tags::Mean
Given the Tag holding a Tensor<DataVector, ...>, swap the DataVector with a double.
Definition: Tags.hpp:23
domain::Tags::Mesh
The computational grid of the Element in the DataBox.
Definition: Tags.hpp:107
db::get
const auto & get(const DataBox< TagList > &box) noexcept
Retrieve the item with tag Tag from the DataBox.
Definition: DataBox.hpp:791
OrientationMap
A mapping of the logical coordinate axes of a host to the logical coordinate axes of a neighbor of th...
Definition: OrientationMap.hpp:34
Direction
Definition: Direction.hpp:23
Element
Definition: Element.hpp:29
domain::Tags::SizeOfElement
Definition: SizeOfElement.hpp:77
ElementId
An ElementId uniquely labels an Element.
Definition: ElementId.hpp:51
ERROR
#define ERROR(m)
prints an error message to the standard error stream and aborts the program.
Definition: Error.hpp:37
std::add_pointer_t
cstddef
Assert.hpp
array
tuples::TaggedTuple
An associative container that is indexed by structs.
Definition: TaggedTuple.hpp:271
DataVector
Stores a collection of function values.
Definition: DataVector.hpp:46
mean_value
double mean_value(const DataVector &f, const Mesh< Dim > &mesh) noexcept
Compute the mean value of a function over a manifold.
Definition: MeanValue.hpp:47
alg::for_each
decltype(auto) for_each(const Container &c, UnaryFunction &&f)
Convenience wrapper around std::for_each, returns the result of std::for_each(begin(c),...
Definition: Algorithm.hpp:291
ASSERT
#define ASSERT(a, m)
Assert that an expression should be true.
Definition: Assert.hpp:49
Variables.hpp
Element.hpp
Mesh< VolumeDim >
limits
Gsl.hpp
Options::String
const char *const String
The string used in option structs.
Definition: Options.hpp:32
Limiters::WenoType
WenoType
Possible types of the WENO limiter.
Definition: WenoType.hpp:21
Tensor.hpp
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
intrp::RegularGrid
Interpolate data from a Mesh onto a regular grid of points.
Definition: RegularGridInterpolant.hpp:45
Prefixes.hpp
unordered_map
std::numeric_limits
type_traits
TMPL.hpp
MeanValue.hpp
Mesh.hpp
gsl::not_null
Require a pointer to not be a nullptr
Definition: ReadSpecPiecewisePolynomial.hpp:13
string