SpECTRE Documentation Coverage Report
Current view: top level - Evolution/DiscontinuousGalerkin/Actions - ApplyBoundaryCorrections.hpp Hit Total Coverage
Commit: fbcce2ed065a8e48da2f38009a84bbfbc0c260ee Lines: 6 33 18.2 %
Date: 2025-11-14 20:55:50
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 <atomic>
       7             : #include <cstddef>
       8             : #include <limits>
       9             : #include <map>
      10             : #include <mutex>
      11             : #include <optional>
      12             : #include <tuple>
      13             : #include <type_traits>
      14             : #include <utility>
      15             : #include <vector>
      16             : 
      17             : #include "DataStructures/DataBox/AsAccess.hpp"
      18             : #include "DataStructures/DataBox/DataBox.hpp"
      19             : #include "DataStructures/DataBox/PrefixHelpers.hpp"
      20             : #include "DataStructures/DataBox/Prefixes.hpp"
      21             : #include "DataStructures/Tensor/EagerMath/Magnitude.hpp"
      22             : #include "Domain/FaceNormal.hpp"
      23             : #include "Domain/Structure/DirectionalIdMap.hpp"
      24             : #include "Domain/Structure/Element.hpp"
      25             : #include "Domain/Structure/ElementId.hpp"
      26             : #include "Domain/Structure/Topology.hpp"
      27             : #include "Domain/Structure/TrimMap.hpp"
      28             : #include "Domain/Tags.hpp"
      29             : #include "Domain/Tags/NeighborMesh.hpp"
      30             : #include "Evolution/BoundaryCorrection.hpp"
      31             : #include "Evolution/BoundaryCorrectionTags.hpp"
      32             : #include "Evolution/DiscontinuousGalerkin/BoundaryData.hpp"
      33             : #include "Evolution/DiscontinuousGalerkin/InboxTags.hpp"
      34             : #include "Evolution/DiscontinuousGalerkin/MortarData.hpp"
      35             : #include "Evolution/DiscontinuousGalerkin/MortarDataHolder.hpp"
      36             : #include "Evolution/DiscontinuousGalerkin/MortarTags.hpp"
      37             : #include "Evolution/DiscontinuousGalerkin/NormalVectorTags.hpp"
      38             : #include "Evolution/DiscontinuousGalerkin/UsingSubcell.hpp"
      39             : #include "NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp"
      40             : #include "NumericalAlgorithms/DiscontinuousGalerkin/LiftFlux.hpp"
      41             : #include "NumericalAlgorithms/DiscontinuousGalerkin/LiftFromBoundary.hpp"
      42             : #include "NumericalAlgorithms/DiscontinuousGalerkin/MortarHelpers.hpp"
      43             : #include "NumericalAlgorithms/DiscontinuousGalerkin/Tags/Formulation.hpp"
      44             : #include "NumericalAlgorithms/Spectral/BoundaryInterpolationMatrices.hpp"
      45             : #include "NumericalAlgorithms/Spectral/Mesh.hpp"
      46             : #include "NumericalAlgorithms/Spectral/Quadrature.hpp"
      47             : #include "NumericalAlgorithms/Spectral/SegmentSize.hpp"
      48             : #include "Parallel/AlgorithmExecution.hpp"
      49             : #include "Parallel/ArrayCollection/IsDgElementCollection.hpp"
      50             : #include "Parallel/GlobalCache.hpp"
      51             : #include "Time/BoundaryHistory.hpp"
      52             : #include "Time/EvolutionOrdering.hpp"
      53             : #include "Time/SelfStart.hpp"
      54             : #include "Time/Time.hpp"
      55             : #include "Time/TimeStepId.hpp"
      56             : #include "Time/TimeSteppers/LtsTimeStepper.hpp"
      57             : #include "Time/TimeSteppers/TimeStepper.hpp"
      58             : #include "Utilities/Algorithm.hpp"
      59             : #include "Utilities/CallWithDynamicType.hpp"
      60             : #include "Utilities/ErrorHandling/Error.hpp"
      61             : #include "Utilities/Gsl.hpp"
      62             : #include "Utilities/MakeArray.hpp"
      63             : #include "Utilities/TMPL.hpp"
      64             : #include "Utilities/TaggedTuple.hpp"
      65             : 
      66             : /// \cond
      67             : namespace Tags {
      68             : struct Time;
      69             : struct TimeStep;
      70             : struct TimeStepId;
      71             : template <typename StepperInterface>
      72             : struct TimeStepper;
      73             : }  // namespace Tags
      74             : 
      75             : namespace evolution::dg::subcell {
      76             : // We use a forward declaration instead of including a header file to avoid
      77             : // coupling to the DG-subcell libraries for executables that don't use subcell.
      78             : template <size_t VolumeDim, typename DgComputeSubcellNeighborPackagedData>
      79             : void neighbor_reconstructed_face_solution(
      80             :     gsl::not_null<db::Access*> box,
      81             :     gsl::not_null<std::pair<
      82             :         TimeStepId,
      83             :         DirectionalIdMap<VolumeDim, evolution::dg::BoundaryData<VolumeDim>>>*>
      84             :         received_temporal_id_and_data);
      85             : template <size_t Dim>
      86             : void neighbor_tci_decision(
      87             :     gsl::not_null<db::Access*> box,
      88             :     const std::pair<TimeStepId,
      89             :                     DirectionalIdMap<Dim, evolution::dg::BoundaryData<Dim>>>&
      90             :         received_temporal_id_and_data);
      91             : }  // namespace evolution::dg::subcell
      92             : /// \endcond
      93             : 
      94             : namespace evolution::dg {
      95             : namespace detail {
      96             : template <typename BoundaryCorrectionClass>
      97             : struct get_dg_boundary_terms {
      98             :   using type = typename BoundaryCorrectionClass::dg_boundary_terms_volume_tags;
      99             : };
     100             : 
     101             : template <typename Tag, typename Type = db::const_item_type<Tag, tmpl::list<>>>
     102             : struct TemporaryReference {
     103             :   using tag = Tag;
     104             :   using type = const Type&;
     105             : };
     106             : 
     107             : template <size_t Dim>
     108             : void retrieve_boundary_data_spsc(
     109             :     const gsl::not_null<std::map<
     110             :         TimeStepId, DirectionalIdMap<Dim, evolution::dg::BoundaryData<Dim>>>*>
     111             :         boundary_data_ptr,
     112             :     const gsl::not_null<evolution::dg::AtomicInboxBoundaryData<Dim>*> inbox_ptr,
     113             :     const Element<Dim>& element) {
     114             :   for (const auto& [direction, neighbors] : element.neighbors()) {
     115             :     for (const ElementId<Dim>& neighbor_element_id : neighbors) {
     116             :       const size_t neighbor_index =
     117             :           inbox_ptr->index(DirectionalId{direction, neighbor_element_id});
     118             :       auto& spsc_in_direction =
     119             :           gsl::at(inbox_ptr->boundary_data_in_directions, neighbor_index);
     120             :       auto* data_in_direction = spsc_in_direction.front();
     121             :       while (data_in_direction != nullptr) {
     122             :         const auto& time_step_id = get<0>(*data_in_direction);
     123             :         auto& data = get<1>(*data_in_direction);
     124             :         auto& directional_element_id = get<2>(*data_in_direction);
     125             :         auto& current_inbox = (*boundary_data_ptr)[time_step_id];
     126             :         if (auto it = current_inbox.find(directional_element_id);
     127             :             it != current_inbox.end()) {
     128             :           auto& [volume_mesh, volume_mesh_ghost_cell_data,
     129             :                  boundary_correction_mesh, ghost_cell_data,
     130             :                  boundary_correction_data, validity_range, tci_status,
     131             :                  integration_order, interpolated_boundary_data] = data;
     132             :           (void)ghost_cell_data;
     133             :           auto& [current_volume_mesh, current_volume_mesh_ghost_cell_data,
     134             :                  current_boundary_correction_mesh, current_ghost_cell_data,
     135             :                  current_boundary_correction_data, current_validity_range,
     136             :                  current_tci_status, current_integration_order,
     137             :                  current_interpolated_boundary_data] = it->second;
     138             :           // Need to use when optimizing subcell
     139             :           (void)current_volume_mesh_ghost_cell_data;
     140             :           // We have already received some data at this time. Receiving
     141             :           // data twice at the same time should only occur when
     142             :           // receiving fluxes after having previously received ghost
     143             :           // cells. We sanity check that the data we already have is the
     144             :           // ghost cells and that we have not yet received flux data.
     145             :           //
     146             :           // This is used if a 2-send implementation is used (which we
     147             :           // don't right now!). We generally find that the number of
     148             :           // communications is more important than the size of each
     149             :           // communication, and so a single communication per time/sub
     150             :           // step is preferred.
     151             :           ASSERT(current_ghost_cell_data.has_value(),
     152             :                  "Have not yet received ghost cells at time step "
     153             :                      << time_step_id
     154             :                      << " but the inbox entry already exists. This is a bug in "
     155             :                         "the ordering of the actions.");
     156             :           ASSERT(not current_boundary_correction_data.has_value() and
     157             :                      not current_boundary_correction_mesh.has_value(),
     158             :                  "The fluxes have already been received at time step "
     159             :                      << time_step_id
     160             :                      << ". They are either being received for a second time, "
     161             :                         "there is a bug in the ordering of the actions (though "
     162             :                         "a different ASSERT should've caught that), or the "
     163             :                         "incorrect temporal ID is being sent.");
     164             : 
     165             :           ASSERT(current_volume_mesh == volume_mesh,
     166             :                  "The mesh being received for the fluxes is different than the "
     167             :                  "mesh received for the ghost cells. Mesh for fluxes: "
     168             :                      << volume_mesh << " mesh for ghost cells "
     169             :                      << current_volume_mesh);
     170             :           ASSERT(current_volume_mesh_ghost_cell_data ==
     171             :                      volume_mesh_ghost_cell_data,
     172             :                  "The mesh being received for the ghost cell data is different "
     173             :                  "than the mesh received previously. Mesh for received when we "
     174             :                  "got fluxes: "
     175             :                      << volume_mesh_ghost_cell_data
     176             :                      << " mesh received when we got ghost cells "
     177             :                      << current_volume_mesh_ghost_cell_data);
     178             : 
     179             :           // We always move here since we take ownership of the data and
     180             :           // moves implicitly decay to copies
     181             :           current_boundary_correction_data =
     182             :               std::move(boundary_correction_data);
     183             :           current_validity_range = validity_range;
     184             :           current_tci_status = tci_status;
     185             :           current_integration_order = integration_order;
     186             :           current_interpolated_boundary_data = interpolated_boundary_data;
     187             :         } else {
     188             :           // We have not received ghost cells or fluxes at this time.
     189             :           if (not current_inbox
     190             :                       .emplace(std::move(directional_element_id),
     191             :                                std::move(data))
     192             :                       .second) {
     193             :             ERROR("Failed to insert data to receive at instance '"
     194             :                   << time_step_id
     195             :                   << "' with tag 'BoundaryCorrectionAndGhostCellsInbox'.\n");
     196             :           }
     197             :         }
     198             : 
     199             :         spsc_in_direction.pop();
     200             :         data_in_direction = spsc_in_direction.front();
     201             :       }  // while data_in_direction != nullptr
     202             :     }    // for neighbor_element_id : neighbors
     203             :   }      // for element.neighbors()
     204             : }
     205             : }  // namespace detail
     206             : 
     207             : /// Receive boundary data for global time-stepping.  Returns true if
     208             : /// all necessary data has been received.
     209             : template <bool UseNodegroupDgElements, typename Metavariables,
     210             :           typename DbTagsList, typename... InboxTags>
     211           1 : bool receive_boundary_data_global_time_stepping(
     212             :     const gsl::not_null<db::DataBox<DbTagsList>*> box,
     213             :     const gsl::not_null<tuples::TaggedTuple<InboxTags...>*> inboxes) {
     214             :   constexpr size_t volume_dim = Metavariables::system::volume_dim;
     215             : 
     216             :   const TimeStepId& temporal_id = get<::Tags::TimeStepId>(*box);
     217             :   using Key = DirectionalId<volume_dim>;
     218             :   using InboxMap = std::map<
     219             :       TimeStepId,
     220             :       DirectionalIdMap<volume_dim, evolution::dg::BoundaryData<volume_dim>>>;
     221             :   using InboxMapValueType =
     222             :       std::pair<typename InboxMap::key_type, typename InboxMap::mapped_type>;
     223             :   using NodeType = typename InboxMap::node_type;
     224             :   auto get_temporal_id_and_data_node =
     225             :       [&temporal_id](const gsl::not_null<InboxMap*> map_ptr,
     226             :                      const Element<volume_dim>& element,
     227             :                      const auto&&...) -> NodeType {
     228             :     const auto received_temporal_id_and_data = map_ptr->find(temporal_id);
     229             :     if (received_temporal_id_and_data == map_ptr->end()) {
     230             :       return NodeType{};
     231             :     }
     232             :     const auto& received_neighbor_data = received_temporal_id_and_data->second;
     233             :     for (const auto& [direction, neighbors] : element.neighbors()) {
     234             :       for (const auto& neighbor : neighbors) {
     235             :         const auto neighbor_received =
     236             :             received_neighbor_data.find(Key{direction, neighbor});
     237             :         if (neighbor_received == received_neighbor_data.end()) {
     238             :           return NodeType{};
     239             :         }
     240             :       }
     241             :     }
     242             :     return map_ptr->extract(received_temporal_id_and_data);
     243             :   };
     244             : 
     245             :   InboxMapValueType received_temporal_id_and_data{};
     246             :   if constexpr (std::is_same_v<
     247             :                     evolution::dg::AtomicInboxBoundaryData<volume_dim>,
     248             :                     typename evolution::dg::Tags::
     249             :                         BoundaryCorrectionAndGhostCellsInbox<
     250             :                             volume_dim, UseNodegroupDgElements>::type>) {
     251             :     bool have_all_data = false;
     252             :     received_temporal_id_and_data =
     253             :         db::mutate<evolution::dg::Tags::BoundaryData<volume_dim>>(
     254             :             [&get_temporal_id_and_data_node](
     255             :                 const auto boundary_data_ptr,
     256             :                 const gsl::not_null<bool*> local_have_all_data,
     257             :                 const auto inbox_ptr, const Element<volume_dim>& element)
     258             :                 -> std::pair<typename NodeType::key_type,
     259             :                              typename NodeType::mapped_type> {
     260             :               if (inbox_ptr->message_count.load(std::memory_order_relaxed) <
     261             :                   element.number_of_neighbors()) {
     262             :                 return {};
     263             :               }
     264             :               detail::retrieve_boundary_data_spsc(boundary_data_ptr, inbox_ptr,
     265             :                                                   element);
     266             : 
     267             :               NodeType node =
     268             :                   get_temporal_id_and_data_node(boundary_data_ptr, element);
     269             :               if (node.empty()) {
     270             :                 return {};
     271             :               }
     272             :               if (UNLIKELY(node.mapped().size() !=
     273             :                            element.number_of_neighbors())) {
     274             :                 ERROR("Incorrect number of element neighbors");
     275             :               }
     276             :               *local_have_all_data = true;
     277             :               // We only decrease the counter if we are done with the current
     278             :               // time and we only decrease it by the number of neighbors at the
     279             :               // current time.
     280             :               inbox_ptr->message_count.fetch_sub(element.number_of_neighbors(),
     281             :                                                  std::memory_order_acq_rel);
     282             : 
     283             :               return std::pair{std::move(node.key()), std::move(node.mapped())};
     284             :             },
     285             :             box, make_not_null(&have_all_data),
     286             :             make_not_null(
     287             :                 &tuples::get<
     288             :                     evolution::dg::Tags::BoundaryCorrectionAndGhostCellsInbox<
     289             :                         volume_dim, UseNodegroupDgElements>>(*inboxes)),
     290             :             db::get<domain::Tags::Element<volume_dim>>(*box));
     291             :     if (not have_all_data) {
     292             :       return false;
     293             :     }
     294             :   } else {
     295             :     // Scope to make sure the `node` can't be used later.
     296             :     NodeType node = get_temporal_id_and_data_node(
     297             :         make_not_null(&tuples::get<
     298             :                       evolution::dg::Tags::BoundaryCorrectionAndGhostCellsInbox<
     299             :                           volume_dim, UseNodegroupDgElements>>(*inboxes)),
     300             :         db::get<domain::Tags::Element<volume_dim>>(*box));
     301             :     if (node.empty()) {
     302             :       return false;
     303             :     }
     304             :     received_temporal_id_and_data.first = std::move(node.key());
     305             :     received_temporal_id_and_data.second = std::move(node.mapped());
     306             :   }
     307             : 
     308             :   // Move inbox contents into the DataBox
     309             :   if constexpr (using_subcell_v<Metavariables>) {
     310             :     evolution::dg::subcell::neighbor_reconstructed_face_solution<
     311             :         volume_dim, typename Metavariables::SubcellOptions::
     312             :                         DgComputeSubcellNeighborPackagedData>(
     313             :         &db::as_access(*box), make_not_null(&received_temporal_id_and_data));
     314             :     evolution::dg::subcell::neighbor_tci_decision<volume_dim>(
     315             :         make_not_null(&db::as_access(*box)), received_temporal_id_and_data);
     316             :   }
     317             : 
     318             :   db::mutate<evolution::dg::Tags::MortarMesh<volume_dim>,
     319             :              evolution::dg::Tags::MortarData<volume_dim>,
     320             :              evolution::dg::Tags::MortarNextTemporalId<volume_dim>,
     321             :              domain::Tags::NeighborMesh<volume_dim>>(
     322             :       [&received_temporal_id_and_data](
     323             :           const gsl::not_null<
     324             :               DirectionalIdMap<volume_dim, Mesh<volume_dim - 1>>*>
     325             :               mortar_meshes,
     326             :           const gsl::not_null<DirectionalIdMap<
     327             :               volume_dim, evolution::dg::MortarDataHolder<volume_dim>>*>
     328             :               mortar_data,
     329             :           const gsl::not_null<DirectionalIdMap<volume_dim, TimeStepId>*>
     330             :               mortar_next_time_step_id,
     331             :           const gsl::not_null<DirectionalIdMap<volume_dim, Mesh<volume_dim>>*>
     332             :               neighbor_mesh,
     333             :           const Mesh<volume_dim>& volume_mesh) {
     334             :         neighbor_mesh->clear();
     335             :         for (auto& received_mortar_data :
     336             :              received_temporal_id_and_data.second) {
     337             :           const auto& mortar_id = received_mortar_data.first;
     338             :           const size_t sliced_away_dim = mortar_id.direction().dimension();
     339             :           const Mesh<volume_dim - 1> face_mesh =
     340             :               volume_mesh.slice_away(sliced_away_dim);
     341             :           const Mesh<volume_dim - 1> neighbor_face_mesh =
     342             :               received_mortar_data.second.volume_mesh.slice_away(
     343             :                   sliced_away_dim);
     344             :           const Mesh<volume_dim - 1> mortar_mesh =
     345             :               ::dg::mortar_mesh(face_mesh, neighbor_face_mesh);
     346             :           mortar_meshes->at(mortar_id) = mortar_mesh;
     347             :           p_project_mortar_data(
     348             :               make_not_null(&mortar_data->at(mortar_id).local()), mortar_mesh);
     349             :           neighbor_mesh->insert_or_assign(
     350             :               mortar_id, received_mortar_data.second.volume_mesh);
     351             :           mortar_next_time_step_id->at(mortar_id) =
     352             :               received_mortar_data.second.validity_range;
     353             :           ASSERT(using_subcell_v<Metavariables> or
     354             :                      received_mortar_data.second.boundary_correction_data
     355             :                          .has_value(),
     356             :                  "Must receive number boundary correction data when not using "
     357             :                  "DG-subcell. Mortar ID is: ("
     358             :                      << mortar_id.direction() << "," << mortar_id.id()
     359             :                      << ") and TimeStepId is "
     360             :                      << received_temporal_id_and_data.first);
     361             :           if (received_mortar_data.second.boundary_correction_data
     362             :                   .has_value()) {
     363             :             mortar_data->at(mortar_id).neighbor().face_mesh =
     364             :                 received_mortar_data.second.volume_mesh.slice_away(
     365             :                     mortar_id.direction().dimension());
     366             :             mortar_data->at(mortar_id).neighbor().mortar_mesh =
     367             :                 received_mortar_data.second.boundary_correction_mesh.value();
     368             :             mortar_data->at(mortar_id).neighbor().mortar_data = std::move(
     369             :                 received_mortar_data.second.boundary_correction_data.value());
     370             :             p_project_mortar_data(
     371             :                 make_not_null(&mortar_data->at(mortar_id).neighbor()),
     372             :                 mortar_mesh);
     373             :           }
     374             :         }
     375             :       },
     376             :       box, db::get<domain::Tags::Mesh<volume_dim>>(*box));
     377             :   return true;
     378             : }
     379             : 
     380             : /// Receive boundary data for local time-stepping.  Returns true if
     381             : /// all necessary data has been received.
     382             : ///
     383             : /// Setting \p DenseOutput to true receives data required for output
     384             : /// at `::Tags::Time` instead of `::Tags::Next<::Tags::TimeStepId>`.
     385             : template <bool UseNodegroupDgElements, typename System, size_t Dim,
     386             :           bool DenseOutput, typename DbTagsList, typename... InboxTags>
     387           1 : bool receive_boundary_data_local_time_stepping(
     388             :     const gsl::not_null<db::DataBox<DbTagsList>*> box,
     389             :     const gsl::not_null<tuples::TaggedTuple<InboxTags...>*> inboxes) {
     390             :   using variables_tag = typename System::variables_tag;
     391             :   using dt_variables_tag = db::add_tag_prefix<::Tags::dt, variables_tag>;
     392             : 
     393             :   const auto needed_time = [&box]() {
     394             :     const LtsTimeStepper& time_stepper =
     395             :         db::get<::Tags::TimeStepper<LtsTimeStepper>>(*box);
     396             :     if constexpr (DenseOutput) {
     397             :       const auto& dense_output_time = db::get<::Tags::Time>(*box);
     398             :       return [&dense_output_time, &time_stepper](const TimeStepId& id) {
     399             :         return time_stepper.neighbor_data_required(dense_output_time, id);
     400             :       };
     401             :     } else {
     402             :       const auto& next_temporal_id =
     403             :           db::get<::Tags::Next<::Tags::TimeStepId>>(*box);
     404             :       return [&next_temporal_id, &time_stepper](const TimeStepId& id) {
     405             :         return time_stepper.neighbor_data_required(next_temporal_id, id);
     406             :       };
     407             :     }
     408             :   }();
     409             : 
     410             :   // The boundary history coupling computation (which computes the _lifted_
     411             :   // boundary correction) returns a Variables<dt<EvolvedVars>> instead of
     412             :   // using the `NormalDotNumericalFlux` prefix tag. This is because the
     413             :   // returned quantity is more a `dt` quantity than a
     414             :   // `NormalDotNormalDotFlux` since it's been lifted to the volume.
     415             :   using InboxMap =
     416             :       std::map<TimeStepId,
     417             :                DirectionalIdMap<Dim, evolution::dg::BoundaryData<Dim>>>;
     418             :   InboxMap* inbox_ptr = nullptr;
     419             :   if constexpr (std::is_same_v<evolution::dg::AtomicInboxBoundaryData<Dim>,
     420             :                                typename evolution::dg::Tags::
     421             :                                    BoundaryCorrectionAndGhostCellsInbox<
     422             :                                        Dim, UseNodegroupDgElements>::type>) {
     423             :     inbox_ptr = db::mutate<evolution::dg::Tags::BoundaryData<Dim>>(
     424             :         [](const auto boundary_data_ptr, const auto local_inbox_ptr,
     425             :            const Element<Dim>& element) -> InboxMap* {
     426             :           detail::retrieve_boundary_data_spsc(boundary_data_ptr,
     427             :                                               local_inbox_ptr, element);
     428             : 
     429             :           return boundary_data_ptr.get();
     430             :         },
     431             :         box,
     432             :         make_not_null(&tuples::get<
     433             :                       evolution::dg::Tags::BoundaryCorrectionAndGhostCellsInbox<
     434             :                           Dim, UseNodegroupDgElements>>(*inboxes)),
     435             :         db::get<domain::Tags::Element<Dim>>(*box));
     436             :   } else {
     437             :     inbox_ptr =
     438             :         &tuples::get<evolution::dg::Tags::BoundaryCorrectionAndGhostCellsInbox<
     439             :             Dim, UseNodegroupDgElements>>(*inboxes);
     440             :   }
     441             :   ASSERT(inbox_ptr != nullptr, "The inbox pointer should not be null.");
     442             :   InboxMap& inbox = *inbox_ptr;
     443             : 
     444             :   const bool have_all_intermediate_messages = db::mutate<
     445             :       evolution::dg::Tags::MortarMesh<Dim>,
     446             :       evolution::dg::Tags::MortarDataHistory<Dim,
     447             :                                              typename dt_variables_tag::type>,
     448             :       evolution::dg::Tags::MortarNextTemporalId<Dim>,
     449             :       domain::Tags::NeighborMesh<Dim>>(
     450             :       [&inbox, &needed_time](
     451             :           const gsl::not_null<DirectionalIdMap<Dim, Mesh<Dim - 1>>*>
     452             :               mortar_meshes,
     453             :           const gsl::not_null<
     454             :               DirectionalIdMap<Dim, TimeSteppers::BoundaryHistory<
     455             :                                         evolution::dg::MortarData<Dim>,
     456             :                                         evolution::dg::MortarData<Dim>,
     457             :                                         typename dt_variables_tag::type>>*>
     458             :               boundary_data_history,
     459             :           const gsl::not_null<DirectionalIdMap<Dim, TimeStepId>*>
     460             :               mortar_next_time_step_ids,
     461             :           const gsl::not_null<DirectionalIdMap<Dim, Mesh<Dim>>*> neighbor_mesh,
     462             :           const Element<Dim>& element, const Mesh<Dim>& volume_mesh) {
     463             :         // Remove neighbor meshes for neighbors that don't exist anymore
     464             :         domain::remove_nonexistent_neighbors(neighbor_mesh, element);
     465             : 
     466             :         // Move received boundary data into boundary history.
     467             :         for (auto& [mortar_id, mortar_next_time_step_id] :
     468             :              *mortar_next_time_step_ids) {
     469             :           if (mortar_id.id() == ElementId<Dim>::external_boundary_id()) {
     470             :             continue;
     471             :           }
     472             :           const size_t sliced_away_dim = mortar_id.direction().dimension();
     473             :           const Mesh<Dim - 1> face_mesh =
     474             :               volume_mesh.slice_away(sliced_away_dim);
     475             :           while (needed_time(mortar_next_time_step_id)) {
     476             :             const auto time_entry = inbox.find(mortar_next_time_step_id);
     477             :             if (time_entry == inbox.end()) {
     478             :               return false;
     479             :             }
     480             :             const auto received_mortar_data =
     481             :                 time_entry->second.find(mortar_id);
     482             :             if (received_mortar_data == time_entry->second.end()) {
     483             :               return false;
     484             :             }
     485             : 
     486             :             const Mesh<Dim - 1> neighbor_face_mesh =
     487             :                 received_mortar_data->second.volume_mesh.slice_away(
     488             :                     sliced_away_dim);
     489             :             const Mesh<Dim - 1> mortar_mesh =
     490             :                 ::dg::mortar_mesh(face_mesh, neighbor_face_mesh);
     491             : 
     492             :             const auto project_boundary_mortar_data =
     493             :                 [&mortar_mesh](
     494             :                     const TimeStepId& /*id*/,
     495             :                     const gsl::not_null<::evolution::dg::MortarData<Dim>*>
     496             :                         mortar_data) {
     497             :                   return p_project_mortar_data(mortar_data, mortar_mesh);
     498             :                 };
     499             : 
     500             :             mortar_meshes->at(mortar_id) = mortar_mesh;
     501             :             boundary_data_history->at(mortar_id).local().for_each(
     502             :                 project_boundary_mortar_data);
     503             :             boundary_data_history->at(mortar_id).remote().for_each(
     504             :                 project_boundary_mortar_data);
     505             : 
     506             :             MortarData<Dim> neighbor_mortar_data{};
     507             :             // Insert:
     508             :             // - the current TimeStepId of the neighbor
     509             :             // - the current face mesh of the neighbor
     510             :             // - the current boundary correction data of the neighbor
     511             :             ASSERT(received_mortar_data->second.boundary_correction_data
     512             :                        .has_value(),
     513             :                    "Did not receive boundary correction data from the "
     514             :                    "neighbor\nMortarId: "
     515             :                        << mortar_id
     516             :                        << "\nTimeStepId: " << mortar_next_time_step_id);
     517             :             neighbor_mesh->insert_or_assign(
     518             :                 mortar_id, received_mortar_data->second.volume_mesh);
     519             :             neighbor_mortar_data.mortar_mesh =
     520             :                 received_mortar_data->second.boundary_correction_mesh.value();
     521             :             neighbor_mortar_data.mortar_data = std::move(
     522             :                 received_mortar_data->second.boundary_correction_data.value());
     523             :             boundary_data_history->at(mortar_id).remote().insert(
     524             :                 time_entry->first,
     525             :                 received_mortar_data->second.integration_order,
     526             :                 std::move(neighbor_mortar_data));
     527             :             boundary_data_history->at(mortar_id).remote().for_each(
     528             :                 project_boundary_mortar_data);
     529             :             mortar_next_time_step_id =
     530             :                 received_mortar_data->second.validity_range;
     531             :             time_entry->second.erase(received_mortar_data);
     532             :             if (time_entry->second.empty()) {
     533             :               inbox.erase(time_entry);
     534             :             }
     535             :           }
     536             :         }
     537             :         return true;
     538             :       },
     539             :       box, db::get<::domain::Tags::Element<Dim>>(*box),
     540             :       db::get<domain::Tags::Mesh<Dim>>(*box));
     541             : 
     542             :   if (not have_all_intermediate_messages) {
     543             :     return false;
     544             :   }
     545             : 
     546             :   if constexpr (std::is_same_v<evolution::dg::AtomicInboxBoundaryData<Dim>,
     547             :                                typename evolution::dg::Tags::
     548             :                                    BoundaryCorrectionAndGhostCellsInbox<
     549             :                                        Dim, UseNodegroupDgElements>::type>) {
     550             :     // We only decrease the counter if we are done with the current time
     551             :     // and we only decrease it by the number of neighbors at the current
     552             :     // time.
     553             :     tuples::get<evolution::dg::Tags::BoundaryCorrectionAndGhostCellsInbox<
     554             :         Dim, UseNodegroupDgElements>>(*inboxes)
     555             :         .message_count.fetch_sub(
     556             :             db::get<domain::Tags::Element<Dim>>(*box).number_of_neighbors(),
     557             :             std::memory_order_acq_rel);
     558             :   }
     559             :   return have_all_intermediate_messages;
     560             : }
     561             : 
     562             : /// Apply corrections from boundary communication.
     563             : ///
     564             : /// If `LocalTimeStepping` is false, updates the derivative of the variables,
     565             : /// which should be done before taking a time step.  If
     566             : /// `LocalTimeStepping` is true, updates the variables themselves, which should
     567             : /// be done after the volume update.
     568             : ///
     569             : /// Setting \p DenseOutput to true receives data required for output
     570             : /// at ::Tags::Time instead of performing a full step.  This is only
     571             : /// used for local time-stepping.
     572             : template <bool LocalTimeStepping, typename Metavariables, size_t VolumeDim,
     573             :           bool DenseOutput>
     574           1 : struct ApplyBoundaryCorrections {
     575           0 :   static constexpr bool local_time_stepping = LocalTimeStepping;
     576             :   static_assert(local_time_stepping or not DenseOutput,
     577             :                 "GTS does not use ApplyBoundaryCorrections for dense output.");
     578             : 
     579           0 :   using system = typename Metavariables::system;
     580           0 :   static constexpr size_t volume_dim = VolumeDim;
     581           0 :   using variables_tag = typename system::variables_tag;
     582           0 :   using dt_variables_tag = db::add_tag_prefix<::Tags::dt, variables_tag>;
     583           0 :   using DtVariables = typename dt_variables_tag::type;
     584           0 :   using derived_boundary_corrections =
     585             :       tmpl::at<typename Metavariables::factory_creation::factory_classes,
     586             :                evolution::BoundaryCorrection>;
     587           0 :   using volume_tags_for_dg_boundary_terms = tmpl::remove_duplicates<
     588             :       tmpl::flatten<tmpl::transform<derived_boundary_corrections,
     589             :                                     detail::get_dg_boundary_terms<tmpl::_1>>>>;
     590             : 
     591           0 :   using TimeStepperType =
     592             :       tmpl::conditional_t<local_time_stepping, LtsTimeStepper, TimeStepper>;
     593             : 
     594           0 :   using tag_to_update =
     595             :       tmpl::conditional_t<local_time_stepping, variables_tag, dt_variables_tag>;
     596           0 :   using mortar_data_tag = tmpl::conditional_t<
     597             :       local_time_stepping,
     598             :       evolution::dg::Tags::MortarDataHistory<volume_dim, DtVariables>,
     599             :       evolution::dg::Tags::MortarData<volume_dim>>;
     600           0 :   using MortarDataType =
     601             :       tmpl::conditional_t<DenseOutput, const typename mortar_data_tag::type,
     602             :                           typename mortar_data_tag::type>;
     603             : 
     604           0 :   using return_tags =
     605             :       tmpl::conditional_t<DenseOutput, tmpl::list<tag_to_update>,
     606             :                           tmpl::list<tag_to_update, mortar_data_tag>>;
     607           0 :   using argument_tags = tmpl::append<
     608             :       tmpl::flatten<tmpl::list<
     609             :           tmpl::conditional_t<DenseOutput, mortar_data_tag, tmpl::list<>>,
     610             :           domain::Tags::Mesh<volume_dim>, domain::Tags::Element<volume_dim>,
     611             :           Tags::MortarMesh<volume_dim>, Tags::MortarInfo<volume_dim>,
     612             :           ::dg::Tags::Formulation,
     613             :           evolution::dg::Tags::NormalCovectorAndMagnitude<volume_dim>,
     614             :           ::Tags::TimeStepper<TimeStepperType>,
     615             :           evolution::Tags::BoundaryCorrection,
     616             :           tmpl::conditional_t<DenseOutput, ::Tags::Time, ::Tags::TimeStep>,
     617             :           tmpl::conditional_t<local_time_stepping, tmpl::list<>,
     618             :                               domain::Tags::DetInvJacobian<
     619             :                                   Frame::ElementLogical, Frame::Inertial>>>>,
     620             :       volume_tags_for_dg_boundary_terms>;
     621             : 
     622             :   // full step
     623             :   template <typename... VolumeArgs>
     624           0 :   static void apply(
     625             :       const gsl::not_null<typename tag_to_update::type*> vars_to_update,
     626             :       const gsl::not_null<MortarDataType*> mortar_data,
     627             :       const Mesh<volume_dim>& volume_mesh, const Element<volume_dim>& element,
     628             :       const typename Tags::MortarMesh<volume_dim>::type& mortar_meshes,
     629             :       const typename Tags::MortarInfo<volume_dim>::type& mortar_infos,
     630             :       const ::dg::Formulation dg_formulation,
     631             :       const DirectionMap<
     632             :           volume_dim, std::optional<Variables<tmpl::list<
     633             :                           evolution::dg::Tags::MagnitudeOfNormal,
     634             :                           evolution::dg::Tags::NormalCovector<volume_dim>>>>>&
     635             :           face_normal_covector_and_magnitude,
     636             :       const TimeStepperType& time_stepper,
     637             :       const evolution::BoundaryCorrection& boundary_correction,
     638             :       const TimeDelta& time_step,
     639             :       const Scalar<DataVector>& gts_det_inv_jacobian,
     640             :       const VolumeArgs&... volume_args) {
     641             :     apply_impl(vars_to_update, mortar_data, volume_mesh, element, mortar_meshes,
     642             :                mortar_infos, dg_formulation, face_normal_covector_and_magnitude,
     643             :                time_stepper, boundary_correction, time_step,
     644             :                std::numeric_limits<double>::signaling_NaN(),
     645             :                gts_det_inv_jacobian, volume_args...);
     646             :   }
     647             : 
     648             :   template <typename... VolumeArgs>
     649           0 :   static void apply(
     650             :       const gsl::not_null<typename tag_to_update::type*> vars_to_update,
     651             :       const gsl::not_null<MortarDataType*> mortar_data,
     652             :       const Mesh<volume_dim>& volume_mesh, const Element<volume_dim>& element,
     653             :       const typename Tags::MortarMesh<volume_dim>::type& mortar_meshes,
     654             :       const typename Tags::MortarInfo<volume_dim>::type& mortar_infos,
     655             :       const ::dg::Formulation dg_formulation,
     656             :       const DirectionMap<
     657             :           volume_dim, std::optional<Variables<tmpl::list<
     658             :                           evolution::dg::Tags::MagnitudeOfNormal,
     659             :                           evolution::dg::Tags::NormalCovector<volume_dim>>>>>&
     660             :           face_normal_covector_and_magnitude,
     661             :       const TimeStepperType& time_stepper,
     662             :       const evolution::BoundaryCorrection& boundary_correction,
     663             :       const TimeDelta& time_step, const VolumeArgs&... volume_args) {
     664             :     apply_impl(vars_to_update, mortar_data, volume_mesh, element, mortar_meshes,
     665             :                mortar_infos, dg_formulation, face_normal_covector_and_magnitude,
     666             :                time_stepper, boundary_correction, time_step,
     667             :                std::numeric_limits<double>::signaling_NaN(), {},
     668             :                volume_args...);
     669             :   }
     670             : 
     671             :   // dense output (LTS only)
     672             :   template <typename... VolumeArgs>
     673           0 :   static void apply(
     674             :       const gsl::not_null<typename variables_tag::type*> vars_to_update,
     675             :       const MortarDataType& mortar_data, const Mesh<volume_dim>& volume_mesh,
     676             :       const Element<volume_dim>& element,
     677             :       const typename Tags::MortarMesh<volume_dim>::type& mortar_meshes,
     678             :       const typename Tags::MortarInfo<volume_dim>::type& mortar_infos,
     679             :       const ::dg::Formulation dg_formulation,
     680             :       const DirectionMap<
     681             :           volume_dim, std::optional<Variables<tmpl::list<
     682             :                           evolution::dg::Tags::MagnitudeOfNormal,
     683             :                           evolution::dg::Tags::NormalCovector<volume_dim>>>>>&
     684             :           face_normal_covector_and_magnitude,
     685             :       const LtsTimeStepper& time_stepper,
     686             :       const evolution::BoundaryCorrection& boundary_correction,
     687             :       const double dense_output_time, const VolumeArgs&... volume_args) {
     688             :     apply_impl(vars_to_update, &mortar_data, volume_mesh, element,
     689             :                mortar_meshes, mortar_infos, dg_formulation,
     690             :                face_normal_covector_and_magnitude, time_stepper,
     691             :                boundary_correction, TimeDelta{}, dense_output_time, {},
     692             :                volume_args...);
     693             :   }
     694             : 
     695             :   template <typename DbTagsList, typename... InboxTags, typename ArrayIndex,
     696             :             typename ParallelComponent>
     697           0 :   static bool is_ready(
     698             :       const gsl::not_null<db::DataBox<DbTagsList>*> box,
     699             :       const gsl::not_null<tuples::TaggedTuple<InboxTags...>*> inboxes,
     700             :       Parallel::GlobalCache<Metavariables>& /*cache*/,
     701             :       const ArrayIndex& /*array_index*/,
     702             :       const ParallelComponent* const /*component*/) {
     703             :     if constexpr (local_time_stepping) {
     704             :       return receive_boundary_data_local_time_stepping<
     705             :           Parallel::is_dg_element_collection_v<ParallelComponent>, system,
     706             :           VolumeDim, DenseOutput>(box, inboxes);
     707             :     } else {
     708             :       return receive_boundary_data_global_time_stepping<
     709             :           Parallel::is_dg_element_collection_v<ParallelComponent>,
     710             :           Metavariables>(box, inboxes);
     711             :     }
     712             :   }
     713             : 
     714             :  private:
     715             :   template <typename... VolumeArgs>
     716           0 :   static void apply_impl(
     717             :       const gsl::not_null<typename tag_to_update::type*> vars_to_update,
     718             :       const gsl::not_null<MortarDataType*> mortar_data,
     719             :       const Mesh<volume_dim>& volume_mesh, const Element<volume_dim>& element,
     720             :       const typename Tags::MortarMesh<volume_dim>::type& mortar_meshes,
     721             :       const typename Tags::MortarInfo<volume_dim>::type& mortar_infos,
     722             :       const ::dg::Formulation dg_formulation,
     723             :       const DirectionMap<
     724             :           volume_dim, std::optional<Variables<tmpl::list<
     725             :                           evolution::dg::Tags::MagnitudeOfNormal,
     726             :                           evolution::dg::Tags::NormalCovector<volume_dim>>>>>&
     727             :           face_normal_covector_and_magnitude,
     728             :       const TimeStepperType& time_stepper,
     729             :       const evolution::BoundaryCorrection& boundary_correction,
     730             :       const TimeDelta& time_step, const double dense_output_time,
     731             :       const Scalar<DataVector>& gts_det_inv_jacobian,
     732             :       const VolumeArgs&... volume_args) {
     733             :     tuples::tagged_tuple_from_typelist<db::wrap_tags_in<
     734             :         detail::TemporaryReference, volume_tags_for_dg_boundary_terms>>
     735             :         volume_args_tuple{volume_args...};
     736             : 
     737             :     // Set up helper lambda that will compute and lift the boundary corrections
     738             :     ASSERT(
     739             :         volume_mesh.quadrature() ==
     740             :                 make_array<volume_dim>(volume_mesh.quadrature(0)) or
     741             :             element.topologies() != domain::topologies::hypercube<volume_dim>,
     742             :         "Must have isotropic quadrature, but got volume mesh: " << volume_mesh);
     743             :     const bool using_gauss_lobatto_points =
     744             :         volume_mesh.quadrature(0) == Spectral::Quadrature::GaussLobatto;
     745             : 
     746             :     Scalar<DataVector> volume_det_inv_jacobian{};
     747             :     Scalar<DataVector> volume_det_jacobian{};
     748             :     if constexpr (not local_time_stepping) {
     749             :       if (not using_gauss_lobatto_points) {
     750             :         get(volume_det_inv_jacobian)
     751             :             .set_data_ref(make_not_null(
     752             :                 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
     753             :                 &const_cast<DataVector&>(get(gts_det_inv_jacobian))));
     754             :         get(volume_det_jacobian) = 1.0 / get(volume_det_inv_jacobian);
     755             :       }
     756             :     }
     757             : 
     758             :     static_assert(
     759             :         tmpl::all<derived_boundary_corrections, std::is_final<tmpl::_1>>::value,
     760             :         "All createable classes for boundary corrections must be marked "
     761             :         "final.");
     762             :     call_with_dynamic_type<void, derived_boundary_corrections>(
     763             :         &boundary_correction,
     764             :         [&dense_output_time, &dg_formulation,
     765             :          &face_normal_covector_and_magnitude, &mortar_data, &mortar_meshes,
     766             :          &mortar_infos, &time_step, &time_stepper, using_gauss_lobatto_points,
     767             :          &vars_to_update, &volume_args_tuple, &volume_det_jacobian,
     768             :          &volume_det_inv_jacobian,
     769             :          &volume_mesh](auto* typed_boundary_correction) {
     770             :           using BcType = std::decay_t<decltype(*typed_boundary_correction)>;
     771             :           // Compute internal boundary quantities on the mortar for sides of
     772             :           // the element that have neighbors, i.e. they are not an external
     773             :           // side.
     774             :           using mortar_tags_list = typename BcType::dg_package_field_tags;
     775             : 
     776             :           // Variables for reusing allocations.  The actual values are
     777             :           // not reused.
     778             :           DtVariables dt_boundary_correction_on_mortar{};
     779             :           DtVariables volume_dt_correction{};
     780             :           // These variables may change size for each mortar and require
     781             :           // a new memory allocation, but they may also happen to need
     782             :           // to be the same size twice in a row, in which case holding
     783             :           // on to the allocation is a win.
     784             :           Scalar<DataVector> face_det_jacobian{};
     785             :           Variables<mortar_tags_list> local_data_on_mortar{};
     786             :           Variables<mortar_tags_list> neighbor_data_on_mortar{};
     787             : 
     788             :           for (auto& mortar_id_and_data : *mortar_data) {
     789             :             const auto& mortar_id = mortar_id_and_data.first;
     790             :             const auto& direction = mortar_id.direction();
     791             :             if (UNLIKELY(mortar_id.id() ==
     792             :                          ElementId<volume_dim>::external_boundary_id())) {
     793             :               ERROR(
     794             :                   "Cannot impose boundary conditions on external boundary in "
     795             :                   "direction "
     796             :                   << direction
     797             :                   << " in the ApplyBoundaryCorrections action. Boundary "
     798             :                      "conditions are applied in the ComputeTimeDerivative "
     799             :                      "action "
     800             :                      "instead. You may have unintentionally added external "
     801             :                      "mortars in one of the initialization actions.");
     802             :             }
     803             : 
     804             :             const Mesh<volume_dim - 1> face_mesh =
     805             :                 volume_mesh.slice_away(direction.dimension());
     806             : 
     807             :             const auto compute_correction_coupling =
     808             :                 [&typed_boundary_correction, &direction, dg_formulation,
     809             :                  &dt_boundary_correction_on_mortar, &face_det_jacobian,
     810             :                  &face_mesh, &face_normal_covector_and_magnitude,
     811             :                  &local_data_on_mortar, &mortar_id, &mortar_meshes,
     812             :                  &mortar_infos, &neighbor_data_on_mortar,
     813             :                  using_gauss_lobatto_points, &volume_args_tuple,
     814             :                  &volume_det_jacobian, &volume_det_inv_jacobian,
     815             :                  &volume_dt_correction, &volume_mesh](
     816             :                     const MortarData<volume_dim>& local_mortar_data,
     817             :                     const MortarData<volume_dim>& neighbor_mortar_data)
     818             :                 -> DtVariables {
     819             :               if (local_time_stepping and not using_gauss_lobatto_points) {
     820             :                 // This needs to be updated every call because the Jacobian
     821             :                 // may be time-dependent. In the case of time-independent maps
     822             :                 // and local time stepping we could first perform the integral
     823             :                 // on the boundaries, and then lift to the volume. This is
     824             :                 // left as a future optimization.
     825             :                 volume_det_inv_jacobian =
     826             :                     local_mortar_data.volume_det_inv_jacobian.value();
     827             :                 get(volume_det_jacobian) = 1.0 / get(volume_det_inv_jacobian);
     828             :               }
     829             :               const auto& mortar_mesh = mortar_meshes.at(mortar_id);
     830             : 
     831             :               // Extract local and neighbor data, copy into Variables because
     832             :               // we store them in a std::vector for type erasure.
     833             :               ASSERT(*local_mortar_data.mortar_mesh ==
     834             :                              *neighbor_mortar_data.mortar_mesh and
     835             :                          *local_mortar_data.mortar_mesh == mortar_mesh,
     836             :                      "local mortar mesh: " << *local_mortar_data.mortar_mesh
     837             :                                            << "\nneighbor mortar mesh: "
     838             :                                            << *neighbor_mortar_data.mortar_mesh
     839             :                                            << "\nmortar mesh: " << mortar_mesh
     840             :                                            << "\n");
     841             :               const DataVector& local_data = *local_mortar_data.mortar_data;
     842             :               const DataVector& neighbor_data =
     843             :                   *neighbor_mortar_data.mortar_data;
     844             :               ASSERT(local_data.size() == neighbor_data.size(),
     845             :                      "local data size: "
     846             :                          << local_data.size()
     847             :                          << "\nneighbor_data: " << neighbor_data.size()
     848             :                          << "\n mortar_mesh: " << mortar_mesh << "\n");
     849             :               ASSERT(local_data_on_mortar.number_of_grid_points() ==
     850             :                          neighbor_data_on_mortar.number_of_grid_points(),
     851             :                      "Local data size = "
     852             :                          << local_data_on_mortar.number_of_grid_points()
     853             :                          << ", but neighbor size = "
     854             :                          << neighbor_data_on_mortar.number_of_grid_points());
     855             :               local_data_on_mortar.set_data_ref(
     856             :                   // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
     857             :                   const_cast<double*>(local_data.data()), local_data.size());
     858             :               neighbor_data_on_mortar.set_data_ref(
     859             :                   // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
     860             :                   const_cast<double*>(neighbor_data.data()),
     861             :                   neighbor_data.size());
     862             : 
     863             :               // The boundary computations and lifting can be further
     864             :               // optimized by in the h-refinement case having only one
     865             :               // allocation for the face and having the projection from the
     866             :               // mortar to the face be done in place. E.g.
     867             :               // local_data_on_mortar and neighbor_data_on_mortar could be
     868             :               // allocated fewer times, as well as `needs_projection` section
     869             :               // below could do an in-place projection.
     870             :               dt_boundary_correction_on_mortar.initialize(
     871             :                   mortar_mesh.number_of_grid_points());
     872             : 
     873             :               call_boundary_correction(
     874             :                   make_not_null(&dt_boundary_correction_on_mortar),
     875             :                   local_data_on_mortar, neighbor_data_on_mortar,
     876             :                   *typed_boundary_correction, dg_formulation, volume_args_tuple,
     877             :                   typename BcType::dg_boundary_terms_volume_tags{});
     878             : 
     879             :               const std::array<Spectral::SegmentSize, volume_dim - 1>&
     880             :                   mortar_size = mortar_infos.at(mortar_id).mortar_size();
     881             : 
     882             :               // This cannot reuse an allocation because it is initialized
     883             :               // via move-assignment.  (If it is used at all.)
     884             :               DtVariables dt_boundary_correction_projected_onto_face{};
     885             :               auto& dt_boundary_correction =
     886             :                   [&dt_boundary_correction_on_mortar,
     887             :                    &dt_boundary_correction_projected_onto_face, &face_mesh,
     888             :                    &mortar_mesh, &mortar_size]() -> DtVariables& {
     889             :                 if (Spectral::needs_projection(face_mesh, mortar_mesh,
     890             :                                                mortar_size)) {
     891             :                   dt_boundary_correction_projected_onto_face =
     892             :                       ::dg::project_from_mortar(
     893             :                           dt_boundary_correction_on_mortar, face_mesh,
     894             :                           mortar_mesh, mortar_size);
     895             :                   return dt_boundary_correction_projected_onto_face;
     896             :                 }
     897             :                 return dt_boundary_correction_on_mortar;
     898             :               }();
     899             : 
     900             :               // Both paths initialize this to be non-owning.
     901             :               Scalar<DataVector> magnitude_of_face_normal{};
     902             :               if constexpr (local_time_stepping) {
     903             :                 (void)face_normal_covector_and_magnitude;
     904             :                 get(magnitude_of_face_normal)
     905             :                     .set_data_ref(make_not_null(&const_cast<DataVector&>(
     906             :                         get(local_mortar_data.face_normal_magnitude.value()))));
     907             :               } else {
     908             :                 ASSERT(
     909             :                     face_normal_covector_and_magnitude.count(direction) == 1 and
     910             :                         face_normal_covector_and_magnitude.at(direction)
     911             :                             .has_value(),
     912             :                     "Face normal covector and magnitude not set in "
     913             :                     "direction: "
     914             :                         << direction);
     915             :                 get(magnitude_of_face_normal)
     916             :                     .set_data_ref(make_not_null(&const_cast<DataVector&>(
     917             :                         get(get<evolution::dg::Tags::MagnitudeOfNormal>(
     918             :                             *face_normal_covector_and_magnitude.at(
     919             :                                 direction))))));
     920             :               }
     921             : 
     922             :               if (using_gauss_lobatto_points) {
     923             :                 // The lift_flux function lifts only on the slice, it does not
     924             :                 // add the contribution to the volume.
     925             :                 ::dg::lift_flux(make_not_null(&dt_boundary_correction),
     926             :                                 volume_mesh.extents(direction.dimension()),
     927             :                                 magnitude_of_face_normal);
     928             :                 return std::move(dt_boundary_correction);
     929             :               } else {
     930             :                 // We are using Gauss points.
     931             :                 //
     932             :                 // Notes:
     933             :                 // - We should really lift both sides simultaneously since this
     934             :                 //   reduces memory accesses. Lifting all sides at the same
     935             :                 //   time is unlikely to improve performance since we lift by
     936             :                 //   jumping through slices. There may also be compatibility
     937             :                 //   issues with local time stepping.
     938             :                 // - If we lift both sides at the same time we first need to
     939             :                 //   deal with projecting from mortars to the face, then lift
     940             :                 //   off the faces. With non-owning Variables memory
     941             :                 //   allocations could be significantly reduced in this code.
     942             :                 if constexpr (local_time_stepping) {
     943             :                   ASSERT(get(volume_det_inv_jacobian).size() > 0,
     944             :                          "For local time stepping the volume determinant of "
     945             :                          "the inverse Jacobian has not been set.");
     946             : 
     947             :                   get(face_det_jacobian)
     948             :                       .set_data_ref(make_not_null(&const_cast<DataVector&>(
     949             :                           get(local_mortar_data.face_det_jacobian.value()))));
     950             :                 } else {
     951             :                   // Project the determinant of the Jacobian to the face. This
     952             :                   // could be optimized by caching in the time-independent case.
     953             :                   get(face_det_jacobian)
     954             :                       .destructive_resize(face_mesh.number_of_grid_points());
     955             :                   const Matrix identity{};
     956             :                   auto interpolation_matrices =
     957             :                       make_array<volume_dim>(std::cref(identity));
     958             :                   const std::pair<Matrix, Matrix>& matrices =
     959             :                       Spectral::boundary_interpolation_matrices(
     960             :                           volume_mesh.slice_through(direction.dimension()));
     961             :                   gsl::at(interpolation_matrices, direction.dimension()) =
     962             :                       direction.side() == Side::Upper ? matrices.second
     963             :                                                       : matrices.first;
     964             :                   apply_matrices(make_not_null(&get(face_det_jacobian)),
     965             :                                  interpolation_matrices,
     966             :                                  get(volume_det_jacobian),
     967             :                                  volume_mesh.extents());
     968             :                 }
     969             : 
     970             :                 volume_dt_correction.initialize(
     971             :                     volume_mesh.number_of_grid_points(), 0.0);
     972             :                 ::dg::lift_boundary_terms_gauss_points(
     973             :                     make_not_null(&volume_dt_correction),
     974             :                     volume_det_inv_jacobian, volume_mesh, direction,
     975             :                     dt_boundary_correction, magnitude_of_face_normal,
     976             :                     face_det_jacobian);
     977             :                 return std::move(volume_dt_correction);
     978             :               }
     979             :             };
     980             : 
     981             :             if constexpr (local_time_stepping) {
     982             :               typename variables_tag::type lgl_lifted_data{};
     983             :               auto& lifted_data = using_gauss_lobatto_points ? lgl_lifted_data
     984             :                                                              : *vars_to_update;
     985             :               if (using_gauss_lobatto_points) {
     986             :                 lifted_data.initialize(face_mesh.number_of_grid_points(), 0.0);
     987             :               }
     988             : 
     989             :               auto& mortar_data_history = mortar_id_and_data.second;
     990             :               if constexpr (DenseOutput) {
     991             :                 (void)time_step;
     992             :                 time_stepper.boundary_dense_output(
     993             :                     &lifted_data, mortar_data_history, dense_output_time,
     994             :                     compute_correction_coupling);
     995             :               } else {
     996             :                 (void)dense_output_time;
     997             :                 time_stepper.add_boundary_delta(&lifted_data,
     998             :                                                 mortar_data_history, time_step,
     999             :                                                 compute_correction_coupling);
    1000             :               }
    1001             : 
    1002             :               if (using_gauss_lobatto_points) {
    1003             :                 // Add the flux contribution to the volume data
    1004             :                 add_slice_to_data(
    1005             :                     vars_to_update, lifted_data, volume_mesh.extents(),
    1006             :                     direction.dimension(),
    1007             :                     index_to_slice_at(volume_mesh.extents(), direction));
    1008             :               }
    1009             :             } else {
    1010             :               (void)time_step;
    1011             :               (void)time_stepper;
    1012             :               (void)dense_output_time;
    1013             : 
    1014             :               // Choose an allocation cache that may be empty, so we
    1015             :               // might be able to reuse the allocation obtained for the
    1016             :               // lifted data.  This may result in a self assignment,
    1017             :               // depending on the code paths taken, but handling the
    1018             :               // results this way makes the GTS and LTS paths more
    1019             :               // similar because the LTS code always stores the result
    1020             :               // in the history and so sometimes benefits from moving
    1021             :               // into the return value of compute_correction_coupling.
    1022             :               auto& lifted_data = using_gauss_lobatto_points
    1023             :                                       ? dt_boundary_correction_on_mortar
    1024             :                                       : volume_dt_correction;
    1025             :               lifted_data = compute_correction_coupling(
    1026             :                   mortar_id_and_data.second.local(),
    1027             :                   mortar_id_and_data.second.neighbor());
    1028             : 
    1029             :               if (using_gauss_lobatto_points) {
    1030             :                 // Add the flux contribution to the volume data
    1031             :                 add_slice_to_data(
    1032             :                     vars_to_update, lifted_data, volume_mesh.extents(),
    1033             :                     direction.dimension(),
    1034             :                     index_to_slice_at(volume_mesh.extents(), direction));
    1035             :               } else {
    1036             :                 *vars_to_update += lifted_data;
    1037             :               }
    1038             :             }
    1039             :           }
    1040             :         });
    1041             :   }
    1042             : 
    1043             :   template <typename... BoundaryCorrectionTags, typename... Tags,
    1044             :             typename BoundaryCorrection, typename... AllVolumeArgs,
    1045             :             typename... VolumeTagsForCorrection>
    1046           0 :   static void call_boundary_correction(
    1047             :       const gsl::not_null<Variables<tmpl::list<BoundaryCorrectionTags...>>*>
    1048             :           boundary_corrections_on_mortar,
    1049             :       const Variables<tmpl::list<Tags...>>& local_boundary_data,
    1050             :       const Variables<tmpl::list<Tags...>>& neighbor_boundary_data,
    1051             :       const BoundaryCorrection& boundary_correction,
    1052             :       const ::dg::Formulation dg_formulation,
    1053             :       const tuples::TaggedTuple<detail::TemporaryReference<AllVolumeArgs>...>&
    1054             :           volume_args_tuple,
    1055             :       tmpl::list<VolumeTagsForCorrection...> /*meta*/) {
    1056             :     boundary_correction.dg_boundary_terms(
    1057             :         make_not_null(
    1058             :             &get<BoundaryCorrectionTags>(*boundary_corrections_on_mortar))...,
    1059             :         get<Tags>(local_boundary_data)..., get<Tags>(neighbor_boundary_data)...,
    1060             :         dg_formulation,
    1061             :         tuples::get<detail::TemporaryReference<VolumeTagsForCorrection>>(
    1062             :             volume_args_tuple)...);
    1063             :   }
    1064             : };
    1065             : 
    1066           1 : namespace Actions {
    1067             : /*!
    1068             :  * \brief Computes the boundary corrections for global time-stepping
    1069             :  * and adds them to the time derivative.
    1070             :  */
    1071             : template <size_t VolumeDim, bool DenseOutput, bool UseNodegroupDgElements>
    1072           1 : struct ApplyBoundaryCorrectionsToTimeDerivative {
    1073           0 :   using inbox_tags =
    1074             :       tmpl::list<evolution::dg::Tags::BoundaryCorrectionAndGhostCellsInbox<
    1075             :           VolumeDim, UseNodegroupDgElements>>;
    1076           0 :   using const_global_cache_tags =
    1077             :       tmpl::list<evolution::Tags::BoundaryCorrection, ::dg::Tags::Formulation>;
    1078             : 
    1079             :   template <typename DbTagsList, typename... InboxTags, typename Metavariables,
    1080             :             typename ArrayIndex, typename ActionList,
    1081             :             typename ParallelComponent>
    1082           0 :   static Parallel::iterable_action_return_t apply(
    1083             :       db::DataBox<DbTagsList>& box, tuples::TaggedTuple<InboxTags...>& inboxes,
    1084             :       const Parallel::GlobalCache<Metavariables>& /*cache*/,
    1085             :       const ArrayIndex& /*array_index*/, ActionList /*meta*/,
    1086             :       const ParallelComponent* const /*meta*/) {
    1087             :     static_assert(
    1088             :         UseNodegroupDgElements ==
    1089             :             Parallel::is_dg_element_collection_v<ParallelComponent>,
    1090             :         "The action ApplyBoundaryCorrectionsToTimeDerivative is told by the "
    1091             :         "template parameter UseNodegroupDgElements that it is being "
    1092             :         "used with a DgElementCollection, but the ParallelComponent "
    1093             :         "is not a DgElementCollection. You need to change the template "
    1094             :         "parameter on the ApplyBoundaryCorrectionsToTimeDerivative action "
    1095             :         "in your action list.");
    1096             :     constexpr size_t volume_dim = Metavariables::system::volume_dim;
    1097             :     const Element<volume_dim>& element =
    1098             :         db::get<domain::Tags::Element<volume_dim>>(box);
    1099             : 
    1100             :     if (UNLIKELY(element.number_of_neighbors() == 0)) {
    1101             :       // We have no neighbors, yay!
    1102             :       return {Parallel::AlgorithmExecution::Continue, std::nullopt};
    1103             :     }
    1104             : 
    1105             :     if (not receive_boundary_data_global_time_stepping<
    1106             :             Parallel::is_dg_element_collection_v<ParallelComponent>,
    1107             :             Metavariables>(make_not_null(&box), make_not_null(&inboxes))) {
    1108             :       return {Parallel::AlgorithmExecution::Retry, std::nullopt};
    1109             :     }
    1110             : 
    1111             :     db::mutate_apply<
    1112             :         ApplyBoundaryCorrections<false, Metavariables, VolumeDim, DenseOutput>>(
    1113             :         make_not_null(&box));
    1114             :     return {Parallel::AlgorithmExecution::Continue, std::nullopt};
    1115             :   }
    1116             : };
    1117             : 
    1118             : /*!
    1119             :  * \brief Computes the boundary corrections for local time-stepping
    1120             :  * and adds them to the variables.
    1121             :  *
    1122             :  * When using local time stepping the neighbor sends data at the neighbor's
    1123             :  * current temporal id. Along with the boundary data, the next temporal id at
    1124             :  * which the neighbor will send data is also sent. This is equal to the
    1125             :  * neighbor's `::Tags::Next<::Tags::TimeStepId>`. When inserting into the mortar
    1126             :  * data history, we insert the received temporal id, that is, the current time
    1127             :  * of the neighbor, along with the boundary correction data.
    1128             :  */
    1129             : template <size_t VolumeDim, bool DenseOutput, bool UseNodegroupDgElements>
    1130           1 : struct ApplyLtsBoundaryCorrections {
    1131           0 :   using inbox_tags =
    1132             :       tmpl::list<evolution::dg::Tags::BoundaryCorrectionAndGhostCellsInbox<
    1133             :           VolumeDim, UseNodegroupDgElements>>;
    1134           0 :   using const_global_cache_tags =
    1135             :       tmpl::list<evolution::Tags::BoundaryCorrection, ::dg::Tags::Formulation>;
    1136             : 
    1137             :   template <typename DbTagsList, typename... InboxTags, typename Metavariables,
    1138             :             typename ArrayIndex, typename ActionList,
    1139             :             typename ParallelComponent>
    1140           0 :   static Parallel::iterable_action_return_t apply(
    1141             :       db::DataBox<DbTagsList>& box, tuples::TaggedTuple<InboxTags...>& inboxes,
    1142             :       const Parallel::GlobalCache<Metavariables>& /*cache*/,
    1143             :       const ArrayIndex& /*array_index*/, ActionList /*meta*/,
    1144             :       const ParallelComponent* const /*meta*/) {
    1145             :     static_assert(
    1146             :         UseNodegroupDgElements ==
    1147             :             Parallel::is_dg_element_collection_v<ParallelComponent>,
    1148             :         "The action ApplyLtsBoundaryCorrections is told by the "
    1149             :         "template parameter UseNodegroupDgElements that it is being "
    1150             :         "used with a DgElementCollection, but the ParallelComponent "
    1151             :         "is not a DgElementCollection. You need to change the "
    1152             :         "template parameter on the ApplyLtsBoundaryCorrections action "
    1153             :         "in your action list.");
    1154             :     constexpr size_t volume_dim = Metavariables::system::volume_dim;
    1155             :     const Element<volume_dim>& element =
    1156             :         db::get<domain::Tags::Element<volume_dim>>(box);
    1157             : 
    1158             :     if (UNLIKELY(element.number_of_neighbors() == 0)) {
    1159             :       // We have no neighbors, yay!
    1160             :       return {Parallel::AlgorithmExecution::Continue, std::nullopt};
    1161             :     }
    1162             : 
    1163             :     if (not receive_boundary_data_local_time_stepping<
    1164             :             Parallel::is_dg_element_collection_v<ParallelComponent>,
    1165             :             typename Metavariables::system, VolumeDim, false>(
    1166             :             make_not_null(&box), make_not_null(&inboxes))) {
    1167             :       return {Parallel::AlgorithmExecution::Retry, std::nullopt};
    1168             :     }
    1169             : 
    1170             :     if (::SelfStart::step_unused(
    1171             :             db::get<::Tags::TimeStepId>(box),
    1172             :             db::get<::Tags::Next<::Tags::TimeStepId>>(box))) {
    1173             :       return {Parallel::AlgorithmExecution::Continue, std::nullopt};
    1174             :     }
    1175             : 
    1176             :     db::mutate_apply<
    1177             :         ApplyBoundaryCorrections<true, Metavariables, VolumeDim, DenseOutput>>(
    1178             :         make_not_null(&box));
    1179             :     return {Parallel::AlgorithmExecution::Continue, std::nullopt};
    1180             :   }
    1181             : };
    1182             : }  // namespace Actions
    1183             : }  // namespace evolution::dg

Generated by: LCOV version 1.14