SpECTRE Documentation Coverage Report
Current view: top level - Elliptic/DiscontinuousGalerkin/Actions - ApplyOperator.hpp Hit Total Coverage
Commit: 2068747df712b64688243d3254666212942d85f2 Lines: 5 12 41.7 %
Date: 2026-05-22 23:35:16
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 <optional>
       8             : #include <string>
       9             : #include <tuple>
      10             : #include <type_traits>
      11             : #include <utility>
      12             : 
      13             : #include "DataStructures/DataBox/DataBox.hpp"
      14             : #include "DataStructures/DataBox/DataBoxTag.hpp"
      15             : #include "DataStructures/TaggedTuple.hpp"
      16             : #include "DataStructures/Tensor/EagerMath/Magnitude.hpp"
      17             : #include "Domain/Creators/Tags/ExternalBoundaryConditions.hpp"
      18             : #include "Domain/Creators/Tags/InitialExtents.hpp"
      19             : #include "Domain/FaceNormal.hpp"
      20             : #include "Domain/Structure/Direction.hpp"
      21             : #include "Domain/Structure/DirectionMap.hpp"
      22             : #include "Domain/Structure/DirectionalIdMap.hpp"
      23             : #include "Domain/Structure/Element.hpp"
      24             : #include "Domain/Tags.hpp"
      25             : #include "Domain/Tags/FaceNormal.hpp"
      26             : #include "Domain/Tags/Faces.hpp"
      27             : #include "Domain/Tags/SurfaceJacobian.hpp"
      28             : #include "Elliptic/BoundaryConditions/ApplyBoundaryCondition.hpp"
      29             : #include "Elliptic/DiscontinuousGalerkin/DgOperator.hpp"
      30             : #include "Elliptic/DiscontinuousGalerkin/Initialization.hpp"
      31             : #include "Elliptic/DiscontinuousGalerkin/Tags.hpp"
      32             : #include "Elliptic/Systems/GetFluxesComputer.hpp"
      33             : #include "Elliptic/Systems/GetModifyBoundaryData.hpp"
      34             : #include "Elliptic/Systems/GetSourcesComputer.hpp"
      35             : #include "Elliptic/Utilities/ApplyAt.hpp"
      36             : #include "NumericalAlgorithms/Convergence/Tags.hpp"
      37             : #include "NumericalAlgorithms/DiscontinuousGalerkin/HasReceivedFromAllMortars.hpp"
      38             : #include "NumericalAlgorithms/DiscontinuousGalerkin/MortarHelpers.hpp"
      39             : #include "NumericalAlgorithms/DiscontinuousGalerkin/Tags.hpp"
      40             : #include "NumericalAlgorithms/Spectral/Mesh.hpp"
      41             : #include "Parallel/AlgorithmExecution.hpp"
      42             : #include "Parallel/GlobalCache.hpp"
      43             : #include "Parallel/InboxInserters.hpp"
      44             : #include "Parallel/Invoke.hpp"
      45             : #include "ParallelAlgorithms/Amr/Projectors/CopyFromCreatorOrLeaveAsIs.hpp"
      46             : #include "ParallelAlgorithms/Amr/Projectors/DefaultInitialize.hpp"
      47             : #include "ParallelAlgorithms/Amr/Protocols/Projector.hpp"
      48             : #include "Utilities/ErrorHandling/Assert.hpp"
      49             : #include "Utilities/GetOutput.hpp"
      50             : #include "Utilities/Gsl.hpp"
      51             : #include "Utilities/Literals.hpp"
      52             : #include "Utilities/TMPL.hpp"
      53             : 
      54             : /// Actions related to elliptic discontinuous Galerkin schemes
      55           1 : namespace elliptic::dg::Actions {
      56             : // The individual actions in this namespace are not exposed publicly because
      57             : // they don't work on their own. Instead, the public interface (defined below)
      58             : // exposes them in action lists.
      59             : namespace detail {
      60             : 
      61             : // This is a global ID that identifies the DG operator application. It
      62             : // increments with each application of the operator.
      63             : struct DgOperatorLabel {};
      64             : using TemporalIdTag = Convergence::Tags::IterationId<DgOperatorLabel>;
      65             : 
      66             : // This tag is used to communicate mortar data across neighbors
      67             : template <size_t Dim, typename TemporalIdTag, typename PrimalFields,
      68             :           typename PrimalFluxes>
      69             : struct MortarDataInboxTag
      70             :     : public Parallel::InboxInserters::Map<
      71             :           MortarDataInboxTag<Dim, TemporalIdTag, PrimalFields, PrimalFluxes>> {
      72             :   using temporal_id = typename TemporalIdTag::type;
      73             :   using type =
      74             :       std::map<temporal_id,
      75             :                DirectionalIdMap<Dim, elliptic::dg::BoundaryData<PrimalFields,
      76             :                                                                 PrimalFluxes>>>;
      77             : };
      78             : 
      79             : // Initializes all quantities the DG operator needs on internal and external
      80             : // faces, as well as the mortars between neighboring elements. Also initializes
      81             : // the variable-independent background fields in the PDEs.
      82             : template <typename System, typename BackgroundTag>
      83             : struct InitializeFacesMortarsAndBackground {
      84             :  private:
      85             :   static constexpr size_t Dim = System::volume_dim;
      86             :   static constexpr bool has_background_fields =
      87             :       not std::is_same_v<typename System::background_fields, tmpl::list<>>;
      88             :   static_assert(
      89             :       not(has_background_fields and std::is_same_v<BackgroundTag, void>),
      90             :       "The system has background fields that need initialization. Supply a "
      91             :       "'BackgroundTag' to 'elliptic::dg::Actions::initialize_operator'.");
      92             : 
      93             :   using InitializeFacesAndMortars = elliptic::dg::InitializeFacesAndMortars<
      94             :       Dim, typename System::inv_metric_tag, BackgroundTag>;
      95             :   using InitializeBackground = elliptic::dg::InitializeBackground<
      96             :       Dim, typename System::background_fields, BackgroundTag>;
      97             : 
      98             :  public:
      99             :   using simple_tags = tmpl::append<
     100             :       typename InitializeFacesAndMortars::return_tags,
     101             :       tmpl::conditional_t<has_background_fields,
     102             :                           typename InitializeBackground::return_tags,
     103             :                           tmpl::list<>>>;
     104             :   using compute_tags = tmpl::list<>;
     105             :   using const_global_cache_tags =
     106             :       tmpl::conditional_t<has_background_fields, tmpl::list<BackgroundTag>,
     107             :                           tmpl::list<>>;
     108             : 
     109             :   template <typename DbTagsList, typename... InboxTags, typename Metavariables,
     110             :             typename ArrayIndex, typename ActionList,
     111             :             typename ParallelComponent>
     112             :   static Parallel::iterable_action_return_t apply(
     113             :       db::DataBox<DbTagsList>& box,
     114             :       const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,
     115             :       const Parallel::GlobalCache<Metavariables>& /*cache*/,
     116             :       const ArrayIndex& /*array_index*/, ActionList /*meta*/,
     117             :       const ParallelComponent* const /*meta*/) {
     118             :     // Initialize faces and mortars
     119             :     db::mutate_apply<InitializeFacesAndMortars>(make_not_null(&box));
     120             :     if constexpr (has_background_fields) {
     121             :       // Initialize background fields
     122             :       db::mutate_apply<InitializeBackground>(make_not_null(&box));
     123             :     }
     124             :     return {Parallel::AlgorithmExecution::Continue, std::nullopt};
     125             :   }
     126             : };
     127             : 
     128             : // Compute auxiliary variables and fluxes from the primal variables, prepare the
     129             : // local side of all mortars and send the local mortar data to neighbors. Also
     130             : // handle boundary conditions by preparing the exterior ("ghost") side of
     131             : // external mortars.
     132             : template <typename System, bool Linearized, typename TemporalIdTag,
     133             :           typename PrimalFieldsTag, typename PrimalFluxesTag,
     134             :           typename OperatorAppliedToFieldsTag, typename PrimalMortarFieldsTag,
     135             :           typename PrimalMortarFluxesTag,
     136             :           typename FluxesArgsTags =
     137             :               elliptic::get_fluxes_argument_tags<System, Linearized>,
     138             :           typename SourcesArgsTags =
     139             :               elliptic::get_sources_argument_tags<System, Linearized>>
     140             : struct PrepareAndSendMortarData;
     141             : 
     142             : template <typename System, bool Linearized, typename TemporalIdTag,
     143             :           typename PrimalFieldsTag, typename PrimalFluxesTag,
     144             :           typename OperatorAppliedToFieldsTag, typename PrimalMortarFieldsTag,
     145             :           typename PrimalMortarFluxesTag, typename... FluxesArgsTags,
     146             :           typename... SourcesArgsTags>
     147             : struct PrepareAndSendMortarData<
     148             :     System, Linearized, TemporalIdTag, PrimalFieldsTag, PrimalFluxesTag,
     149             :     OperatorAppliedToFieldsTag, PrimalMortarFieldsTag, PrimalMortarFluxesTag,
     150             :     tmpl::list<FluxesArgsTags...>, tmpl::list<SourcesArgsTags...>> {
     151             :  private:
     152             :   static constexpr size_t Dim = System::volume_dim;
     153             :   using all_mortar_data_tag = ::Tags::Mortars<
     154             :       elliptic::dg::Tags::MortarData<typename TemporalIdTag::type,
     155             :                                      typename PrimalMortarFieldsTag::tags_list,
     156             :                                      typename PrimalMortarFluxesTag::tags_list>,
     157             :       Dim>;
     158             :   using mortar_data_inbox_tag =
     159             :       MortarDataInboxTag<Dim, TemporalIdTag,
     160             :                          typename PrimalMortarFieldsTag::tags_list,
     161             :                          typename PrimalMortarFluxesTag::tags_list>;
     162             :   using BoundaryConditionsBase = typename System::boundary_conditions_base;
     163             :   using DerivFieldsTag = db::add_tag_prefix<::Tags::deriv, PrimalFieldsTag,
     164             :                                             tmpl::size_t<Dim>, Frame::Inertial>;
     165             : 
     166             :  public:
     167             :   // Request these tags be added to the DataBox. We
     168             :   // don't actually need to initialize them, because the
     169             :   // `PrimalFieldsTag` will be set by other actions before applying the operator
     170             :   // and the remaining tags hold output of the operator.
     171             :   using simple_tags = tmpl::list<TemporalIdTag, PrimalFieldsTag, DerivFieldsTag,
     172             :                                  PrimalFluxesTag, OperatorAppliedToFieldsTag,
     173             :                                  all_mortar_data_tag>;
     174             :   using compute_tags = tmpl::list<>;
     175             :   using const_global_cache_tags =
     176             :       tmpl::list<domain::Tags::ExternalBoundaryConditions<Dim>>;
     177             : 
     178             :   template <typename DbTagsList, typename... InboxTags, typename Metavariables,
     179             :             typename ActionList, typename ParallelComponent>
     180             :   static Parallel::iterable_action_return_t apply(
     181             :       db::DataBox<DbTagsList>& box,
     182             :       const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,
     183             :       Parallel::GlobalCache<Metavariables>& cache,
     184             :       const ElementId<Dim>& element_id, const ActionList /*meta*/,
     185             :       const ParallelComponent* const /*meta*/) {
     186             :     const auto& temporal_id = db::get<TemporalIdTag>(box);
     187             :     const auto& element = db::get<domain::Tags::Element<Dim>>(box);
     188             : 
     189             :     const auto& mortar_meshes =
     190             :         db::get<::Tags::Mortars<domain::Tags::Mesh<Dim - 1>, Dim>>(box);
     191             :     const auto& boundary_conditions =
     192             :         db::get<domain::Tags::ExternalBoundaryConditions<Dim>>(box).at(
     193             :             element_id.block_id());
     194             :     const auto apply_boundary_condition = [&box, &boundary_conditions,
     195             :                                            &element_id](
     196             :                                               const Direction<Dim>& direction,
     197             :                                               auto&&... fields_and_fluxes) {
     198             :       ASSERT(boundary_conditions.contains(direction),
     199             :              "No boundary condition is available in block "
     200             :                  << element_id.block_id() << " in direction " << direction
     201             :                  << ". Make sure you are setting up boundary conditions when "
     202             :                     "creating the domain.");
     203             :       ASSERT(dynamic_cast<const BoundaryConditionsBase*>(
     204             :                  boundary_conditions.at(direction).get()) != nullptr,
     205             :              "The boundary condition in block "
     206             :                  << element_id.block_id() << " in direction " << direction
     207             :                  << " has an unexpected type. Make sure it derives off the "
     208             :                     "'boundary_conditions_base' class set in the system.");
     209             :       const auto& boundary_condition =
     210             :           dynamic_cast<const BoundaryConditionsBase&>(
     211             :               *boundary_conditions.at(direction));
     212             :       elliptic::apply_boundary_condition<Linearized>(
     213             :           boundary_condition, box, direction,
     214             :           std::forward<decltype(fields_and_fluxes)>(fields_and_fluxes)...);
     215             :     };
     216             : 
     217             :     // Can't `db::get` the arguments for the boundary conditions within
     218             :     // `db::mutate`, so we extract the data to mutate and move it back in when
     219             :     // we're done.
     220             :     // Possible optimization: also keep memory for the mortar data around in the
     221             :     // DataBox. Currently the mortar data is extracted and erased by
     222             :     // `apply_operator` anyway, so we can just create a new map here to avoid
     223             :     // dealing with AMR resizing the mortars. When we keep the memory around,
     224             :     // its size has to be adjusted when the mesh changes during AMR.
     225             :     typename PrimalFluxesTag::type primal_fluxes;
     226             :     typename DerivFieldsTag::type deriv_fields;
     227             :     typename all_mortar_data_tag::type all_mortar_data{};
     228             :     db::mutate<PrimalFluxesTag, DerivFieldsTag>(
     229             :         [&primal_fluxes, &deriv_fields](const auto local_primal_fluxes,
     230             :                                         const auto local_deriv_fields) {
     231             :           primal_fluxes = std::move(*local_primal_fluxes);
     232             :           deriv_fields = std::move(*local_deriv_fields);
     233             :         },
     234             :         make_not_null(&box));
     235             : 
     236             :     elliptic::dg::prepare_mortar_data<System, Linearized>(
     237             :         make_not_null(&deriv_fields), make_not_null(&primal_fluxes),
     238             :         make_not_null(&all_mortar_data), db::get<PrimalFieldsTag>(box), element,
     239             :         db::get<domain::Tags::Mesh<Dim>>(box),
     240             :         db::get<domain::Tags::InverseJacobian<Dim, Frame::ElementLogical,
     241             :                                               Frame::Inertial>>(box),
     242             :         db::get<domain::Tags::Faces<Dim, domain::Tags::FaceNormal<Dim>>>(box),
     243             :         mortar_meshes,
     244             :         db::get<::Tags::Mortars<::Tags::MortarSize<Dim - 1>, Dim>>(box),
     245             :         temporal_id, apply_boundary_condition,
     246             :         std::forward_as_tuple(db::get<FluxesArgsTags>(box)...));
     247             : 
     248             :     // Move the mutated data back into the DataBox
     249             :     db::mutate<PrimalFluxesTag, DerivFieldsTag, all_mortar_data_tag>(
     250             :         [&primal_fluxes, &deriv_fields, &all_mortar_data](
     251             :             const auto local_primal_fluxes, const auto local_deriv_fields,
     252             :             const auto local_all_mortar_data) {
     253             :           *local_primal_fluxes = std::move(primal_fluxes);
     254             :           *local_deriv_fields = std::move(deriv_fields);
     255             :           *local_all_mortar_data = std::move(all_mortar_data);
     256             :         },
     257             :         make_not_null(&box));
     258             : 
     259             :     // Send mortar data to neighbors
     260             :     auto& receiver_proxy =
     261             :         Parallel::get_parallel_component<ParallelComponent>(cache);
     262             :     for (const auto& [direction, neighbors] : element.neighbors()) {
     263             :       const size_t dimension = direction.dimension();
     264             :       for (const auto& neighbor_id : neighbors) {
     265             :         const auto& orientation = neighbors.orientation(neighbor_id);
     266             :         const auto direction_from_neighbor = orientation(direction.opposite());
     267             :         const ::dg::MortarId<Dim> mortar_id{direction, neighbor_id};
     268             :         // Make a copy of the local boundary data on the mortar to send to the
     269             :         // neighbor
     270             :         auto remote_boundary_data_on_mortar =
     271             :             get<all_mortar_data_tag>(box).at(mortar_id).local_data(temporal_id);
     272             :         // Reorient the data to the neighbor orientation if necessary
     273             :         if (not orientation.is_aligned()) {
     274             :           remote_boundary_data_on_mortar.orient_on_slice(
     275             :               mortar_meshes.at(mortar_id).extents(), dimension, orientation);
     276             :         }
     277             :         // Send remote data to neighbor
     278             :         Parallel::receive_data<mortar_data_inbox_tag>(
     279             :             receiver_proxy[neighbor_id], temporal_id,
     280             :             std::make_pair(
     281             :                 ::dg::MortarId<Dim>{direction_from_neighbor, element.id()},
     282             :                 std::move(remote_boundary_data_on_mortar)));
     283             :       }  // loop over neighbors in direction
     284             :     }  // loop over directions
     285             : 
     286             :     return {Parallel::AlgorithmExecution::Continue, std::nullopt};
     287             :   }
     288             : };
     289             : 
     290             : // Wait until all mortar data from neighbors is available. Then add boundary
     291             : // corrections to the primal fluxes, compute their derivatives (i.e. the second
     292             : // derivatives of the primal variables) and add boundary corrections to the
     293             : // result.
     294             : template <typename System, bool Linearized, typename TemporalIdTag,
     295             :           typename PrimalFieldsTag, typename PrimalFluxesTag,
     296             :           typename OperatorAppliedToFieldsTag, typename PrimalMortarFieldsTag,
     297             :           typename PrimalMortarFluxesTag,
     298             :           typename FluxesArgsTags =
     299             :               elliptic::get_fluxes_argument_tags<System, Linearized>,
     300             :           typename SourcesArgsTags =
     301             :               elliptic::get_sources_argument_tags<System, Linearized>,
     302             :           typename ModifyBoundaryDataArgsTags =
     303             :               elliptic::get_modify_boundary_data_args_tags<System, Linearized>>
     304             : struct ReceiveMortarDataAndApplyOperator;
     305             : 
     306             : template <typename System, bool Linearized, typename TemporalIdTag,
     307             :           typename PrimalFieldsTag, typename PrimalFluxesTag,
     308             :           typename OperatorAppliedToFieldsTag, typename PrimalMortarFieldsTag,
     309             :           typename PrimalMortarFluxesTag, typename... FluxesArgsTags,
     310             :           typename... SourcesArgsTags, typename... ModifyBoundaryDataArgsTags>
     311             : struct ReceiveMortarDataAndApplyOperator<
     312             :     System, Linearized, TemporalIdTag, PrimalFieldsTag, PrimalFluxesTag,
     313             :     OperatorAppliedToFieldsTag, PrimalMortarFieldsTag, PrimalMortarFluxesTag,
     314             :     tmpl::list<FluxesArgsTags...>, tmpl::list<SourcesArgsTags...>,
     315             :     tmpl::list<ModifyBoundaryDataArgsTags...>> {
     316             :  private:
     317             :   static constexpr size_t Dim = System::volume_dim;
     318             :   using all_mortar_data_tag = ::Tags::Mortars<
     319             :       elliptic::dg::Tags::MortarData<typename TemporalIdTag::type,
     320             :                                      typename PrimalMortarFieldsTag::tags_list,
     321             :                                      typename PrimalMortarFluxesTag::tags_list>,
     322             :       Dim>;
     323             :   using mortar_data_inbox_tag =
     324             :       MortarDataInboxTag<Dim, TemporalIdTag,
     325             :                          typename PrimalMortarFieldsTag::tags_list,
     326             :                          typename PrimalMortarFluxesTag::tags_list>;
     327             :   using DerivFieldsTag = db::add_tag_prefix<::Tags::deriv, PrimalFieldsTag,
     328             :                                             tmpl::size_t<Dim>, Frame::Inertial>;
     329             :  public:
     330             :   using const_global_cache_tags =
     331             :       tmpl::list<elliptic::dg::Tags::PenaltyParameter,
     332             :                  elliptic::dg::Tags::Massive, elliptic::dg::Tags::Formulation>;
     333             :   using inbox_tags = tmpl::list<mortar_data_inbox_tag>;
     334             : 
     335             :   template <typename DbTags, typename... InboxTags, typename Metavariables,
     336             :             typename ArrayIndex, typename ActionList,
     337             :             typename ParallelComponent>
     338             :   static Parallel::iterable_action_return_t apply(
     339             :       db::DataBox<DbTags>& box, tuples::TaggedTuple<InboxTags...>& inboxes,
     340             :       const Parallel::GlobalCache<Metavariables>& /*cache*/,
     341             :       const ArrayIndex& /*array_index*/, const ActionList /*meta*/,
     342             :       const ParallelComponent* const /*meta*/) {
     343             :     const auto& temporal_id = get<TemporalIdTag>(box);
     344             :     const auto& element = get<domain::Tags::Element<Dim>>(box);
     345             : 
     346             :     if (not::dg::has_received_from_all_mortars<mortar_data_inbox_tag>(
     347             :             temporal_id, element, inboxes)) {
     348             :       return {Parallel::AlgorithmExecution::Retry, std::nullopt};
     349             :     }
     350             : 
     351             :     // Move received "remote" mortar data into the DataBox
     352             :     if (LIKELY(element.number_of_neighbors() > 0)) {
     353             :       auto received_mortar_data =
     354             :           std::move(tuples::get<mortar_data_inbox_tag>(inboxes)
     355             :                         .extract(temporal_id)
     356             :                         .mapped());
     357             :       db::mutate<all_mortar_data_tag>(
     358             :           [&received_mortar_data, &temporal_id](const auto all_mortar_data) {
     359             :             for (auto& [mortar_id, mortar_data] : received_mortar_data) {
     360             :               all_mortar_data->at(mortar_id).remote_insert(
     361             :                   temporal_id, std::move(mortar_data));
     362             :             }
     363             :           },
     364             :           make_not_null(&box));
     365             :     }
     366             : 
     367             :     // Used to retrieve items out of the DataBox to forward to functions
     368             :     const auto get_items = [](const auto&... args) {
     369             :       return std::forward_as_tuple(args...);
     370             :     };
     371             : 
     372             :     // Apply DG operator
     373             :     using fluxes_args_tags =
     374             :         typename elliptic::get_fluxes_argument_tags<System, Linearized>;
     375             :     using fluxes_args_volume_tags =
     376             :         typename elliptic::get_fluxes_volume_tags<System, Linearized>;
     377             :     DirectionMap<Dim, std::tuple<decltype(db::get<FluxesArgsTags>(box))...>>
     378             :         fluxes_args_on_faces{};
     379             :     for (const auto& direction : Direction<Dim>::all_directions()) {
     380             :       fluxes_args_on_faces.emplace(
     381             :           direction, elliptic::util::apply_at<
     382             :                          domain::make_faces_tags<Dim, fluxes_args_tags,
     383             :                                                  fluxes_args_volume_tags>,
     384             :                          fluxes_args_volume_tags>(get_items, box, direction));
     385             :     }
     386             :     db::mutate<OperatorAppliedToFieldsTag, all_mortar_data_tag>(
     387             :         [](const auto&... args) {
     388             :           elliptic::dg::apply_operator<System, Linearized>(args...);
     389             :         },
     390             :         make_not_null(&box), db::get<PrimalFieldsTag>(box),
     391             :         db::get<DerivFieldsTag>(box), db::get<PrimalFluxesTag>(box), element,
     392             :         db::get<domain::Tags::Mesh<Dim>>(box),
     393             :         db::get<domain::Tags::InverseJacobian<Dim, Frame::ElementLogical,
     394             :                                               Frame::Inertial>>(box),
     395             :         db::get<domain::Tags::DetInvJacobian<Frame::ElementLogical,
     396             :                                              Frame::Inertial>>(box),
     397             :         db::get<
     398             :             domain::Tags::DetJacobian<Frame::ElementLogical, Frame::Inertial>>(
     399             :             box),
     400             :         db::get<domain::Tags::DetTimesInvJacobian<Dim, Frame::ElementLogical,
     401             :                                                   Frame::Inertial>>(box),
     402             :         db::get<domain::Tags::Faces<Dim, domain::Tags::FaceNormal<Dim>>>(box),
     403             :         db::get<domain::Tags::Faces<Dim, domain::Tags::FaceNormalVector<Dim>>>(
     404             :             box),
     405             :         db::get<domain::Tags::Faces<
     406             :             Dim, domain::Tags::UnnormalizedFaceNormalMagnitude<Dim>>>(box),
     407             :         db::get<domain::Tags::Faces<
     408             :             Dim, domain::Tags::DetSurfaceJacobian<Frame::ElementLogical,
     409             :                                                   Frame::Inertial>>>(box),
     410             :         db::get<domain::Tags::Faces<
     411             :             Dim, domain::Tags::DetTimesInvJacobian<Dim, Frame::ElementLogical,
     412             :                                                    Frame::Inertial>>>(box),
     413             :         db::get<::Tags::Mortars<domain::Tags::Mesh<Dim - 1>, Dim>>(box),
     414             :         db::get<::Tags::Mortars<::Tags::MortarSize<Dim - 1>, Dim>>(box),
     415             :         db::get<::Tags::Mortars<domain::Tags::DetSurfaceJacobian<
     416             :                                     Frame::ElementLogical, Frame::Inertial>,
     417             :                                 Dim>>(box),
     418             :         db::get<::Tags::Mortars<elliptic::dg::Tags::PenaltyFactor, Dim>>(box),
     419             :         db::get<elliptic::dg::Tags::Massive>(box),
     420             :         db::get<elliptic::dg::Tags::Formulation>(box), temporal_id,
     421             :         fluxes_args_on_faces,
     422             :         std::forward_as_tuple(db::get<SourcesArgsTags>(box)...),
     423             :         std::forward_as_tuple(db::get<ModifyBoundaryDataArgsTags>(box)...));
     424             : 
     425             :     // Increment temporal ID
     426             :     db::mutate<TemporalIdTag>(
     427             :         [](const gsl::not_null<size_t*> stored_temporal_id) {
     428             :           ++(*stored_temporal_id);
     429             :         },
     430             :         make_not_null(&box));
     431             : 
     432             :     return {Parallel::AlgorithmExecution::Continue, std::nullopt};
     433             :   }
     434             : };
     435             : 
     436             : }  // namespace detail
     437             : 
     438             : /*!
     439             :  * \brief Initialize geometric and background quantities for the elliptic DG
     440             :  * operator
     441             :  *
     442             :  * The geometric and background quantities are initialized together because the
     443             :  * geometry depends on the background metric through the normalization of face
     444             :  * normals. Other examples for background fields are curvature quantities
     445             :  * associated with the background metric, or matter sources such as a
     446             :  * mass-density in the XCTS equations. All `System::background_fields` are
     447             :  * retrieved from the `BackgroundTag` together, to enable re-using cached
     448             :  * temporary quantities in the computations. The `variables` function is invoked
     449             :  * on the `BackgroundTag` with the inertial coordinates, the element's `Mesh`
     450             :  * and the element's inverse Jacobian. These arguments allow computing numeric
     451             :  * derivatives, if necessary. The `BackgroundTag` can be set to `void` (default)
     452             :  * if the `System` has no background fields.
     453             :  *
     454             :  * DataBox:
     455             :  * - Uses:
     456             :  *   - `domain::Tags::InitialExtents<Dim>`
     457             :  *   - `BackgroundTag`
     458             :  * - Adds:
     459             :  *   - `::Tags::Mortars<domain::Tags::Mesh<Dim - 1>, Dim>`
     460             :  *   - `::Tags::Mortars<::Tags::MortarSize<Dim - 1>, Dim>`
     461             :  *   - `::Tags::Variables<background_fields>`
     462             :  * - Adds on internal and external faces:
     463             :  *   - `domain::Tags::Coordinates<Dim, Frame::Inertial>`
     464             :  *   - `::Tags::Normalized<domain::Tags::UnnormalizedFaceNormal<Dim>>`
     465             :  *   - `::Tags::Magnitude<domain::Tags::UnnormalizedFaceNormal<Dim>>`
     466             :  *   - `::Tags::Variables<background_fields>`
     467             :  *
     468             :  * \see elliptic::dg::Actions::apply_operator
     469             :  */
     470             : template <typename System, typename BackgroundTag = void>
     471           1 : using initialize_operator = tmpl::list<
     472             :     detail::InitializeFacesMortarsAndBackground<System, BackgroundTag>>;
     473             : 
     474             : /*!
     475             :  * \brief AMR projectors for the tags added by `initialize_operator`
     476             :  */
     477             : template <typename System, typename BackgroundTag = void>
     478           1 : using amr_projectors = tmpl::append<
     479             :     tmpl::list<elliptic::dg::InitializeFacesAndMortars<
     480             :         System::volume_dim, typename System::inv_metric_tag, BackgroundTag>>,
     481             :     tmpl::conditional_t<
     482             :         std::is_same_v<typename System::background_fields, tmpl::list<>>,
     483             :         tmpl::list<>,
     484             :         tmpl::list<elliptic::dg::InitializeBackground<
     485             :             System::volume_dim, typename System::background_fields,
     486             :             BackgroundTag>>>>;
     487             : 
     488             : /*!
     489             :  * \brief Apply the DG operator to the `PrimalFieldsTag` and write the result to
     490             :  * the `OperatorAppliedToFieldsTag`
     491             :  *
     492             :  * Add the `apply_actions` list to the action list of a parallel component to
     493             :  * compute the elliptic DG operator or its linearization. The operator involves
     494             :  * a communication between nearest-neighbor elements. See `elliptic::dg` for
     495             :  * details on the elliptic DG operator. Make sure to add
     496             :  * `elliptic::dg::Actions::initialize_operator` to the initialization phase of
     497             :  * your parallel component so the required DataBox tags are set up before
     498             :  * applying the operator.
     499             :  *
     500             :  * The result of the computation is written to the `OperatorAppliedToFieldsTag`.
     501             :  * Additionally, the primal fluxes are written to the `PrimalFluxesTag` as an
     502             :  * intermediate result. The auxiliary fields and fluxes are discarded to avoid
     503             :  * inflating the memory usage.
     504             :  *
     505             :  * You can specify the `PrimalMortarFieldsTag` and the `PrimalMortarFluxesTag`
     506             :  * to re-use mortar-data memory buffers from other operator applications, for
     507             :  * example when applying the nonlinear and linearized operator. They default to
     508             :  * the `PrimalFieldsTag` and the `PrimalFluxesTag`, meaning memory buffers
     509             :  * corresponding to these tags are set up in the DataBox.
     510             :  *
     511             :  * \par AMR
     512             :  * Also add the `amr_projectors` to the list of AMR projectors to support AMR.
     513             :  */
     514             : template <typename System, bool Linearized, typename PrimalFieldsTag,
     515             :           typename PrimalFluxesTag, typename OperatorAppliedToFieldsTag,
     516             :           typename PrimalMortarFieldsTag = PrimalFieldsTag,
     517             :           typename PrimalMortarFluxesTag = PrimalFluxesTag>
     518           1 : struct DgOperator {
     519           0 :   using system = System;
     520           0 :   using temporal_id_tag = detail::TemporalIdTag;
     521             : 
     522             :  private:
     523           0 :   static constexpr size_t Dim = System::volume_dim;
     524           0 :   using DerivVarsTag = db::add_tag_prefix<::Tags::deriv, PrimalFieldsTag,
     525             :                                           tmpl::size_t<Dim>, Frame::Inertial>;
     526             :  public:
     527           0 :   using apply_actions =
     528             :       tmpl::list<detail::PrepareAndSendMortarData<
     529             :                      System, Linearized, temporal_id_tag, PrimalFieldsTag,
     530             :                      PrimalFluxesTag, OperatorAppliedToFieldsTag,
     531             :                      PrimalMortarFieldsTag, PrimalMortarFluxesTag>,
     532             :                  detail::ReceiveMortarDataAndApplyOperator<
     533             :                      System, Linearized, temporal_id_tag, PrimalFieldsTag,
     534             :                      PrimalFluxesTag, OperatorAppliedToFieldsTag,
     535             :                      PrimalMortarFieldsTag, PrimalMortarFluxesTag>>;
     536           0 :   using amr_projectors = tmpl::list<
     537             :       ::amr::projectors::DefaultInitialize<
     538             :           DerivVarsTag, PrimalFluxesTag, OperatorAppliedToFieldsTag,
     539             :           ::Tags::Mortars<elliptic::dg::Tags::MortarData<
     540             :                               typename temporal_id_tag::type,
     541             :                               typename PrimalMortarFieldsTag::tags_list,
     542             :                               typename PrimalMortarFluxesTag::tags_list>,
     543             :                           Dim>>,
     544             :       ::amr::projectors::CopyFromCreatorOrLeaveAsIs<temporal_id_tag>>;
     545             : };
     546             : 
     547             : /*!
     548             :  * \brief For linear systems, impose inhomogeneous boundary conditions as
     549             :  * contributions to the fixed sources (i.e. the RHS of the equations).
     550             :  *
     551             :  * \see elliptic::dg::impose_inhomogeneous_boundary_conditions_on_source
     552             :  */
     553             : template <typename System, typename FixedSourcesTag,
     554             :           typename FluxesArgsTags =
     555             :               elliptic::get_fluxes_argument_tags<System, false>,
     556             :           typename SourcesArgsTags =
     557             :               elliptic::get_sources_argument_tags<System, false>,
     558             :           typename ModifyBoundaryDataArgsTags =
     559             :               elliptic::get_modify_boundary_data_args_tags<System, false>>
     560           1 : struct ImposeInhomogeneousBoundaryConditionsOnSource;
     561             : 
     562             : /// \cond
     563             : template <typename System, typename FixedSourcesTag, typename... FluxesArgsTags,
     564             :           typename... SourcesArgsTags, typename... ModifyBoundaryDataArgsTags>
     565             : struct ImposeInhomogeneousBoundaryConditionsOnSource<
     566             :     System, FixedSourcesTag, tmpl::list<FluxesArgsTags...>,
     567             :     tmpl::list<SourcesArgsTags...>, tmpl::list<ModifyBoundaryDataArgsTags...>>
     568             :     : tt::ConformsTo<::amr::protocols::Projector> {
     569             :  private:
     570             :   static constexpr size_t Dim = System::volume_dim;
     571             :   using BoundaryConditionsBase = typename System::boundary_conditions_base;
     572             : 
     573             :  public:  // DataBox mutator, amr::protocols::Projector
     574             :   using const_global_cache_tags =
     575             :       tmpl::list<elliptic::dg::Tags::PenaltyParameter,
     576             :                  elliptic::dg::Tags::Massive, elliptic::dg::Tags::Formulation,
     577             :                  domain::Tags::ExternalBoundaryConditions<Dim>>;
     578             : 
     579             :   using return_tags = tmpl::list<::Tags::DataBox>;
     580             :   using argument_tags = tmpl::list<>;
     581             : 
     582             :   template <typename DbTagsList, typename... AmrData>
     583             :   static void apply(const gsl::not_null<db::DataBox<DbTagsList>*> box_ptr,
     584             :                     const AmrData&... /*amr_data*/) {
     585             :     // Just to get the same semantics as in actions
     586             :     auto& box = *box_ptr;
     587             :     const auto& element_id = db::get<domain::Tags::Element<Dim>>(box).id();
     588             :     // Used to retrieve items out of the DataBox to forward to functions
     589             :     const auto get_items = [](const auto&... args) {
     590             :       return std::forward_as_tuple(args...);
     591             :     };
     592             :     const auto& boundary_conditions =
     593             :         db::get<domain::Tags::ExternalBoundaryConditions<Dim>>(box).at(
     594             :             element_id.block_id());
     595             :     const auto apply_boundary_condition = [&box, &boundary_conditions,
     596             :                                            &element_id](
     597             :                                               const Direction<Dim>& direction,
     598             :                                               auto&&... fields_and_fluxes) {
     599             :       ASSERT(boundary_conditions.contains(direction),
     600             :              "No boundary condition is available in block "
     601             :                  << element_id.block_id() << " in direction " << direction
     602             :                  << ". Make sure you are setting up boundary conditions when "
     603             :                     "creating the domain.");
     604             :       ASSERT(dynamic_cast<const BoundaryConditionsBase*>(
     605             :                  boundary_conditions.at(direction).get()) != nullptr,
     606             :              "The boundary condition in block "
     607             :                  << element_id.block_id() << " in direction " << direction
     608             :                  << " has an unexpected type. Make sure it derives off the "
     609             :                     "'boundary_conditions_base' class set in the system.");
     610             :       const auto& boundary_condition =
     611             :           dynamic_cast<const BoundaryConditionsBase&>(
     612             :               *boundary_conditions.at(direction));
     613             :       elliptic::apply_boundary_condition<false>(
     614             :           boundary_condition, box, direction,
     615             :           std::forward<decltype(fields_and_fluxes)>(fields_and_fluxes)...);
     616             :     };
     617             : 
     618             :     // Can't `db::get` the arguments for the boundary conditions within
     619             :     // `db::mutate`, so we extract the data to mutate and move it back in when
     620             :     // we're done.
     621             :     typename FixedSourcesTag::type fixed_sources;
     622             :     db::mutate<FixedSourcesTag>(
     623             :         [&fixed_sources](const auto local_fixed_sources) {
     624             :           fixed_sources = std::move(*local_fixed_sources);
     625             :         },
     626             :         make_not_null(&box));
     627             : 
     628             :     using fluxes_args_tags =
     629             :         typename elliptic::get_fluxes_argument_tags<System, false>;
     630             :     using fluxes_args_volume_tags =
     631             :         elliptic::get_fluxes_volume_tags<System, false>;
     632             :     DirectionMap<Dim, std::tuple<decltype(db::get<FluxesArgsTags>(box))...>>
     633             :         fluxes_args_on_faces{};
     634             :     for (const auto& direction : Direction<Dim>::all_directions()) {
     635             :       fluxes_args_on_faces.emplace(
     636             :           direction, elliptic::util::apply_at<
     637             :                          domain::make_faces_tags<Dim, fluxes_args_tags,
     638             :                                                  fluxes_args_volume_tags>,
     639             :                          fluxes_args_volume_tags>(get_items, box, direction));
     640             :     }
     641             : 
     642             :     elliptic::dg::impose_inhomogeneous_boundary_conditions_on_source<System>(
     643             :         make_not_null(&fixed_sources), db::get<domain::Tags::Element<Dim>>(box),
     644             :         db::get<domain::Tags::Mesh<Dim>>(box),
     645             :         db::get<domain::Tags::InverseJacobian<Dim, Frame::ElementLogical,
     646             :                                               Frame::Inertial>>(box),
     647             :         db::get<domain::Tags::DetInvJacobian<Frame::ElementLogical,
     648             :                                              Frame::Inertial>>(box),
     649             :         db::get<
     650             :             domain::Tags::DetJacobian<Frame::ElementLogical, Frame::Inertial>>(
     651             :             box),
     652             :         db::get<domain::Tags::DetTimesInvJacobian<Dim, Frame::ElementLogical,
     653             :                                                   Frame::Inertial>>(box),
     654             :         db::get<domain::Tags::Faces<Dim, domain::Tags::FaceNormal<Dim>>>(box),
     655             :         db::get<domain::Tags::Faces<Dim, domain::Tags::FaceNormalVector<Dim>>>(
     656             :             box),
     657             :         db::get<domain::Tags::Faces<
     658             :             Dim, domain::Tags::UnnormalizedFaceNormalMagnitude<Dim>>>(box),
     659             :         db::get<domain::Tags::Faces<
     660             :             Dim, domain::Tags::DetSurfaceJacobian<Frame::ElementLogical,
     661             :                                                   Frame::Inertial>>>(box),
     662             :         db::get<domain::Tags::Faces<
     663             :             Dim, domain::Tags::DetTimesInvJacobian<Dim, Frame::ElementLogical,
     664             :                                                    Frame::Inertial>>>(box),
     665             :         db::get<::Tags::Mortars<domain::Tags::Mesh<Dim - 1>, Dim>>(box),
     666             :         db::get<::Tags::Mortars<::Tags::MortarSize<Dim - 1>, Dim>>(box),
     667             :         db::get<::Tags::Mortars<domain::Tags::DetSurfaceJacobian<
     668             :                                     Frame::ElementLogical, Frame::Inertial>,
     669             :                                 Dim>>(box),
     670             :         db::get<::Tags::Mortars<elliptic::dg::Tags::PenaltyFactor, Dim>>(box),
     671             :         db::get<elliptic::dg::Tags::Massive>(box),
     672             :         db::get<elliptic::dg::Tags::Formulation>(box), apply_boundary_condition,
     673             :         std::forward_as_tuple(db::get<FluxesArgsTags>(box)...),
     674             :         std::forward_as_tuple(db::get<SourcesArgsTags>(box)...),
     675             :         fluxes_args_on_faces,
     676             :         std::forward_as_tuple(db::get<ModifyBoundaryDataArgsTags>(box)...));
     677             : 
     678             :     // Move the mutated data back into the DataBox
     679             :     db::mutate<FixedSourcesTag>(
     680             :         [&fixed_sources](const auto local_fixed_sources) {
     681             :           *local_fixed_sources = std::move(fixed_sources);
     682             :         },
     683             :         make_not_null(&box));
     684             :   }
     685             : };
     686             : /// \endcond
     687             : 
     688             : }  // namespace elliptic::dg::Actions

Generated by: LCOV version 1.14