SpECTRE Documentation Coverage Report
Current view: top level - Evolution/DiscontinuousGalerkin/Actions - ApplyBoundaryCorrections.hpp Hit Total Coverage
Commit: 1c32b58340e006addc79befb2cdaa7547247e09c Lines: 6 32 18.8 %
Date: 2024-04-19 07:30:15
Legend: Lines: hit not hit

          Line data    Source code
       1           0 : // Distributed under the MIT License.
       2             : // See LICENSE.txt for details.
       3             : 
       4             : #pragma once
       5             : 
       6             : #include <cstddef>
       7             : #include <limits>
       8             : #include <map>
       9             : #include <optional>
      10             : #include <tuple>
      11             : #include <type_traits>
      12             : #include <utility>
      13             : #include <vector>
      14             : 
      15             : #include "DataStructures/DataBox/AsAccess.hpp"
      16             : #include "DataStructures/DataBox/DataBox.hpp"
      17             : #include "DataStructures/DataBox/PrefixHelpers.hpp"
      18             : #include "DataStructures/DataBox/Prefixes.hpp"
      19             : #include "DataStructures/Tensor/EagerMath/Magnitude.hpp"
      20             : #include "Domain/FaceNormal.hpp"
      21             : #include "Domain/Structure/DirectionalIdMap.hpp"
      22             : #include "Domain/Structure/Element.hpp"
      23             : #include "Domain/Structure/ElementId.hpp"
      24             : #include "Domain/Structure/TrimMap.hpp"
      25             : #include "Domain/Tags.hpp"
      26             : #include "Domain/Tags/NeighborMesh.hpp"
      27             : #include "Evolution/BoundaryCorrectionTags.hpp"
      28             : #include "Evolution/DiscontinuousGalerkin/InboxTags.hpp"
      29             : #include "Evolution/DiscontinuousGalerkin/MortarData.hpp"
      30             : #include "Evolution/DiscontinuousGalerkin/MortarTags.hpp"
      31             : #include "Evolution/DiscontinuousGalerkin/NormalVectorTags.hpp"
      32             : #include "Evolution/DiscontinuousGalerkin/UsingSubcell.hpp"
      33             : #include "NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp"
      34             : #include "NumericalAlgorithms/DiscontinuousGalerkin/LiftFlux.hpp"
      35             : #include "NumericalAlgorithms/DiscontinuousGalerkin/LiftFromBoundary.hpp"
      36             : #include "NumericalAlgorithms/DiscontinuousGalerkin/MortarHelpers.hpp"
      37             : #include "NumericalAlgorithms/DiscontinuousGalerkin/Tags/Formulation.hpp"
      38             : #include "NumericalAlgorithms/Spectral/Mesh.hpp"
      39             : #include "NumericalAlgorithms/Spectral/Projection.hpp"
      40             : #include "NumericalAlgorithms/Spectral/Quadrature.hpp"
      41             : #include "NumericalAlgorithms/Spectral/Spectral.hpp"
      42             : #include "Parallel/AlgorithmExecution.hpp"
      43             : #include "Parallel/GlobalCache.hpp"
      44             : #include "Time/BoundaryHistory.hpp"
      45             : #include "Time/EvolutionOrdering.hpp"
      46             : #include "Time/Time.hpp"
      47             : #include "Time/TimeStepId.hpp"
      48             : #include "Time/TimeSteppers/LtsTimeStepper.hpp"
      49             : #include "Time/TimeSteppers/TimeStepper.hpp"
      50             : #include "Utilities/Algorithm.hpp"
      51             : #include "Utilities/CallWithDynamicType.hpp"
      52             : #include "Utilities/ErrorHandling/Error.hpp"
      53             : #include "Utilities/Gsl.hpp"
      54             : #include "Utilities/MakeArray.hpp"
      55             : #include "Utilities/TMPL.hpp"
      56             : #include "Utilities/TaggedTuple.hpp"
      57             : 
      58             : /// \cond
      59             : namespace Tags {
      60             : struct Time;
      61             : struct TimeStep;
      62             : struct TimeStepId;
      63             : template <typename StepperInterface>
      64             : struct TimeStepper;
      65             : }  // namespace Tags
      66             : /// \endcond
      67             : 
      68             : /// \cond
      69             : namespace evolution::dg::subcell {
      70             : // We use a forward declaration instead of including a header file to avoid
      71             : // coupling to the DG-subcell libraries for executables that don't use subcell.
      72             : template <size_t VolumeDim, typename DgComputeSubcellNeighborPackagedData>
      73             : void neighbor_reconstructed_face_solution(
      74             :     gsl::not_null<db::Access*> box,
      75             :     gsl::not_null<std::pair<
      76             :         const TimeStepId,
      77             :         DirectionalIdMap<
      78             :             VolumeDim,
      79             :             std::tuple<Mesh<VolumeDim>, Mesh<VolumeDim - 1>,
      80             :                        std::optional<DataVector>, std::optional<DataVector>,
      81             :                        ::TimeStepId, int>>>*>
      82             :         received_temporal_id_and_data);
      83             : template <size_t Dim>
      84             : void neighbor_tci_decision(
      85             :     gsl::not_null<db::Access*> box,
      86             :     const std::pair<
      87             :         const TimeStepId,
      88             :         DirectionalIdMap<
      89             :             Dim, std::tuple<Mesh<Dim>, Mesh<Dim - 1>, std::optional<DataVector>,
      90             :                             std::optional<DataVector>, ::TimeStepId, int>>>&
      91             :         received_temporal_id_and_data);
      92             : }  // namespace evolution::dg::subcell
      93             : /// \endcond
      94             : 
      95             : namespace evolution::dg {
      96             : namespace detail {
      97             : template <typename BoundaryCorrectionClass>
      98             : struct get_dg_boundary_terms {
      99             :   using type = typename BoundaryCorrectionClass::dg_boundary_terms_volume_tags;
     100             : };
     101             : 
     102             : template <typename Tag, typename Type = db::const_item_type<Tag, tmpl::list<>>>
     103             : struct TemporaryReference {
     104             :   using tag = Tag;
     105             :   using type = const Type&;
     106             : };
     107             : }  // namespace detail
     108             : 
     109             : /// Receive boundary data for global time-stepping.  Returns true if
     110             : /// all necessary data has been received.
     111             : template <typename Metavariables, 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             :   using Key = DirectionalId<volume_dim>;
     119             :   std::map<TimeStepId,
     120             :            DirectionalIdMap<
     121             :                volume_dim,
     122             :                std::tuple<Mesh<volume_dim>, Mesh<volume_dim - 1>,
     123             :                           std::optional<DataVector>, std::optional<DataVector>,
     124             :                           ::TimeStepId, int>>>& inbox =
     125             :       tuples::get<evolution::dg::Tags::BoundaryCorrectionAndGhostCellsInbox<
     126             :           volume_dim>>(*inboxes);
     127             :   const auto received_temporal_id_and_data = inbox.find(temporal_id);
     128             :   if (received_temporal_id_and_data == inbox.end()) {
     129             :     return false;
     130             :   }
     131             :   const auto& received_neighbor_data = received_temporal_id_and_data->second;
     132             :   const Element<volume_dim>& element =
     133             :       db::get<domain::Tags::Element<volume_dim>>(*box);
     134             :   for (const auto& [direction, neighbors] : element.neighbors()) {
     135             :     for (const auto& neighbor : neighbors) {
     136             :       const auto neighbor_received =
     137             :           received_neighbor_data.find(Key{direction, neighbor});
     138             :       if (neighbor_received == received_neighbor_data.end()) {
     139             :         return false;
     140             :       }
     141             :     }
     142             :   }
     143             : 
     144             :   // Move inbox contents into the DataBox
     145             :   if constexpr (using_subcell_v<Metavariables>) {
     146             :     evolution::dg::subcell::neighbor_reconstructed_face_solution<
     147             :         volume_dim, typename Metavariables::SubcellOptions::
     148             :                         DgComputeSubcellNeighborPackagedData>(
     149             :         &db::as_access(*box), make_not_null(&*received_temporal_id_and_data));
     150             :     evolution::dg::subcell::neighbor_tci_decision<volume_dim>(
     151             :         make_not_null(&db::as_access(*box)), *received_temporal_id_and_data);
     152             :   }
     153             : 
     154             :   db::mutate<evolution::dg::Tags::MortarData<volume_dim>,
     155             :              evolution::dg::Tags::MortarNextTemporalId<volume_dim>,
     156             :              domain::Tags::NeighborMesh<volume_dim>>(
     157             :       [&received_temporal_id_and_data](
     158             :           const gsl::not_null<std::unordered_map<
     159             :               Key, evolution::dg::MortarData<volume_dim>, boost::hash<Key>>*>
     160             :               mortar_data,
     161             :           const gsl::not_null<
     162             :               std::unordered_map<Key, TimeStepId, boost::hash<Key>>*>
     163             :               mortar_next_time_step_id,
     164             :           const gsl::not_null<DirectionalIdMap<volume_dim, Mesh<volume_dim>>*>
     165             :               neighbor_mesh) {
     166             :         neighbor_mesh->clear();
     167             :         for (auto& received_mortar_data :
     168             :              received_temporal_id_and_data->second) {
     169             :           const auto& mortar_id = received_mortar_data.first;
     170             :           ASSERT(received_temporal_id_and_data->first ==
     171             :                      mortar_data->at(mortar_id).time_step_id(),
     172             :                  "Expected to receive mortar data on mortar "
     173             :                      << mortar_id << " at time "
     174             :                      << mortar_next_time_step_id->at(mortar_id)
     175             :                      << " but actually received at time "
     176             :                      << received_temporal_id_and_data->first);
     177             :           neighbor_mesh->insert_or_assign(
     178             :               mortar_id, std::get<0>(received_mortar_data.second));
     179             :           mortar_next_time_step_id->at(mortar_id) =
     180             :               std::get<4>(received_mortar_data.second);
     181             :           ASSERT(using_subcell_v<Metavariables> or
     182             :                      std::get<3>(received_mortar_data.second).has_value(),
     183             :                  "Must receive number boundary correction data when not using "
     184             :                  "DG-subcell.");
     185             :           if (std::get<3>(received_mortar_data.second).has_value()) {
     186             :             mortar_data->at(mortar_id).insert_neighbor_mortar_data(
     187             :                 received_temporal_id_and_data->first,
     188             :                 std::get<1>(received_mortar_data.second),
     189             :                 std::move(*std::get<3>(received_mortar_data.second)));
     190             :           }
     191             :         }
     192             :       },
     193             :       box);
     194             :   inbox.erase(received_temporal_id_and_data);
     195             :   return true;
     196             : }
     197             : 
     198             : /// Receive boundary data for local time-stepping.  Returns true if
     199             : /// all necessary data has been received.
     200             : ///
     201             : /// Setting \p DenseOutput to true receives data required for output
     202             : /// at `::Tags::Time` instead of `::Tags::Next<::Tags::TimeStepId>`.
     203             : template <typename System, size_t Dim, bool DenseOutput, typename DbTagsList,
     204             :           typename... InboxTags>
     205           1 : bool receive_boundary_data_local_time_stepping(
     206             :     const gsl::not_null<db::DataBox<DbTagsList>*> box,
     207             :     const gsl::not_null<tuples::TaggedTuple<InboxTags...>*> inboxes) {
     208             :   using variables_tag = typename System::variables_tag;
     209             :   using dt_variables_tag = db::add_tag_prefix<::Tags::dt, variables_tag>;
     210             : 
     211             :   // The boundary history coupling computation (which computes the _lifted_
     212             :   // boundary correction) returns a Variables<dt<EvolvedVars>> instead of
     213             :   // using the `NormalDotNumericalFlux` prefix tag. This is because the
     214             :   // returned quantity is more a `dt` quantity than a
     215             :   // `NormalDotNormalDotFlux` since it's been lifted to the volume.
     216             :   using Key = DirectionalId<Dim>;
     217             :   std::map<TimeStepId,
     218             :            DirectionalIdMap<
     219             :                Dim,
     220             :                std::tuple<Mesh<Dim>, Mesh<Dim - 1>,
     221             :                           std::optional<DataVector>, std::optional<DataVector>,
     222             :                           ::TimeStepId, int>>>& inbox =
     223             :       tuples::get<evolution::dg::Tags::BoundaryCorrectionAndGhostCellsInbox<
     224             :           Dim>>(*inboxes);
     225             : 
     226             :   const auto needed_time = [&box]() {
     227             :     const LtsTimeStepper& time_stepper =
     228             :         db::get<::Tags::TimeStepper<LtsTimeStepper>>(*box);
     229             :     if constexpr (DenseOutput) {
     230             :       const auto& dense_output_time = db::get<::Tags::Time>(*box);
     231             :       return [&dense_output_time, &time_stepper](const TimeStepId& id) {
     232             :         return time_stepper.neighbor_data_required(dense_output_time, id);
     233             :       };
     234             :     } else {
     235             :       const auto& next_temporal_id =
     236             :           db::get<::Tags::Next<::Tags::TimeStepId>>(*box);
     237             :       return [&next_temporal_id, &time_stepper](const TimeStepId& id) {
     238             :         return time_stepper.neighbor_data_required(next_temporal_id, id);
     239             :       };
     240             :     }
     241             :   }();
     242             : 
     243             :   const bool have_all_intermediate_messages = db::mutate<
     244             :       evolution::dg::Tags::MortarDataHistory<Dim,
     245             :                                              typename dt_variables_tag::type>,
     246             :       evolution::dg::Tags::MortarNextTemporalId<Dim>,
     247             :       domain::Tags::NeighborMesh<Dim>>(
     248             :       [&inbox, &needed_time](
     249             :           const gsl::not_null<
     250             :               std::unordered_map<Key,
     251             :                                  TimeSteppers::BoundaryHistory<
     252             :                                      evolution::dg::MortarData<Dim>,
     253             :                                      evolution::dg::MortarData<Dim>,
     254             :                                      typename dt_variables_tag::type>,
     255             :                                  boost::hash<Key>>*>
     256             :               boundary_data_history,
     257             :           const gsl::not_null<
     258             :               std::unordered_map<Key, TimeStepId, boost::hash<Key>>*>
     259             :               mortar_next_time_step_ids,
     260             :           const gsl::not_null<DirectionalIdMap<Dim, Mesh<Dim>>*>
     261             :               neighbor_mesh,
     262             :           const Element<Dim>& element) {
     263             :         // Remove neighbor meshes for neighbors that don't exist anymore
     264             :         domain::remove_nonexistent_neighbors(neighbor_mesh, element);
     265             : 
     266             :         // Move received boundary data into boundary history.
     267             :         for (auto& [mortar_id, mortar_next_time_step_id] :
     268             :              *mortar_next_time_step_ids) {
     269             :           if (mortar_id.id == ElementId<Dim>::external_boundary_id()) {
     270             :             continue;
     271             :           }
     272             :           while (needed_time(mortar_next_time_step_id)) {
     273             :             const auto time_entry = inbox.find(mortar_next_time_step_id);
     274             :             if (time_entry == inbox.end()) {
     275             :               return false;
     276             :             }
     277             :             const auto received_mortar_data =
     278             :                 time_entry->second.find(mortar_id);
     279             :             if (received_mortar_data == time_entry->second.end()) {
     280             :               return false;
     281             :             }
     282             : 
     283             :             MortarData<Dim> neighbor_mortar_data{};
     284             :             // Insert:
     285             :             // - the current TimeStepId of the neighbor
     286             :             // - the current face mesh of the neighbor
     287             :             // - the current boundary correction data of the neighbor
     288             :             ASSERT(std::get<3>(received_mortar_data->second).has_value(),
     289             :                    "Did not receive boundary correction data from the "
     290             :                    "neighbor\nMortarId: "
     291             :                        << mortar_id
     292             :                        << "\nTimeStepId: " << mortar_next_time_step_id);
     293             :             neighbor_mesh->insert_or_assign(
     294             :                 mortar_id, std::get<0>(received_mortar_data->second));
     295             :             neighbor_mortar_data.insert_neighbor_mortar_data(
     296             :                 mortar_next_time_step_id,
     297             :                 std::get<1>(received_mortar_data->second),
     298             :                 std::move(*std::get<3>(received_mortar_data->second)));
     299             :             // We don't yet communicate the integration order, because
     300             :             // we don't have any variable-order methods.  The
     301             :             // fixed-order methods ignore the field.
     302             :             boundary_data_history->at(mortar_id).remote().insert(
     303             :                 mortar_next_time_step_id, std::numeric_limits<size_t>::max(),
     304             :                 std::move(neighbor_mortar_data));
     305             :             mortar_next_time_step_id =
     306             :                 std::get<4>(received_mortar_data->second);
     307             :             time_entry->second.erase(received_mortar_data);
     308             :             if (time_entry->second.empty()) {
     309             :               inbox.erase(time_entry);
     310             :             }
     311             :           }
     312             :         }
     313             :         return true;
     314             :       },
     315             :       box, db::get<::domain::Tags::Element<Dim>>(*box));
     316             : 
     317             :   return have_all_intermediate_messages;
     318             : }
     319             : 
     320             : /// Apply corrections from boundary communication.
     321             : ///
     322             : /// If `LocalTimeStepping` is false, updates the derivative of the variables,
     323             : /// which should be done before taking a time step.  If
     324             : /// `LocalTimeStepping` is true, updates the variables themselves, which should
     325             : /// be done after the volume update.
     326             : ///
     327             : /// Setting \p DenseOutput to true receives data required for output
     328             : /// at ::Tags::Time instead of performing a full step.  This is only
     329             : /// used for local time-stepping.
     330             : template <bool LocalTimeStepping, typename System, size_t VolumeDim,
     331             :           bool DenseOutput>
     332           1 : struct ApplyBoundaryCorrections {
     333           0 :   static constexpr bool local_time_stepping = LocalTimeStepping;
     334             :   static_assert(local_time_stepping or not DenseOutput,
     335             :                 "GTS does not use ApplyBoundaryCorrections for dense output.");
     336             : 
     337           0 :   using system = System;
     338           0 :   static constexpr size_t volume_dim = VolumeDim;
     339           0 :   using variables_tag = typename system::variables_tag;
     340           0 :   using dt_variables_tag = db::add_tag_prefix<::Tags::dt, variables_tag>;
     341           0 :   using DtVariables = typename dt_variables_tag::type;
     342           0 :   using volume_tags_for_dg_boundary_terms =
     343             :       tmpl::remove_duplicates<tmpl::flatten<tmpl::transform<
     344             :           typename system::boundary_correction_base::creatable_classes,
     345             :           detail::get_dg_boundary_terms<tmpl::_1>>>>;
     346             : 
     347           0 :   using TimeStepperType =
     348             :       tmpl::conditional_t<local_time_stepping, LtsTimeStepper, TimeStepper>;
     349             : 
     350           0 :   using tag_to_update =
     351             :       tmpl::conditional_t<local_time_stepping, variables_tag, dt_variables_tag>;
     352           0 :   using mortar_data_tag = tmpl::conditional_t<
     353             :       local_time_stepping,
     354             :       evolution::dg::Tags::MortarDataHistory<volume_dim, DtVariables>,
     355             :       evolution::dg::Tags::MortarData<volume_dim>>;
     356           0 :   using MortarDataType =
     357             :       tmpl::conditional_t<DenseOutput, const typename mortar_data_tag::type,
     358             :                           typename mortar_data_tag::type>;
     359             : 
     360           0 :   using return_tags =
     361             :       tmpl::conditional_t<DenseOutput, tmpl::list<tag_to_update>,
     362             :                           tmpl::list<tag_to_update, mortar_data_tag>>;
     363           0 :   using argument_tags = tmpl::append<
     364             :       tmpl::flatten<tmpl::list<
     365             :           tmpl::conditional_t<DenseOutput, mortar_data_tag, tmpl::list<>>,
     366             :           domain::Tags::Mesh<volume_dim>, Tags::MortarMesh<volume_dim>,
     367             :           Tags::MortarSize<volume_dim>, ::dg::Tags::Formulation,
     368             :           evolution::dg::Tags::NormalCovectorAndMagnitude<volume_dim>,
     369             :           ::Tags::TimeStepper<TimeStepperType>,
     370             :           evolution::Tags::BoundaryCorrection<system>,
     371             :           tmpl::conditional_t<DenseOutput, ::Tags::Time, ::Tags::TimeStep>,
     372             :           tmpl::conditional_t<local_time_stepping, tmpl::list<>,
     373             :                               domain::Tags::DetInvJacobian<
     374             :                                   Frame::ElementLogical, Frame::Inertial>>>>,
     375             :       volume_tags_for_dg_boundary_terms>;
     376             : 
     377             :   // full step
     378             :   template <typename... VolumeArgs>
     379           0 :   static void apply(
     380             :       const gsl::not_null<typename tag_to_update::type*> vars_to_update,
     381             :       const gsl::not_null<MortarDataType*> mortar_data,
     382             :       const Mesh<volume_dim>& volume_mesh,
     383             :       const typename Tags::MortarMesh<volume_dim>::type& mortar_meshes,
     384             :       const typename Tags::MortarSize<volume_dim>::type& mortar_sizes,
     385             :       const ::dg::Formulation dg_formulation,
     386             :       const DirectionMap<
     387             :           volume_dim, std::optional<Variables<tmpl::list<
     388             :                           evolution::dg::Tags::MagnitudeOfNormal,
     389             :                           evolution::dg::Tags::NormalCovector<volume_dim>>>>>&
     390             :           face_normal_covector_and_magnitude,
     391             :       const TimeStepperType& time_stepper,
     392             :       const typename system::boundary_correction_base& boundary_correction,
     393             :       const TimeDelta& time_step,
     394             :       const Scalar<DataVector>& gts_det_inv_jacobian,
     395             :       const VolumeArgs&... volume_args) {
     396             :     apply_impl(vars_to_update, mortar_data, volume_mesh, mortar_meshes,
     397             :                mortar_sizes, dg_formulation, face_normal_covector_and_magnitude,
     398             :                time_stepper, boundary_correction, time_step,
     399             :                std::numeric_limits<double>::signaling_NaN(),
     400             :                gts_det_inv_jacobian, volume_args...);
     401             :   }
     402             : 
     403             :   template <typename... VolumeArgs>
     404           0 :   static void apply(
     405             :       const gsl::not_null<typename tag_to_update::type*> vars_to_update,
     406             :       const gsl::not_null<MortarDataType*> mortar_data,
     407             :       const Mesh<volume_dim>& volume_mesh,
     408             :       const typename Tags::MortarMesh<volume_dim>::type& mortar_meshes,
     409             :       const typename Tags::MortarSize<volume_dim>::type& mortar_sizes,
     410             :       const ::dg::Formulation dg_formulation,
     411             :       const DirectionMap<
     412             :           volume_dim, std::optional<Variables<tmpl::list<
     413             :                           evolution::dg::Tags::MagnitudeOfNormal,
     414             :                           evolution::dg::Tags::NormalCovector<volume_dim>>>>>&
     415             :           face_normal_covector_and_magnitude,
     416             :       const TimeStepperType& time_stepper,
     417             :       const typename system::boundary_correction_base& boundary_correction,
     418             :       const TimeDelta& time_step, const VolumeArgs&... volume_args) {
     419             :     apply_impl(vars_to_update, mortar_data, volume_mesh, mortar_meshes,
     420             :                mortar_sizes, dg_formulation, face_normal_covector_and_magnitude,
     421             :                time_stepper, boundary_correction, time_step,
     422             :                std::numeric_limits<double>::signaling_NaN(), {},
     423             :                volume_args...);
     424             :   }
     425             : 
     426             :   // dense output (LTS only)
     427             :   template <typename... VolumeArgs>
     428           0 :   static void apply(
     429             :       const gsl::not_null<typename variables_tag::type*> vars_to_update,
     430             :       const MortarDataType& mortar_data, const Mesh<volume_dim>& volume_mesh,
     431             :       const typename Tags::MortarMesh<volume_dim>::type& mortar_meshes,
     432             :       const typename Tags::MortarSize<volume_dim>::type& mortar_sizes,
     433             :       const ::dg::Formulation dg_formulation,
     434             :       const DirectionMap<
     435             :           volume_dim, std::optional<Variables<tmpl::list<
     436             :                           evolution::dg::Tags::MagnitudeOfNormal,
     437             :                           evolution::dg::Tags::NormalCovector<volume_dim>>>>>&
     438             :           face_normal_covector_and_magnitude,
     439             :       const LtsTimeStepper& time_stepper,
     440             :       const typename system::boundary_correction_base& boundary_correction,
     441             :       const double dense_output_time, const VolumeArgs&... volume_args) {
     442             :     apply_impl(vars_to_update, &mortar_data, volume_mesh, mortar_meshes,
     443             :                mortar_sizes, dg_formulation, face_normal_covector_and_magnitude,
     444             :                time_stepper, boundary_correction, TimeDelta{},
     445             :                dense_output_time, {}, volume_args...);
     446             :   }
     447             : 
     448             :   template <typename DbTagsList, typename... InboxTags, typename Metavariables,
     449             :             typename ArrayIndex, typename ParallelComponent>
     450           0 :   static bool is_ready(
     451             :       const gsl::not_null<db::DataBox<DbTagsList>*> box,
     452             :       const gsl::not_null<tuples::TaggedTuple<InboxTags...>*> inboxes,
     453             :       Parallel::GlobalCache<Metavariables>& /*cache*/,
     454             :       const ArrayIndex& /*array_index*/,
     455             :       const ParallelComponent* const /*component*/) {
     456             :     if constexpr (local_time_stepping) {
     457             :       return receive_boundary_data_local_time_stepping<System, VolumeDim,
     458             :                                                        DenseOutput>(box,
     459             :                                                                     inboxes);
     460             :     } else {
     461             :       return receive_boundary_data_global_time_stepping<Metavariables>(box,
     462             :                                                                        inboxes);
     463             :     }
     464             :   }
     465             : 
     466             :  private:
     467             :   template <typename... VolumeArgs>
     468           0 :   static void apply_impl(
     469             :       const gsl::not_null<typename tag_to_update::type*> vars_to_update,
     470             :       const gsl::not_null<MortarDataType*> mortar_data,
     471             :       const Mesh<volume_dim>& volume_mesh,
     472             :       const typename Tags::MortarMesh<volume_dim>::type& mortar_meshes,
     473             :       const typename Tags::MortarSize<volume_dim>::type& mortar_sizes,
     474             :       const ::dg::Formulation dg_formulation,
     475             :       const DirectionMap<
     476             :           volume_dim, std::optional<Variables<tmpl::list<
     477             :                           evolution::dg::Tags::MagnitudeOfNormal,
     478             :                           evolution::dg::Tags::NormalCovector<volume_dim>>>>>&
     479             :           face_normal_covector_and_magnitude,
     480             :       const TimeStepperType& time_stepper,
     481             :       const typename system::boundary_correction_base& boundary_correction,
     482             :       const TimeDelta& time_step, const double dense_output_time,
     483             :       const Scalar<DataVector>& gts_det_inv_jacobian,
     484             :       const VolumeArgs&... volume_args) {
     485             :     tuples::tagged_tuple_from_typelist<db::wrap_tags_in<
     486             :         detail::TemporaryReference, volume_tags_for_dg_boundary_terms>>
     487             :         volume_args_tuple{volume_args...};
     488             : 
     489             :     // Set up helper lambda that will compute and lift the boundary corrections
     490             :     ASSERT(
     491             :         volume_mesh.quadrature() ==
     492             :             make_array<volume_dim>(volume_mesh.quadrature(0)),
     493             :         "Must have isotropic quadrature, but got volume mesh: " << volume_mesh);
     494             :     const bool using_gauss_lobatto_points =
     495             :         volume_mesh.quadrature(0) == Spectral::Quadrature::GaussLobatto;
     496             : 
     497             :     Scalar<DataVector> volume_det_inv_jacobian{};
     498             :     Scalar<DataVector> volume_det_jacobian{};
     499             :     if constexpr (not local_time_stepping) {
     500             :       if (not using_gauss_lobatto_points) {
     501             :         get(volume_det_inv_jacobian)
     502             :             .set_data_ref(make_not_null(
     503             :                 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
     504             :                 &const_cast<DataVector&>(get(gts_det_inv_jacobian))));
     505             :         get(volume_det_jacobian) = 1.0 / get(volume_det_inv_jacobian);
     506             :       }
     507             :     }
     508             : 
     509             :     using derived_boundary_corrections =
     510             :         typename std::decay_t<decltype(boundary_correction)>::creatable_classes;
     511             : 
     512             :     static_assert(
     513             :         tmpl::all<derived_boundary_corrections, std::is_final<tmpl::_1>>::value,
     514             :         "All createable classes for boundary corrections must be marked "
     515             :         "final.");
     516             :     call_with_dynamic_type<void, derived_boundary_corrections>(
     517             :         &boundary_correction,
     518             :         [&dense_output_time, &dg_formulation,
     519             :          &face_normal_covector_and_magnitude, &mortar_data, &mortar_meshes,
     520             :          &mortar_sizes, &time_step, &time_stepper, using_gauss_lobatto_points,
     521             :          &vars_to_update, &volume_args_tuple, &volume_det_jacobian,
     522             :          &volume_det_inv_jacobian,
     523             :          &volume_mesh](auto* typed_boundary_correction) {
     524             :           using BcType = std::decay_t<decltype(*typed_boundary_correction)>;
     525             :           // Compute internal boundary quantities on the mortar for sides of
     526             :           // the element that have neighbors, i.e. they are not an external
     527             :           // side.
     528             :           using mortar_tags_list = typename BcType::dg_package_field_tags;
     529             : 
     530             :           // Variables for reusing allocations.  The actual values are
     531             :           // not reused.
     532             :           DtVariables dt_boundary_correction_on_mortar{};
     533             :           DtVariables volume_dt_correction{};
     534             :           // These variables may change size for each mortar and require
     535             :           // a new memory allocation, but they may also happen to need
     536             :           // to be the same size twice in a row, in which case holding
     537             :           // on to the allocation is a win.
     538             :           Scalar<DataVector> face_det_jacobian{};
     539             :           Variables<mortar_tags_list> local_data_on_mortar{};
     540             :           Variables<mortar_tags_list> neighbor_data_on_mortar{};
     541             : 
     542             :           for (auto& mortar_id_and_data : *mortar_data) {
     543             :             const auto& mortar_id = mortar_id_and_data.first;
     544             :             const auto& direction = mortar_id.direction;
     545             :             if (UNLIKELY(mortar_id.id ==
     546             :                          ElementId<volume_dim>::external_boundary_id())) {
     547             :               ERROR(
     548             :                   "Cannot impose boundary conditions on external boundary in "
     549             :                   "direction "
     550             :                   << direction
     551             :                   << " in the ApplyBoundaryCorrections action. Boundary "
     552             :                      "conditions are applied in the ComputeTimeDerivative "
     553             :                      "action "
     554             :                      "instead. You may have unintentionally added external "
     555             :                      "mortars in one of the initialization actions.");
     556             :             }
     557             : 
     558             :             const Mesh<volume_dim - 1> face_mesh =
     559             :                 volume_mesh.slice_away(direction.dimension());
     560             : 
     561             :             const auto compute_correction_coupling =
     562             :                 [&typed_boundary_correction, &direction, dg_formulation,
     563             :                  &dt_boundary_correction_on_mortar, &face_det_jacobian,
     564             :                  &face_mesh, &face_normal_covector_and_magnitude,
     565             :                  &local_data_on_mortar, &mortar_id, &mortar_meshes,
     566             :                  &mortar_sizes, &neighbor_data_on_mortar,
     567             :                  using_gauss_lobatto_points, &volume_args_tuple,
     568             :                  &volume_det_jacobian, &volume_det_inv_jacobian,
     569             :                  &volume_dt_correction, &volume_mesh](
     570             :                     const MortarData<volume_dim>& local_mortar_data,
     571             :                     const MortarData<volume_dim>& neighbor_mortar_data)
     572             :                 -> DtVariables {
     573             :               if (local_time_stepping and not using_gauss_lobatto_points) {
     574             :                 // This needs to be updated every call because the Jacobian
     575             :                 // may be time-dependent. In the case of time-independent maps
     576             :                 // and local time stepping we could first perform the integral
     577             :                 // on the boundaries, and then lift to the volume. This is
     578             :                 // left as a future optimization.
     579             :                 local_mortar_data.get_local_volume_det_inv_jacobian(
     580             :                     make_not_null(&volume_det_inv_jacobian));
     581             :                 get(volume_det_jacobian) = 1.0 / get(volume_det_inv_jacobian);
     582             :               }
     583             :               const auto& mortar_mesh = mortar_meshes.at(mortar_id);
     584             : 
     585             :               // Extract local and neighbor data, copy into Variables because
     586             :               // we store them in a std::vector for type erasure.
     587             :               const std::pair<Mesh<volume_dim - 1>, DataVector>&
     588             :                   local_mesh_and_data = *local_mortar_data.local_mortar_data();
     589             :               const std::pair<Mesh<volume_dim - 1>, DataVector>&
     590             :                   neighbor_mesh_and_data =
     591             :                       *neighbor_mortar_data.neighbor_mortar_data();
     592             :               local_data_on_mortar.set_data_ref(
     593             :                   const_cast<double*>(std::get<1>(local_mesh_and_data).data()),
     594             :                   std::get<1>(local_mesh_and_data).size());
     595             :               neighbor_data_on_mortar.set_data_ref(
     596             :                   const_cast<double*>(
     597             :                       std::get<1>(neighbor_mesh_and_data).data()),
     598             :                   std::get<1>(neighbor_mesh_and_data).size());
     599             : 
     600             :               // The boundary computations and lifting can be further
     601             :               // optimized by in the h-refinement case having only one
     602             :               // allocation for the face and having the projection from the
     603             :               // mortar to the face be done in place. E.g.
     604             :               // local_data_on_mortar and neighbor_data_on_mortar could be
     605             :               // allocated fewer times, as well as `needs_projection` section
     606             :               // below could do an in-place projection.
     607             :               dt_boundary_correction_on_mortar.initialize(
     608             :                   mortar_mesh.number_of_grid_points());
     609             : 
     610             :               call_boundary_correction(
     611             :                   make_not_null(&dt_boundary_correction_on_mortar),
     612             :                   local_data_on_mortar, neighbor_data_on_mortar,
     613             :                   *typed_boundary_correction, dg_formulation, volume_args_tuple,
     614             :                   typename BcType::dg_boundary_terms_volume_tags{});
     615             : 
     616             :               const std::array<Spectral::MortarSize, volume_dim - 1>&
     617             :                   mortar_size = mortar_sizes.at(mortar_id);
     618             : 
     619             :               // This cannot reuse an allocation because it is initialized
     620             :               // via move-assignment.  (If it is used at all.)
     621             :               DtVariables dt_boundary_correction_projected_onto_face{};
     622             :               auto& dt_boundary_correction =
     623             :                   [&dt_boundary_correction_on_mortar,
     624             :                    &dt_boundary_correction_projected_onto_face, &face_mesh,
     625             :                    &mortar_mesh, &mortar_size]() -> DtVariables& {
     626             :                 if (Spectral::needs_projection(face_mesh, mortar_mesh,
     627             :                                                mortar_size)) {
     628             :                   dt_boundary_correction_projected_onto_face =
     629             :                       ::dg::project_from_mortar(
     630             :                           dt_boundary_correction_on_mortar, face_mesh,
     631             :                           mortar_mesh, mortar_size);
     632             :                   return dt_boundary_correction_projected_onto_face;
     633             :                 }
     634             :                 return dt_boundary_correction_on_mortar;
     635             :               }();
     636             : 
     637             :               // Both paths initialize this to be non-owning.
     638             :               Scalar<DataVector> magnitude_of_face_normal{};
     639             :               if constexpr (local_time_stepping) {
     640             :                 (void)face_normal_covector_and_magnitude;
     641             :                 local_mortar_data.get_local_face_normal_magnitude(
     642             :                     &magnitude_of_face_normal);
     643             :               } else {
     644             :                 ASSERT(
     645             :                     face_normal_covector_and_magnitude.count(direction) == 1 and
     646             :                         face_normal_covector_and_magnitude.at(direction)
     647             :                             .has_value(),
     648             :                     "Face normal covector and magnitude not set in "
     649             :                     "direction: "
     650             :                         << direction);
     651             :                 get(magnitude_of_face_normal)
     652             :                     .set_data_ref(make_not_null(&const_cast<DataVector&>(
     653             :                         get(get<evolution::dg::Tags::MagnitudeOfNormal>(
     654             :                             *face_normal_covector_and_magnitude.at(
     655             :                                 direction))))));
     656             :               }
     657             : 
     658             :               if (using_gauss_lobatto_points) {
     659             :                 // The lift_flux function lifts only on the slice, it does not
     660             :                 // add the contribution to the volume.
     661             :                 ::dg::lift_flux(make_not_null(&dt_boundary_correction),
     662             :                                 volume_mesh.extents(direction.dimension()),
     663             :                                 magnitude_of_face_normal);
     664             :                 return std::move(dt_boundary_correction);
     665             :               } else {
     666             :                 // We are using Gauss points.
     667             :                 //
     668             :                 // Notes:
     669             :                 // - We should really lift both sides simultaneously since this
     670             :                 //   reduces memory accesses. Lifting all sides at the same
     671             :                 //   time is unlikely to improve performance since we lift by
     672             :                 //   jumping through slices. There may also be compatibility
     673             :                 //   issues with local time stepping.
     674             :                 // - If we lift both sides at the same time we first need to
     675             :                 //   deal with projecting from mortars to the face, then lift
     676             :                 //   off the faces. With non-owning Variables memory
     677             :                 //   allocations could be significantly reduced in this code.
     678             :                 if constexpr (local_time_stepping) {
     679             :                   ASSERT(get(volume_det_inv_jacobian).size() > 0,
     680             :                          "For local time stepping the volume determinant of "
     681             :                          "the inverse Jacobian has not been set.");
     682             : 
     683             :                   local_mortar_data.get_local_face_det_jacobian(
     684             :                       make_not_null(&face_det_jacobian));
     685             :                 } else {
     686             :                   // Project the determinant of the Jacobian to the face. This
     687             :                   // could be optimized by caching in the time-independent case.
     688             :                   get(face_det_jacobian)
     689             :                       .destructive_resize(face_mesh.number_of_grid_points());
     690             :                   const Matrix identity{};
     691             :                   auto interpolation_matrices =
     692             :                       make_array<volume_dim>(std::cref(identity));
     693             :                   const std::pair<Matrix, Matrix>& matrices =
     694             :                       Spectral::boundary_interpolation_matrices(
     695             :                           volume_mesh.slice_through(direction.dimension()));
     696             :                   gsl::at(interpolation_matrices, direction.dimension()) =
     697             :                       direction.side() == Side::Upper ? matrices.second
     698             :                                                       : matrices.first;
     699             :                   apply_matrices(make_not_null(&get(face_det_jacobian)),
     700             :                                  interpolation_matrices,
     701             :                                  get(volume_det_jacobian),
     702             :                                  volume_mesh.extents());
     703             :                 }
     704             : 
     705             :                 volume_dt_correction.initialize(
     706             :                     volume_mesh.number_of_grid_points(), 0.0);
     707             :                 ::dg::lift_boundary_terms_gauss_points(
     708             :                     make_not_null(&volume_dt_correction),
     709             :                     volume_det_inv_jacobian, volume_mesh, direction,
     710             :                     dt_boundary_correction, magnitude_of_face_normal,
     711             :                     face_det_jacobian);
     712             :                 return std::move(volume_dt_correction);
     713             :               }
     714             :             };
     715             : 
     716             :             if constexpr (local_time_stepping) {
     717             :               typename variables_tag::type lgl_lifted_data{};
     718             :               auto& lifted_data = using_gauss_lobatto_points ? lgl_lifted_data
     719             :                                                              : *vars_to_update;
     720             :               if (using_gauss_lobatto_points) {
     721             :                 lifted_data.initialize(face_mesh.number_of_grid_points(), 0.0);
     722             :               }
     723             : 
     724             :               auto& mortar_data_history = mortar_id_and_data.second;
     725             :               if constexpr (DenseOutput) {
     726             :                 (void)time_step;
     727             :                 time_stepper.boundary_dense_output(
     728             :                     &lifted_data, mortar_data_history, dense_output_time,
     729             :                     compute_correction_coupling);
     730             :               } else {
     731             :                 (void)dense_output_time;
     732             :                 time_stepper.add_boundary_delta(
     733             :                     &lifted_data, make_not_null(&mortar_data_history),
     734             :                     time_step, compute_correction_coupling);
     735             :               }
     736             : 
     737             :               if (using_gauss_lobatto_points) {
     738             :                 // Add the flux contribution to the volume data
     739             :                 add_slice_to_data(
     740             :                     vars_to_update, lifted_data, volume_mesh.extents(),
     741             :                     direction.dimension(),
     742             :                     index_to_slice_at(volume_mesh.extents(), direction));
     743             :               }
     744             :             } else {
     745             :               (void)time_step;
     746             :               (void)time_stepper;
     747             :               (void)dense_output_time;
     748             : 
     749             :               // Choose an allocation cache that may be empty, so we
     750             :               // might be able to reuse the allocation obtained for the
     751             :               // lifted data.  This may result in a self assignment,
     752             :               // depending on the code paths taken, but handling the
     753             :               // results this way makes the GTS and LTS paths more
     754             :               // similar because the LTS code always stores the result
     755             :               // in the history and so sometimes benefits from moving
     756             :               // into the return value of compute_correction_coupling.
     757             :               auto& lifted_data = using_gauss_lobatto_points
     758             :                                       ? dt_boundary_correction_on_mortar
     759             :                                       : volume_dt_correction;
     760             :               lifted_data = compute_correction_coupling(
     761             :                   mortar_id_and_data.second, mortar_id_and_data.second);
     762             :               // Remove data since it's tagged with the time. In the future we
     763             :               // _might_ be able to reuse allocations, but this optimization
     764             :               // should only be done after profiling.
     765             :               mortar_id_and_data.second.extract();
     766             : 
     767             :               if (using_gauss_lobatto_points) {
     768             :                 // Add the flux contribution to the volume data
     769             :                 add_slice_to_data(
     770             :                     vars_to_update, lifted_data, volume_mesh.extents(),
     771             :                     direction.dimension(),
     772             :                     index_to_slice_at(volume_mesh.extents(), direction));
     773             :               } else {
     774             :                 *vars_to_update += lifted_data;
     775             :               }
     776             :             }
     777             :           }
     778             :         });
     779             :   }
     780             : 
     781             :   template <typename... BoundaryCorrectionTags, typename... Tags,
     782             :             typename BoundaryCorrection, typename... AllVolumeArgs,
     783             :             typename... VolumeTagsForCorrection>
     784           0 :   static void call_boundary_correction(
     785             :       const gsl::not_null<Variables<tmpl::list<BoundaryCorrectionTags...>>*>
     786             :           boundary_corrections_on_mortar,
     787             :       const Variables<tmpl::list<Tags...>>& local_boundary_data,
     788             :       const Variables<tmpl::list<Tags...>>& neighbor_boundary_data,
     789             :       const BoundaryCorrection& boundary_correction,
     790             :       const ::dg::Formulation dg_formulation,
     791             :       const tuples::TaggedTuple<detail::TemporaryReference<AllVolumeArgs>...>&
     792             :           volume_args_tuple,
     793             :       tmpl::list<VolumeTagsForCorrection...> /*meta*/) {
     794             :     boundary_correction.dg_boundary_terms(
     795             :         make_not_null(
     796             :             &get<BoundaryCorrectionTags>(*boundary_corrections_on_mortar))...,
     797             :         get<Tags>(local_boundary_data)..., get<Tags>(neighbor_boundary_data)...,
     798             :         dg_formulation,
     799             :         tuples::get<detail::TemporaryReference<VolumeTagsForCorrection>>(
     800             :             volume_args_tuple)...);
     801             :   }
     802             : };
     803             : 
     804           1 : namespace Actions {
     805             : /*!
     806             :  * \brief Computes the boundary corrections for global time-stepping
     807             :  * and adds them to the time derivative.
     808             :  */
     809             : template <typename System, size_t VolumeDim, bool DenseOutput>
     810           1 : struct ApplyBoundaryCorrectionsToTimeDerivative {
     811           0 :   using inbox_tags = tmpl::list<
     812             :       evolution::dg::Tags::BoundaryCorrectionAndGhostCellsInbox<VolumeDim>>;
     813           0 :   using const_global_cache_tags =
     814             :       tmpl::list<evolution::Tags::BoundaryCorrection<System>,
     815             :                  ::dg::Tags::Formulation>;
     816             : 
     817             :   template <typename DbTagsList, typename... InboxTags, typename Metavariables,
     818             :             typename ArrayIndex, typename ActionList,
     819             :             typename ParallelComponent>
     820           0 :   static Parallel::iterable_action_return_t apply(
     821             :       db::DataBox<DbTagsList>& box, tuples::TaggedTuple<InboxTags...>& inboxes,
     822             :       const Parallel::GlobalCache<Metavariables>& /*cache*/,
     823             :       const ArrayIndex& /*array_index*/, ActionList /*meta*/,
     824             :       const ParallelComponent* const /*meta*/) {
     825             :     constexpr size_t volume_dim = Metavariables::system::volume_dim;
     826             :     const Element<volume_dim>& element =
     827             :         db::get<domain::Tags::Element<volume_dim>>(box);
     828             : 
     829             :     if (UNLIKELY(element.number_of_neighbors() == 0)) {
     830             :       // We have no neighbors, yay!
     831             :       return {Parallel::AlgorithmExecution::Continue, std::nullopt};
     832             :     }
     833             : 
     834             :     if (not receive_boundary_data_global_time_stepping<Metavariables>(
     835             :             make_not_null(&box), make_not_null(&inboxes))) {
     836             :       return {Parallel::AlgorithmExecution::Retry, std::nullopt};
     837             :     }
     838             : 
     839             :     db::mutate_apply<
     840             :         ApplyBoundaryCorrections<false, System, VolumeDim, DenseOutput>>(
     841             :         make_not_null(&box));
     842             :     return {Parallel::AlgorithmExecution::Continue, std::nullopt};
     843             :   }
     844             : };
     845             : 
     846             : /*!
     847             :  * \brief Computes the boundary corrections for local time-stepping
     848             :  * and adds them to the variables.
     849             :  *
     850             :  * When using local time stepping the neighbor sends data at the neighbor's
     851             :  * current temporal id. Along with the boundary data, the next temporal id at
     852             :  * which the neighbor will send data is also sent. This is equal to the
     853             :  * neighbor's `::Tags::Next<::Tags::TimeStepId>`. When inserting into the mortar
     854             :  * data history, we insert the received temporal id, that is, the current time
     855             :  * of the neighbor, along with the boundary correction data.
     856             :  */
     857             : template <typename System, size_t VolumeDim, bool DenseOutput>
     858           1 : struct ApplyLtsBoundaryCorrections {
     859           0 :   using inbox_tags = tmpl::list<
     860             :       evolution::dg::Tags::BoundaryCorrectionAndGhostCellsInbox<VolumeDim>>;
     861           0 :   using const_global_cache_tags =
     862             :       tmpl::list<evolution::Tags::BoundaryCorrection<System>,
     863             :                  ::dg::Tags::Formulation>;
     864             : 
     865             :   template <typename DbTagsList, typename... InboxTags, typename Metavariables,
     866             :             typename ArrayIndex, typename ActionList,
     867             :             typename ParallelComponent>
     868           0 :   static Parallel::iterable_action_return_t apply(
     869             :       db::DataBox<DbTagsList>& box, tuples::TaggedTuple<InboxTags...>& inboxes,
     870             :       const Parallel::GlobalCache<Metavariables>& /*cache*/,
     871             :       const ArrayIndex& /*array_index*/, ActionList /*meta*/,
     872             :       const ParallelComponent* const /*meta*/) {
     873             :     constexpr size_t volume_dim = Metavariables::system::volume_dim;
     874             :     const Element<volume_dim>& element =
     875             :         db::get<domain::Tags::Element<volume_dim>>(box);
     876             : 
     877             :     if (UNLIKELY(element.number_of_neighbors() == 0)) {
     878             :       // We have no neighbors, yay!
     879             :       return {Parallel::AlgorithmExecution::Continue, std::nullopt};
     880             :     }
     881             : 
     882             :     if (not receive_boundary_data_local_time_stepping<System, VolumeDim, false>(
     883             :             make_not_null(&box), make_not_null(&inboxes))) {
     884             :       return {Parallel::AlgorithmExecution::Retry, std::nullopt};
     885             :     }
     886             : 
     887             :     db::mutate_apply<
     888             :         ApplyBoundaryCorrections<true, System, VolumeDim, DenseOutput>>(
     889             :         make_not_null(&box));
     890             :     return {Parallel::AlgorithmExecution::Continue, std::nullopt};
     891             :   }
     892             : };
     893             : }  // namespace Actions
     894             : }  // namespace evolution::dg

Generated by: LCOV version 1.14