BoundaryCorrections.hpp
1 // Distributed under the MIT License.
2 // See LICENSE.txt for details.
3 
4 #pragma once
5 
7 
8 #include <cstddef>
9 #include <optional>
10 #include <random>
11 #include <string>
12 #include <type_traits>
13 
14 #include "DataStructures/DataBox/PrefixHelpers.hpp"
16 #include "DataStructures/DataVector.hpp"
18 #include "DataStructures/Tensor/EagerMath/Magnitude.hpp"
21 #include "Framework/Pypp.hpp"
24 #include "NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp"
26 #include "Utilities/Gsl.hpp"
27 #include "Utilities/TMPL.hpp"
28 #include "Utilities/TaggedTuple.hpp"
29 
30 namespace TestHelpers::evolution::dg {
31 /// Indicate if the boundary correction should be zero when the solution is
32 /// smooth (should pretty much always be `Yes`)
33 enum class ZeroOnSmoothSolution { Yes, No };
34 
35 namespace detail {
36 template <bool HasPrimitiveVars = false>
37 struct get_correction_primitive_vars_impl {
38  template <typename BoundaryCorrection>
39  using f = tmpl::list<>;
40 };
41 
42 template <>
43 struct get_correction_primitive_vars_impl<true> {
44  template <typename BoundaryCorrection>
45  using f = typename BoundaryCorrection::dg_package_data_primitive_tags;
46 };
47 
48 template <bool HasPrimitiveVars, typename BoundaryCorrection>
49 using get_correction_primitive_vars =
50  typename get_correction_primitive_vars_impl<HasPrimitiveVars>::template f<
51  BoundaryCorrection>;
52 
53 template <bool HasPrimitiveVars = false>
54 struct get_system_primitive_vars_impl {
55  template <typename System>
56  using f = tmpl::list<>;
57 };
58 
59 template <>
60 struct get_system_primitive_vars_impl<true> {
61  template <typename System>
62  using f = typename System::primitive_variables_tag::tags_list;
63 };
64 
65 template <bool HasPrimitiveVars, typename System>
66 using get_system_primitive_vars = typename get_system_primitive_vars_impl<
67  HasPrimitiveVars>::template f<System>;
68 
69 template <typename BoundaryCorrection, typename... PackageTags,
70  typename... FaceTags, typename... VolumeTags, size_t Dim>
71 void call_dg_package_data(
72  const gsl::not_null<Variables<tmpl::list<PackageTags...>>*> package_data,
73  const BoundaryCorrection& correction,
74  const Variables<tmpl::list<FaceTags...>>& face_variables,
75  const tuples::TaggedTuple<VolumeTags...>& volume_data,
76  const tnsr::i<DataVector, Dim, Frame::Inertial>& unit_normal_covector,
77  const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&
78  mesh_velocity) {
79  std::optional<Scalar<DataVector>> normal_dot_mesh_velocity{};
80  if (mesh_velocity.has_value()) {
81  normal_dot_mesh_velocity =
82  dot_product(*mesh_velocity, unit_normal_covector);
83  }
84  correction.dg_package_data(
85  make_not_null(&get<PackageTags>(*package_data))...,
86  get<FaceTags>(face_variables)..., unit_normal_covector, mesh_velocity,
87  normal_dot_mesh_velocity, get<VolumeTags>(volume_data)...);
88 }
89 
90 template <typename BoundaryCorrection, typename... BoundaryCorrectionTags,
91  typename... PackageTags>
92 void call_dg_boundary_terms(
93  const gsl::not_null<Variables<tmpl::list<BoundaryCorrectionTags...>>*>
94  boundary_corrections,
95  const BoundaryCorrection& correction,
96  const Variables<tmpl::list<PackageTags...>>& interior_package_data,
97  const Variables<tmpl::list<PackageTags...>>& exterior_package_data,
98  const ::dg::Formulation dg_formulation) {
99  correction.dg_boundary_terms(
100  make_not_null(&get<BoundaryCorrectionTags>(*boundary_corrections))...,
101  get<PackageTags>(interior_package_data)...,
102  get<PackageTags>(exterior_package_data)..., dg_formulation);
103 }
104 
105 template <typename System, typename BoundaryCorrection, size_t FaceDim,
106  typename... VolumeTags>
107 void test_boundary_correction_impl(
108  const BoundaryCorrection& correction, const Mesh<FaceDim>& face_mesh,
109  const tuples::TaggedTuple<VolumeTags...>& volume_data,
110  const bool use_moving_mesh, const ::dg::Formulation dg_formulation,
111  const ZeroOnSmoothSolution zero_on_smooth_solution) {
112  CAPTURE(use_moving_mesh);
113  CAPTURE(dg_formulation);
114  CAPTURE(FaceDim);
115  using variables_tags = typename System::variables_tag::tags_list;
116  using primitive_variables_tags = detail::get_system_primitive_vars<
117  System::has_primitive_and_conservative_vars, System>;
118  using flux_variables = typename System::flux_variables;
119  using flux_tags =
122  using temporary_tags =
123  typename System::compute_volume_time_derivative_terms::temporary_tags;
124  using dt_variables_tags = db::wrap_tags_in<::Tags::dt, variables_tags>;
125 
126  using dg_package_field_tags =
127  typename BoundaryCorrection::dg_package_field_tags;
128  using package_temporary_tags =
129  typename BoundaryCorrection::dg_package_data_temporary_tags;
130  using package_primitive_tags = detail::get_correction_primitive_vars<
131  System::has_primitive_and_conservative_vars, BoundaryCorrection>;
132 
133  // Check that the temporary tags needed on the boundary
134  // (package_temporary_tags) are listed as temporary tags for the volume time
135  // derivative computation (temporary_tags).
136  static_assert(
137  std::is_same_v<
138  tmpl::list_difference<package_temporary_tags, temporary_tags>,
139  tmpl::list<>>,
140  "There are temporary tags needed by the boundary correction that are not "
141  "computed as temporary tags by the system");
142 
143  // Check that the primitive tags needed on the boundary
144  // (package_primitive_tags) are listed as the primitive tags in
145  // the system (primitive_variables_tags).
146  static_assert(
147  std::is_same_v<tmpl::list_difference<package_primitive_tags,
148  primitive_variables_tags>,
149  tmpl::list<>>,
150  "There are primitive tags needed by the boundary correction that are not "
151  "listed in the system as being primitive variables");
152 
153  MAKE_GENERATOR(gen);
154  std::uniform_real_distribution<> dist(0.0, 1.0);
155  std::uniform_real_distribution<> pos_neg_dist(-1.0, 1.0);
156  DataVector used_for_size{face_mesh.number_of_grid_points()};
157 
159  mesh_velocity{};
160  if (use_moving_mesh) {
161  mesh_velocity = make_with_random_values<
162  tnsr::I<DataVector, FaceDim + 1, Frame::Inertial>>(
163  make_not_null(&gen), make_not_null(&dist), used_for_size);
164  }
165 
166  Variables<dg_package_field_tags> interior_package_data{used_for_size.size()};
167  const auto interior_fields_on_face = make_with_random_values<
168  Variables<tmpl::append<variables_tags, flux_tags, package_temporary_tags,
169  package_primitive_tags>>>(
170  make_not_null(&gen), make_not_null(&dist), used_for_size);
171 
172  Variables<dg_package_field_tags> exterior_package_data{used_for_size.size()};
173  const auto exterior_fields_on_face = make_with_random_values<
174  Variables<tmpl::append<variables_tags, flux_tags, package_temporary_tags,
175  package_primitive_tags>>>(
176  make_not_null(&gen), make_not_null(&dist), used_for_size);
177 
178  // Compute the interior and exterior normal vectors so they are pointing in
179  // opposite directions.
180  auto interior_unit_normal_covector = make_with_random_values<
181  tnsr::i<DataVector, FaceDim + 1, Frame::Inertial>>(
182  make_not_null(&gen), make_not_null(&pos_neg_dist), used_for_size);
183  const Scalar<DataVector> interior_normal_magnitude =
184  magnitude(interior_unit_normal_covector);
185  for (auto& t : interior_unit_normal_covector) {
186  t /= get(interior_normal_magnitude);
187  }
188  auto exterior_unit_normal_covector = interior_unit_normal_covector;
189  for (auto& t : exterior_unit_normal_covector) {
190  t *= -1.0;
191  }
192 
193  call_dg_package_data(make_not_null(&interior_package_data), correction,
194  interior_fields_on_face, volume_data,
195  interior_unit_normal_covector, mesh_velocity);
196  call_dg_package_data(make_not_null(&exterior_package_data), correction,
197  exterior_fields_on_face, volume_data,
198  exterior_unit_normal_covector, mesh_velocity);
199 
200  Variables<dt_variables_tags> boundary_corrections{
201  face_mesh.number_of_grid_points()};
202  call_dg_boundary_terms(make_not_null(&boundary_corrections), correction,
203  interior_package_data, exterior_package_data,
204  dg_formulation);
205 
206  if (dg_formulation == ::dg::Formulation::StrongInertial) {
207  // The strong form should be (WeakForm - (n_i F^i)_{interior}).
208  // Since we also test conservation for the weak form we just need to test
209  // that the strong form satisfies the above definition.
210 
211  Variables<dt_variables_tags> expected_boundary_corrections{
212  face_mesh.number_of_grid_points()};
213  call_dg_boundary_terms(make_not_null(&expected_boundary_corrections),
214  correction, interior_package_data,
215  exterior_package_data,
216  ::dg::Formulation::WeakInertial);
217 
218  tmpl::for_each<flux_variables>([&interior_package_data,
219  &expected_boundary_corrections](
220  auto flux_variable_tag_v) noexcept {
221  using flux_variable_tag = tmpl::type_from<decltype(flux_variable_tag_v)>;
222  using normal_dot_flux_tag = ::Tags::NormalDotFlux<flux_variable_tag>;
223  using dt_tag = ::Tags::dt<flux_variable_tag>;
224  const auto& normal_dot_flux =
225  get<normal_dot_flux_tag>(interior_package_data);
226  auto& expected_boundary_correction =
227  get<dt_tag>(expected_boundary_corrections);
228  for (size_t tensor_index = 0;
229  tensor_index < expected_boundary_correction.size(); ++tensor_index) {
230  expected_boundary_correction[tensor_index] -=
231  normal_dot_flux[tensor_index];
232  }
233  });
234  {
235  INFO("Check weak and strong boundary terms match.");
236  CHECK(boundary_corrections == expected_boundary_corrections);
237  }
238 
239  if (zero_on_smooth_solution == ZeroOnSmoothSolution::Yes) {
240  INFO(
241  "Testing that if the solution is the same on both sides the "
242  "StrongInertial correction is identically zero.");
243  Variables<dg_package_field_tags> interior_package_data_opposite_signs{
244  used_for_size.size()};
245  call_dg_package_data(make_not_null(&interior_package_data_opposite_signs),
246  correction, interior_fields_on_face, volume_data,
247  exterior_unit_normal_covector, mesh_velocity);
248  Variables<dt_variables_tags> zero_boundary_correction{
249  face_mesh.number_of_grid_points()};
250  call_dg_boundary_terms(make_not_null(&zero_boundary_correction),
251  correction, interior_package_data,
252  interior_package_data_opposite_signs,
253  ::dg::Formulation::StrongInertial);
254  Variables<dt_variables_tags> expected_zero_boundary_correction{
255  face_mesh.number_of_grid_points(), 0.0};
256  tmpl::for_each<dt_variables_tags>([&expected_zero_boundary_correction,
257  &zero_boundary_correction](
258  auto dt_variables_tag_v) noexcept {
259  using dt_variables_tag = tmpl::type_from<decltype(dt_variables_tag_v)>;
260  const std::string tag_name = db::tag_name<dt_variables_tag>();
261  CAPTURE(tag_name);
262  Approx custom_approx = Approx::custom().epsilon(1.e-12).scale(1.0);
264  get<dt_variables_tag>(zero_boundary_correction),
265  get<dt_variables_tag>(expected_zero_boundary_correction),
266  custom_approx);
267  });
268  }
269  } else if (dg_formulation == ::dg::Formulation::WeakInertial) {
270  INFO(
271  "Checking that swapping the two sides results in an overall minus "
272  "sign.");
273  Variables<dt_variables_tags> reverse_side_boundary_corrections{
274  face_mesh.number_of_grid_points()};
275  call_dg_boundary_terms(make_not_null(&reverse_side_boundary_corrections),
276  correction, exterior_package_data,
277  interior_package_data, dg_formulation);
278  // Check that the flux leaving one element equals the flux entering its
279  // neighbor, i.e., F*(interior, exterior) == -F*(exterior, interior)
280  reverse_side_boundary_corrections *= -1.0;
281  tmpl::for_each<flux_variables>([&boundary_corrections,
282  &reverse_side_boundary_corrections](
283  auto flux_variable_tag_v) {
284  using flux_variable_tag = tmpl::type_from<decltype(flux_variable_tag_v)>;
285  const std::string tag_name = db::tag_name<flux_variable_tag>();
286  CAPTURE(tag_name);
288  get<::Tags::dt<flux_variable_tag>>(reverse_side_boundary_corrections),
289  get<::Tags::dt<flux_variable_tag>>(boundary_corrections));
290  });
291  } else {
292  ERROR("DG formulation must be StrongInertial or WeakInertial, not "
293  << dg_formulation);
294  }
295 }
296 } // namespace detail
297 
298 /*!
299  * \ingroup TestingFrameworkGroup
300  * \brief Checks that the boundary correction is conservative and that for
301  * smooth solutions the strong-form correction is zero.
302  */
303 template <typename System, typename BoundaryCorrection, size_t FaceDim,
304  typename... VolumeTags>
306  const BoundaryCorrection& correction, const Mesh<FaceDim>& face_mesh,
307  const tuples::TaggedTuple<VolumeTags...>& volume_data,
308  const ZeroOnSmoothSolution zero_on_smooth_solution =
309  ZeroOnSmoothSolution::Yes) {
310  for (const auto use_moving_mesh : {true, false}) {
311  for (const auto& dg_formulation :
312  {::dg::Formulation::StrongInertial, ::dg::Formulation::WeakInertial}) {
313  detail::test_boundary_correction_impl<System>(
314  correction, face_mesh, volume_data, use_moving_mesh, dg_formulation,
315  zero_on_smooth_solution);
316  }
317  }
318 }
319 
320 namespace detail {
321 template <typename ConversionClassList, typename VariablesTags,
322  typename BoundaryCorrection, size_t FaceDim, typename... FaceTags,
323  typename... VolumeTags, typename... DgPackageDataTags>
324 void test_with_python(
325  const std::string& python_module,
326  const std::array<
327  std::string,
328  tmpl::size<typename BoundaryCorrection::dg_package_field_tags>::value>&
329  python_dg_package_data_functions,
330  const std::array<std::string, tmpl::size<VariablesTags>::value>&
331  python_dg_boundary_terms_functions,
332  const BoundaryCorrection& correction, const Mesh<FaceDim>& face_mesh,
333  const tuples::TaggedTuple<VolumeTags...>& volume_data,
334  const bool use_moving_mesh, const ::dg::Formulation dg_formulation,
335  const double epsilon, tmpl::list<FaceTags...> /*meta*/,
336  tmpl::list<DgPackageDataTags...> /*meta*/) {
337  CAPTURE(face_mesh);
338  CAPTURE(dg_formulation);
339  CAPTURE(use_moving_mesh);
340  REQUIRE(face_mesh.number_of_grid_points() >= 1);
341  using dg_package_field_tags =
342  typename BoundaryCorrection::dg_package_field_tags;
343 
344  MAKE_GENERATOR(gen);
345  std::uniform_real_distribution<> dist(0.0, 1.0);
346  DataVector used_for_size{face_mesh.number_of_grid_points()};
347 
348  Variables<dg_package_field_tags> package_data{used_for_size.size()};
349  const auto fields_on_face =
350  make_with_random_values<Variables<tmpl::list<FaceTags...>>>(
351  make_not_null(&gen), make_not_null(&dist), used_for_size);
352  auto unit_normal_covector = make_with_random_values<
353  tnsr::i<DataVector, FaceDim + 1, Frame::Inertial>>(
354  make_not_null(&gen), make_not_null(&dist), used_for_size);
355  const auto normal_magnitude = magnitude(unit_normal_covector);
356  for (auto& component : unit_normal_covector) {
357  component /= get(normal_magnitude);
358  }
360  mesh_velocity{};
361  if (use_moving_mesh) {
362  mesh_velocity = make_with_random_values<
363  tnsr::I<DataVector, FaceDim + 1, Frame::Inertial>>(
364  make_not_null(&gen), make_not_null(&dist), used_for_size);
365  }
366  std::optional<Scalar<DataVector>> normal_dot_mesh_velocity{};
367  if (mesh_velocity.has_value()) {
368  normal_dot_mesh_velocity =
369  dot_product(*mesh_velocity, unit_normal_covector);
370  }
371 
372  // Call C++ implementation of dg_package_data
373  call_dg_package_data(make_not_null(&package_data), correction, fields_on_face,
374  volume_data, unit_normal_covector, mesh_velocity);
375 
376  // Call python implementation of dg_package_data
377  size_t function_name_index = 0;
378  tmpl::for_each<dg_package_field_tags>(
379  [epsilon, &fields_on_face, &function_name_index, &mesh_velocity,
380  &normal_dot_mesh_velocity, &package_data,
381  &python_dg_package_data_functions, &python_module, &unit_normal_covector,
382  &volume_data](auto package_data_tag_v) {
383  // avoid compiler warnings if there isn't any volume data
384  (void)volume_data;
385  INFO("Testing package data");
386  using package_data_tag = tmpl::type_from<decltype(package_data_tag_v)>;
387  using ResultType = typename package_data_tag::type;
388  try {
389  CAPTURE(python_module);
390  CAPTURE(
391  gsl::at(python_dg_package_data_functions, function_name_index));
392  const auto python_result =
393  pypp::call<ResultType, ConversionClassList>(
394  python_module,
395  gsl::at(python_dg_package_data_functions,
396  function_name_index),
397  get<FaceTags>(fields_on_face)..., unit_normal_covector,
398  mesh_velocity, normal_dot_mesh_velocity,
399  get<VolumeTags>(volume_data)...);
401  get<package_data_tag>(package_data), python_result,
402  Approx::custom().epsilon(epsilon).scale(1.0));
403  } catch (const std::exception& e) {
404  INFO("On line " << __LINE__ << " Python call to "
405  << gsl::at(python_dg_package_data_functions,
406  function_name_index)
407  << " in module " << python_module
408  << " failed: " << e.what());
409  REQUIRE(false);
410  }
411  ++function_name_index;
412  });
413 
414  // Now we need to check the dg_boundary_terms function.
415  const auto interior_package_data =
416  make_with_random_values<Variables<dg_package_field_tags>>(
417  make_not_null(&gen), make_not_null(&dist), used_for_size);
418  const auto exterior_package_data =
419  make_with_random_values<Variables<dg_package_field_tags>>(
420  make_not_null(&gen), make_not_null(&dist), used_for_size);
421  // We don't need to prefix the VariablesTags with anything because we are not
422  // interacting with any code that cares about what the tags are, just that the
423  // types matched the evolved variables.
424  Variables<VariablesTags> boundary_corrections{
425  face_mesh.number_of_grid_points()};
426 
427  // Call C++ implementation of dg_boundary_terms
428  call_dg_boundary_terms(make_not_null(&boundary_corrections), correction,
429  interior_package_data, exterior_package_data,
430  dg_formulation);
431 
432  // Call python implementation of dg_boundary_terms
433  function_name_index = 0;
434  tmpl::for_each<VariablesTags>([&boundary_corrections, &dg_formulation,
435  epsilon, &exterior_package_data,
436  &function_name_index, &interior_package_data,
437  &python_dg_boundary_terms_functions,
438  &python_module](auto package_data_tag_v) {
439  INFO("Testing boundary terms");
440  using boundary_correction_tag =
441  tmpl::type_from<decltype(package_data_tag_v)>;
442  using ResultType = typename boundary_correction_tag::type;
443  try {
444  CAPTURE(python_module);
445  const std::string& python_function =
446  gsl::at(python_dg_boundary_terms_functions, function_name_index);
447  CAPTURE(python_function);
448  // Make explicitly depend on tag type to avoid type deduction issues with
449  // GCC7
450  const typename boundary_correction_tag::type python_result =
451  pypp::call<ResultType>(
452  python_module, python_function,
453  get<DgPackageDataTags>(interior_package_data)...,
454  get<DgPackageDataTags>(exterior_package_data)...,
455  dg_formulation == ::dg::Formulation::StrongInertial);
457  get<boundary_correction_tag>(boundary_corrections), python_result,
458  Approx::custom().epsilon(epsilon).scale(1.0));
459  } catch (const std::exception& e) {
460  INFO("On line " << __LINE__ << " Python call to "
461  << gsl::at(python_dg_boundary_terms_functions,
462  function_name_index)
463  << " in module " << python_module
464  << " failed: " << e.what());
465  REQUIRE(false);
466  }
467  ++function_name_index;
468  });
469 }
470 } // namespace detail
471 
472 /*!
473  * \ingroup TestingFrameworkGroup
474  * \brief Tests that the `dg_package_data` and `dg_boundary_terms` functions
475  * agree with the python implementation.
476  *
477  * The variables are filled with random numbers between zero and one before
478  * being passed to the implementations. If in the future we need support for
479  * negative numbers we can add the ability to specify a single range for all
480  * random numbers or each individually.
481  *
482  * Please note the following:
483  * - The `pypp::SetupLocalPythonEnvironment` must be created before the
484  * `test_boundary_correction_with_python` can be called.
485  * - The `dg_formulation` is passed as a bool `use_strong_form` to the python
486  * functions since we don't want to rely on python bindings for the enum.
487  * - You can convert custom types using the `ConversionClassList` template
488  * parameter, which is then passed to `pypp::call()`. This allows you to,
489  * e.g., convert an equation of state into an array locally in a test file.
490  * - There must be one python function to compute the packaged data for each tag
491  * in `dg_package_field_tags`
492  * - There must be one python function to compute the boundary correction for
493  * each tag in `System::variables_tag`
494  * - The arguments to the python functions for computing the packaged data are
495  * the same as the arguments for the C++ `dg_package_data` function, excluding
496  * the `gsl::not_null` arguments.
497  * - The arguments to the python functions for computing the boundary
498  * corrections are the same as the arguments for the C++ `dg_boundary_terms`
499  * function, excluding the `gsl::not_null` arguments.
500  */
501 template <typename System, typename ConversionClassList = tmpl::list<>,
502  typename BoundaryCorrection, size_t FaceDim, typename... VolumeTags>
504  const std::string& python_module,
505  const std::array<
506  std::string,
507  tmpl::size<typename BoundaryCorrection::dg_package_field_tags>::value>&
508  python_dg_package_data_functions,
509  const std::array<
510  std::string,
511  tmpl::size<typename System::variables_tag::tags_list>::value>&
512  python_dg_boundary_terms_functions,
513  const BoundaryCorrection& correction, const Mesh<FaceDim>& face_mesh,
514  const tuples::TaggedTuple<VolumeTags...>& volume_data,
515  const double epsilon = 1.0e-12) {
516  static_assert(std::is_final_v<std::decay_t<BoundaryCorrection>>,
517  "All boundary correction classes must be marked `final`.");
518  using package_temporary_tags =
519  typename BoundaryCorrection::dg_package_data_temporary_tags;
520  using package_primitive_tags = detail::get_correction_primitive_vars<
521  System::has_primitive_and_conservative_vars, BoundaryCorrection>;
522  using variables_tags = typename System::variables_tag::tags_list;
523  using flux_variables = typename System::flux_variables;
524  using flux_tags =
527 
528  for (const auto use_moving_mesh : {false, true}) {
529  for (const auto dg_formulation :
530  {::dg::Formulation::StrongInertial, ::dg::Formulation::WeakInertial}) {
531  detail::test_with_python<ConversionClassList, variables_tags>(
532  python_module, python_dg_package_data_functions,
533  python_dg_boundary_terms_functions, correction, face_mesh,
534  volume_data, use_moving_mesh, dg_formulation, epsilon,
535  tmpl::append<variables_tags, flux_tags, package_temporary_tags,
536  package_primitive_tags>{},
537  typename BoundaryCorrection::dg_package_field_tags{});
538  }
539  }
540 }
541 } // namespace TestHelpers::evolution::dg
TestHelpers::evolution::dg::test_boundary_correction_with_python
void test_boundary_correction_with_python(const std::string &python_module, const std::array< std::string, tmpl::size< typename BoundaryCorrection::dg_package_field_tags >::value > &python_dg_package_data_functions, const std::array< std::string, tmpl::size< typename System::variables_tag::tags_list >::value > &python_dg_boundary_terms_functions, const BoundaryCorrection &correction, const Mesh< FaceDim > &face_mesh, const tuples::TaggedTuple< VolumeTags... > &volume_data, const double epsilon=1.0e-12)
Tests that the dg_package_data and dg_boundary_terms functions agree with the python implementation.
Definition: BoundaryCorrections.hpp:503
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
std::string
Frame::Inertial
Definition: IndexType.hpp:44
std::exception
get
constexpr Tag::type & get(Variables< TagList > &v) noexcept
Return Tag::type pointing into the contiguous array.
Definition: Variables.hpp:638
TestingFramework.hpp
db::tag_name
std::string tag_name() noexcept
Get the name of a DataBox tag, including prefixes.
Definition: TagName.hpp:44
random
MakeWithRandomValues.hpp
Tags::NormalDotFlux
Prefix indicating a boundary unit normal vector dotted into the flux.
Definition: Prefixes.hpp:96
CHECK_ITERABLE_CUSTOM_APPROX
#define CHECK_ITERABLE_CUSTOM_APPROX(a, b, appx)
Same as CHECK_ITERABLE_APPROX with user-defined Approx. The third argument should be of type Approx.
Definition: TestingFramework.hpp:134
CHECK_ITERABLE_APPROX
#define CHECK_ITERABLE_APPROX(a, b)
A wrapper around Catch's CHECK macro that checks approximate equality of entries in iterable containe...
Definition: TestingFramework.hpp:122
std::uniform_real_distribution
TestHelpers.hpp
dot_product
void dot_product(const gsl::not_null< Scalar< DataType > * > dot_product, const Tensor< DataType, Symmetry< 1 >, index_list< Index >> &vector_a, const Tensor< DataType, Symmetry< 1 >, index_list< Index >> &vector_b) noexcept
Compute the Euclidean dot product of two vectors or one forms.
Definition: DotProduct.hpp:24
ERROR
#define ERROR(m)
prints an error message to the standard error stream and aborts the program.
Definition: Error.hpp:36
Pypp.hpp
DotProduct.hpp
cstddef
std::array
Mesh::number_of_grid_points
size_t number_of_grid_points() const noexcept
The total number of grid points in all dimensions.
Definition: Mesh.hpp:156
tuples::TaggedTuple
An associative container that is indexed by structs.
Definition: TaggedTuple.hpp:271
DataVector
Stores a collection of function values.
Definition: DataVector.hpp:42
TestHelpers::evolution::dg::test_boundary_correction_conservation
void test_boundary_correction_conservation(const BoundaryCorrection &correction, const Mesh< FaceDim > &face_mesh, const tuples::TaggedTuple< VolumeTags... > &volume_data, const ZeroOnSmoothSolution zero_on_smooth_solution=ZeroOnSmoothSolution::Yes)
Checks that the boundary correction is conservative and that for smooth solutions the strong-form cor...
Definition: BoundaryCorrections.hpp:305
std::decay_t
MAKE_GENERATOR
#define MAKE_GENERATOR(...)
MAKE_GENERATOR(NAME [, SEED]) declares a variable of name NAME containing a generator of type std::mt...
Definition: TestHelpers.hpp:433
Variables.hpp
Mesh
Holds the number of grid points, basis, and quadrature in each direction of the computational grid.
Definition: Mesh.hpp:48
Tags::dt
Prefix indicating a time derivative.
Definition: Prefixes.hpp:29
Scalar
Tensor< T, Symmetry<>, index_list<> > Scalar
Definition: TypeAliases.hpp:21
make_with_random_values
ReturnType make_with_random_values(const gsl::not_null< UniformRandomBitGenerator * > generator, const gsl::not_null< RandomNumberDistribution * > distribution, const T &used_for_size) noexcept
Make a data structure and fill it with random values.
Definition: MakeWithRandomValues.hpp:164
Gsl.hpp
Tensor.hpp
db::wrap_tags_in
tmpl::transform< TagList, tmpl::bind< Wrapper, tmpl::_1, tmpl::pin< Args >... > > wrap_tags_in
Create a new tmpl::list of tags by wrapping each tag in TagList in Wrapper<_, Args....
Definition: PrefixHelpers.hpp:30
magnitude
Scalar< DataType > magnitude(const Tensor< DataType, Symmetry< 1 >, index_list< Index >> &vector) noexcept
Compute the Euclidean magnitude of a rank-1 tensor.
Definition: Magnitude.hpp:27
optional
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
Mesh.hpp
dg::Formulation
Formulation
The DG formulation to use.
Definition: Formulation.hpp:34
Prefixes.hpp
type_traits
TMPL.hpp
gsl::not_null
Require a pointer to not be a nullptr
Definition: ReadSpecThirdOrderPiecewisePolynomial.hpp:13
string