SpECTRE Documentation Coverage Report
Current view: top level - Evolution/DiscontinuousGalerkin/Actions - ApplyBoundaryCorrections.hpp Hit Total Coverage
Commit: 1f2210958b4f38fdc0400907ee7c6d5af5111418 Lines: 6 33 18.2 %
Date: 2025-12-05 05:03:31
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             : }  // namespace detail
     107             : 
     108             : /// Receive boundary data for global time-stepping.  Returns true if
     109             : /// all necessary data has been received.
     110             : template <bool UseNodegroupDgElements, typename Metavariables,
     111             :           typename DbTagsList, typename... InboxTags>
     112           1 : bool receive_boundary_data_global_time_stepping(
     113             :     const gsl::not_null<db::DataBox<DbTagsList>*> box,
     114             :     const gsl::not_null<tuples::TaggedTuple<InboxTags...>*> inboxes) {
     115             :   constexpr size_t volume_dim = Metavariables::system::volume_dim;
     116             : 
     117             :   const TimeStepId& temporal_id = get<::Tags::TimeStepId>(*box);
     118             : 
     119             :   const auto number_of_neighbors =
     120             :       db::get<domain::Tags::Element<volume_dim>>(*box).number_of_neighbors();
     121             : 
     122             :   auto& inbox =
     123             :       tuples::get<evolution::dg::Tags::BoundaryCorrectionAndGhostCellsInbox<
     124             :           volume_dim, UseNodegroupDgElements>>(*inboxes);
     125             : collect_messages:
     126             :   inbox.collect_messages();
     127             :   const auto received_record = inbox.messages.find(temporal_id);
     128             :   if (received_record == inbox.messages.end()) {
     129             :     if (inbox.set_missing_messages(number_of_neighbors)) {
     130             :       // We've received new messages while this function was running,
     131             :       // so try again.
     132             :       goto collect_messages;  // NOLINT(cppcoreguidelines-avoid-goto)
     133             :     }
     134             :     return false;
     135             :   }
     136             :   auto& received_neighbor_data = received_record->second;
     137             :   if (received_neighbor_data.size() != number_of_neighbors) {
     138             :     ASSERT(received_neighbor_data.size() < number_of_neighbors,
     139             :            "Received too many messages: " << received_neighbor_data);
     140             :     if (inbox.set_missing_messages(number_of_neighbors -
     141             :                                    received_neighbor_data.size())) {
     142             :       // We've received new messages while this function was running,
     143             :       // so try again.
     144             :       goto collect_messages;  // NOLINT(cppcoreguidelines-avoid-goto)
     145             :     }
     146             :     return false;
     147             :   }
     148             : 
     149             :   std::pair received_temporal_id_and_data{temporal_id,
     150             :                                           std::move(received_neighbor_data)};
     151             :   inbox.messages.erase(received_record);
     152             : 
     153             :   // Move inbox contents into the DataBox
     154             :   if constexpr (using_subcell_v<Metavariables>) {
     155             :     evolution::dg::subcell::neighbor_reconstructed_face_solution<
     156             :         volume_dim, typename Metavariables::SubcellOptions::
     157             :                         DgComputeSubcellNeighborPackagedData>(
     158             :         &db::as_access(*box), make_not_null(&received_temporal_id_and_data));
     159             :     evolution::dg::subcell::neighbor_tci_decision<volume_dim>(
     160             :         make_not_null(&db::as_access(*box)), received_temporal_id_and_data);
     161             :   }
     162             : 
     163             :   db::mutate<evolution::dg::Tags::MortarMesh<volume_dim>,
     164             :              evolution::dg::Tags::MortarData<volume_dim>,
     165             :              evolution::dg::Tags::MortarNextTemporalId<volume_dim>,
     166             :              domain::Tags::NeighborMesh<volume_dim>>(
     167             :       [&received_temporal_id_and_data](
     168             :           const gsl::not_null<
     169             :               DirectionalIdMap<volume_dim, Mesh<volume_dim - 1>>*>
     170             :               mortar_meshes,
     171             :           const gsl::not_null<DirectionalIdMap<
     172             :               volume_dim, evolution::dg::MortarDataHolder<volume_dim>>*>
     173             :               mortar_data,
     174             :           const gsl::not_null<DirectionalIdMap<volume_dim, TimeStepId>*>
     175             :               mortar_next_time_step_id,
     176             :           const gsl::not_null<DirectionalIdMap<volume_dim, Mesh<volume_dim>>*>
     177             :               neighbor_mesh,
     178             :           const Mesh<volume_dim>& volume_mesh) {
     179             :         neighbor_mesh->clear();
     180             :         for (auto& received_mortar_data :
     181             :              received_temporal_id_and_data.second) {
     182             :           const auto& mortar_id = received_mortar_data.first;
     183             :           const size_t sliced_away_dim = mortar_id.direction().dimension();
     184             :           const Mesh<volume_dim - 1> face_mesh =
     185             :               volume_mesh.slice_away(sliced_away_dim);
     186             :           const Mesh<volume_dim - 1> neighbor_face_mesh =
     187             :               received_mortar_data.second.volume_mesh.slice_away(
     188             :                   sliced_away_dim);
     189             :           const Mesh<volume_dim - 1> mortar_mesh =
     190             :               ::dg::mortar_mesh(face_mesh, neighbor_face_mesh);
     191             :           mortar_meshes->at(mortar_id) = mortar_mesh;
     192             :           p_project_mortar_data(
     193             :               make_not_null(&mortar_data->at(mortar_id).local()), mortar_mesh);
     194             :           neighbor_mesh->insert_or_assign(
     195             :               mortar_id, received_mortar_data.second.volume_mesh);
     196             :           mortar_next_time_step_id->at(mortar_id) =
     197             :               received_mortar_data.second.validity_range;
     198             :           ASSERT(using_subcell_v<Metavariables> or
     199             :                      received_mortar_data.second.boundary_correction_data
     200             :                          .has_value(),
     201             :                  "Must receive number boundary correction data when not using "
     202             :                  "DG-subcell. Mortar ID is: ("
     203             :                      << mortar_id.direction() << "," << mortar_id.id()
     204             :                      << ") and TimeStepId is "
     205             :                      << received_temporal_id_and_data.first);
     206             :           if (received_mortar_data.second.boundary_correction_data
     207             :                   .has_value()) {
     208             :             mortar_data->at(mortar_id).neighbor().face_mesh =
     209             :                 neighbor_face_mesh;
     210             :             mortar_data->at(mortar_id).neighbor().mortar_mesh =
     211             :                 received_mortar_data.second.boundary_correction_mesh.value();
     212             :             mortar_data->at(mortar_id).neighbor().mortar_data = std::move(
     213             :                 received_mortar_data.second.boundary_correction_data.value());
     214             :             p_project_mortar_data(
     215             :                 make_not_null(&mortar_data->at(mortar_id).neighbor()),
     216             :                 mortar_mesh);
     217             :           }
     218             :         }
     219             :       },
     220             :       box, db::get<domain::Tags::Mesh<volume_dim>>(*box));
     221             :   return true;
     222             : }
     223             : 
     224             : /// Receive boundary data for local time-stepping.  Returns true if
     225             : /// all necessary data has been received.
     226             : ///
     227             : /// Setting \p DenseOutput to true receives data required for output
     228             : /// at `::Tags::Time` instead of `::Tags::Next<::Tags::TimeStepId>`.
     229             : template <bool UseNodegroupDgElements, typename System, size_t Dim,
     230             :           bool DenseOutput, typename DbTagsList, typename... InboxTags>
     231           1 : bool receive_boundary_data_local_time_stepping(
     232             :     const gsl::not_null<db::DataBox<DbTagsList>*> box,
     233             :     const gsl::not_null<tuples::TaggedTuple<InboxTags...>*> inboxes) {
     234             :   using variables_tag = typename System::variables_tag;
     235             :   using dt_variables_tag = db::add_tag_prefix<::Tags::dt, variables_tag>;
     236             : 
     237             :   const auto needed_time = [&box]() {
     238             :     const LtsTimeStepper& time_stepper =
     239             :         db::get<::Tags::TimeStepper<LtsTimeStepper>>(*box);
     240             :     if constexpr (DenseOutput) {
     241             :       const auto& dense_output_time = db::get<::Tags::Time>(*box);
     242             :       return [&dense_output_time, &time_stepper](const TimeStepId& id) {
     243             :         return time_stepper.neighbor_data_required(dense_output_time, id);
     244             :       };
     245             :     } else {
     246             :       const auto& next_temporal_id =
     247             :           db::get<::Tags::Next<::Tags::TimeStepId>>(*box);
     248             :       return [&next_temporal_id, &time_stepper](const TimeStepId& id) {
     249             :         return time_stepper.neighbor_data_required(next_temporal_id, id);
     250             :       };
     251             :     }
     252             :   }();
     253             : 
     254             :   auto& inbox =
     255             :       tuples::get<evolution::dg::Tags::BoundaryCorrectionAndGhostCellsInbox<
     256             :           Dim, UseNodegroupDgElements>>(*inboxes);
     257             : 
     258             :   size_t missing_messages{};
     259             :   do {
     260             :     // The boundary history coupling computation (which computes the _lifted_
     261             :     // boundary correction) returns a Variables<dt<EvolvedVars>> instead of
     262             :     // using the `NormalDotNumericalFlux` prefix tag. This is because the
     263             :     // returned quantity is more a `dt` quantity than a
     264             :     // `NormalDotNormalDotFlux` since it's been lifted to the volume.
     265             :     using InboxMap =
     266             :         std::map<TimeStepId,
     267             :                  DirectionalIdMap<Dim, evolution::dg::BoundaryData<Dim>>>;
     268             :     inbox.collect_messages();
     269             :     InboxMap& inbox_data = inbox.messages;
     270             : 
     271             :     missing_messages = 0;
     272             : 
     273             :     db::mutate<evolution::dg::Tags::MortarMesh<Dim>,
     274             :                evolution::dg::Tags::MortarDataHistory<
     275             :                    Dim, typename dt_variables_tag::type>,
     276             :                evolution::dg::Tags::MortarNextTemporalId<Dim>,
     277             :                domain::Tags::NeighborMesh<Dim>>(
     278             :         [&inbox_data, &missing_messages, &needed_time](
     279             :             const gsl::not_null<DirectionalIdMap<Dim, Mesh<Dim - 1>>*>
     280             :                 mortar_meshes,
     281             :             const gsl::not_null<
     282             :                 DirectionalIdMap<Dim, TimeSteppers::BoundaryHistory<
     283             :                                           evolution::dg::MortarData<Dim>,
     284             :                                           evolution::dg::MortarData<Dim>,
     285             :                                           typename dt_variables_tag::type>>*>
     286             :                 boundary_data_history,
     287             :             const gsl::not_null<DirectionalIdMap<Dim, TimeStepId>*>
     288             :                 mortar_next_time_step_ids,
     289             :             const gsl::not_null<DirectionalIdMap<Dim, Mesh<Dim>>*>
     290             :                 neighbor_mesh,
     291             :             const Element<Dim>& element, const Mesh<Dim>& volume_mesh) {
     292             :           // Remove neighbor meshes for neighbors that don't exist anymore
     293             :           domain::remove_nonexistent_neighbors(neighbor_mesh, element);
     294             : 
     295             :           // Move received boundary data into boundary history.
     296             :           for (auto& [mortar_id, mortar_next_time_step_id] :
     297             :                *mortar_next_time_step_ids) {
     298             :             if (mortar_id.id() == ElementId<Dim>::external_boundary_id()) {
     299             :               continue;
     300             :             }
     301             :             const size_t sliced_away_dim = mortar_id.direction().dimension();
     302             :             const Mesh<Dim - 1> face_mesh =
     303             :                 volume_mesh.slice_away(sliced_away_dim);
     304             :             while (needed_time(mortar_next_time_step_id)) {
     305             :               const auto time_entry = inbox_data.find(mortar_next_time_step_id);
     306             :               if (time_entry == inbox_data.end()) {
     307             :                 ++missing_messages;
     308             :                 break;
     309             :               }
     310             :               const auto received_mortar_data =
     311             :                   time_entry->second.find(mortar_id);
     312             :               if (received_mortar_data == time_entry->second.end()) {
     313             :                 ++missing_messages;
     314             :                 break;
     315             :               }
     316             : 
     317             :               const Mesh<Dim - 1> neighbor_face_mesh =
     318             :                   received_mortar_data->second.volume_mesh.slice_away(
     319             :                       sliced_away_dim);
     320             :               const Mesh<Dim - 1> mortar_mesh =
     321             :                   ::dg::mortar_mesh(face_mesh, neighbor_face_mesh);
     322             : 
     323             :               const auto project_boundary_mortar_data =
     324             :                   [&mortar_mesh](
     325             :                       const TimeStepId& /*id*/,
     326             :                       const gsl::not_null<::evolution::dg::MortarData<Dim>*>
     327             :                           mortar_data) {
     328             :                     return p_project_mortar_data(mortar_data, mortar_mesh);
     329             :                   };
     330             : 
     331             :               mortar_meshes->at(mortar_id) = mortar_mesh;
     332             :               boundary_data_history->at(mortar_id).local().for_each(
     333             :                   project_boundary_mortar_data);
     334             : 
     335             :               MortarData<Dim> neighbor_mortar_data{};
     336             :               // Insert:
     337             :               // - the current TimeStepId of the neighbor
     338             :               // - the current face mesh of the neighbor
     339             :               // - the current boundary correction data of the neighbor
     340             :               ASSERT(received_mortar_data->second.boundary_correction_data
     341             :                          .has_value(),
     342             :                      "Did not receive boundary correction data from the "
     343             :                      "neighbor\nMortarId: "
     344             :                          << mortar_id
     345             :                          << "\nTimeStepId: " << mortar_next_time_step_id);
     346             :               neighbor_mesh->insert_or_assign(
     347             :                   mortar_id, received_mortar_data->second.volume_mesh);
     348             :               neighbor_mortar_data.mortar_mesh =
     349             :                   received_mortar_data->second.boundary_correction_mesh.value();
     350             :               neighbor_mortar_data.mortar_data =
     351             :                   std::move(received_mortar_data->second
     352             :                                 .boundary_correction_data.value());
     353             :               boundary_data_history->at(mortar_id).remote().insert(
     354             :                   time_entry->first,
     355             :                   received_mortar_data->second.integration_order,
     356             :                   std::move(neighbor_mortar_data));
     357             :               boundary_data_history->at(mortar_id).remote().for_each(
     358             :                   project_boundary_mortar_data);
     359             :               mortar_next_time_step_id =
     360             :                   received_mortar_data->second.validity_range;
     361             :               time_entry->second.erase(received_mortar_data);
     362             :               if (time_entry->second.empty()) {
     363             :                 inbox_data.erase(time_entry);
     364             :               }
     365             :             }
     366             :           }
     367             :         },
     368             :         box, db::get<::domain::Tags::Element<Dim>>(*box),
     369             :         db::get<domain::Tags::Mesh<Dim>>(*box));
     370             : 
     371             :     if (missing_messages == 0) {
     372             :       return true;
     373             :     }
     374             :   } while (inbox.set_missing_messages(missing_messages));
     375             :   return false;
     376             : }
     377             : 
     378             : /// Apply corrections from boundary communication.
     379             : ///
     380             : /// If `LocalTimeStepping` is false, updates the derivative of the variables,
     381             : /// which should be done before taking a time step.  If
     382             : /// `LocalTimeStepping` is true, updates the variables themselves, which should
     383             : /// be done after the volume update.
     384             : ///
     385             : /// Setting \p DenseOutput to true receives data required for output
     386             : /// at ::Tags::Time instead of performing a full step.  This is only
     387             : /// used for local time-stepping.
     388             : template <bool LocalTimeStepping, typename Metavariables, size_t VolumeDim,
     389             :           bool DenseOutput>
     390           1 : struct ApplyBoundaryCorrections {
     391           0 :   static constexpr bool local_time_stepping = LocalTimeStepping;
     392             :   static_assert(local_time_stepping or not DenseOutput,
     393             :                 "GTS does not use ApplyBoundaryCorrections for dense output.");
     394             : 
     395           0 :   using system = typename Metavariables::system;
     396           0 :   static constexpr size_t volume_dim = VolumeDim;
     397           0 :   using variables_tag = typename system::variables_tag;
     398           0 :   using dt_variables_tag = db::add_tag_prefix<::Tags::dt, variables_tag>;
     399           0 :   using DtVariables = typename dt_variables_tag::type;
     400           0 :   using derived_boundary_corrections =
     401             :       tmpl::at<typename Metavariables::factory_creation::factory_classes,
     402             :                evolution::BoundaryCorrection>;
     403           0 :   using volume_tags_for_dg_boundary_terms = tmpl::remove_duplicates<
     404             :       tmpl::flatten<tmpl::transform<derived_boundary_corrections,
     405             :                                     detail::get_dg_boundary_terms<tmpl::_1>>>>;
     406             : 
     407           0 :   using TimeStepperType =
     408             :       tmpl::conditional_t<local_time_stepping, LtsTimeStepper, TimeStepper>;
     409             : 
     410           0 :   using tag_to_update =
     411             :       tmpl::conditional_t<local_time_stepping, variables_tag, dt_variables_tag>;
     412           0 :   using mortar_data_tag = tmpl::conditional_t<
     413             :       local_time_stepping,
     414             :       evolution::dg::Tags::MortarDataHistory<volume_dim, DtVariables>,
     415             :       evolution::dg::Tags::MortarData<volume_dim>>;
     416           0 :   using MortarDataType =
     417             :       tmpl::conditional_t<DenseOutput, const typename mortar_data_tag::type,
     418             :                           typename mortar_data_tag::type>;
     419             : 
     420           0 :   using return_tags =
     421             :       tmpl::conditional_t<DenseOutput, tmpl::list<tag_to_update>,
     422             :                           tmpl::list<tag_to_update, mortar_data_tag>>;
     423           0 :   using argument_tags = tmpl::append<
     424             :       tmpl::flatten<tmpl::list<
     425             :           tmpl::conditional_t<DenseOutput, mortar_data_tag, tmpl::list<>>,
     426             :           domain::Tags::Mesh<volume_dim>, domain::Tags::Element<volume_dim>,
     427             :           Tags::MortarMesh<volume_dim>, Tags::MortarInfo<volume_dim>,
     428             :           ::dg::Tags::Formulation,
     429             :           evolution::dg::Tags::NormalCovectorAndMagnitude<volume_dim>,
     430             :           ::Tags::TimeStepper<TimeStepperType>,
     431             :           evolution::Tags::BoundaryCorrection,
     432             :           tmpl::conditional_t<DenseOutput, ::Tags::Time, ::Tags::TimeStep>,
     433             :           tmpl::conditional_t<local_time_stepping, tmpl::list<>,
     434             :                               domain::Tags::DetInvJacobian<
     435             :                                   Frame::ElementLogical, Frame::Inertial>>>>,
     436             :       volume_tags_for_dg_boundary_terms>;
     437             : 
     438             :   // full step
     439             :   template <typename... VolumeArgs>
     440           0 :   static void apply(
     441             :       const gsl::not_null<typename tag_to_update::type*> vars_to_update,
     442             :       const gsl::not_null<MortarDataType*> mortar_data,
     443             :       const Mesh<volume_dim>& volume_mesh, const Element<volume_dim>& element,
     444             :       const typename Tags::MortarMesh<volume_dim>::type& mortar_meshes,
     445             :       const typename Tags::MortarInfo<volume_dim>::type& mortar_infos,
     446             :       const ::dg::Formulation dg_formulation,
     447             :       const DirectionMap<
     448             :           volume_dim, std::optional<Variables<tmpl::list<
     449             :                           evolution::dg::Tags::MagnitudeOfNormal,
     450             :                           evolution::dg::Tags::NormalCovector<volume_dim>>>>>&
     451             :           face_normal_covector_and_magnitude,
     452             :       const TimeStepperType& time_stepper,
     453             :       const evolution::BoundaryCorrection& boundary_correction,
     454             :       const TimeDelta& time_step,
     455             :       const Scalar<DataVector>& gts_det_inv_jacobian,
     456             :       const VolumeArgs&... volume_args) {
     457             :     apply_impl(vars_to_update, mortar_data, volume_mesh, element, mortar_meshes,
     458             :                mortar_infos, dg_formulation, face_normal_covector_and_magnitude,
     459             :                time_stepper, boundary_correction, time_step,
     460             :                std::numeric_limits<double>::signaling_NaN(),
     461             :                gts_det_inv_jacobian, volume_args...);
     462             :   }
     463             : 
     464             :   template <typename... VolumeArgs>
     465           0 :   static void apply(
     466             :       const gsl::not_null<typename tag_to_update::type*> vars_to_update,
     467             :       const gsl::not_null<MortarDataType*> mortar_data,
     468             :       const Mesh<volume_dim>& volume_mesh, const Element<volume_dim>& element,
     469             :       const typename Tags::MortarMesh<volume_dim>::type& mortar_meshes,
     470             :       const typename Tags::MortarInfo<volume_dim>::type& mortar_infos,
     471             :       const ::dg::Formulation dg_formulation,
     472             :       const DirectionMap<
     473             :           volume_dim, std::optional<Variables<tmpl::list<
     474             :                           evolution::dg::Tags::MagnitudeOfNormal,
     475             :                           evolution::dg::Tags::NormalCovector<volume_dim>>>>>&
     476             :           face_normal_covector_and_magnitude,
     477             :       const TimeStepperType& time_stepper,
     478             :       const evolution::BoundaryCorrection& boundary_correction,
     479             :       const TimeDelta& time_step, const VolumeArgs&... volume_args) {
     480             :     apply_impl(vars_to_update, mortar_data, volume_mesh, element, mortar_meshes,
     481             :                mortar_infos, dg_formulation, face_normal_covector_and_magnitude,
     482             :                time_stepper, boundary_correction, time_step,
     483             :                std::numeric_limits<double>::signaling_NaN(), {},
     484             :                volume_args...);
     485             :   }
     486             : 
     487             :   // dense output (LTS only)
     488             :   template <typename... VolumeArgs>
     489           0 :   static void apply(
     490             :       const gsl::not_null<typename variables_tag::type*> vars_to_update,
     491             :       const MortarDataType& mortar_data, const Mesh<volume_dim>& volume_mesh,
     492             :       const Element<volume_dim>& element,
     493             :       const typename Tags::MortarMesh<volume_dim>::type& mortar_meshes,
     494             :       const typename Tags::MortarInfo<volume_dim>::type& mortar_infos,
     495             :       const ::dg::Formulation dg_formulation,
     496             :       const DirectionMap<
     497             :           volume_dim, std::optional<Variables<tmpl::list<
     498             :                           evolution::dg::Tags::MagnitudeOfNormal,
     499             :                           evolution::dg::Tags::NormalCovector<volume_dim>>>>>&
     500             :           face_normal_covector_and_magnitude,
     501             :       const LtsTimeStepper& time_stepper,
     502             :       const evolution::BoundaryCorrection& boundary_correction,
     503             :       const double dense_output_time, const VolumeArgs&... volume_args) {
     504             :     apply_impl(vars_to_update, &mortar_data, volume_mesh, element,
     505             :                mortar_meshes, mortar_infos, dg_formulation,
     506             :                face_normal_covector_and_magnitude, time_stepper,
     507             :                boundary_correction, TimeDelta{}, dense_output_time, {},
     508             :                volume_args...);
     509             :   }
     510             : 
     511             :   template <typename DbTagsList, typename... InboxTags, typename ArrayIndex,
     512             :             typename ParallelComponent>
     513           0 :   static bool is_ready(
     514             :       const gsl::not_null<db::DataBox<DbTagsList>*> box,
     515             :       const gsl::not_null<tuples::TaggedTuple<InboxTags...>*> inboxes,
     516             :       Parallel::GlobalCache<Metavariables>& /*cache*/,
     517             :       const ArrayIndex& /*array_index*/,
     518             :       const ParallelComponent* const /*component*/) {
     519             :     if constexpr (local_time_stepping) {
     520             :       return receive_boundary_data_local_time_stepping<
     521             :           Parallel::is_dg_element_collection_v<ParallelComponent>, system,
     522             :           VolumeDim, DenseOutput>(box, inboxes);
     523             :     } else {
     524             :       return receive_boundary_data_global_time_stepping<
     525             :           Parallel::is_dg_element_collection_v<ParallelComponent>,
     526             :           Metavariables>(box, inboxes);
     527             :     }
     528             :   }
     529             : 
     530             :  private:
     531             :   template <typename... VolumeArgs>
     532           0 :   static void apply_impl(
     533             :       const gsl::not_null<typename tag_to_update::type*> vars_to_update,
     534             :       const gsl::not_null<MortarDataType*> mortar_data,
     535             :       const Mesh<volume_dim>& volume_mesh, const Element<volume_dim>& element,
     536             :       const typename Tags::MortarMesh<volume_dim>::type& mortar_meshes,
     537             :       const typename Tags::MortarInfo<volume_dim>::type& mortar_infos,
     538             :       const ::dg::Formulation dg_formulation,
     539             :       const DirectionMap<
     540             :           volume_dim, std::optional<Variables<tmpl::list<
     541             :                           evolution::dg::Tags::MagnitudeOfNormal,
     542             :                           evolution::dg::Tags::NormalCovector<volume_dim>>>>>&
     543             :           face_normal_covector_and_magnitude,
     544             :       const TimeStepperType& time_stepper,
     545             :       const evolution::BoundaryCorrection& boundary_correction,
     546             :       const TimeDelta& time_step, const double dense_output_time,
     547             :       const Scalar<DataVector>& gts_det_inv_jacobian,
     548             :       const VolumeArgs&... volume_args) {
     549             :     tuples::tagged_tuple_from_typelist<db::wrap_tags_in<
     550             :         detail::TemporaryReference, volume_tags_for_dg_boundary_terms>>
     551             :         volume_args_tuple{volume_args...};
     552             : 
     553             :     // Set up helper lambda that will compute and lift the boundary corrections
     554             :     ASSERT(
     555             :         volume_mesh.quadrature() ==
     556             :                 make_array<volume_dim>(volume_mesh.quadrature(0)) or
     557             :             element.topologies() != domain::topologies::hypercube<volume_dim>,
     558             :         "Must have isotropic quadrature, but got volume mesh: " << volume_mesh);
     559             :     const bool using_gauss_lobatto_points =
     560             :         volume_mesh.quadrature(0) == Spectral::Quadrature::GaussLobatto;
     561             : 
     562             :     Scalar<DataVector> volume_det_inv_jacobian{};
     563             :     Scalar<DataVector> volume_det_jacobian{};
     564             :     if constexpr (not local_time_stepping) {
     565             :       if (not using_gauss_lobatto_points) {
     566             :         get(volume_det_inv_jacobian)
     567             :             .set_data_ref(make_not_null(
     568             :                 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
     569             :                 &const_cast<DataVector&>(get(gts_det_inv_jacobian))));
     570             :         get(volume_det_jacobian) = 1.0 / get(volume_det_inv_jacobian);
     571             :       }
     572             :     }
     573             : 
     574             :     static_assert(
     575             :         tmpl::all<derived_boundary_corrections, std::is_final<tmpl::_1>>::value,
     576             :         "All createable classes for boundary corrections must be marked "
     577             :         "final.");
     578             :     call_with_dynamic_type<void, derived_boundary_corrections>(
     579             :         &boundary_correction,
     580             :         [&dense_output_time, &dg_formulation,
     581             :          &face_normal_covector_and_magnitude, &mortar_data, &mortar_meshes,
     582             :          &mortar_infos, &time_step, &time_stepper, using_gauss_lobatto_points,
     583             :          &vars_to_update, &volume_args_tuple, &volume_det_jacobian,
     584             :          &volume_det_inv_jacobian,
     585             :          &volume_mesh](auto* typed_boundary_correction) {
     586             :           using BcType = std::decay_t<decltype(*typed_boundary_correction)>;
     587             :           // Compute internal boundary quantities on the mortar for sides of
     588             :           // the element that have neighbors, i.e. they are not an external
     589             :           // side.
     590             :           using mortar_tags_list = typename BcType::dg_package_field_tags;
     591             : 
     592             :           // Variables for reusing allocations.  The actual values are
     593             :           // not reused.
     594             :           DtVariables dt_boundary_correction_on_mortar{};
     595             :           DtVariables volume_dt_correction{};
     596             :           // These variables may change size for each mortar and require
     597             :           // a new memory allocation, but they may also happen to need
     598             :           // to be the same size twice in a row, in which case holding
     599             :           // on to the allocation is a win.
     600             :           Scalar<DataVector> face_det_jacobian{};
     601             :           Variables<mortar_tags_list> local_data_on_mortar{};
     602             :           Variables<mortar_tags_list> neighbor_data_on_mortar{};
     603             : 
     604             :           for (auto& mortar_id_and_data : *mortar_data) {
     605             :             const auto& mortar_id = mortar_id_and_data.first;
     606             :             const auto& direction = mortar_id.direction();
     607             :             if (UNLIKELY(mortar_id.id() ==
     608             :                          ElementId<volume_dim>::external_boundary_id())) {
     609             :               ERROR(
     610             :                   "Cannot impose boundary conditions on external boundary in "
     611             :                   "direction "
     612             :                   << direction
     613             :                   << " in the ApplyBoundaryCorrections action. Boundary "
     614             :                      "conditions are applied in the ComputeTimeDerivative "
     615             :                      "action "
     616             :                      "instead. You may have unintentionally added external "
     617             :                      "mortars in one of the initialization actions.");
     618             :             }
     619             : 
     620             :             const Mesh<volume_dim - 1> face_mesh =
     621             :                 volume_mesh.slice_away(direction.dimension());
     622             : 
     623             :             const auto compute_correction_coupling =
     624             :                 [&typed_boundary_correction, &direction, dg_formulation,
     625             :                  &dt_boundary_correction_on_mortar, &face_det_jacobian,
     626             :                  &face_mesh, &face_normal_covector_and_magnitude,
     627             :                  &local_data_on_mortar, &mortar_id, &mortar_meshes,
     628             :                  &mortar_infos, &neighbor_data_on_mortar,
     629             :                  using_gauss_lobatto_points, &volume_args_tuple,
     630             :                  &volume_det_jacobian, &volume_det_inv_jacobian,
     631             :                  &volume_dt_correction, &volume_mesh](
     632             :                     const MortarData<volume_dim>& local_mortar_data,
     633             :                     const MortarData<volume_dim>& neighbor_mortar_data)
     634             :                 -> DtVariables {
     635             :               if (local_time_stepping and not using_gauss_lobatto_points) {
     636             :                 // This needs to be updated every call because the Jacobian
     637             :                 // may be time-dependent. In the case of time-independent maps
     638             :                 // and local time stepping we could first perform the integral
     639             :                 // on the boundaries, and then lift to the volume. This is
     640             :                 // left as a future optimization.
     641             :                 volume_det_inv_jacobian =
     642             :                     local_mortar_data.volume_det_inv_jacobian.value();
     643             :                 get(volume_det_jacobian) = 1.0 / get(volume_det_inv_jacobian);
     644             :               }
     645             :               const auto& mortar_mesh = mortar_meshes.at(mortar_id);
     646             : 
     647             :               // Extract local and neighbor data, copy into Variables because
     648             :               // we store them in a std::vector for type erasure.
     649             :               ASSERT(*local_mortar_data.mortar_mesh ==
     650             :                              *neighbor_mortar_data.mortar_mesh and
     651             :                          *local_mortar_data.mortar_mesh == mortar_mesh,
     652             :                      "local mortar mesh: " << *local_mortar_data.mortar_mesh
     653             :                                            << "\nneighbor mortar mesh: "
     654             :                                            << *neighbor_mortar_data.mortar_mesh
     655             :                                            << "\nmortar mesh: " << mortar_mesh
     656             :                                            << "\n");
     657             :               const DataVector& local_data = *local_mortar_data.mortar_data;
     658             :               const DataVector& neighbor_data =
     659             :                   *neighbor_mortar_data.mortar_data;
     660             :               ASSERT(local_data.size() == neighbor_data.size(),
     661             :                      "local data size: "
     662             :                          << local_data.size()
     663             :                          << "\nneighbor_data: " << neighbor_data.size()
     664             :                          << "\n mortar_mesh: " << mortar_mesh << "\n");
     665             :               ASSERT(local_data_on_mortar.number_of_grid_points() ==
     666             :                          neighbor_data_on_mortar.number_of_grid_points(),
     667             :                      "Local data size = "
     668             :                          << local_data_on_mortar.number_of_grid_points()
     669             :                          << ", but neighbor size = "
     670             :                          << neighbor_data_on_mortar.number_of_grid_points());
     671             :               local_data_on_mortar.set_data_ref(
     672             :                   // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
     673             :                   const_cast<double*>(local_data.data()), local_data.size());
     674             :               neighbor_data_on_mortar.set_data_ref(
     675             :                   // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
     676             :                   const_cast<double*>(neighbor_data.data()),
     677             :                   neighbor_data.size());
     678             : 
     679             :               // The boundary computations and lifting can be further
     680             :               // optimized by in the h-refinement case having only one
     681             :               // allocation for the face and having the projection from the
     682             :               // mortar to the face be done in place. E.g.
     683             :               // local_data_on_mortar and neighbor_data_on_mortar could be
     684             :               // allocated fewer times, as well as `needs_projection` section
     685             :               // below could do an in-place projection.
     686             :               dt_boundary_correction_on_mortar.initialize(
     687             :                   mortar_mesh.number_of_grid_points());
     688             : 
     689             :               call_boundary_correction(
     690             :                   make_not_null(&dt_boundary_correction_on_mortar),
     691             :                   local_data_on_mortar, neighbor_data_on_mortar,
     692             :                   *typed_boundary_correction, dg_formulation, volume_args_tuple,
     693             :                   typename BcType::dg_boundary_terms_volume_tags{});
     694             : 
     695             :               const std::array<Spectral::SegmentSize, volume_dim - 1>&
     696             :                   mortar_size = mortar_infos.at(mortar_id).mortar_size();
     697             : 
     698             :               // This cannot reuse an allocation because it is initialized
     699             :               // via move-assignment.  (If it is used at all.)
     700             :               DtVariables dt_boundary_correction_projected_onto_face{};
     701             :               auto& dt_boundary_correction =
     702             :                   [&dt_boundary_correction_on_mortar,
     703             :                    &dt_boundary_correction_projected_onto_face, &face_mesh,
     704             :                    &mortar_mesh, &mortar_size]() -> DtVariables& {
     705             :                 if (Spectral::needs_projection(face_mesh, mortar_mesh,
     706             :                                                mortar_size)) {
     707             :                   dt_boundary_correction_projected_onto_face =
     708             :                       ::dg::project_from_mortar(
     709             :                           dt_boundary_correction_on_mortar, face_mesh,
     710             :                           mortar_mesh, mortar_size);
     711             :                   return dt_boundary_correction_projected_onto_face;
     712             :                 }
     713             :                 return dt_boundary_correction_on_mortar;
     714             :               }();
     715             : 
     716             :               // Both paths initialize this to be non-owning.
     717             :               Scalar<DataVector> magnitude_of_face_normal{};
     718             :               if constexpr (local_time_stepping) {
     719             :                 (void)face_normal_covector_and_magnitude;
     720             :                 get(magnitude_of_face_normal)
     721             :                     .set_data_ref(make_not_null(&const_cast<DataVector&>(
     722             :                         get(local_mortar_data.face_normal_magnitude.value()))));
     723             :               } else {
     724             :                 ASSERT(
     725             :                     face_normal_covector_and_magnitude.count(direction) == 1 and
     726             :                         face_normal_covector_and_magnitude.at(direction)
     727             :                             .has_value(),
     728             :                     "Face normal covector and magnitude not set in "
     729             :                     "direction: "
     730             :                         << direction);
     731             :                 get(magnitude_of_face_normal)
     732             :                     .set_data_ref(make_not_null(&const_cast<DataVector&>(
     733             :                         get(get<evolution::dg::Tags::MagnitudeOfNormal>(
     734             :                             *face_normal_covector_and_magnitude.at(
     735             :                                 direction))))));
     736             :               }
     737             : 
     738             :               if (using_gauss_lobatto_points) {
     739             :                 // The lift_flux function lifts only on the slice, it does not
     740             :                 // add the contribution to the volume.
     741             :                 ::dg::lift_flux(make_not_null(&dt_boundary_correction),
     742             :                                 volume_mesh.extents(direction.dimension()),
     743             :                                 magnitude_of_face_normal);
     744             :                 return std::move(dt_boundary_correction);
     745             :               } else {
     746             :                 // We are using Gauss points.
     747             :                 //
     748             :                 // Notes:
     749             :                 // - We should really lift both sides simultaneously since this
     750             :                 //   reduces memory accesses. Lifting all sides at the same
     751             :                 //   time is unlikely to improve performance since we lift by
     752             :                 //   jumping through slices. There may also be compatibility
     753             :                 //   issues with local time stepping.
     754             :                 // - If we lift both sides at the same time we first need to
     755             :                 //   deal with projecting from mortars to the face, then lift
     756             :                 //   off the faces. With non-owning Variables memory
     757             :                 //   allocations could be significantly reduced in this code.
     758             :                 if constexpr (local_time_stepping) {
     759             :                   ASSERT(get(volume_det_inv_jacobian).size() > 0,
     760             :                          "For local time stepping the volume determinant of "
     761             :                          "the inverse Jacobian has not been set.");
     762             : 
     763             :                   get(face_det_jacobian)
     764             :                       .set_data_ref(make_not_null(&const_cast<DataVector&>(
     765             :                           get(local_mortar_data.face_det_jacobian.value()))));
     766             :                 } else {
     767             :                   // Project the determinant of the Jacobian to the face. This
     768             :                   // could be optimized by caching in the time-independent case.
     769             :                   get(face_det_jacobian)
     770             :                       .destructive_resize(face_mesh.number_of_grid_points());
     771             :                   const Matrix identity{};
     772             :                   auto interpolation_matrices =
     773             :                       make_array<volume_dim>(std::cref(identity));
     774             :                   const std::pair<Matrix, Matrix>& matrices =
     775             :                       Spectral::boundary_interpolation_matrices(
     776             :                           volume_mesh.slice_through(direction.dimension()));
     777             :                   gsl::at(interpolation_matrices, direction.dimension()) =
     778             :                       direction.side() == Side::Upper ? matrices.second
     779             :                                                       : matrices.first;
     780             :                   apply_matrices(make_not_null(&get(face_det_jacobian)),
     781             :                                  interpolation_matrices,
     782             :                                  get(volume_det_jacobian),
     783             :                                  volume_mesh.extents());
     784             :                 }
     785             : 
     786             :                 volume_dt_correction.initialize(
     787             :                     volume_mesh.number_of_grid_points(), 0.0);
     788             :                 ::dg::lift_boundary_terms_gauss_points(
     789             :                     make_not_null(&volume_dt_correction),
     790             :                     volume_det_inv_jacobian, volume_mesh, direction,
     791             :                     dt_boundary_correction, magnitude_of_face_normal,
     792             :                     face_det_jacobian);
     793             :                 return std::move(volume_dt_correction);
     794             :               }
     795             :             };
     796             : 
     797             :             if constexpr (local_time_stepping) {
     798             :               typename variables_tag::type lgl_lifted_data{};
     799             :               auto& lifted_data = using_gauss_lobatto_points ? lgl_lifted_data
     800             :                                                              : *vars_to_update;
     801             :               if (using_gauss_lobatto_points) {
     802             :                 lifted_data.initialize(face_mesh.number_of_grid_points(), 0.0);
     803             :               }
     804             : 
     805             :               auto& mortar_data_history = mortar_id_and_data.second;
     806             :               if constexpr (DenseOutput) {
     807             :                 (void)time_step;
     808             :                 time_stepper.boundary_dense_output(
     809             :                     &lifted_data, mortar_data_history, dense_output_time,
     810             :                     compute_correction_coupling);
     811             :               } else {
     812             :                 (void)dense_output_time;
     813             :                 time_stepper.add_boundary_delta(&lifted_data,
     814             :                                                 mortar_data_history, time_step,
     815             :                                                 compute_correction_coupling);
     816             :               }
     817             : 
     818             :               if (using_gauss_lobatto_points) {
     819             :                 // Add the flux contribution to the volume data
     820             :                 add_slice_to_data(
     821             :                     vars_to_update, lifted_data, volume_mesh.extents(),
     822             :                     direction.dimension(),
     823             :                     index_to_slice_at(volume_mesh.extents(), direction));
     824             :               }
     825             :             } else {
     826             :               (void)time_step;
     827             :               (void)time_stepper;
     828             :               (void)dense_output_time;
     829             : 
     830             :               // Choose an allocation cache that may be empty, so we
     831             :               // might be able to reuse the allocation obtained for the
     832             :               // lifted data.  This may result in a self assignment,
     833             :               // depending on the code paths taken, but handling the
     834             :               // results this way makes the GTS and LTS paths more
     835             :               // similar because the LTS code always stores the result
     836             :               // in the history and so sometimes benefits from moving
     837             :               // into the return value of compute_correction_coupling.
     838             :               auto& lifted_data = using_gauss_lobatto_points
     839             :                                       ? dt_boundary_correction_on_mortar
     840             :                                       : volume_dt_correction;
     841             :               lifted_data = compute_correction_coupling(
     842             :                   mortar_id_and_data.second.local(),
     843             :                   mortar_id_and_data.second.neighbor());
     844             : 
     845             :               if (using_gauss_lobatto_points) {
     846             :                 // Add the flux contribution to the volume data
     847             :                 add_slice_to_data(
     848             :                     vars_to_update, lifted_data, volume_mesh.extents(),
     849             :                     direction.dimension(),
     850             :                     index_to_slice_at(volume_mesh.extents(), direction));
     851             :               } else {
     852             :                 *vars_to_update += lifted_data;
     853             :               }
     854             :             }
     855             :           }
     856             :         });
     857             :   }
     858             : 
     859             :   template <typename... BoundaryCorrectionTags, typename... Tags,
     860             :             typename BoundaryCorrection, typename... AllVolumeArgs,
     861             :             typename... VolumeTagsForCorrection>
     862           0 :   static void call_boundary_correction(
     863             :       const gsl::not_null<Variables<tmpl::list<BoundaryCorrectionTags...>>*>
     864             :           boundary_corrections_on_mortar,
     865             :       const Variables<tmpl::list<Tags...>>& local_boundary_data,
     866             :       const Variables<tmpl::list<Tags...>>& neighbor_boundary_data,
     867             :       const BoundaryCorrection& boundary_correction,
     868             :       const ::dg::Formulation dg_formulation,
     869             :       const tuples::TaggedTuple<detail::TemporaryReference<AllVolumeArgs>...>&
     870             :           volume_args_tuple,
     871             :       tmpl::list<VolumeTagsForCorrection...> /*meta*/) {
     872             :     boundary_correction.dg_boundary_terms(
     873             :         make_not_null(
     874             :             &get<BoundaryCorrectionTags>(*boundary_corrections_on_mortar))...,
     875             :         get<Tags>(local_boundary_data)..., get<Tags>(neighbor_boundary_data)...,
     876             :         dg_formulation,
     877             :         tuples::get<detail::TemporaryReference<VolumeTagsForCorrection>>(
     878             :             volume_args_tuple)...);
     879             :   }
     880             : };
     881             : 
     882           1 : namespace Actions {
     883             : /*!
     884             :  * \brief Computes the boundary corrections for global time-stepping
     885             :  * and adds them to the time derivative.
     886             :  */
     887             : template <size_t VolumeDim, bool DenseOutput, bool UseNodegroupDgElements>
     888           1 : struct ApplyBoundaryCorrectionsToTimeDerivative {
     889           0 :   using inbox_tags =
     890             :       tmpl::list<evolution::dg::Tags::BoundaryCorrectionAndGhostCellsInbox<
     891             :           VolumeDim, UseNodegroupDgElements>>;
     892           0 :   using const_global_cache_tags =
     893             :       tmpl::list<evolution::Tags::BoundaryCorrection, ::dg::Tags::Formulation>;
     894             : 
     895             :   template <typename DbTagsList, typename... InboxTags, typename Metavariables,
     896             :             typename ArrayIndex, typename ActionList,
     897             :             typename ParallelComponent>
     898           0 :   static Parallel::iterable_action_return_t apply(
     899             :       db::DataBox<DbTagsList>& box, tuples::TaggedTuple<InboxTags...>& inboxes,
     900             :       const Parallel::GlobalCache<Metavariables>& /*cache*/,
     901             :       const ArrayIndex& /*array_index*/, ActionList /*meta*/,
     902             :       const ParallelComponent* const /*meta*/) {
     903             :     static_assert(
     904             :         UseNodegroupDgElements ==
     905             :             Parallel::is_dg_element_collection_v<ParallelComponent>,
     906             :         "The action ApplyBoundaryCorrectionsToTimeDerivative is told by the "
     907             :         "template parameter UseNodegroupDgElements that it is being "
     908             :         "used with a DgElementCollection, but the ParallelComponent "
     909             :         "is not a DgElementCollection. You need to change the template "
     910             :         "parameter on the ApplyBoundaryCorrectionsToTimeDerivative action "
     911             :         "in your action list.");
     912             :     constexpr size_t volume_dim = Metavariables::system::volume_dim;
     913             :     const Element<volume_dim>& element =
     914             :         db::get<domain::Tags::Element<volume_dim>>(box);
     915             : 
     916             :     if (UNLIKELY(element.number_of_neighbors() == 0)) {
     917             :       // We have no neighbors, yay!
     918             :       return {Parallel::AlgorithmExecution::Continue, std::nullopt};
     919             :     }
     920             : 
     921             :     if (not receive_boundary_data_global_time_stepping<
     922             :             Parallel::is_dg_element_collection_v<ParallelComponent>,
     923             :             Metavariables>(make_not_null(&box), make_not_null(&inboxes))) {
     924             :       return {Parallel::AlgorithmExecution::Retry, std::nullopt};
     925             :     }
     926             : 
     927             :     db::mutate_apply<
     928             :         ApplyBoundaryCorrections<false, Metavariables, VolumeDim, DenseOutput>>(
     929             :         make_not_null(&box));
     930             :     return {Parallel::AlgorithmExecution::Continue, std::nullopt};
     931             :   }
     932             : };
     933             : 
     934             : /*!
     935             :  * \brief Computes the boundary corrections for local time-stepping
     936             :  * and adds them to the variables.
     937             :  *
     938             :  * When using local time stepping the neighbor sends data at the neighbor's
     939             :  * current temporal id. Along with the boundary data, the next temporal id at
     940             :  * which the neighbor will send data is also sent. This is equal to the
     941             :  * neighbor's `::Tags::Next<::Tags::TimeStepId>`. When inserting into the mortar
     942             :  * data history, we insert the received temporal id, that is, the current time
     943             :  * of the neighbor, along with the boundary correction data.
     944             :  */
     945             : template <size_t VolumeDim, bool DenseOutput, bool UseNodegroupDgElements>
     946           1 : struct ApplyLtsBoundaryCorrections {
     947           0 :   using inbox_tags =
     948             :       tmpl::list<evolution::dg::Tags::BoundaryCorrectionAndGhostCellsInbox<
     949             :           VolumeDim, UseNodegroupDgElements>>;
     950           0 :   using const_global_cache_tags =
     951             :       tmpl::list<evolution::Tags::BoundaryCorrection, ::dg::Tags::Formulation>;
     952             : 
     953             :   template <typename DbTagsList, typename... InboxTags, typename Metavariables,
     954             :             typename ArrayIndex, typename ActionList,
     955             :             typename ParallelComponent>
     956           0 :   static Parallel::iterable_action_return_t apply(
     957             :       db::DataBox<DbTagsList>& box, tuples::TaggedTuple<InboxTags...>& inboxes,
     958             :       const Parallel::GlobalCache<Metavariables>& /*cache*/,
     959             :       const ArrayIndex& /*array_index*/, ActionList /*meta*/,
     960             :       const ParallelComponent* const /*meta*/) {
     961             :     static_assert(
     962             :         UseNodegroupDgElements ==
     963             :             Parallel::is_dg_element_collection_v<ParallelComponent>,
     964             :         "The action ApplyLtsBoundaryCorrections is told by the "
     965             :         "template parameter UseNodegroupDgElements that it is being "
     966             :         "used with a DgElementCollection, but the ParallelComponent "
     967             :         "is not a DgElementCollection. You need to change the "
     968             :         "template parameter on the ApplyLtsBoundaryCorrections action "
     969             :         "in your action list.");
     970             :     constexpr size_t volume_dim = Metavariables::system::volume_dim;
     971             :     const Element<volume_dim>& element =
     972             :         db::get<domain::Tags::Element<volume_dim>>(box);
     973             : 
     974             :     if (UNLIKELY(element.number_of_neighbors() == 0)) {
     975             :       // We have no neighbors, yay!
     976             :       return {Parallel::AlgorithmExecution::Continue, std::nullopt};
     977             :     }
     978             : 
     979             :     if (not receive_boundary_data_local_time_stepping<
     980             :             Parallel::is_dg_element_collection_v<ParallelComponent>,
     981             :             typename Metavariables::system, VolumeDim, false>(
     982             :             make_not_null(&box), make_not_null(&inboxes))) {
     983             :       return {Parallel::AlgorithmExecution::Retry, std::nullopt};
     984             :     }
     985             : 
     986             :     if (::SelfStart::step_unused(
     987             :             db::get<::Tags::TimeStepId>(box),
     988             :             db::get<::Tags::Next<::Tags::TimeStepId>>(box))) {
     989             :       return {Parallel::AlgorithmExecution::Continue, std::nullopt};
     990             :     }
     991             : 
     992             :     db::mutate_apply<
     993             :         ApplyBoundaryCorrections<true, Metavariables, VolumeDim, DenseOutput>>(
     994             :         make_not_null(&box));
     995             :     return {Parallel::AlgorithmExecution::Continue, std::nullopt};
     996             :   }
     997             : };
     998             : }  // namespace Actions
     999             : }  // namespace evolution::dg

Generated by: LCOV version 1.14