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

Generated by: LCOV version 1.14