SpECTRE Documentation Coverage Report
Current view: top level - Evolution/DgSubcell/Actions - ReconstructionCommunication.hpp Hit Total Coverage
Commit: fbcce2ed065a8e48da2f38009a84bbfbc0c260ee Lines: 3 9 33.3 %
Date: 2025-11-14 20:55:50
Legend: Lines: hit not hit

          Line data    Source code
       1           0 : // Distributed under the MIT License.
       2             : // See LICENSE.txt for details.
       3             : 
       4             : #pragma once
       5             : 
       6             : #include <algorithm>
       7             : #include <array>
       8             : #include <cstddef>
       9             : #include <iterator>
      10             : #include <limits>
      11             : #include <map>
      12             : #include <optional>
      13             : #include <tuple>
      14             : #include <unordered_set>
      15             : #include <utility>
      16             : 
      17             : #include "DataStructures/DataBox/DataBox.hpp"
      18             : #include "DataStructures/DataBox/Prefixes.hpp"
      19             : #include "DataStructures/DataVector.hpp"
      20             : #include "DataStructures/Index.hpp"
      21             : #include "DataStructures/Tensor/Tensor.hpp"
      22             : #include "DataStructures/Variables.hpp"
      23             : #include "DataStructures/VariablesTag.hpp"
      24             : #include "Domain/Structure/Direction.hpp"
      25             : #include "Domain/Structure/DirectionalId.hpp"
      26             : #include "Domain/Structure/DirectionalIdMap.hpp"
      27             : #include "Domain/Structure/Element.hpp"
      28             : #include "Domain/Structure/ElementId.hpp"
      29             : #include "Domain/Structure/OrientationMapHelpers.hpp"
      30             : #include "Domain/Structure/TrimMap.hpp"
      31             : #include "Domain/Tags.hpp"
      32             : #include "Domain/Tags/NeighborMesh.hpp"
      33             : #include "Evolution/DgSubcell/ActiveGrid.hpp"
      34             : #include "Evolution/DgSubcell/CombineVolumeGhostData.hpp"
      35             : #include "Evolution/DgSubcell/GhostData.hpp"
      36             : #include "Evolution/DgSubcell/NeighborRdmpAndVolumeData.hpp"
      37             : #include "Evolution/DgSubcell/Projection.hpp"
      38             : #include "Evolution/DgSubcell/RdmpTci.hpp"
      39             : #include "Evolution/DgSubcell/RdmpTciData.hpp"
      40             : #include "Evolution/DgSubcell/SliceData.hpp"
      41             : #include "Evolution/DgSubcell/SubcellOptions.hpp"
      42             : #include "Evolution/DgSubcell/Tags/ActiveGrid.hpp"
      43             : #include "Evolution/DgSubcell/Tags/CellCenteredFlux.hpp"
      44             : #include "Evolution/DgSubcell/Tags/DataForRdmpTci.hpp"
      45             : #include "Evolution/DgSubcell/Tags/GhostDataForReconstruction.hpp"
      46             : #include "Evolution/DgSubcell/Tags/Interpolators.hpp"
      47             : #include "Evolution/DgSubcell/Tags/Mesh.hpp"
      48             : #include "Evolution/DgSubcell/Tags/MeshForGhostData.hpp"
      49             : #include "Evolution/DgSubcell/Tags/SubcellOptions.hpp"
      50             : #include "Evolution/DgSubcell/Tags/TciStatus.hpp"
      51             : #include "Evolution/DiscontinuousGalerkin/BoundaryData.hpp"
      52             : #include "Evolution/DiscontinuousGalerkin/InboxTags.hpp"
      53             : #include "Evolution/DiscontinuousGalerkin/MortarData.hpp"
      54             : #include "Evolution/DiscontinuousGalerkin/MortarDataHolder.hpp"
      55             : #include "Evolution/DiscontinuousGalerkin/MortarTags.hpp"
      56             : #include "NumericalAlgorithms/Interpolation/IrregularInterpolant.hpp"
      57             : #include "NumericalAlgorithms/Spectral/Mesh.hpp"
      58             : #include "Parallel/AlgorithmExecution.hpp"
      59             : #include "Parallel/ArrayCollection/IsDgElementCollection.hpp"
      60             : #include "Parallel/GlobalCache.hpp"
      61             : #include "Time/Tags/HistoryEvolvedVariables.hpp"
      62             : #include "Time/TimeStepId.hpp"
      63             : #include "Utilities/ErrorHandling/Assert.hpp"
      64             : #include "Utilities/Gsl.hpp"
      65             : #include "Utilities/Literals.hpp"
      66             : #include "Utilities/MakeArray.hpp"
      67             : #include "Utilities/TMPL.hpp"
      68             : 
      69             : /// \cond
      70             : namespace Tags {
      71             : struct TimeStepId;
      72             : }  // namespace Tags
      73             : /// \endcond
      74             : 
      75             : namespace evolution::dg::subcell::Actions {
      76             : /*!
      77             :  * \brief Sets the local data from the relaxed discrete maximum principle
      78             :  * troubled-cell indicator and sends ghost zone data to neighboring elements.
      79             :  *
      80             :  * The action proceeds as follows:
      81             :  *
      82             :  * 1. Determine in which directions we have neighbors
      83             :  * 2. Slice the variables provided by GhostDataMutator to send to our neighbors
      84             :  *    for ghost zones
      85             :  * 3. Send the ghost zone data, appending the max/min for the TCI at the end of
      86             :  *    the `DataVector` we are sending.
      87             :  *
      88             :  * \warning This assumes the RDMP TCI data in the DataBox has been set, it does
      89             :  * not calculate it automatically. The reason is this way we can only calculate
      90             :  * the RDMP data when it's needed since computing it can be pretty expensive.
      91             :  *
      92             :  * Some notes:
      93             :  * - In the future we will need to send the cell-centered fluxes to do
      94             :  *   high-order FD without additional reconstruction being necessary.
      95             :  *
      96             :  * GlobalCache:
      97             :  * - Uses:
      98             :  *   - `ParallelComponent` proxy
      99             :  *
     100             :  * DataBox:
     101             :  * - Uses:
     102             :  *   - `domain::Tags::Mesh<Dim>`
     103             :  *   - `subcell::Tags::Mesh<Dim>`
     104             :  *   - `domain::Tags::Element<Dim>`
     105             :  *   - `Tags::TimeStepId`
     106             :  *   - `Tags::Next<Tags::TimeStepId>`
     107             :  *   - `subcell::Tags::ActiveGrid`
     108             :  *   - `System::variables_tag`
     109             :  *   - `subcell::Tags::DataForRdmpTci`
     110             :  * - Adds: nothing
     111             :  * - Removes: nothing
     112             :  * - Modifies:
     113             :  *   - `subcell::Tags::GhostDataForReconstruction<Dim>`
     114             :  */
     115             : template <size_t Dim, typename GhostDataMutator, bool LocalTimeStepping,
     116             :           bool UseNodegroupDgElements>
     117           1 : struct SendDataForReconstruction {
     118           0 :   using inbox_tags =
     119             :       tmpl::list<evolution::dg::Tags::BoundaryCorrectionAndGhostCellsInbox<
     120             :           Dim, UseNodegroupDgElements>>;
     121             : 
     122             :   template <typename DbTags, typename... InboxTags, typename ArrayIndex,
     123             :             typename ActionList, typename ParallelComponent,
     124             :             typename Metavariables>
     125           0 :   static Parallel::iterable_action_return_t apply(
     126             :       db::DataBox<DbTags>& box, tuples::TaggedTuple<InboxTags...>& /*inboxes*/,
     127             :       Parallel::GlobalCache<Metavariables>& cache,
     128             :       const ArrayIndex& /*array_index*/, const ActionList /*meta*/,
     129             :       const ParallelComponent* const /*meta*/) {
     130             :     static_assert(
     131             :         not LocalTimeStepping,
     132             :         "DG-subcell does not yet support local time stepping. The "
     133             :         "reconstruction data must be sent using dense output sometimes, and "
     134             :         "not at all other times. However, the data for the RDMP TCI should be "
     135             :         "sent along with the data for reconstruction each time.");
     136             :     static_assert(UseNodegroupDgElements ==
     137             :                       Parallel::is_dg_element_collection_v<ParallelComponent>,
     138             :                   "The action SendDataForReconstruction is told by the "
     139             :                   "template parameter UseNodegroupDgElements that it is being "
     140             :                   "used with a DgElementCollection, but the ParallelComponent "
     141             :                   "is not a DgElementCollection. You need to change the "
     142             :                   "template parameter on the SendDataForReconstruction action "
     143             :                   "in your action list.");
     144             : 
     145             :     ASSERT(db::get<Tags::ActiveGrid>(box) == ActiveGrid::Subcell,
     146             :            "The SendDataForReconstruction action can only be called when "
     147             :            "Subcell is the active scheme.");
     148             :     using flux_variables = typename Metavariables::system::flux_variables;
     149             : 
     150             :     db::mutate<Tags::GhostDataForReconstruction<Dim>>(
     151             :         [](const auto ghost_data_ptr) {
     152             :           // Clear the previous neighbor data and add current local data
     153             :           ghost_data_ptr->clear();
     154             :         },
     155             :         make_not_null(&box));
     156             : 
     157             :     const Mesh<Dim>& dg_mesh = db::get<::domain::Tags::Mesh<Dim>>(box);
     158             :     const Mesh<Dim>& subcell_mesh = db::get<Tags::Mesh<Dim>>(box);
     159             :     const Element<Dim>& element = db::get<::domain::Tags::Element<Dim>>(box);
     160             :     const size_t ghost_zone_size =
     161             :         Metavariables::SubcellOptions::ghost_zone_size(box);
     162             : 
     163             :     // Optimization note: could save a copy+allocation if we moved
     164             :     // all_sliced_data when possible before sending.
     165             :     //
     166             :     // Note: RDMP size doesn't help here since we need to slice data after
     167             :     // anyway, so no way to save an allocation through that.
     168             :     const auto& cell_centered_flux =
     169             :         db::get<Tags::CellCenteredFlux<flux_variables, Dim>>(box);
     170             :     DataVector volume_data_to_slice = db::mutate_apply(
     171             :         GhostDataMutator{}, make_not_null(&box),
     172             :         cell_centered_flux.has_value() ? cell_centered_flux.value().size()
     173             :                                        : 0_st);
     174             :     if (cell_centered_flux.has_value()) {
     175             :       std::copy(
     176             :           cell_centered_flux.value().data(),
     177             :           std::next(
     178             :               cell_centered_flux.value().data(),
     179             :               static_cast<std::ptrdiff_t>(cell_centered_flux.value().size())),
     180             :           std::next(
     181             :               volume_data_to_slice.data(),
     182             :               static_cast<std::ptrdiff_t>(volume_data_to_slice.size() -
     183             :                                           cell_centered_flux.value().size())));
     184             :     }
     185             : 
     186             :     // When using enable_extension_directions, we send the ghost data
     187             :     // for problematic directions separately with the new action
     188             :     // ReceiveAndSendDataForReconstruction, so we only slice the data
     189             :     // for non-problematic directions here. (see the documentation of
     190             :     // ReceiveAndSendDataForReconstruction for what "problematic" means)
     191             :     const auto& extension_directions =
     192             :         db::get<evolution::dg::subcell::Tags::ExtensionDirections<Dim>>(box);
     193             :     std::unordered_set<Direction<Dim>> directions_to_work;
     194             :     for (const auto& internal_direction : element.internal_boundaries()) {
     195             :       if (extension_directions.contains(internal_direction)) {
     196             :         continue;
     197             :       } else {
     198             :         directions_to_work.insert(internal_direction);
     199             :       }
     200             :     }
     201             : 
     202             :     const DirectionMap<Dim, DataVector> all_sliced_data = slice_data(
     203             :         volume_data_to_slice, subcell_mesh.extents(), ghost_zone_size,
     204             :         directions_to_work, 0,
     205             :         db::get<
     206             :             evolution::dg::subcell::Tags::InterpolatorsFromFdToNeighborFd<Dim>>(
     207             :             box));
     208             : 
     209             :     auto& receiver_proxy =
     210             :         Parallel::get_parallel_component<ParallelComponent>(cache);
     211             :     const RdmpTciData& rdmp_tci_data = db::get<Tags::DataForRdmpTci>(box);
     212             :     const TimeStepId& time_step_id = db::get<::Tags::TimeStepId>(box);
     213             :     const TimeStepId& next_time_step_id =
     214             :         db::get<::Tags::Next<::Tags::TimeStepId>>(box);
     215             : 
     216             :     const int tci_decision =
     217             :         db::get<evolution::dg::subcell::Tags::TciDecision>(box);
     218             :     using history_tags = ::Tags::get_all_history_tags<DbTags>;
     219             :     static_assert(tmpl::size<history_tags>::value == 1);
     220             :     const auto& integration_order =
     221             :         db::get<tmpl::front<history_tags>>(box).integration_order();
     222             :     // Compute and send actual variables
     223             :     for (const auto& [direction, neighbors_in_direction] :
     224             :          element.neighbors()) {
     225             :       // Only need to send data for directions that are not
     226             :       // problematic directions (keys of extension_directions).
     227             :       if (not extension_directions.contains(direction)) {
     228             :         ASSERT(neighbors_in_direction.size() == 1,
     229             :                "AMR is not yet supported when using DG-subcell. Note that this "
     230             :                "condition could be relaxed to support AMR only where the "
     231             :                "evolution is using DG without any changes to subcell.");
     232             : 
     233             :         for (const ElementId<Dim>& neighbor : neighbors_in_direction) {
     234             :           const auto& orientation =
     235             :               neighbors_in_direction.orientation(neighbor);
     236             :           const auto direction_from_neighbor =
     237             :               orientation(direction.opposite());
     238             :           const size_t rdmp_size = rdmp_tci_data.max_variables_values.size() +
     239             :                                    rdmp_tci_data.min_variables_values.size();
     240             :           const auto& sliced_data_in_direction = all_sliced_data.at(direction);
     241             : 
     242             :           // Allocate with subcell data and rdmp data
     243             :           DataVector subcell_data_to_send{sliced_data_in_direction.size() +
     244             :                                           rdmp_size};
     245             :           // Note: Currently we interpolate our solution to our neighbor FD grid
     246             :           // even when grid points align but are oriented differently. There's a
     247             :           // possible optimization for the rare (almost never?) edge case where
     248             :           // two blocks have the same ghost zone coordinates but have different
     249             :           // orientations (e.g. RotatedBricks). Since this shouldn't ever happen
     250             :           // outside of tests, we currently don't bother with it. If we wanted
     251             :           // to, here's the code:
     252             :           //
     253             :           // if (not orientation.is_aligned()) {
     254             :           //   std::array<size_t, Dim> slice_extents{};
     255             :           //   for (size_t d = 0; d < Dim; ++d) {
     256             :           //     gsl::at(slice_extents, d) = subcell_mesh.extents(d);
     257             :           //   }
     258             :           //   gsl::at(slice_extents, direction.dimension()) = ghost_zone_size;
     259             :           //   // Need a view so we only get the subcell data and not the rdmp
     260             :           //   // data
     261             :           //   DataVector subcell_data_to_send_view{
     262             :           //       subcell_data_to_send.data(),
     263             :           //       subcell_data_to_send.size() - rdmp_size};
     264             :           //   orient_variables(make_not_null(&subcell_data_to_send_view),
     265             :           //                  sliced_data_in_direction,
     266             :           //                  Index<Dim>{slice_extents}, orientation);
     267             :           // } else { std::copy(...); }
     268             :           //
     269             : 
     270             :           // Copy over data since it's already oriented from interpolation
     271             :           std::copy(sliced_data_in_direction.begin(),
     272             :                     sliced_data_in_direction.end(),
     273             :                     subcell_data_to_send.begin());
     274             :           // Copy rdmp data to end of subcell_data_to_send
     275             :           std::copy(rdmp_tci_data.max_variables_values.cbegin(),
     276             :                     rdmp_tci_data.max_variables_values.cend(),
     277             :                     std::prev(subcell_data_to_send.end(),
     278             :                               static_cast<int>(rdmp_size)));
     279             :           std::copy(rdmp_tci_data.min_variables_values.cbegin(),
     280             :                     rdmp_tci_data.min_variables_values.cend(),
     281             :                     std::prev(subcell_data_to_send.end(),
     282             :                               static_cast<int>(
     283             :                                   rdmp_tci_data.min_variables_values.size())));
     284             : 
     285             :           evolution::dg::BoundaryData<Dim> data{
     286             :               dg_mesh,      subcell_mesh,
     287             :               std::nullopt, std::move(subcell_data_to_send),
     288             :               std::nullopt, next_time_step_id,
     289             :               tci_decision, integration_order};
     290             : 
     291             :           Parallel::receive_data<
     292             :               evolution::dg::Tags::BoundaryCorrectionAndGhostCellsInbox<
     293             :                   Dim,
     294             :                   Parallel::is_dg_element_collection_v<ParallelComponent>>>(
     295             :               receiver_proxy[neighbor], time_step_id,
     296             :               std::pair{
     297             :                   DirectionalId<Dim>{direction_from_neighbor, element.id()},
     298             :                   std::move(data)});
     299             :         }
     300             :       }
     301             :     }
     302             :     return {Parallel::AlgorithmExecution::Continue, std::nullopt};
     303             :   }
     304             : };
     305             : 
     306             : /*!
     307             :  * \brief Handles ghost data at block boundaries to ensure interpolation
     308             :  * is (almost) always used instead of extrapolation.
     309             :  *
     310             :  * Since the coordinate maps are only continuous and not smooth at block
     311             :  * boundaries, the logical coordinate axes, and therefore grid point axes,
     312             :  * do not necessarily align. An example is given in the image below, which is
     313             :  * a snapshot of 2d circular domain built from one central square surrounded
     314             :  * by four deformed wedges. We zoom in on the upper right corner of the
     315             :  * cube for illustration purposes.
     316             :  *
     317             :  * \image html curved_mesh_illustration.png width=600px
     318             :  *
     319             :  * Blue circles denote the cell-centered FD points in the two elements whose
     320             :  * ghost points are being exchanged, bright red diamonds denote the ghost
     321             :  * points needed for reconstruction in the element on the right, and dark red
     322             :  * squares denote the cell-centered FD points in a neighboring element not
     323             :  * directly participating in the exchange. The dashed blue and dash-dotted
     324             :  * red lines show lines of constant logical coordinates in the left and
     325             :  * right elements, respectively. Notice that they intersect on the boundary,
     326             :  * but do not align.
     327             :  *
     328             :  * In this example, three lowest ghost points cannot be directly
     329             :  * interpolated from the element on the left. In such a case,
     330             :  * we flag the direction (from the perspective of the element on the left,
     331             :  * in this example, the direction the green arrow points to) as
     332             :  * problematic and the ghost points may be filled either
     333             :  * by extrapolation or interpolation. Extrapolation can lead to an unphysical
     334             :  * state like negative densities, so interpolation is generally preferred.
     335             :  * To enable interpolation, set `EnableExtensionDirections` to true in
     336             :  * `SubcellOptions` part of the input file. This enables the use of
     337             :  * ghost data from the neighboring element in the extension direction
     338             :  * (in this example, the direction the grey arrow points to) to fill the
     339             :  * ghost points.
     340             :  *
     341             :  * \warning This option is only available in the case where we are only
     342             :  * using FD scheme for the evolution, i.e. we are using true for
     343             :  * `AlwaysUseSubcell` in the `SubcellOptions` part of the input file.
     344             :  * Currently, the following cases are not supported:
     345             :  * 1. There are multiple neighbors in a direction
     346             :  * 2. There are multiple extension directions required for a
     347             :  *    single problematic direction.
     348             :  * 3. The extension direction is itself a problematic direction, which can
     349             :  *    result in a deadlock.
     350             :  *
     351             :  * When disabled, this action does nothing.
     352             :  *
     353             :  * When enabled, the action:
     354             :  * 1. Receives subcell ghost data from neighbors for all non-problematic
     355             :  *    directions.
     356             :  * 2. For “problematic directions” it extends the element’s volume data
     357             :  *    using ghost data from another neighbor (in the "extension direction").
     358             :  *    This extension ensures that the ghost data can be filled using
     359             :  *    interpolation.
     360             :  * 3. Interpolates to the requested ghost points, and then sends the completed
     361             :  *    ghost data to the original neighbor.
     362             :  */
     363             : template <size_t Dim, typename GhostDataMutator, bool UseNodegroupDgElements>
     364           1 : struct ReceiveAndSendDataForReconstruction {
     365           0 :   using inbox_tags =
     366             :       tmpl::list<evolution::dg::Tags::BoundaryCorrectionAndGhostCellsInbox<
     367             :           Dim, UseNodegroupDgElements>>;
     368             :   template <typename DbTags, typename... InboxTags, typename ArrayIndex,
     369             :             typename ActionList, typename ParallelComponent,
     370             :             typename Metavariables>
     371           0 :   static Parallel::iterable_action_return_t apply(
     372             :       db::DataBox<DbTags>& box, tuples::TaggedTuple<InboxTags...>& inboxes,
     373             :       Parallel::GlobalCache<Metavariables>& cache,
     374             :       const ArrayIndex& /*array_index*/, const ActionList /*meta*/,
     375             :       const ParallelComponent* const /*meta*/) {
     376             :     if (not db::get<evolution::dg::subcell::Tags::SubcellOptions<Dim>>(box)
     377             :                 .enable_extension_directions()) {
     378             :       // We are not using extension directions, so just continue without doing
     379             :       // any work.
     380             :       return {Parallel::AlgorithmExecution::Continue, std::nullopt};
     381             :     }
     382             : 
     383             :     const auto& extension_directions =
     384             :         db::get<evolution::dg::subcell::Tags::ExtensionDirections<Dim>>(box);
     385             :     if (extension_directions.empty()) {
     386             :       // For this element, we have no extension directions, so just
     387             :       // continue without doing any work.
     388             :       return {Parallel::AlgorithmExecution::Continue, std::nullopt};
     389             :     }
     390             : 
     391             :     const Element<Dim>& element = db::get<::domain::Tags::Element<Dim>>(box);
     392             :     // Need to subtract number of problematic directions.
     393             :     const auto number_of_expected_messages =
     394             :         element.neighbors().size() - extension_directions.size();
     395             : 
     396             :     if (UNLIKELY(number_of_expected_messages == 0)) {
     397             :       // We have no neighbors, so just continue without doing any work.
     398             :       // Technically, this could also happen if all of the neighbors are in
     399             :       // problematic directions, but this case is unlikely to ever happen.
     400             :       return {Parallel::AlgorithmExecution::Continue, std::nullopt};
     401             :     }
     402             : 
     403             :     std::unordered_set<DirectionalId<Dim>> expected_keys;
     404             :     for (const auto& internal_direction : element.internal_boundaries()) {
     405             :       if (not extension_directions.contains(internal_direction)) {
     406             :         // Note here, we assume that we have only one neighbor per
     407             :         // direction, so we can just take the first one.
     408             :         ASSERT(element.neighbors().at(internal_direction).ids().size() == 1,
     409             :                "Assumption one neighbor per direction failed. "
     410             :                "direction: "
     411             :                    << internal_direction << ", neighbors.size() = "
     412             :                    << element.neighbors().at(internal_direction).ids().size()
     413             :                    << ", element: " << element.id());
     414             :         expected_keys.emplace(
     415             :             internal_direction,
     416             :             *element.neighbors().at(internal_direction).ids().begin());
     417             :       }
     418             :     }
     419             : 
     420             :     const auto& interpolants = db::get<
     421             :         evolution::dg::subcell::Tags::InterpolatorsFromFdToNeighborFd<Dim>>(
     422             :         box);
     423             : 
     424             :     const auto& current_time_step_id = db::get<::Tags::TimeStepId>(box);
     425             :     std::map<TimeStepId,
     426             :              DirectionalIdMap<Dim, evolution::dg::BoundaryData<Dim>>>& inbox =
     427             :         tuples::get<evolution::dg::Tags::BoundaryCorrectionAndGhostCellsInbox<
     428             :             Metavariables::volume_dim,
     429             :             Parallel::is_dg_element_collection_v<ParallelComponent>>>(inboxes);
     430             :     const auto& received = inbox.find(current_time_step_id);
     431             :     // Check we have at least some data from correct time, and then check
     432             :     // we have received all data
     433             :     if (received == inbox.end() or
     434             :         not std::all_of(expected_keys.begin(), expected_keys.end(),
     435             :                         [&received](const auto& key) {
     436             :                           return received->second.find(key) !=
     437             :                                  received->second.end();
     438             :                         })) {
     439             :       return {Parallel::AlgorithmExecution::Retry, std::nullopt};
     440             :     }
     441             : 
     442             :     const size_t ghost_zone_size =
     443             :         Metavariables::SubcellOptions::ghost_zone_size(box);
     444             :     const Mesh<Dim>& dg_mesh = db::get<::domain::Tags::Mesh<Dim>>(box);
     445             :     const Mesh<Dim>& subcell_mesh = db::get<Tags::Mesh<Dim>>(box);
     446             :     const Index<Dim>& subcell_extents = subcell_mesh.extents();
     447             : 
     448             :     const auto& received_data = received->second;
     449             :     ASSERT(received_data.size() >= number_of_expected_messages,
     450             :            "received_data size: " << received_data.size()
     451             :                                   << " less than expected number of messages: "
     452             :                                   << number_of_expected_messages << " !");
     453             : 
     454             :     using flux_variables = typename Metavariables::system::flux_variables;
     455             :     const auto& cell_centered_flux =
     456             :         db::get<Tags::CellCenteredFlux<flux_variables, Dim>>(box);
     457             :     DataVector volume_data_to_slice = db::mutate_apply(
     458             :         GhostDataMutator{}, make_not_null(&box),
     459             :         cell_centered_flux.has_value() ? cell_centered_flux.value().size()
     460             :                                        : 0_st);
     461             :     if (cell_centered_flux.has_value()) {
     462             :       std::copy(
     463             :           cell_centered_flux.value().data(),
     464             :           std::next(
     465             :               cell_centered_flux.value().data(),
     466             :               static_cast<std::ptrdiff_t>(cell_centered_flux.value().size())),
     467             :           std::next(
     468             :               volume_data_to_slice.data(),
     469             :               static_cast<std::ptrdiff_t>(volume_data_to_slice.size() -
     470             :                                           cell_centered_flux.value().size())));
     471             :     }
     472             : 
     473             :     auto& receiver_proxy =
     474             :         Parallel::get_parallel_component<ParallelComponent>(cache);
     475             :     const RdmpTciData& rdmp_tci_data = db::get<Tags::DataForRdmpTci>(box);
     476             :     const TimeStepId& time_step_id = db::get<::Tags::TimeStepId>(box);
     477             :     const TimeStepId& next_time_step_id =
     478             :         db::get<::Tags::Next<::Tags::TimeStepId>>(box);
     479             : 
     480             :     const int tci_decision =
     481             :         db::get<evolution::dg::subcell::Tags::TciDecision>(box);
     482             :     using history_tags = ::Tags::get_all_history_tags<DbTags>;
     483             :     static_assert(tmpl::size<history_tags>::value == 1);
     484             :     const auto& integration_order =
     485             :         db::get<tmpl::front<history_tags>>(box).integration_order();
     486             : 
     487             :     const size_t number_of_points = subcell_mesh.extents().product();
     488             :     // Number of independent components per grid point.
     489             :     const size_t number_of_components =
     490             :         volume_data_to_slice.size() / number_of_points;
     491             : 
     492             :     // For each problematic direction (a direction for which the element
     493             :     // cannot directly provide ghost data):
     494             :     // 1. Receive ghost data from our neighbor in the direction specified by
     495             :     // extension_direction.direction_to_extend.
     496             :     // 2. Use this data to extend our own volume data, then interpolate to the
     497             :     // required ghost points.
     498             :     // 3. Send the final ghost data to the neighbor at problematic_direction.
     499             :     for (const auto& [problematic_direction, extension_direction] :
     500             :          extension_directions) {
     501             :       // Direction to extend the volume data.
     502             :       const Direction<Dim> direction_to_extend =
     503             :           extension_direction.direction_to_extend;
     504             : 
     505             :       // Check that direction_to_extend is not a problematic direction.
     506             :       ASSERT(problematic_direction != direction_to_extend,
     507             :              "The direction to extend must not be a problematic direction. "
     508             :              "problematic_direction: "
     509             :                  << problematic_direction
     510             :                  << ", direction_to_extend: " << direction_to_extend);
     511             : 
     512             :       // Only one neighbor per direction.
     513             :       ASSERT(element.neighbors().at(direction_to_extend).ids().size() == 1,
     514             :              "Assumption one neighbor per direction failed. "
     515             :              "direction_to_extend: "
     516             :                  << direction_to_extend << ", neighbors.size() = "
     517             :                  << element.neighbors().at(direction_to_extend).ids().size()
     518             :                  << ", element: " << element.id());
     519             : 
     520             :       // Only internal boundary for the extension.
     521             :       ASSERT(element.internal_boundaries().contains(direction_to_extend),
     522             :              "Direction to extend not an internal boundary! "
     523             :              "dir_to_extend = "
     524             :                  << direction_to_extend << ", element.internal_boundaries() = "
     525             :                  << element.internal_boundaries()
     526             :                  << ", element = " << element.id());
     527             : 
     528             :       // Again, we are assuming that we have only one neighbor per direction,
     529             :       // and we are just taking the first one.
     530             :       const ElementId<Dim> relevant_neighbor_id =
     531             :           *((element.neighbors()).at(direction_to_extend).ids().begin());
     532             : 
     533             :       // Received data must have entry for direction to extend.
     534             :       ASSERT((received_data.contains(DirectionalId<Dim>{direction_to_extend,
     535             :                                                         relevant_neighbor_id})),
     536             :              "Received data missing entry for direction to extend."
     537             :                  << " direction_to_extend = " << direction_to_extend
     538             :                  << ", relevant_neighbor_id = " << relevant_neighbor_id
     539             :                  << ", problematic_direction = " << problematic_direction
     540             :                  << ", element = " << element.id());
     541             : 
     542             :       // Received data must have received ghost data for this extension
     543             :       // direction.
     544             :       ASSERT(((received_data.at(DirectionalId<Dim>{direction_to_extend,
     545             :                                                    relevant_neighbor_id}))
     546             :                   .ghost_cell_data.has_value()),
     547             :              "Ghost data missing for this extension direction."
     548             :                  << " direction_to_extend = " << direction_to_extend
     549             :                  << ", relevant_neighbor_id = " << relevant_neighbor_id
     550             :                  << ", problematic_direction = " << problematic_direction
     551             :                  << ", element = " << element.id());
     552             : 
     553             :       const ElementId<Dim> problematic_neighbor_id =
     554             :           *((element.neighbors()).at(problematic_direction).ids().begin());
     555             : 
     556             :       const auto& orientation = (element.neighbors())
     557             :                                     .at(problematic_direction)
     558             :                                     .orientation(problematic_neighbor_id);
     559             :       const auto direction_from_neighbor =
     560             :           orientation(problematic_direction.opposite());
     561             : 
     562             :       const size_t rdmp_size = rdmp_tci_data.max_variables_values.size() +
     563             :                                rdmp_tci_data.min_variables_values.size();
     564             : 
     565             :       const DataVector& full_ghost_cell_data =
     566             :           received_data
     567             :               .at(DirectionalId<Dim>{direction_to_extend, relevant_neighbor_id})
     568             :               .ghost_cell_data.value();
     569             :       const size_t relevant_ghost_data_size =
     570             :           full_ghost_cell_data.size() - rdmp_size;
     571             : 
     572             :       const DataVector relevant_ghost_data;
     573             :       make_const_view(make_not_null(&relevant_ghost_data), full_ghost_cell_data,
     574             :                       0, relevant_ghost_data_size);
     575             : 
     576             :       const auto& interpolant =
     577             :           (interpolants.at(DirectionalId<Dim>{problematic_direction,
     578             :                                               problematic_neighbor_id}))
     579             :               .value();
     580             :       const DataVector combined_data = combine_volume_ghost_data(
     581             :           volume_data_to_slice, relevant_ghost_data, subcell_extents,
     582             :           ghost_zone_size, direction_to_extend);
     583             :       const size_t result_size =
     584             :           ghost_zone_size * subcell_mesh.extents()
     585             :                                 .slice_away(problematic_direction.dimension())
     586             :                                 .product();
     587             :       const size_t span_size = result_size * number_of_components;
     588             : 
     589             :       DataVector subcell_data_to_send{span_size + rdmp_size};
     590             : 
     591             :       auto result_span = gsl::make_span(subcell_data_to_send.data(), span_size);
     592             :       interpolant.interpolate(
     593             :           make_not_null(&result_span),
     594             :           gsl::make_span(combined_data.data(), combined_data.size()));
     595             : 
     596             :       // Copy rdmp data to end of subcell_data_to_send
     597             :       std::copy(
     598             :           rdmp_tci_data.max_variables_values.cbegin(),
     599             :           rdmp_tci_data.max_variables_values.cend(),
     600             :           std::prev(subcell_data_to_send.end(), static_cast<int>(rdmp_size)));
     601             :       std::copy(rdmp_tci_data.min_variables_values.cbegin(),
     602             :                 rdmp_tci_data.min_variables_values.cend(),
     603             :                 std::prev(subcell_data_to_send.end(),
     604             :                           static_cast<int>(
     605             :                               rdmp_tci_data.min_variables_values.size())));
     606             :       evolution::dg::BoundaryData<Dim> data{
     607             :           dg_mesh,      subcell_mesh,
     608             :           std::nullopt, std::move(subcell_data_to_send),
     609             :           std::nullopt, next_time_step_id,
     610             :           tci_decision, integration_order};
     611             :       Parallel::receive_data<
     612             :           evolution::dg::Tags::BoundaryCorrectionAndGhostCellsInbox<
     613             :               Dim, Parallel::is_dg_element_collection_v<ParallelComponent>>>(
     614             :           receiver_proxy[problematic_neighbor_id], time_step_id,
     615             :           std::pair{DirectionalId<Dim>{direction_from_neighbor, element.id()},
     616             :                     std::move(data)});
     617             :     }
     618             :     return {Parallel::AlgorithmExecution::Continue, std::nullopt};
     619             :   }
     620             : };
     621             : /*!
     622             :  * \brief Receive the subcell data from our neighbor, and accumulate the data
     623             :  * from the relaxed discrete maximum principle troubled-cell indicator.
     624             :  *
     625             :  * Note:
     626             :  * - Since we only care about the min/max over all neighbors and ourself at the
     627             :  *   past time, we accumulate all data immediately into the `RdmpTciData`.
     628             :  * - If the neighbor is using DG and therefore sends boundary correction data
     629             :  *   then that is added into the `evolution::dg::Tags::MortarData` tag
     630             :  * - The next `TimeStepId` is recorded, but we do not yet support local time
     631             :  *   stepping.
     632             :  * - This action will never care about what variables are sent for
     633             :  *   reconstruction. It is only responsible for receiving the data and storing
     634             :  *   it in the `NeighborData`.
     635             :  *
     636             :  * GlobalCache:
     637             :  * -Uses: nothing
     638             :  *
     639             :  * DataBox:
     640             :  * - Uses:
     641             :  *   - `domain::Tags::Element<Dim>`
     642             :  *   - `Tags::TimeStepId`
     643             :  *   - `domain::Tags::Mesh<Dim>`
     644             :  *   - `subcell::Tags::Mesh<Dim>`
     645             :  *   - `domain::Tags::Element<Dim>`
     646             :  *   - `Tags::Next<Tags::TimeStepId>`
     647             :  *   - `subcell::Tags::ActiveGrid`
     648             :  *   - `System::variables_tag`
     649             :  * - Adds: nothing
     650             :  * - Removes: nothing
     651             :  * - Modifies:
     652             :  *   - `subcell::Tags::GhostDataForReconstruction<Dim>`
     653             :  *   - `subcell::Tags::DataForRdmpTci`
     654             :  *   - `evolution::dg::Tags::MortarData`
     655             :  *   - `evolution::dg::Tags::MortarNextTemporalId`
     656             :  */
     657             : template <size_t Dim>
     658           1 : struct ReceiveDataForReconstruction {
     659             :   template <typename DbTags, typename... InboxTags, typename ArrayIndex,
     660             :             typename ActionList, typename ParallelComponent,
     661             :             typename Metavariables>
     662           0 :   static Parallel::iterable_action_return_t apply(
     663             :       db::DataBox<DbTags>& box, tuples::TaggedTuple<InboxTags...>& inboxes,
     664             :       const Parallel::GlobalCache<Metavariables>& /*cache*/,
     665             :       const ArrayIndex& /*array_index*/, const ActionList /*meta*/,
     666             :       const ParallelComponent* const /*meta*/) {
     667             :     const Element<Dim>& element = db::get<::domain::Tags::Element<Dim>>(box);
     668             :     const auto number_of_expected_messages = element.neighbors().size();
     669             :     if (UNLIKELY(number_of_expected_messages == 0)) {
     670             :       // We have no neighbors, so just continue without doing any work
     671             :       return {Parallel::AlgorithmExecution::Continue, std::nullopt};
     672             :     }
     673             : 
     674             :     using ::operator<<;
     675             :     const auto& current_time_step_id = db::get<::Tags::TimeStepId>(box);
     676             :     std::map<TimeStepId,
     677             :              DirectionalIdMap<Dim, evolution::dg::BoundaryData<Dim>>>& inbox =
     678             :         tuples::get<evolution::dg::Tags::BoundaryCorrectionAndGhostCellsInbox<
     679             :             Metavariables::volume_dim,
     680             :             Parallel::is_dg_element_collection_v<ParallelComponent>>>(inboxes);
     681             :     const auto& received = inbox.find(current_time_step_id);
     682             :     // Check we have at least some data from correct time, and then check that
     683             :     // we have received all data
     684             :     if (received == inbox.end() or
     685             :         received->second.size() != number_of_expected_messages) {
     686             :       return {Parallel::AlgorithmExecution::Retry, std::nullopt};
     687             :     }
     688             : 
     689             :     // Now that we have received all the data, copy it over as needed.
     690             :     DirectionalIdMap<Dim, evolution::dg::BoundaryData<Dim>> received_data =
     691             :         std::move(inbox[current_time_step_id]);
     692             :     inbox.erase(current_time_step_id);
     693             : 
     694             :     const Mesh<Dim>& subcell_mesh = db::get<Tags::Mesh<Dim>>(box);
     695             :     const auto& mortar_meshes = get<evolution::dg::Tags::MortarMesh<Dim>>(box);
     696             : 
     697             :     db::mutate<Tags::GhostDataForReconstruction<Dim>, Tags::DataForRdmpTci,
     698             :                evolution::dg::Tags::MortarData<Dim>,
     699             :                evolution::dg::Tags::MortarNextTemporalId<Dim>,
     700             :                domain::Tags::NeighborMesh<Dim>,
     701             :                evolution::dg::subcell::Tags::MeshForGhostData<Dim>,
     702             :                evolution::dg::subcell::Tags::NeighborTciDecisions<Dim>>(
     703             :         [&element,
     704             :          ghost_zone_size = Metavariables::SubcellOptions::ghost_zone_size(box),
     705             :          &received_data, &subcell_mesh, &mortar_meshes](
     706             :             const gsl::not_null<DirectionalIdMap<Dim, GhostData>*>
     707             :                 ghost_data_ptr,
     708             :             const gsl::not_null<RdmpTciData*> rdmp_tci_data_ptr,
     709             :             const gsl::not_null<
     710             :                 DirectionalIdMap<Dim, evolution::dg::MortarDataHolder<Dim>>*>
     711             :                 mortar_data,
     712             :             const gsl::not_null<DirectionalIdMap<Dim, TimeStepId>*>
     713             :                 mortar_next_time_step_id,
     714             :             const gsl::not_null<DirectionalIdMap<Dim, Mesh<Dim>>*>
     715             :                 neighbor_mesh,
     716             :             const gsl::not_null<DirectionalIdMap<Dim, Mesh<Dim>>*>
     717             :                 mesh_for_ghost_data,
     718             :             const auto neighbor_tci_decisions,
     719             :             const DirectionalIdMap<Dim, std::optional<intrp::Irregular<Dim>>>&
     720             :                 neighbor_dg_to_fd_interpolants) {
     721             :           // Remove neighbor meshes for neighbors that don't exist anymore
     722             :           domain::remove_nonexistent_neighbors(neighbor_mesh, element);
     723             :           domain::remove_nonexistent_neighbors(mesh_for_ghost_data, element);
     724             : 
     725             :           // Get the next time step id, and also the fluxes data if the neighbor
     726             :           // is doing DG.
     727             :           for (auto& received_mortar_data : received_data) {
     728             :             const auto& mortar_id = received_mortar_data.first;
     729             :             try {
     730             :               mortar_next_time_step_id->at(mortar_id) =
     731             :                   received_mortar_data.second.validity_range;
     732             :             } catch (std::exception& e) {
     733             :               ERROR("Failed retrieving the MortarId: ("
     734             :                     << mortar_id.direction() << ',' << mortar_id.id()
     735             :                     << ") from the mortar_next_time_step_id. Got exception: "
     736             :                     << e.what());
     737             :             }
     738             :             if (received_mortar_data.second.boundary_correction_data
     739             :                     .has_value()) {
     740             :               mortar_data->at(mortar_id).neighbor().face_mesh =
     741             :                   received_mortar_data.second.volume_mesh.slice_away(
     742             :                       mortar_id.direction().dimension());
     743             :               mortar_data->at(mortar_id).neighbor().mortar_mesh =
     744             :                   mortar_meshes.at(mortar_id);
     745             :               mortar_data->at(mortar_id).neighbor().mortar_data = std::move(
     746             :                   *received_mortar_data.second.boundary_correction_data);
     747             :             }
     748             :             // Set new neighbor mesh
     749             :             neighbor_mesh->insert_or_assign(
     750             :                 mortar_id, received_mortar_data.second.volume_mesh);
     751             :             mesh_for_ghost_data->insert_or_assign(
     752             :                 mortar_id, received_mortar_data.second
     753             :                                .volume_mesh_ghost_cell_data.value());
     754             :           }
     755             : 
     756             :           ASSERT(ghost_data_ptr->empty(),
     757             :                  "Should have no elements in the neighbor data when "
     758             :                  "receiving neighbor data");
     759             :           const size_t number_of_rdmp_vars =
     760             :               rdmp_tci_data_ptr->max_variables_values.size();
     761             :           ASSERT(rdmp_tci_data_ptr->min_variables_values.size() ==
     762             :                      number_of_rdmp_vars,
     763             :                  "The number of RDMP variables for which we have a maximum "
     764             :                  "and minimum should be the same, but we have "
     765             :                      << number_of_rdmp_vars << " for the max and "
     766             :                      << rdmp_tci_data_ptr->min_variables_values.size()
     767             :                      << " for the min.");
     768             : 
     769             :           for (const auto& [direction, neighbors_in_direction] :
     770             :                element.neighbors()) {
     771             :             for (const auto& neighbor : neighbors_in_direction) {
     772             :               DirectionalId<Dim> directional_element_id{direction, neighbor};
     773             :               ASSERT(ghost_data_ptr->count(directional_element_id) == 0,
     774             :                      "Found neighbor already inserted in direction "
     775             :                          << direction << " with ElementId " << neighbor);
     776             :               ASSERT(received_data[directional_element_id]
     777             :                          .ghost_cell_data.has_value(),
     778             :                      "Received subcell data message that does not contain any "
     779             :                      "actual subcell data for reconstruction.");
     780             :               // Collect the max/min of u(t^n) for the RDMP as we receive data.
     781             :               // This reduces the memory footprint.
     782             : 
     783             :               evolution::dg::subcell::insert_neighbor_rdmp_and_volume_data(
     784             :                   rdmp_tci_data_ptr, ghost_data_ptr,
     785             :                   *received_data[directional_element_id].ghost_cell_data,
     786             :                   number_of_rdmp_vars, directional_element_id,
     787             :                   mesh_for_ghost_data->at(directional_element_id), element,
     788             :                   subcell_mesh, ghost_zone_size,
     789             :                   neighbor_dg_to_fd_interpolants);
     790             :               ASSERT(neighbor_tci_decisions->contains(directional_element_id),
     791             :                      "The NeighorTciDecisions should contain the neighbor ("
     792             :                          << directional_element_id.direction() << ", "
     793             :                          << directional_element_id.id() << ") but doesn't");
     794             :               neighbor_tci_decisions->at(directional_element_id) =
     795             :                   received_data[directional_element_id].tci_status;
     796             :             }
     797             :           }
     798             :         },
     799             :         make_not_null(&box),
     800             :         db::get<
     801             :             evolution::dg::subcell::Tags::InterpolatorsFromNeighborDgToFd<Dim>>(
     802             :             box));
     803             :     return {Parallel::AlgorithmExecution::Continue, std::nullopt};
     804             :   }
     805             : };
     806             : }  // namespace evolution::dg::subcell::Actions

Generated by: LCOV version 1.14