SpECTRE Documentation Coverage Report
Current view: top level - Evolution/DiscontinuousGalerkin/Actions - BoundaryConditionsImpl.hpp Coverage Total Hit
Commit: 0d1d717af84a81fb1284a20c4013708fe85cc2f0 Lines: 0.0 % 1 0
Test Date: 2026-10-22 17:29:42
Legend: Lines:     hit not hit

            Line data    Source code
       1            0 : // Distributed under the MIT License.
       2              : // See LICENSE.txt for details.
       3              : 
       4              : #pragma once
       5              : 
       6              : #include <cstddef>
       7              : #include <functional>
       8              : #include <memory>
       9              : #include <optional>
      10              : #include <string>
      11              : #include <tuple>
      12              : #include <unordered_map>
      13              : #include <utility>
      14              : 
      15              : #include "DataStructures/DataBox/DataBox.hpp"
      16              : #include "DataStructures/DataBox/MetavariablesTag.hpp"
      17              : #include "DataStructures/DataBox/PrefixHelpers.hpp"
      18              : #include "DataStructures/DataBox/Prefixes.hpp"
      19              : #include "DataStructures/DataVector.hpp"
      20              : #include "DataStructures/Tensor/EagerMath/Magnitude.hpp"
      21              : #include "DataStructures/Tensor/Tensor.hpp"
      22              : #include "DataStructures/Variables.hpp"
      23              : #include "DataStructures/VariablesTag.hpp"
      24              : #include "Domain/Block.hpp"
      25              : #include "Domain/BoundaryConditions/Cartoon.hpp"
      26              : #include "Domain/BoundaryConditions/None.hpp"
      27              : #include "Domain/BoundaryConditions/Periodic.hpp"
      28              : #include "Domain/CoordinateMaps/CoordinateMap.hpp"
      29              : #include "Domain/Creators/Tags/ExternalBoundaryConditions.hpp"
      30              : #include "Domain/Domain.hpp"
      31              : #include "Domain/ElementMap.hpp"
      32              : #include "Domain/FunctionsOfTime/FunctionOfTime.hpp"
      33              : #include "Domain/FunctionsOfTime/Tags.hpp"
      34              : #include "Domain/InterfaceLogicalCoordinates.hpp"
      35              : #include "Domain/Structure/Direction.hpp"
      36              : #include "Domain/Structure/Element.hpp"
      37              : #include "Domain/Structure/ElementId.hpp"
      38              : #include "Domain/Tags.hpp"
      39              : #include "Domain/TagsTimeDependent.hpp"
      40              : #include "Evolution/BoundaryConditions/Type.hpp"
      41              : #include "Evolution/DiscontinuousGalerkin/Actions/ComputeTimeDerivativeHelpers.hpp"
      42              : #include "Evolution/DiscontinuousGalerkin/Actions/NormalCovectorAndMagnitude.hpp"
      43              : #include "Evolution/DiscontinuousGalerkin/Actions/PackageDataImpl.hpp"
      44              : #include "Evolution/DiscontinuousGalerkin/BoundaryEvolvedVariables.hpp"
      45              : #include "NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp"
      46              : #include "NumericalAlgorithms/DiscontinuousGalerkin/InterpolateFromBoundary.hpp"
      47              : #include "NumericalAlgorithms/DiscontinuousGalerkin/LiftFlux.hpp"
      48              : #include "NumericalAlgorithms/DiscontinuousGalerkin/LiftFromBoundary.hpp"
      49              : #include "NumericalAlgorithms/DiscontinuousGalerkin/ProjectToBoundary.hpp"
      50              : #include "NumericalAlgorithms/DiscontinuousGalerkin/Tags/Formulation.hpp"
      51              : #include "NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp"
      52              : #include "NumericalAlgorithms/Spectral/BoundaryInterpolationMatrices.hpp"
      53              : #include "NumericalAlgorithms/Spectral/Mesh.hpp"
      54              : #include "NumericalAlgorithms/Spectral/Quadrature.hpp"
      55              : #include "Utilities/ErrorHandling/Assert.hpp"
      56              : #include "Utilities/ErrorHandling/Error.hpp"
      57              : #include "Utilities/Gsl.hpp"
      58              : #include "Utilities/TMPL.hpp"
      59              : 
      60              : /// \cond
      61              : namespace Tags {
      62              : struct Time;
      63              : }  // namespace Tags
      64              : /// \endcond
      65              : 
      66              : namespace evolution::dg::Actions::detail {
      67              : template <typename BoundaryConditionHelper, typename AllTagsOnFaceList,
      68              :           typename... TagsFromFace, typename... VolumeArgs>
      69              : std::optional<std::string> apply_boundary_condition_impl(
      70              :     BoundaryConditionHelper& boundary_condition_helper,
      71              :     const Variables<AllTagsOnFaceList>& fields_on_interior_face,
      72              :     tmpl::list<TagsFromFace...> /*meta*/, const VolumeArgs&... volume_args) {
      73              :   return boundary_condition_helper(
      74              :       get<TagsFromFace>(fields_on_interior_face)..., volume_args...);
      75              : }
      76              : 
      77              : template <typename System, size_t Dim, bool ComputeAuxiliary = false,
      78              :           typename DbTagsList, typename BoundaryCorrection,
      79              :           typename BoundaryCondition, typename... EvolvedVariablesTags,
      80              :           typename... PackageDataVolumeTags,
      81              :           typename... BoundaryConditionVolumeTags, typename... PackageFieldTags,
      82              :           typename... BoundaryTermsVolumeTags,
      83              :           typename... BoundaryCorrectionPackagedDataInputTags,
      84              :           typename... AuxiliaryCorrectionTags>
      85              : void apply_boundary_condition_on_face(
      86              :     const gsl::not_null<db::DataBox<DbTagsList>*> box,
      87              :     [[maybe_unused]] const BoundaryCorrection& boundary_correction,
      88              :     const BoundaryCondition& boundary_condition,
      89              :     const Direction<Dim>& direction,
      90              :     [[maybe_unused]] const Variables<tmpl::list<EvolvedVariablesTags...>>&
      91              :         volume_evolved_vars,
      92              :     // The auxiliary variables are projected to the face in both passes
      93              :     // because dg_ghost is called in the auxiliary pass and may declare
      94              :     // auxiliary variables in dg_interior_evolved_variables_tags.
      95              :     [[maybe_unused]] const Variables<
      96              :         get_auxiliary_variables_or_default_t<System, tmpl::list<>>>* const
      97              :         volume_auxiliary_variables,
      98              :     [[maybe_unused]] const Variables<
      99              :         db::wrap_tags_in<::Tags::Flux, typename System::flux_variables,
     100              :                          tmpl::size_t<Dim>, Frame::Inertial>>& volume_fluxes,
     101              :     [[maybe_unused]] const Variables<
     102              :         db::wrap_tags_in<::Tags::deriv, typename System::gradient_variables,
     103              :                          tmpl::size_t<Dim>, Frame::Inertial>>& partial_derivs,
     104              :     [[maybe_unused]] const Variables<
     105              :         typename System::compute_volume_time_derivative_terms::temporary_tags>&
     106              :         volume_temporaries,
     107              :     [[maybe_unused]] const Variables<
     108              :         detail::get_primitive_vars_tags_from_system<System>>* const
     109              :         volume_primitive_variables,
     110              :     [[maybe_unused]] const ::dg::Formulation dg_formulation,
     111              :     const Mesh<Dim>& volume_mesh, [[maybe_unused]] const Element<Dim>& element,
     112              :     [[maybe_unused]] const ::ElementMap<Dim, Frame::Grid>& logical_to_grid_map,
     113              :     const domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, Dim>&
     114              :         moving_mesh_map,
     115              :     [[maybe_unused]] const double time,
     116              :     [[maybe_unused]] const std::unordered_map<
     117              :         std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&
     118              :         functions_of_time,
     119              :     const std::optional<tnsr::I<DataVector, Dim>>& volume_mesh_velocity,
     120              :     const InverseJacobian<DataVector, Dim, Frame::ElementLogical,
     121              :                           Frame::Inertial>& volume_inverse_jacobian,
     122              :     [[maybe_unused]] const Scalar<DataVector>& volume_det_inv_jacobian,
     123              :     tmpl::list<PackageDataVolumeTags...> /*meta*/,
     124              :     tmpl::list<PackageFieldTags...> /*meta*/,
     125              :     tmpl::list<BoundaryTermsVolumeTags...> /*meta*/,
     126              :     tmpl::list<BoundaryCorrectionPackagedDataInputTags...> /*meta*/,
     127              :     tmpl::list<BoundaryConditionVolumeTags...> /*meta*/,
     128              :     tmpl::list<AuxiliaryCorrectionTags...> /*meta*/) {
     129              :   using variables_tags = tmpl::list<EvolvedVariablesTags...>;
     130              :   using variables_tag = ::Tags::Variables<variables_tags>;
     131              :   using flux_variables = typename System::flux_variables;
     132              :   using dt_variables_tags = db::wrap_tags_in<::Tags::dt, variables_tags>;
     133              :   using dt_variables_tag = db::add_tag_prefix<::Tags::dt, variables_tag>;
     134              :   using auxiliary_variables =
     135              :       get_auxiliary_variables_or_default_t<System, tmpl::list<>>;
     136              :   using auxiliary_variables_tag = ::Tags::Variables<auxiliary_variables>;
     137              :   using tag_to_update =
     138              :       tmpl::conditional_t<ComputeAuxiliary, auxiliary_variables_tag,
     139              :                           dt_variables_tag>;
     140              :   using projected_auxiliary_vars_tags =
     141              :       tmpl::conditional_t<ComputeAuxiliary, tmpl::list<>, auxiliary_variables>;
     142              : 
     143              :   constexpr bool bc_evolves_boundary_variables =
     144              :       evolution::dg::evolves_boundary_variables_v<BoundaryCondition>;
     145              :   static_assert(
     146              :       not bc_evolves_boundary_variables or
     147              :           evolution::dg::system_has_boundary_variables_v<System>,
     148              :       "This boundary condition opts into evolving boundary variables, but "
     149              :       "the system's variables_tag has no ::Tags::BoundaryVariables entry.");
     150              :   // DemandOutgoingCharSpeeds should not need boundary evolved fields
     151              :   // as it does not write to external state.
     152              :   static_assert(
     153              :       not(bc_evolves_boundary_variables and
     154              :           BoundaryCondition::bc_type ==
     155              :               evolution::BoundaryConditions::Type::DemandOutgoingCharSpeeds),
     156              :       "A boundary condition that evolves boundary variables must not have "
     157              :       "`bc_type == DemandOutgoingCharSpeeds`.");
     158              :   using boundary_variables_tag_or_empty =
     159              :       tmpl::conditional_t<bc_evolves_boundary_variables,
     160              :                           evolution::dg::boundary_variables_tag<System>,
     161              :                           ::Tags::Variables<tmpl::list<>>>;
     162              :   using boundary_field_tags =
     163              :       typename boundary_variables_tag_or_empty::tags_list;
     164              : 
     165              :   // The interior fields `boundary_field_time_derivatives` need as input.
     166              :   // `boundary_field_time_derivatives` runs only on the physical pass.
     167              :   using bdry_evolved_field_projected_evolved_vars_tags = tmpl::conditional_t<
     168              :       bc_evolves_boundary_variables and not ComputeAuxiliary,
     169              :       get_boundary_field_time_derivatives_evolved_variables_tags_or_default_t<
     170              :           BoundaryCondition, tmpl::list<>>,
     171              :       tmpl::list<>>;
     172              :   using bdry_evolved_field_projected_primitive_tags = tmpl::conditional_t<
     173              :       bc_evolves_boundary_variables and not ComputeAuxiliary,
     174              :       get_boundary_field_time_derivatives_primitive_tags_or_default_t<
     175              :           BoundaryCondition, tmpl::list<>>,
     176              :       tmpl::list<>>;
     177              :   using bdry_evolved_field_projected_temporary_tags = tmpl::conditional_t<
     178              :       bc_evolves_boundary_variables and not ComputeAuxiliary,
     179              :       get_boundary_field_time_derivatives_temporary_tags_or_default_t<
     180              :           BoundaryCondition, tmpl::list<>>,
     181              :       tmpl::list<>>;
     182              :   static_assert(
     183              :       System::has_primitive_and_conservative_vars or
     184              :           tmpl::size<bdry_evolved_field_projected_primitive_tags>::value == 0,
     185              :       "A boundary condition cannot request "
     186              :       "`boundary_field_time_derivatives_primitive_tags` when the system has no "
     187              :       "primitive variables.");
     188              : 
     189              :   const Mesh<Dim - 1> face_mesh = volume_mesh.slice_away(direction.dimension());
     190              :   const size_t number_of_points_on_face = face_mesh.number_of_grid_points();
     191              : 
     192              :   // We figure out all the tags we need to project from the interior, both for
     193              :   // the boundary condition computation and for the boundary correction. We do
     194              :   // this by:
     195              :   // 1. get all interior tags for the boundary condition
     196              :   // 2. get all interior tags for the boundary correction (if ghost condition)
     197              :   // 3. combine these lists
     198              :   // 4. project from the interior
     199              :   //
     200              :   // Note: we only need to consider the boundary correction tags if a ghost
     201              :   // boundary condition is imposed.
     202              : 
     203              :   constexpr bool uses_ghost_condition =
     204              :       BoundaryCondition::bc_type ==
     205              :           evolution::BoundaryConditions::Type::Ghost or
     206              :       BoundaryCondition::bc_type ==
     207              :           evolution::BoundaryConditions::Type::GhostAndTimeDerivative;
     208              :   constexpr bool uses_time_derivative_condition =
     209              :       BoundaryCondition::bc_type ==
     210              :           evolution::BoundaryConditions::Type::TimeDerivative or
     211              :       BoundaryCondition::bc_type ==
     212              :           evolution::BoundaryConditions::Type::GhostAndTimeDerivative;
     213              :   // List that holds the inverse spatial metric if it's needed
     214              :   using inverse_spatial_metric_list =
     215              :       detail::inverse_spatial_metric_tag<System>;
     216              :   constexpr bool has_inv_spatial_metric =
     217              :       detail::has_inverse_spatial_metric_tag_v<System>;
     218              : 
     219              :   // Set up tags for boundary conditions
     220              :   using bcondition_interior_temp_tags =
     221              :       typename BoundaryCondition::dg_interior_temporary_tags;
     222              :   using bcondition_interior_prim_tags =
     223              :       detail::boundary_condition_primitive_tags<
     224              :           System::has_primitive_and_conservative_vars, BoundaryCondition>;
     225              :   using bcondition_interior_evolved_vars_tags =
     226              :       typename BoundaryCondition::dg_interior_evolved_variables_tags;
     227              :   using bcondition_interior_dt_evolved_vars_tags =
     228              :       detail::get_dt_vars_from_boundary_condition<BoundaryCondition>;
     229              :   using bcondition_interior_deriv_evolved_vars_tags =
     230              :       detail::get_deriv_vars_from_boundary_condition<BoundaryCondition>;
     231              :   using bcondition_interior_tags = tmpl::append<
     232              :       tmpl::conditional_t<has_inv_spatial_metric,
     233              :                           tmpl::list<detail::NormalVector<Dim>>, tmpl::list<>>,
     234              :       bcondition_interior_evolved_vars_tags, bcondition_interior_prim_tags,
     235              :       bcondition_interior_temp_tags, bcondition_interior_dt_evolved_vars_tags,
     236              :       bcondition_interior_deriv_evolved_vars_tags>;
     237              : 
     238              :   // Set up tags for boundary correction
     239              :   using correction_temp_tags = tmpl::conditional_t<
     240              :       uses_ghost_condition,
     241              :       typename BoundaryCorrection::dg_package_data_temporary_tags,
     242              :       tmpl::list<>>;
     243              :   using correction_prim_tags = tmpl::conditional_t<
     244              :       uses_ghost_condition,
     245              :       detail::boundary_correction_primitive_tags<
     246              :           System::has_primitive_and_conservative_vars, BoundaryCorrection>,
     247              :       tmpl::list<>>;
     248              :   using correction_evolved_and_auxiliary_vars_tags =
     249              :       tmpl::conditional_t<uses_ghost_condition,
     250              :                           tmpl::append<variables_tags, auxiliary_variables>,
     251              :                           tmpl::list<>>;
     252              : 
     253              :   // Now combine the tags lists for each type of tag. These are all the tags
     254              :   // we need to project from the interior, excluding the inverse spatial
     255              :   // metric. They are the input to `dg_package_data` in the boundary
     256              :   // correction.
     257              :   using interior_temp_tags = tmpl::remove_duplicates<
     258              :       tmpl::append<bcondition_interior_temp_tags, correction_temp_tags,
     259              :                    bdry_evolved_field_projected_temporary_tags>>;
     260              :   using interior_prim_tags = tmpl::remove_duplicates<
     261              :       tmpl::append<bcondition_interior_prim_tags, correction_prim_tags,
     262              :                    bdry_evolved_field_projected_primitive_tags>>;
     263              :   using interior_vars_tags = tmpl::remove_duplicates<
     264              :       tmpl::append<correction_evolved_and_auxiliary_vars_tags,
     265              :                    bcondition_interior_evolved_vars_tags,
     266              :                    bdry_evolved_field_projected_evolved_vars_tags>>;
     267              : 
     268              :   constexpr bool needs_coordinates =
     269              :       tmpl::list_contains_v<interior_temp_tags,
     270              :                             ::domain::Tags::Coordinates<Dim, Frame::Inertial>>;
     271              : 
     272              :   // List tags on the interior of the face. We list the exterior side
     273              :   // separately in the `else` branch of the if-constexpr where we actually use
     274              :   // the exterior fields.
     275              :   using fluxes_tags =
     276              :       tmpl::conditional_t<uses_ghost_condition,
     277              :                           db::wrap_tags_in<::Tags::Flux, flux_variables,
     278              :                                            tmpl::size_t<Dim>, Frame::Inertial>,
     279              :                           tmpl::list<>>;
     280              :   using tags_on_interior_face = tmpl::remove_duplicates<tmpl::append<
     281              :       fluxes_tags, interior_temp_tags, interior_prim_tags, interior_vars_tags,
     282              :       bcondition_interior_dt_evolved_vars_tags,
     283              :       bcondition_interior_deriv_evolved_vars_tags, inverse_spatial_metric_list,
     284              :       tmpl::list<detail::OneOverNormalVectorMagnitude,
     285              :                  detail::NormalVector<Dim>>>>;
     286              : 
     287              :   Variables<tags_on_interior_face> interior_face_fields{
     288              :       number_of_points_on_face};
     289              : 
     290              :   // Perform projection into `interior_face_fields`. This also covers all the
     291              :   // fields for the exterior except for the time derivatives that might be
     292              :   // needed for Bjorhus/TimeDerivative boundary conditions.
     293              :   //
     294              :   // Note on the ordering of the data to project: if we are using a ghost
     295              :   // boundary condition with a boundary correction, then we know that all the
     296              :   // evolved variables are needed, whereas when using DemandOutgoingCharSpeeds
     297              :   // or Bjorhus boundary conditions none of the evolved variables might be
     298              :   // needed (or only some subset). Also, the way the typelist is assembled, the
     299              :   // evolved vars are guaranteed to be contiguous, but only if we are doing a
     300              :   // ghost boundary condition.
     301              :   if constexpr (uses_ghost_condition) {
     302              :     ::dg::project_contiguous_data_to_boundary(
     303              :         make_not_null(&interior_face_fields), volume_evolved_vars, volume_mesh,
     304              :         direction);
     305              :     if constexpr (tmpl::size<auxiliary_variables>::value != 0) {
     306              :       ASSERT(volume_auxiliary_variables != nullptr,
     307              :              "The auxiliary variables must be provided when the system has "
     308              :              "auxiliary variables.");
     309              :       ::dg::project_tensors_to_boundary<auxiliary_variables>(
     310              :           make_not_null(&interior_face_fields), *volume_auxiliary_variables,
     311              :           volume_mesh, direction);
     312              :     }
     313              :   } else {
     314              :     ::dg::project_tensors_to_boundary<interior_vars_tags>(
     315              :         make_not_null(&interior_face_fields), volume_evolved_vars, volume_mesh,
     316              :         direction);
     317              :   }
     318              :   if constexpr (tmpl::size<fluxes_tags>::value != 0) {
     319              :     ::dg::project_contiguous_data_to_boundary(
     320              :         make_not_null(&interior_face_fields), volume_fluxes, volume_mesh,
     321              :         direction);
     322              :   } else {
     323              :     (void)volume_fluxes;
     324              :   }
     325              :   using temp_tags_no_coordinates =
     326              :       tmpl::remove<interior_temp_tags,
     327              :                    domain::Tags::Coordinates<Dim, Frame::Inertial>>;
     328              :   if constexpr (tmpl::size<tmpl::append<
     329              :                     temp_tags_no_coordinates,
     330              :                     detail::inverse_spatial_metric_tag<System>>>::value != 0) {
     331              :     ::dg::project_tensors_to_boundary<tmpl::append<
     332              :         temp_tags_no_coordinates, detail::inverse_spatial_metric_tag<System>>>(
     333              :         make_not_null(&interior_face_fields), volume_temporaries, volume_mesh,
     334              :         direction);
     335              :   }
     336              :   if constexpr (System::has_primitive_and_conservative_vars and
     337              :                 tmpl::size<interior_prim_tags>::value != 0) {
     338              :     ASSERT(volume_primitive_variables != nullptr,
     339              :            "The volume primitive variables are not set even though the "
     340              :            "system has primitive variables.");
     341              :     ::dg::project_tensors_to_boundary<interior_prim_tags>(
     342              :         make_not_null(&interior_face_fields), *volume_primitive_variables,
     343              :         volume_mesh, direction);
     344              :   } else {
     345              :     (void)volume_primitive_variables;
     346              :   }
     347              :   if constexpr (tmpl::size<
     348              :                     bcondition_interior_deriv_evolved_vars_tags>::value != 0) {
     349              :     ::dg::project_tensors_to_boundary<
     350              :         bcondition_interior_deriv_evolved_vars_tags>(
     351              :         make_not_null(&interior_face_fields), partial_derivs, volume_mesh,
     352              :         direction);
     353              :   }
     354              :   if constexpr (tmpl::size<bcondition_interior_dt_evolved_vars_tags>::value !=
     355              :                 0) {
     356              :     ::dg::project_tensors_to_boundary<bcondition_interior_dt_evolved_vars_tags>(
     357              :         make_not_null(&interior_face_fields), db::get<dt_variables_tag>(*box),
     358              :         volume_mesh, direction);
     359              :   }
     360              : 
     361              :   std::optional<tnsr::I<DataVector, Dim>> face_mesh_velocity{};
     362              :   if (volume_mesh_velocity.has_value()) {
     363              :     face_mesh_velocity = tnsr::I<DataVector, Dim>{number_of_points_on_face};
     364              :     ::dg::project_tensor_to_boundary(make_not_null(&*face_mesh_velocity),
     365              :                                      *volume_mesh_velocity, volume_mesh,
     366              :                                      direction);
     367              :   }
     368              : 
     369              :   // Normalize the normal vectors. We cache the unit normal covector For
     370              :   // flat geometry and static meshes.
     371              :   const auto normalize_normal_vectors =
     372              :       [&direction, mesh_is_moving = not moving_mesh_map.is_identity(),
     373              :        number_of_points_on_face, &volume_inverse_jacobian,
     374              :        &volume_mesh](const auto normal_covector_magnitude_in_direction_ptr,
     375              :                      auto fields_on_face_ptr) {
     376              :         if (auto& normal_covector_quantity =
     377              :                 *normal_covector_magnitude_in_direction_ptr;
     378              :             has_inv_spatial_metric or mesh_is_moving or
     379              :             not normal_covector_quantity.has_value()) {
     380              :           if (not normal_covector_quantity.has_value()) {
     381              :             normal_covector_quantity =
     382              :                 Variables<tmpl::list<evolution::dg::Tags::MagnitudeOfNormal,
     383              :                                      evolution::dg::Tags::NormalCovector<Dim>>>{
     384              :                     number_of_points_on_face};
     385              :           }
     386              :           tnsr::i<DataVector, Dim> volume_unnormalized_normal_covector{};
     387              : 
     388              :           for (size_t inertial_index = 0; inertial_index < Dim;
     389              :                ++inertial_index) {
     390              :             volume_unnormalized_normal_covector.get(inertial_index)
     391              :                 .set_data_ref(
     392              :                     const_cast<double*>(  // NOLINT
     393              :                         volume_inverse_jacobian
     394              :                             .get(direction.dimension(), inertial_index)
     395              :                             .data()),
     396              :                     volume_mesh.number_of_grid_points());
     397              :           }
     398              :           ::dg::project_tensor_to_boundary(
     399              :               make_not_null(&get<evolution::dg::Tags::NormalCovector<Dim>>(
     400              :                   *normal_covector_quantity)),
     401              :               volume_unnormalized_normal_covector, volume_mesh, direction);
     402              : 
     403              :           if (const double sign = direction.sign(); sign != 1.0) {
     404              :             for (auto& normal_covector_component :
     405              :                  get<evolution::dg::Tags::NormalCovector<Dim>>(
     406              :                      *normal_covector_quantity)) {
     407              :               normal_covector_component *= sign;
     408              :             }
     409              :           }
     410              : 
     411              :           detail::unit_normal_vector_and_covector_and_magnitude_impl<System>(
     412              :               make_not_null(&get<evolution::dg::Tags::MagnitudeOfNormal>(
     413              :                   *normal_covector_quantity)),
     414              :               make_not_null(&get<evolution::dg::Tags::NormalCovector<Dim>>(
     415              :                   *normal_covector_quantity)),
     416              :               fields_on_face_ptr,
     417              :               get<evolution::dg::Tags::NormalCovector<Dim>>(
     418              :                   *normal_covector_quantity));
     419              :         }
     420              :       };
     421              :   // Normalize the outward facing normal vector on the interior side
     422              :   db::mutate<evolution::dg::Tags::NormalCovectorAndMagnitude<Dim>>(
     423              :       [&direction, &interior_face_fields, &normalize_normal_vectors](
     424              :           const auto normal_covector_and_magnitude_ptr) {
     425              :         normalize_normal_vectors(
     426              :             make_not_null(&normal_covector_and_magnitude_ptr->at(direction)),
     427              :             make_not_null(&interior_face_fields));
     428              :       },
     429              :       box);
     430              : 
     431              :   const tnsr::i<DataVector, Dim, Frame::Inertial>& interior_normal_covector =
     432              :       get<evolution::dg::Tags::NormalCovector<Dim>>(
     433              :           *db::get<evolution::dg::Tags::NormalCovectorAndMagnitude<Dim>>(*box)
     434              :                .at(direction));
     435              : 
     436              :   if constexpr (needs_coordinates) {
     437              :     // Compute the coordinates on the interface
     438              :     get<domain::Tags::Coordinates<Dim, Frame::Inertial>>(interior_face_fields) =
     439              :         moving_mesh_map(logical_to_grid_map(interface_logical_coordinates(
     440              :                             face_mesh, direction)),
     441              :                         time, functions_of_time);
     442              :   }
     443              : 
     444              :   if constexpr (BoundaryCondition::bc_type ==
     445              :                 evolution::BoundaryConditions::Type::DemandOutgoingCharSpeeds) {
     446              :     // DemandOutgoingCharSpeeds boundary conditions only check that all
     447              :     // characteristic speeds are directed out of the element. If there are any
     448              :     // inward directed fields then the boundary condition should error.
     449              :     const auto apply_bc =
     450              :         [&boundary_condition, &face_mesh_velocity,
     451              :          &interior_normal_covector](const auto&... face_and_volume_args) {
     452              :           return boundary_condition.dg_demand_outgoing_char_speeds(
     453              :               face_mesh_velocity, interior_normal_covector,
     454              :               face_and_volume_args...);
     455              :         };
     456              :     const std::optional<std::string> error_message =
     457              :         apply_boundary_condition_impl(
     458              :             apply_bc, interior_face_fields, bcondition_interior_tags{},
     459              :             db::get<BoundaryConditionVolumeTags>(*box)...);
     460              :     if (error_message.has_value()) {
     461              :       ERROR(*error_message << "\n\nIn element:" << element.id()
     462              :                            << "\nIn direction: " << direction);
     463              :     }
     464              :     return;
     465              :   }
     466              : 
     467              :   // We add the time derivative boundary conditions and lift the ghost boundary
     468              :   // conditions after both have been computed in case either depends on the
     469              :   // time derivatives in the volume projected on to the face.
     470              : 
     471              :   Variables<dt_variables_tags> dt_time_derivative_correction{};
     472              :   if constexpr (uses_time_derivative_condition and not ComputeAuxiliary) {
     473              :     dt_time_derivative_correction.initialize(number_of_points_on_face);
     474              :     auto apply_bc = [&boundary_condition, &dt_time_derivative_correction,
     475              :                      &face_mesh_velocity, &interior_normal_covector](
     476              :                         const auto&... interior_face_and_volume_args) {
     477              :       return boundary_condition.dg_time_derivative(
     478              :           make_not_null(&get<::Tags::dt<EvolvedVariablesTags>>(
     479              :               dt_time_derivative_correction))...,
     480              :           face_mesh_velocity, interior_normal_covector,
     481              :           interior_face_and_volume_args...);
     482              :     };
     483              :     const std::optional<std::string> error_message =
     484              :         apply_boundary_condition_impl(
     485              :             apply_bc, interior_face_fields, bcondition_interior_tags{},
     486              :             db::get<BoundaryConditionVolumeTags>(*box)...);
     487              :     if (error_message.has_value()) {
     488              :       ERROR(*error_message << "\n\nIn element:" << element.id()
     489              :                            << "\nIn direction: " << direction);
     490              :     }
     491              :   } else {
     492              :     (void)dt_time_derivative_correction;
     493              :   }
     494              : 
     495              :   [[maybe_unused]] const Variables<boundary_field_tags>*
     496              :       stored_boundary_variables = nullptr;
     497              :   if constexpr (bc_evolves_boundary_variables) {
     498              :     stored_boundary_variables =
     499              :         &db::get<boundary_variables_tag_or_empty>(*box).variables().at(
     500              :             direction);
     501              :   }
     502              :   if constexpr (bc_evolves_boundary_variables and not ComputeAuxiliary) {
     503              :     using bdry_evolved_field_interior_tags =
     504              :         evolution::dg::boundary_field_time_derivatives_interior_tags<
     505              :             BoundaryCondition>;
     506              :     using dt_boundary_variables_tag =
     507              :         db::add_tag_prefix<::Tags::dt, boundary_variables_tag_or_empty>;
     508              : 
     509              :     db::mutate_apply<tmpl::list<dt_boundary_variables_tag>,
     510              :                      tmpl::list<BoundaryConditionVolumeTags...>>(
     511              :         [&](const auto dt_boundary_variables, const auto&... volume_args) {
     512              :           auto& entry = dt_boundary_variables->variables().at(direction);
     513              :           ASSERT(
     514              :               entry.number_of_grid_points() == number_of_points_on_face,
     515              :               "The dt storage of the boundary-evolved variables for direction "
     516              :                   << direction << " has " << entry.number_of_grid_points()
     517              :                   << " grid points, but the face mesh has "
     518              :                   << number_of_points_on_face
     519              :                   << ". The entry must be sized to the face mesh.");
     520              :           const std::optional<std::string> error_message = tmpl::as_pack<
     521              :               boundary_field_tags>(
     522              :               [&]<typename... BoundaryFieldTags>(
     523              :                   tmpl::type_<BoundaryFieldTags>... /*meta*/) {
     524              :                 auto apply_boundary_field_time_derivatives =
     525              :                     [&](const auto&... interior_face_and_volume_args) {
     526              :                       return boundary_condition.boundary_field_time_derivatives(
     527              :                           make_not_null(
     528              :                               &get<::Tags::dt<BoundaryFieldTags>>(entry))...,
     529              :                           face_mesh_velocity, interior_normal_covector,
     530              :                           get<BoundaryFieldTags>(*stored_boundary_variables)...,
     531              :                           interior_face_and_volume_args...);
     532              :                     };
     533              :                 return apply_boundary_condition_impl(
     534              :                     apply_boundary_field_time_derivatives, interior_face_fields,
     535              :                     bdry_evolved_field_interior_tags{}, volume_args...);
     536              :               });
     537              :           if (error_message.has_value()) {
     538              :             ERROR(*error_message << "\n\nIn element:" << element.id()
     539              :                                  << "\nIn direction: " << direction);
     540              :           }
     541              :         },
     542              :         box);
     543              :   }
     544              : 
     545              :   // Now we populate the fields on the exterior side of the face using the
     546              :   // boundary condition.
     547              :   // `auxiliary_variables` is included unconditionally: `dg_ghost` supplies the
     548              :   // exterior values of every field it declares in both the physical and the
     549              :   // auxiliary pass.
     550              :   using tags_on_exterior_face = tmpl::remove_duplicates<tmpl::append<
     551              :       variables_tags, auxiliary_variables, fluxes_tags, correction_temp_tags,
     552              :       correction_prim_tags, inverse_spatial_metric_list,
     553              :       tmpl::list<detail::OneOverNormalVectorMagnitude,
     554              :                  detail::NormalVector<Dim>,
     555              :                  evolution::dg::Tags::NormalCovector<Dim>>>>;
     556              :   Variables<tags_on_exterior_face> exterior_face_fields{
     557              :       number_of_points_on_face};
     558              : 
     559              :   const bool has_collocation_points_on_side =
     560              :       volume_mesh.quadrature(direction.dimension()) ==
     561              :           Spectral::Quadrature::GaussLobatto or
     562              :       (volume_mesh.quadrature(direction.dimension()) ==
     563              :            Spectral::Quadrature::GaussRadauUpper and
     564              :        direction.side() == Side::Upper);
     565              : 
     566              :   if constexpr (uses_ghost_condition) {
     567              :     using mortar_tags_list = tmpl::list<PackageFieldTags...>;
     568              :     using dg_package_data_projected_tags =
     569              :         tmpl::append<variables_tags, projected_auxiliary_vars_tags, fluxes_tags,
     570              :                      correction_temp_tags, correction_prim_tags>;
     571              : 
     572              :     Variables<mortar_tags_list> internal_packaged_data{
     573              :         number_of_points_on_face};
     574              :     if constexpr (ComputeAuxiliary) {
     575              :       const double max_abs_char_speed_on_face =
     576              :           detail::dg_auxiliary_package_data<System>(
     577              :               make_not_null(&internal_packaged_data), boundary_correction,
     578              :               interior_face_fields, interior_normal_covector,
     579              :               face_mesh_velocity, dg_package_data_projected_tags{},
     580              :               db::get<PackageDataVolumeTags>(*box)...);
     581              :       (void)max_abs_char_speed_on_face;
     582              :     } else {
     583              :       const double max_abs_char_speed_on_face = detail::dg_package_data<System>(
     584              :           make_not_null(&internal_packaged_data), boundary_correction,
     585              :           interior_face_fields, interior_normal_covector, face_mesh_velocity,
     586              :           dg_package_data_projected_tags{},
     587              :           db::get<PackageDataVolumeTags>(*box)...);
     588              :       (void)max_abs_char_speed_on_face;
     589              :     }
     590              : 
     591              :     // Notes:
     592              :     // - We pass the outward directed normal vector normalized using the
     593              :     //   interior variables to the boundary condition. This is because the
     594              :     //   boundary condition should only need the normal vector for computing
     595              :     //   things like reflecting BCs where the normal component of an interior
     596              :     //   quantity is reversed.
     597              :     // - If needed, the boundary condition returns the inverse spatial metric on
     598              :     //   the exterior side, which is then used to normalize the normal vector on
     599              :     //   the exterior side. We need the exterior normal vector for computing
     600              :     //   flux terms. The inverse spatial metric on the exterior side can be
     601              :     //   equal to the inverse spatial metric on the interior side. This would be
     602              :     //   true when, e.g. imposing reflecting boundary conditions.
     603              :     // - In addition to the evolved variables and fluxes, the boundary condition
     604              :     //   must compute the `dg_packaged_data_temporary_tags` and the primitive
     605              :     //   tags that the boundary correction needs.
     606              :     // - For systems with constraint damping parameters, the constraint damping
     607              :     //   parameters are just copied from the projected values from the interior.
     608              :     // - If a boundary condition has boundary evolved fields, those fields are
     609              :     //   passed as input arguments to dg_ghost. Their time derivatives are
     610              :     //   computed by the boundary condition's method
     611              :     //   boundary_field_time_derivatives.
     612              :     const auto boundary_field_values_args = [&stored_boundary_variables]() {
     613              :       if constexpr (bc_evolves_boundary_variables) {
     614              :         return tmpl::as_pack<boundary_field_tags>(
     615              :             [&stored_boundary_variables]<typename... BoundaryValueTags>(
     616              :                 tmpl::type_<BoundaryValueTags>... /*meta*/) {
     617              :               return std::forward_as_tuple(
     618              :                   get<BoundaryValueTags>(*stored_boundary_variables)...);
     619              :             });
     620              :       } else {
     621              :         (void)stored_boundary_variables;
     622              :         return std::tuple<>{};
     623              :       }
     624              :     }();
     625              :     auto apply_bc = [&boundary_condition, &exterior_face_fields,
     626              :                      &face_mesh_velocity, &interior_normal_covector,
     627              :                      &boundary_field_values_args](
     628              :                         const auto&... interior_face_and_volume_args) {
     629              :       if constexpr (has_inv_spatial_metric) {
     630              :         return std::apply(
     631              :             [&](const auto&... boundary_field_values) {
     632              :               return boundary_condition.dg_ghost(
     633              :                   make_not_null(&get<BoundaryCorrectionPackagedDataInputTags>(
     634              :                       exterior_face_fields))...,
     635              :                   make_not_null(
     636              :                       &get<tmpl::front<
     637              :                           detail::inverse_spatial_metric_tag<System>>>(
     638              :                           exterior_face_fields)),
     639              :                   face_mesh_velocity, interior_normal_covector,
     640              :                   boundary_field_values..., interior_face_and_volume_args...);
     641              :             },
     642              :             boundary_field_values_args);
     643              :       } else {
     644              :         return std::apply(
     645              :             [&](const auto&... boundary_field_values) {
     646              :               return boundary_condition.dg_ghost(
     647              :                   make_not_null(&get<BoundaryCorrectionPackagedDataInputTags>(
     648              :                       exterior_face_fields))...,
     649              :                   face_mesh_velocity, interior_normal_covector,
     650              :                   boundary_field_values..., interior_face_and_volume_args...);
     651              :             },
     652              :             boundary_field_values_args);
     653              :       }
     654              :     };
     655              :     const std::optional<std::string> error_message =
     656              :         apply_boundary_condition_impl(
     657              :             apply_bc, interior_face_fields, bcondition_interior_tags{},
     658              :             db::get<BoundaryConditionVolumeTags>(*box)...);
     659              :     if (error_message.has_value()) {
     660              :       ERROR(*error_message << "\n\nIn element:" << element.id()
     661              :                            << "\nIn direction: " << direction);
     662              :     }
     663              :     // Subtract mesh velocity from the _exterior_ fluxes
     664              :     if (face_mesh_velocity.has_value()) {
     665              :       tmpl::for_each<flux_variables>(
     666              :           [&face_mesh_velocity, &exterior_face_fields](auto tag_v) {
     667              :             // Modify fluxes for moving mesh
     668              :             using var_tag = typename decltype(tag_v)::type;
     669              :             using flux_var_tag =
     670              :                 db::add_tag_prefix<::Tags::Flux, var_tag, tmpl::size_t<Dim>,
     671              :                                    Frame::Inertial>;
     672              :             auto& flux_var = get<flux_var_tag>(exterior_face_fields);
     673              :             const auto& var = get<var_tag>(exterior_face_fields);
     674              :             const auto& mesh_velocity = *face_mesh_velocity;
     675              :             // Loop over all independent components of flux_var
     676              :             for (size_t flux_var_storage_index = 0;
     677              :                  flux_var_storage_index < flux_var.size();
     678              :                  ++flux_var_storage_index) {
     679              :               // Get the flux variable's tensor index, e.g. (i,j) for a F^i of
     680              :               // the spatial velocity (or some other spatial tensor).
     681              :               const auto flux_var_tensor_index =
     682              :                   flux_var.get_tensor_index(flux_var_storage_index);
     683              :               // Remove the first index from the flux tensor index, gets back
     684              :               // (j)
     685              :               const auto var_tensor_index =
     686              :                   all_but_specified_element_of(flux_var_tensor_index, 0);
     687              :               // Set flux_index to (i)
     688              :               const size_t flux_index = gsl::at(flux_var_tensor_index, 0);
     689              : 
     690              :               // We now need to index flux(i,j) -= u(j) * v_g(i)
     691              :               flux_var[flux_var_storage_index] -=
     692              :                   var.get(var_tensor_index) * mesh_velocity.get(flux_index);
     693              :             }
     694              :           });
     695              :     }
     696              :     // Now that we have computed the inverse spatial metric on the exterior, we
     697              :     // can compute the normalized normal (co)vector on the exterior side. If
     698              :     // there is no inverse spatial metric, then we just copy from the interior
     699              :     // and reverse the sign.
     700              :     for (size_t i = 0; i < Dim; ++i) {
     701              :       get<evolution::dg::Tags::NormalCovector<Dim>>(exterior_face_fields)
     702              :           .get(i) = -interior_normal_covector.get(i);
     703              :     }
     704              :     if constexpr (has_inv_spatial_metric) {
     705              :       const tnsr::II<DataVector, Dim, Frame::Inertial>& inv_spatial_metric =
     706              :           get<tmpl::front<inverse_spatial_metric_list>>(exterior_face_fields);
     707              :       tnsr::i<DataVector, Dim, Frame::Inertial>& exterior_normal_covector =
     708              :           get<evolution::dg::Tags::NormalCovector<Dim>>(exterior_face_fields);
     709              :       tnsr::I<DataVector, Dim, Frame::Inertial>& exterior_normal_vector =
     710              :           get<detail::NormalVector<Dim>>(exterior_face_fields);
     711              : 
     712              :       // Since the spatial metric is different on the exterior side of the
     713              :       // interface, we need to normalize the direction-reversed interior normal
     714              :       // vector using the exterior inverse spatial metric.
     715              :       for (size_t i = 0; i < Dim; ++i) {
     716              :         exterior_normal_vector.get(i) =
     717              :             get<0>(exterior_normal_covector) * inv_spatial_metric.get(i, 0);
     718              :         for (size_t j = 1; j < Dim; ++j) {
     719              :           exterior_normal_vector.get(i) +=
     720              :               exterior_normal_covector.get(j) * inv_spatial_metric.get(i, j);
     721              :         }
     722              :       }
     723              :       // Use detail::OneOverNormalVectorMagnitude as a buffer for the
     724              :       // magnitude. We don't need one over the normal magnitude on the
     725              :       // exterior side since we aren't lifting there.
     726              :       Scalar<DataVector>& magnitude =
     727              :           get<detail::OneOverNormalVectorMagnitude>(exterior_face_fields);
     728              :       dot_product(make_not_null(&magnitude), exterior_normal_covector,
     729              :                   exterior_normal_vector);
     730              :       get(magnitude) = sqrt(get(magnitude));
     731              :       for (size_t i = 0; i < Dim; ++i) {
     732              :         exterior_normal_covector.get(i) /= get(magnitude);
     733              :         exterior_normal_vector.get(i) /= get(magnitude);
     734              :       }
     735              :     }
     736              : 
     737              :     // Package the external-side data for the boundary correction
     738              :     Variables<mortar_tags_list> external_packaged_data{
     739              :         number_of_points_on_face};
     740              :     if constexpr (ComputeAuxiliary) {
     741              :       detail::dg_auxiliary_package_data<System>(
     742              :           make_not_null(&external_packaged_data), boundary_correction,
     743              :           exterior_face_fields,
     744              :           get<evolution::dg::Tags::NormalCovector<Dim>>(exterior_face_fields),
     745              :           face_mesh_velocity, dg_package_data_projected_tags{},
     746              :           db::get<PackageDataVolumeTags>(*box)...);
     747              :     } else {
     748              :       detail::dg_package_data<System>(
     749              :           make_not_null(&external_packaged_data), boundary_correction,
     750              :           exterior_face_fields,
     751              :           get<evolution::dg::Tags::NormalCovector<Dim>>(exterior_face_fields),
     752              :           face_mesh_velocity, dg_package_data_projected_tags{},
     753              :           db::get<PackageDataVolumeTags>(*box)...);
     754              :     }
     755              : 
     756              :     typename tag_to_update::type boundary_corrections_on_face{
     757              :         number_of_points_on_face};
     758              : 
     759              :     // Compute boundary correction
     760              :     if constexpr (ComputeAuxiliary) {
     761              :       // The auxiliary boundary terms write one correction per auxiliary
     762              :       // variable (the buffer's own tags), not per evolved variable.
     763              :       boundary_correction.dg_auxiliary_boundary_terms(
     764              :           make_not_null(
     765              :               &get<AuxiliaryCorrectionTags>(boundary_corrections_on_face))...,
     766              :           get<PackageFieldTags>(internal_packaged_data)...,
     767              :           get<PackageFieldTags>(external_packaged_data)..., dg_formulation,
     768              :           get<BoundaryTermsVolumeTags>(*box)...);
     769              :     } else {
     770              :       boundary_correction.dg_boundary_terms(
     771              :           make_not_null(&get<::Tags::dt<EvolvedVariablesTags>>(
     772              :               boundary_corrections_on_face))...,
     773              :           get<PackageFieldTags>(internal_packaged_data)...,
     774              :           get<PackageFieldTags>(external_packaged_data)..., dg_formulation,
     775              :           get<BoundaryTermsVolumeTags>(*box)...);
     776              :     }
     777              : 
     778              :     // Lift the boundary correction
     779              :     const auto& magnitude_of_interior_face_normal =
     780              :         get<evolution::dg::Tags::MagnitudeOfNormal>(
     781              :             *db::get<evolution::dg::Tags::NormalCovectorAndMagnitude<Dim>>(*box)
     782              :                  .at(direction));
     783              :     if (has_collocation_points_on_side) {
     784              :       // The lift_flux function lifts only on the slice, it does not add
     785              :       // the contribution to the volume.
     786              :       ::dg::lift_flux(make_not_null(&boundary_corrections_on_face),
     787              :                       volume_mesh.extents(direction.dimension()),
     788              :                       magnitude_of_interior_face_normal,
     789              :                       volume_mesh.basis(direction.dimension()));
     790              : 
     791              :       // Add the flux contribution to the volume data
     792              :       db::mutate<tag_to_update>(
     793              :           [&direction, &boundary_corrections_on_face,
     794              :            &volume_mesh](const auto vars_ptr) {
     795              :             add_slice_to_data(
     796              :                 vars_ptr, boundary_corrections_on_face, volume_mesh.extents(),
     797              :                 direction.dimension(),
     798              :                 index_to_slice_at(volume_mesh.extents(), direction));
     799              :           },
     800              :           box);
     801              :     } else {
     802              :       // We are using Gauss points.
     803              :       //
     804              :       // Optimization note: eliminate allocations for volume and face det
     805              :       // jacobian. Should probably compute face det inv jacobian, then divide
     806              :       // (fewer grid points => fewer FLOPs).
     807              :       const DataVector volume_det_jacobian = 1.0 / get(volume_det_inv_jacobian);
     808              : 
     809              :       // Project the determinant of the Jacobian to the face. This could
     810              :       // be optimized by caching in the time-independent case.
     811              :       Scalar<DataVector> face_det_jacobian{face_mesh.number_of_grid_points()};
     812              :       const Matrix identity{};
     813              :       auto interpolation_matrices = make_array<Dim>(std::cref(identity));
     814              :       const std::pair<Matrix, Matrix>& matrices =
     815              :           Spectral::boundary_interpolation_matrices(
     816              :               volume_mesh.slice_through(direction.dimension()));
     817              :       gsl::at(interpolation_matrices, direction.dimension()) =
     818              :           direction.side() == Side::Upper ? matrices.second : matrices.first;
     819              :       apply_matrices(make_not_null(&get(face_det_jacobian)),
     820              :                      interpolation_matrices, volume_det_jacobian,
     821              :                      volume_mesh.extents());
     822              : 
     823              :       db::mutate<tag_to_update>(
     824              :           [&direction, &boundary_corrections_on_face, &face_det_jacobian,
     825              :            &magnitude_of_interior_face_normal, &volume_det_inv_jacobian,
     826              :            &volume_mesh](const auto vars_ptr) {
     827              :             ::dg::lift_boundary_terms_gauss_points(
     828              :                 vars_ptr, volume_det_inv_jacobian, volume_mesh, direction,
     829              :                 boundary_corrections_on_face, magnitude_of_interior_face_normal,
     830              :                 face_det_jacobian);
     831              :           },
     832              :           box);
     833              :     }
     834              :   }
     835              :   // Add TimeDerivative correction to volume time derivatives.
     836              :   if constexpr (uses_time_derivative_condition and not ComputeAuxiliary) {
     837              :     if (has_collocation_points_on_side) {
     838              :       db::mutate<dt_variables_tag>(
     839              :           [&direction, &dt_time_derivative_correction,
     840              :            &volume_mesh](const auto dt_variables_ptr) {
     841              :             add_slice_to_data(
     842              :                 dt_variables_ptr, dt_time_derivative_correction,
     843              :                 volume_mesh.extents(), direction.dimension(),
     844              :                 index_to_slice_at(volume_mesh.extents(), direction));
     845              :           },
     846              :           box);
     847              :     } else {
     848              :       db::mutate<dt_variables_tag>(
     849              :           [&direction, &dt_time_derivative_correction,
     850              :            &volume_mesh](const auto dt_variables_ptr) {
     851              :             ::dg::interpolate_dt_terms_gauss_points(
     852              :                 dt_variables_ptr, volume_mesh, direction,
     853              :                 dt_time_derivative_correction);
     854              :           },
     855              :           box);
     856              :     }
     857              :   }
     858              : }
     859              : 
     860              : /*!
     861              :  * \brief Applies the boundary conditions using the `boundary_correction`
     862              :  * on all external faces.
     863              :  *
     864              :  * A `tmpl::for_each` loop along with a `typeid` comparison checks which of the
     865              :  * known boundary conditions is being used. Since each direction can have a
     866              :  * different boundary condition, we must check each boundary condition in
     867              :  * each external direction.
     868              :  *
     869              :  * When `ComputeAuxiliary` is `true` the LDG auxiliary pass is applied instead
     870              :  * of the physical pass: ghost faces package data with the boundary
     871              :  * correction's `dg_auxiliary_*` interface and lift the resulting correction
     872              :  * into the auxiliary-variable storage `::Tags::Variables<auxiliary_variables>`
     873              :  * rather than into the time derivatives, and time-derivative boundary
     874              :  * conditions are skipped.
     875              :  */
     876              : template <typename System, size_t Dim, typename VariablesTag,
     877              :           bool ComputeAuxiliary = false, typename DbTagsList,
     878              :           typename BoundaryCorrection>
     879              : void apply_boundary_conditions_on_all_external_faces(
     880              :     const gsl::not_null<db::DataBox<DbTagsList>*> box,
     881              :     const BoundaryCorrection& boundary_correction,
     882              :     const Variables<
     883              :         typename System::compute_volume_time_derivative_terms::temporary_tags>&
     884              :         temporaries,
     885              :     const Variables<
     886              :         db::wrap_tags_in<::Tags::Flux, typename System::flux_variables,
     887              :                          tmpl::size_t<Dim>, Frame::Inertial>>& volume_fluxes,
     888              :     const Variables<
     889              :         db::wrap_tags_in<::Tags::deriv, typename System::gradient_variables,
     890              :                          tmpl::size_t<Dim>, Frame::Inertial>>& partial_derivs,
     891              :     const Variables<detail::get_primitive_vars_tags_from_system<System>>* const
     892              :         primitive_vars) {
     893              :   using factory_classes =
     894              :       typename std::decay_t<decltype(db::get<Parallel::Tags::Metavariables>(
     895              :           *box))>::factory_creation::factory_classes;
     896              : 
     897              :   using derived_boundary_conditions = tmpl::remove_if<
     898              :       tmpl::at<factory_classes, typename System::boundary_conditions_base>,
     899              :       tmpl::or_<
     900              :           std::is_base_of<domain::BoundaryConditions::MarkAsCartoon, tmpl::_1>,
     901              :           std::is_base_of<domain::BoundaryConditions::MarkAsNone, tmpl::_1>,
     902              :           std::is_base_of<domain::BoundaryConditions::MarkAsPeriodic,
     903              :                           tmpl::_1>>>;
     904              : 
     905              :   using variables_tag = VariablesTag;
     906              :   using flux_variables = typename System::flux_variables;
     907              :   using fluxes_tags = db::wrap_tags_in<::Tags::Flux, flux_variables,
     908              :                                        tmpl::size_t<Dim>, Frame::Inertial>;
     909              :   using auxiliary_variables =
     910              :       get_auxiliary_variables_or_default_t<System, tmpl::list<>>;
     911              : 
     912              :   const Element<Dim>& element = db::get<domain::Tags::Element<Dim>>(*box);
     913              :   size_t number_of_boundaries_left = element.external_boundaries().size();
     914              : 
     915              :   if (number_of_boundaries_left == 0) {
     916              :     return;
     917              :   }
     918              : 
     919              :   const Variables<auxiliary_variables>* volume_auxiliary_variables = nullptr;
     920              :   if constexpr (tmpl::size<auxiliary_variables>::value != 0) {
     921              :     volume_auxiliary_variables =
     922              :         &db::get<::Tags::Variables<auxiliary_variables>>(*box);
     923              :   }
     924              : 
     925              :   const auto& external_boundary_conditions =
     926              :       db::get<domain::Tags::ExternalBoundaryConditions<Dim>>(*box).at(
     927              :           element.id().block_id());
     928              : 
     929              :   // Error if any cartoon-type BC is used with an incompatible mesh.
     930              :   // Cartoon BCs are coupled only to ZernikeB1 basis elements, which do not need
     931              :   // a DG boundary condition but do require one with subcell
     932              :   if constexpr (domain::BoundaryConditions::detail::has_cartoon_bc_v<
     933              :                     tmpl::at<factory_classes,
     934              :                              typename System::boundary_conditions_base>>) {
     935              :     const auto& mesh = db::get<::domain::Tags::Mesh<Dim>>(*box);
     936              :     if (not domain::BoundaryConditions::dg_mesh_is_cartoon_compatible(mesh)) {
     937              :       for (const Direction<Dim>& direction : element.external_boundaries()) {
     938              :         if (domain::BoundaryConditions::is_cartoon(
     939              :                 external_boundary_conditions.at(direction))) {
     940              :           ERROR(
     941              :               "You might have used a Cartoon boundary condition on an external "
     942              :               "boundary condition. Alternatively and less likely, there is a "
     943              :               "bug. The problematic BC is in direction "
     944              :               << direction << ", the mesh is: " << mesh);
     945              :         }
     946              :       }
     947              :     }
     948              :   }
     949              : 
     950              :   tmpl::for_each<
     951              :       derived_boundary_conditions>([&boundary_correction, &box, &element,
     952              :                                     &external_boundary_conditions,
     953              :                                     &number_of_boundaries_left, &partial_derivs,
     954              :                                     &primitive_vars, &temporaries,
     955              :                                     &volume_fluxes, volume_auxiliary_variables](
     956              :                                        auto derived_boundary_condition_v) {
     957              :     using DerivedBoundaryCondition =
     958              :         tmpl::type_from<decltype(derived_boundary_condition_v)>;
     959              : 
     960              :     if (number_of_boundaries_left == 0) {
     961              :       return;
     962              :     }
     963              : 
     964              :     for (const Direction<Dim>& direction : element.external_boundaries()) {
     965              :       const auto& boundary_condition =
     966              :           *external_boundary_conditions.at(direction);
     967              :       if (typeid(boundary_condition) == typeid(DerivedBoundaryCondition)) {
     968              :         // Select auxiliary or physical tags based on ComputeAuxiliary
     969              :         using package_data_volume_tags = tmpl::conditional_t<
     970              :             ComputeAuxiliary,
     971              :             get_dg_auxiliary_package_data_volume_tags_or_default_t<
     972              :                 BoundaryCorrection, tmpl::list<>>,
     973              :             typename BoundaryCorrection::dg_package_data_volume_tags>;
     974              :         using package_field_tags = tmpl::conditional_t<
     975              :             ComputeAuxiliary,
     976              :             get_dg_auxiliary_package_field_tags_or_default_t<BoundaryCorrection,
     977              :                                                              tmpl::list<>>,
     978              :             typename BoundaryCorrection::dg_package_field_tags>;
     979              :         using boundary_terms_volume_tags = tmpl::conditional_t<
     980              :             ComputeAuxiliary,
     981              :             get_dg_auxiliary_boundary_terms_volume_tags_or_default_t<
     982              :                 BoundaryCorrection, tmpl::list<>>,
     983              :             typename BoundaryCorrection::dg_boundary_terms_volume_tags>;
     984              :         using package_data_temp_tags = tmpl::conditional_t<
     985              :             ComputeAuxiliary,
     986              :             get_dg_auxiliary_package_data_temporary_tags_or_default_t<
     987              :                 BoundaryCorrection, tmpl::list<>>,
     988              :             typename BoundaryCorrection::dg_package_data_temporary_tags>;
     989              :         detail::apply_boundary_condition_on_face<System, Dim, ComputeAuxiliary>(
     990              :             box, boundary_correction,
     991              :             dynamic_cast<const DerivedBoundaryCondition&>(boundary_condition),
     992              :             direction, db::get<variables_tag>(*box), volume_auxiliary_variables,
     993              :             volume_fluxes, partial_derivs, temporaries, primitive_vars,
     994              :             db::get<::dg::Tags::Formulation>(*box),
     995              :             db::get<::domain::Tags::Mesh<Dim>>(*box),
     996              :             db::get<::domain::Tags::Element<Dim>>(*box),
     997              :             db::get<::domain::Tags::ElementMap<Dim, Frame::Grid>>(*box),
     998              :             db::get<::domain::CoordinateMaps::Tags::CoordinateMap<
     999              :                 Dim, Frame::Grid, Frame::Inertial>>(*box),
    1000              :             db::get<::Tags::Time>(*box),
    1001              :             db::get<::domain::Tags::FunctionsOfTime>(*box),
    1002              :             db::get<::domain::Tags::MeshVelocity<Dim>>(*box),
    1003              :             db::get<::domain::Tags::InverseJacobian<Dim, Frame::ElementLogical,
    1004              :                                                     Frame::Inertial>>(*box),
    1005              :             db::get<::domain::Tags::DetInvJacobian<Frame::ElementLogical,
    1006              :                                                    Frame::Inertial>>(*box),
    1007              :             package_data_volume_tags{}, package_field_tags{},
    1008              :             boundary_terms_volume_tags{},
    1009              :             tmpl::remove_duplicates<tmpl::append<
    1010              :                 typename variables_tag::tags_list, auxiliary_variables,
    1011              :                 fluxes_tags, package_data_temp_tags,
    1012              :                 typename detail::get_primitive_vars<
    1013              :                     System::has_primitive_and_conservative_vars>::
    1014              :                     template f<BoundaryCorrection>>>{},
    1015              :             typename DerivedBoundaryCondition::dg_gridless_tags{},
    1016              :             auxiliary_variables{});
    1017              :         --number_of_boundaries_left;
    1018              :       }
    1019              :       if (number_of_boundaries_left == 0) {
    1020              :         return;
    1021              :       }
    1022              :     }
    1023              :   });
    1024              : }
    1025              : }  // namespace evolution::dg::Actions::detail
        

Generated by: LCOV version 2.0-1