SpECTRE Documentation Coverage Report
Current view: top level - Evolution/DgSubcell - PrepareNeighborData.hpp Coverage Total Hit
Commit: a37d7b3c1803b88b537d2aef1c27ca5e6b499398 Lines: 50.0 % 2 1
Test Date: 2026-11-12 04:47:19
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 <array>
       7              : #include <cstddef>
       8              : #include <optional>
       9              : #include <unordered_set>
      10              : #include <utility>
      11              : #include <vector>
      12              : 
      13              : #include "DataStructures/DataBox/DataBox.hpp"
      14              : #include "DataStructures/DataBox/PrefixHelpers.hpp"
      15              : #include "DataStructures/DataBox/Prefixes.hpp"
      16              : #include "DataStructures/DataVector.hpp"
      17              : #include "DataStructures/Index.hpp"
      18              : #include "Domain/Structure/Direction.hpp"
      19              : #include "Domain/Structure/DirectionMap.hpp"
      20              : #include "Domain/Structure/Element.hpp"
      21              : #include "Domain/Structure/ElementId.hpp"
      22              : #include "Domain/Structure/OrientationMapHelpers.hpp"
      23              : #include "Domain/Tags.hpp"
      24              : #include "Domain/Tags/NeighborMesh.hpp"
      25              : #include "Evolution/DgSubcell/Mesh.hpp"
      26              : #include "Evolution/DgSubcell/Projection.hpp"
      27              : #include "Evolution/DgSubcell/RdmpTci.hpp"
      28              : #include "Evolution/DgSubcell/RdmpTciData.hpp"
      29              : #include "Evolution/DgSubcell/SliceData.hpp"
      30              : #include "Evolution/DgSubcell/SubcellOptions.hpp"
      31              : #include "Evolution/DgSubcell/Tags/DataForRdmpTci.hpp"
      32              : #include "Evolution/DgSubcell/Tags/Interpolators.hpp"
      33              : #include "Evolution/DgSubcell/Tags/Mesh.hpp"
      34              : #include "Evolution/DgSubcell/Tags/SubcellOptions.hpp"
      35              : #include "Evolution/DiscontinuousGalerkin/OnlyDgBlockIds.hpp"
      36              : #include "NumericalAlgorithms/Spectral/Mesh.hpp"
      37              : #include "Utilities/Algorithm.hpp"
      38              : #include "Utilities/ErrorHandling/Assert.hpp"
      39              : #include "Utilities/Gsl.hpp"
      40              : #include "Utilities/TMPL.hpp"
      41              : 
      42              : namespace evolution::dg::subcell {
      43              : /*!
      44              :  * \brief Add local data for our and our neighbor's relaxed discrete maximum
      45              :  * principle troubled-cell indicator, and for reconstruction.
      46              :  *
      47              :  * The local maximum and minimum of the evolved variables is added to
      48              :  * `Tags::DataForRdmpTci` for the RDMP TCI. Then the
      49              :  * data needed by neighbor elements to do reconstruction on the FD grid is sent.
      50              :  * The data to be sent is computed in the mutator
      51              :  * `Metavariables::SubcellOptions::GhostVariables`, which returns a
      52              :  * `Variables` of the tensors to send to the neighbors. The main reason for
      53              :  * having the mutator `GhostVariables` is to allow sending primitive or
      54              :  * characteristic variables for reconstruction.
      55              :  *
      56              :  * \note If all neighbors are using DG (i.e. none of their meshes is an FD mesh)
      57              :  * then we send our DG volume data _without_ orienting it. This elides the
      58              :  * expense of projection and slicing. If any
      59              :  * neighbors are doing FD, we project and slice to all neighbors. A future
      60              :  * optimization would be to measure the cost of slicing data, and figure out how
      61              :  * many neighbors need to be doing FD before it's worth projecting and slicing
      62              :  * vs. just projecting to the ghost cells. Another optimization is to always
      63              :  * send DG volume data to neighbors that we think are doing DG, so that we can
      64              :  * elide any slicing or projection cost in that direction.
      65              :  *
      66              :  * No data is prepared for neighbors that can never use subcell. If our own
      67              :  * block is DG-only (see `evolution::dg::Tags::OnlyDgBlockIds`), all of our
      68              :  * neighbors border a DG-only block and so never use subcell. In this case
      69              :  * data is never prepared and `all_neighbor_data_for_reconstruction` is left
      70              :  * empty. Otherwise, we skip the directions in which all neighbors are in
      71              :  * DG-only blocks, meaing we prepare only for directions in which there are
      72              :  * neighbors that could use subcell (this check currently still sends extra
      73              :  * data to DG-only elements buffering subcell and DG-only blocks as
      74              :  * `OnlyDgBlockIds` does not have element granulaity).
      75              :  */
      76              : template <typename Metavariables, typename DbTagsList, size_t Dim>
      77            1 : void prepare_neighbor_data(
      78              :     const gsl::not_null<DirectionMap<Dim, DataVector>*>
      79              :         all_neighbor_data_for_reconstruction,
      80              :     const gsl::not_null<std::optional<Mesh<Dim>>*> ghost_data_mesh,
      81              :     const gsl::not_null<db::DataBox<DbTagsList>*> box,
      82              :     [[maybe_unused]] const Variables<db::wrap_tags_in<
      83              :         ::Tags::Flux, typename Metavariables::system::flux_variables,
      84              :         tmpl::size_t<Dim>, Frame::Inertial>>& volume_fluxes) {
      85              :   const Mesh<Dim>& dg_mesh = db::get<::domain::Tags::Mesh<Dim>>(*box);
      86              :   const Mesh<Dim>& subcell_mesh =
      87              :       db::get<::evolution::dg::subcell::Tags::Mesh<Dim>>(*box);
      88              :   const auto& element = db::get<::domain::Tags::Element<Dim>>(*box);
      89              :   const std::vector<size_t>& only_dg_block_ids =
      90              :       db::get<evolution::dg::Tags::OnlyDgBlockIds<Dim>>(*box);
      91              : 
      92              :   all_neighbor_data_for_reconstruction->clear();
      93              :   *ghost_data_mesh = dg_mesh;
      94              :   // Ghost data is only used to fill the ghost cells of neighbors doing FD, and
      95              :   // elements in or bordering a DG-only block never use subcell (see
      96              :   // `subcell::Actions::Initialize`). So if our block is DG-only none of our
      97              :   // neighbors needs ghost data, and otherwise we skip the directions in which
      98              :   // all neighbors are in DG-only blocks.
      99              :   if (alg::found(only_dg_block_ids, element.id().block_id())) {
     100              :     return;
     101              :   }
     102              :   std::unordered_set<Direction<Dim>> directions_to_slice{};
     103              :   for (const auto& [direction, neighbors] : element.neighbors()) {
     104              :     if (not alg::all_of(neighbors.ids(), [&only_dg_block_ids](
     105              :                                              const ElementId<Dim>& neighbor) {
     106              :           return alg::found(only_dg_block_ids, neighbor.block_id());
     107              :         })) {
     108              :       directions_to_slice.insert(direction);
     109              :     }
     110              :   }
     111              :   if (directions_to_slice.empty()) {
     112              :     return;
     113              :   }
     114              : 
     115              :   const auto& neighbor_meshes = db::get<domain::Tags::NeighborMesh<Dim>>(*box);
     116              :   const subcell::RdmpTciData& rdmp_data =
     117              :       db::get<subcell::Tags::DataForRdmpTci>(*box);
     118              :   const ::fd::DerivativeOrder fd_derivative_order =
     119              :       get<evolution::dg::subcell::Tags::SubcellOptions<Dim>>(*box)
     120              :           .finite_difference_derivative_order();
     121              :   const size_t rdmp_size = rdmp_data.max_variables_values.size() +
     122              :                            rdmp_data.min_variables_values.size();
     123              :   const size_t extra_size_for_ghost_data =
     124              :       (fd_derivative_order == ::fd::DerivativeOrder::Two
     125              :            ? 0
     126              :            : volume_fluxes.size());
     127              : 
     128              :   if (DataVector ghost_variables =
     129              :           [&box, &extra_size_for_ghost_data, &rdmp_size, &volume_fluxes]() {
     130              :             if (extra_size_for_ghost_data == 0) {
     131              :               return db::mutate_apply(
     132              :                   typename Metavariables::SubcellOptions::GhostVariables{}, box,
     133              :                   rdmp_size);
     134              :             } else {
     135              :               DataVector ghost_vars = db::mutate_apply(
     136              :                   typename Metavariables::SubcellOptions::GhostVariables{}, box,
     137              :                   rdmp_size + extra_size_for_ghost_data);
     138              :               std::copy(
     139              :                   volume_fluxes.data(),
     140              :                   std::next(volume_fluxes.data(),
     141              :                             static_cast<std::ptrdiff_t>(volume_fluxes.size())),
     142              :                   std::prev(ghost_vars.end(),
     143              :                             static_cast<std::ptrdiff_t>(
     144              :                                 rdmp_size + extra_size_for_ghost_data)));
     145              :               return ghost_vars;
     146              :             }
     147              :           }();
     148              :       (not fd::dg_mesh_supports_subcell(dg_mesh) or
     149              :        alg::all_of(neighbor_meshes,
     150              :                    [](const auto& directional_element_id_and_mesh) {
     151              :                      ASSERT(directional_element_id_and_mesh.second.basis(0) !=
     152              :                                 Spectral::Basis::Chebyshev,
     153              :                             "Don't yet support Chebyshev basis with DG-FD");
     154              :                      return directional_element_id_and_mesh.second.basis(0) !=
     155              :                             Spectral::Basis::FiniteDifference;
     156              :                    }))) {
     157              :     *ghost_data_mesh = dg_mesh;
     158              :     const size_t total_to_slice = directions_to_slice.size();
     159              :     size_t slice_count = 0;
     160              :     for (const auto& direction : directions_to_slice) {
     161              :       ++slice_count;
     162              :       // Move instead of copy on the last iteration. Elides a memory
     163              :       // allocation and copy.
     164              :       [[maybe_unused]] const auto insert_result =
     165              :           UNLIKELY(slice_count == total_to_slice)
     166              :               ? all_neighbor_data_for_reconstruction->insert_or_assign(
     167              :                     direction, std::move(ghost_variables))
     168              :               : all_neighbor_data_for_reconstruction->insert_or_assign(
     169              :                     direction, ghost_variables);
     170              :       ASSERT(all_neighbor_data_for_reconstruction->size() == total_to_slice or
     171              :                  insert_result.second,
     172              :              "Failed to insert the neighbor data in direction " << direction);
     173              :     }
     174              :   } else {
     175              :     *ghost_data_mesh = subcell_mesh;
     176              :     const size_t ghost_zone_size =
     177              :         Metavariables::SubcellOptions::ghost_zone_size(*box);
     178              : 
     179              :     const DataVector data_to_project{};
     180              :     make_const_view(make_not_null(&data_to_project), ghost_variables, 0,
     181              :                     ghost_variables.size() - rdmp_size);
     182              :     const DataVector projected_data = evolution::dg::subcell::fd::project(
     183              :         data_to_project, dg_mesh, subcell_mesh.extents(),
     184              :         typename Metavariables::SubcellOptions::GhostVariables::
     185              :             ghost_variables_tag_list{});
     186              :     typename evolution::dg::subcell::Tags::InterpolatorsFromFdToNeighborFd<
     187              :         Dim>::type directions_not_to_slice{};
     188              :     for (const auto& [directional_element_id, _] :
     189              :          db::get<evolution::dg::subcell::Tags::InterpolatorsFromFdToNeighborFd<
     190              :              Dim>>(*box)) {
     191              :       directions_not_to_slice[directional_element_id] = std::nullopt;
     192              :     }
     193              :     *all_neighbor_data_for_reconstruction = subcell::slice_data(
     194              :         projected_data, db::get<subcell::Tags::Mesh<Dim>>(*box).extents(),
     195              :         ghost_zone_size, directions_to_slice, rdmp_size,
     196              :         directions_not_to_slice);
     197              :     for (const auto& [directional_element_id, interpolator] :
     198              :          db::get<evolution::dg::subcell::Tags::InterpolatorsFromDgToNeighborFd<
     199              :              Dim>>(*box)) {
     200              :       // NOTE: no orienting is needed, that's done by the interpolation.
     201              :       ASSERT(interpolator.has_value(),
     202              :              "All interpolators must have values. The std::optional is used "
     203              :              "for some local optimizations.");
     204              :       if (not directions_to_slice.contains(
     205              :               directional_element_id.direction())) {
     206              :         continue;
     207              :       }
     208              :       DataVector& data_in_direction = all_neighbor_data_for_reconstruction->at(
     209              :           directional_element_id.direction());
     210              :       gsl::span result_in_direction{data_in_direction.data(),
     211              :                                     data_in_direction.size() - rdmp_size};
     212              :       interpolator->interpolate(
     213              :           make_not_null(&result_in_direction),
     214              :           {data_to_project.data(), data_to_project.size()});
     215              :     }
     216              :   }
     217              : 
     218              :   // Note: The RDMP TCI data must be filled by the TCIs before getting to this
     219              :   // call. That means once in the initial data and then in both the DG and FD
     220              :   // TCIs.
     221              :   for (auto& [direction, neighbor_data] :
     222              :        *all_neighbor_data_for_reconstruction) {
     223              :     // Add the RDMP TCI data to what we will be sending.
     224              :     // Note that this is added _after_ the reconstruction data has been
     225              :     // re-oriented (in the case where we have neighbors doing FD).
     226              :     std::copy(rdmp_data.max_variables_values.begin(),
     227              :               rdmp_data.max_variables_values.end(),
     228              :               std::prev(neighbor_data.end(), static_cast<int>(rdmp_size)));
     229              :     std::copy(
     230              :         rdmp_data.min_variables_values.begin(),
     231              :         rdmp_data.min_variables_values.end(),
     232              :         std::prev(neighbor_data.end(),
     233              :                   static_cast<int>(rdmp_data.min_variables_values.size())));
     234              :   }
     235              : }
     236              : }  // namespace evolution::dg::subcell
        

Generated by: LCOV version 2.0-1