SpECTRE Documentation Coverage Report
Current view: top level - Evolution/DgSubcell - SetInterpolators.hpp Coverage Total Hit
Commit: a37d7b3c1803b88b537d2aef1c27ca5e6b499398 Lines: 20.0 % 5 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 <cstddef>
       7              : #include <optional>
       8              : #include <utility>
       9              : 
      10              : #include "DataStructures/ExtractPoint.hpp"
      11              : #include "DataStructures/Tensor/Tensor.hpp"
      12              : #include "Domain/Creators/Tags/Domain.hpp"
      13              : #include "Domain/Domain.hpp"
      14              : #include "Domain/ElementMap.hpp"
      15              : #include "Domain/Structure/BlockId.hpp"
      16              : #include "Domain/Structure/Direction.hpp"
      17              : #include "Domain/Structure/DirectionMap.hpp"
      18              : #include "Domain/Structure/DirectionalId.hpp"
      19              : #include "Domain/Structure/DirectionalIdMap.hpp"
      20              : #include "Domain/Structure/Element.hpp"
      21              : #include "Domain/Structure/ElementId.hpp"
      22              : #include "Domain/Tags.hpp"
      23              : #include "Evolution/DgSubcell/GhostZoneLogicalCoordinates.hpp"
      24              : #include "Evolution/DgSubcell/Mesh.hpp"
      25              : #include "Evolution/DgSubcell/SliceTensor.hpp"
      26              : #include "Evolution/DgSubcell/SubcellOptions.hpp"
      27              : #include "Evolution/DgSubcell/Tags/Interpolators.hpp"
      28              : #include "Evolution/DgSubcell/Tags/Mesh.hpp"
      29              : #include "Evolution/DgSubcell/Tags/SubcellOptions.hpp"
      30              : #include "Evolution/DiscontinuousGalerkin/OnlyDgBlockIds.hpp"
      31              : #include "NumericalAlgorithms/Interpolation/IrregularInterpolant.hpp"
      32              : #include "NumericalAlgorithms/Spectral/Mesh.hpp"
      33              : #include "Utilities/ErrorHandling/Error.hpp"
      34              : #include "Utilities/Gsl.hpp"
      35              : #include "Utilities/TMPL.hpp"
      36              : 
      37              : namespace evolution::dg::subcell {
      38              : /*!
      39              :  * \brief Sets the `intrp::IrregularInterpolant`s for interpolating to ghost
      40              :  * zone data at block boundaries.
      41              :  *
      42              :  * The DG to FD interpolants are at full order of the DG grid. The FD to FD
      43              :  * interpolant is piecewise linear with no support for neighboring
      44              :  * elements. We will want to use high-order slope-limited FD interpolation in
      45              :  * the future, but that requires neighbor communication. A slightly simpler
      46              :  * approach would be to use high-order Lagrange interpolation, which still
      47              :  * requires neighbor communication but does not require any additional changes
      48              :  * to the reconstruction routines to work on non-uniform grids. This is what
      49              :  * the Multipatch-MHD code does, relying on the slope limiting from the ghost
      50              :  * zones to remove oscillations. I (Nils Deppe) am not sure I love that, but
      51              :  * it's worth a try since it should be pretty easy to do.
      52              :  *
      53              :  * \warning Currently assumes that neighboring DG/FD elements are on the same
      54              :  * refinement level and have the same DG mesh and subcell mesh.
      55              :  */
      56              : template <size_t Dim, typename ReconstructorTag>
      57            1 : struct SetInterpolators {
      58            0 :   using return_tags = tmpl::list<
      59              :       evolution::dg::subcell::Tags::InterpolatorsFromFdToNeighborFd<Dim>,
      60              :       evolution::dg::subcell::Tags::InterpolatorsFromDgToNeighborFd<Dim>,
      61              :       evolution::dg::subcell::Tags::InterpolatorsFromNeighborDgToFd<Dim>,
      62              :       evolution::dg::subcell::Tags::ExtensionDirections<Dim>>;
      63            0 :   using argument_tags =
      64              :       tmpl::list<::domain::Tags::Element<Dim>, ::domain::Tags::Domain<Dim>,
      65              :                  domain::Tags::Mesh<Dim>, domain::Tags::Mesh<Dim>,
      66              :                  evolution::dg::subcell::Tags::Mesh<Dim>,
      67              :                  evolution::dg::subcell::Tags::Mesh<Dim>,
      68              :                  ::domain::Tags::ElementMap<Dim, Frame::Grid>, ReconstructorTag,
      69              :                  evolution::dg::subcell::Tags::SubcellOptions<Dim>,
      70              :                  evolution::dg::Tags::OnlyDgBlockIds<Dim>>;
      71              : 
      72              :   template <typename ReconstructorType>
      73            0 :   static void apply(
      74              :       const gsl::not_null<
      75              :           DirectionalIdMap<Dim, std::optional<intrp::Irregular<Dim>>>*>
      76              :           interpolators_fd_to_neighbor_fd_ptr,
      77              :       const gsl::not_null<
      78              :           DirectionalIdMap<Dim, std::optional<intrp::Irregular<Dim>>>*>
      79              :           interpolators_dg_to_neighbor_fd_ptr,
      80              :       const gsl::not_null<
      81              :           DirectionalIdMap<Dim, std::optional<intrp::Irregular<Dim>>>*>
      82              :           interpolators_neighbor_dg_to_fd_ptr,
      83              :       const gsl::not_null<
      84              :           DirectionMap<Dim, interpolators_detail::ExtensionDirection<Dim>>*>
      85              :           extension_direction_ptr,
      86              :       const Element<Dim>& element, const Domain<Dim>& domain,
      87              :       const Mesh<Dim>& my_dg_mesh,
      88              :       // Needs to be updated to support non-uniform h/p-refinement
      89              :       const Mesh<Dim>& neighbor_dg_mesh, const Mesh<Dim>& my_fd_mesh,
      90              :       // Needs to be updated to support non-uniform h/p-refinement
      91              :       const Mesh<Dim>& neighbor_fd_mesh,
      92              :       const ElementMap<Dim, Frame::Grid>& element_map,
      93              :       const ReconstructorType& reconstructor,
      94              :       const evolution::dg::subcell::SubcellOptions& subcell_options,
      95              :       const std::vector<size_t>& only_dg_block_ids) {
      96              :     // Skip for elements that are DG-only: either in a DG-only block,
      97              :     // bordering a DG-only block, or on a non-subcell-compatible mesh.
      98              :     if (not fd::dg_mesh_supports_subcell(my_dg_mesh) or
      99              :         alg::found(only_dg_block_ids, element.id().block_id()) or
     100              :         evolution::dg::borders_only_dg_block(element, only_dg_block_ids)) {
     101              :       return;
     102              :     }
     103              :     const bool enable_extension_directions =
     104              :         subcell_options.enable_extension_directions();
     105              :     if (enable_extension_directions) {
     106              :       *extension_direction_ptr = {};
     107              :     }  // Initialize the extension directions to empty.
     108              : 
     109              :     const size_t number_of_ghost_zones = reconstructor.ghost_zone_size();
     110              :     const size_t my_block_id = element.id().block_id();
     111              :     for (const auto& direction_neighbors_in_direction : element.neighbors()) {
     112              :       const auto& direction = direction_neighbors_in_direction.first;
     113              :       const auto& neighbors_in_direction =
     114              :           direction_neighbors_in_direction.second;
     115              :       for (const ElementId<Dim>& neighbor_id : neighbors_in_direction) {
     116              :         const auto& orientation =
     117              :             neighbors_in_direction.orientation(neighbor_id);
     118              :         const auto direction_from_neighbor = orientation(direction.opposite());
     119              :         const size_t neighbor_block_id = neighbor_id.block_id();
     120              :         if (neighbor_block_id == my_block_id) {
     121              :           continue;
     122              :         }
     123              :         const auto& neighbor_block = domain.blocks()[neighbor_block_id];
     124              :         // InterpolatorsFromFdToNeighborFd &
     125              :         // InterpolatorsFromDgToNeighborFd
     126              :         // 1. Compute the grid coordinates of my neighbor's ghost zones.
     127              :         // 2. Compute the element logical coordinates of my neighbor's
     128              :         //    ghost zones.
     129              :         // 3. Create interpolators
     130              : 
     131              :         if (not is_isotropic(neighbor_fd_mesh) and
     132              :             neighbor_fd_mesh.basis(Dim - 1) != Spectral::Basis::Cartoon) {
     133              :           ERROR("We assume an isotropic mesh but got "
     134              :                 << neighbor_fd_mesh << " ElementID is " << element.id());
     135              :         }
     136              :         // Extra checks for cartoon meshes not checked above
     137              :         fd::verify_subcell_mesh(neighbor_fd_mesh, true);
     138              : 
     139              :         const auto get_logical_coords = [&element, &neighbor_id, &direction](
     140              :                                             const auto& map,
     141              :                                             const auto& grid_coords) {
     142              :           tnsr::I<DataVector, Dim, Frame::ElementLogical> logical_coords{
     143              :               get<0>(grid_coords).size()};
     144              :           for (size_t i = 0; i < get<0>(grid_coords).size(); ++i) {
     145              :             try {
     146              :               tnsr::I<double, Dim, Frame::ElementLogical> logical_coord =
     147              :                   map.inverse(extract_point(grid_coords, i));
     148              :               for (size_t d = 0; d < Dim; ++d) {
     149              :                 logical_coords.get(d)[i] = logical_coord.get(d);
     150              :               }
     151              :             } catch (const std::bad_optional_access& e) {
     152              :               ERROR(
     153              :                   "Failed to get logical coordinates for neighbor's "
     154              :                   "ghost zone grid coordinates. This could be because the "
     155              :                   "ghost zones are not in the nearest neighbor but instead in "
     156              :                   "the next-to-nearest neighbor. The code assumes all ghost "
     157              :                   "zones, even on curved meshes, are in the nearest neighbors. "
     158              :                   "The current element is "
     159              :                   << element.id() << " and the neighbor id is " << neighbor_id
     160              :                   << " in direction " << direction
     161              :                   << " The neighbor grid coordinates are \n"
     162              :                   << extract_point(grid_coords, i) << "\n");
     163              :             }
     164              :           }
     165              :           return logical_coords;
     166              :         };
     167              : 
     168              :         tnsr::I<DataVector, Dim, Frame::Grid> neighbor_grid_ghost_zone_coords{};
     169              :         // Get the neighbor's ghost zone coordinates in the grid
     170              :         // frame.
     171              :         if (const tnsr::I<DataVector, Dim, Frame::ElementLogical>
     172              :                 neighbor_logical_ghost_zone_coords =
     173              :                     evolution::dg::subcell::fd::ghost_zone_logical_coordinates(
     174              :                         neighbor_fd_mesh, number_of_ghost_zones,
     175              :                         direction_from_neighbor);
     176              :             neighbor_block.is_time_dependent()) {
     177              :           const ElementMap neighbor_element_map(
     178              :               neighbor_id,
     179              :               neighbor_block.moving_mesh_logical_to_grid_map().get_clone());
     180              :           neighbor_grid_ghost_zone_coords =
     181              :               neighbor_element_map(neighbor_logical_ghost_zone_coords);
     182              :         } else {
     183              :           const ElementMap neighbor_element_map(
     184              :               neighbor_id, neighbor_block.stationary_map().get_clone());
     185              :           const tnsr::I<DataVector, Dim, Frame::Inertial>
     186              :               neighbor_inertial_ghost_zone_coords =
     187              :                   neighbor_element_map(neighbor_logical_ghost_zone_coords);
     188              :           for (size_t i = 0; i < Dim; ++i) {
     189              :             neighbor_grid_ghost_zone_coords[i] =
     190              :                 neighbor_inertial_ghost_zone_coords[i];
     191              :           }
     192              :         }
     193              :         // Map the ghost zone grid coordinates back to our logical
     194              :         // coordinates.
     195              :         const tnsr::I<DataVector, Dim, Frame::ElementLogical>
     196              :             neighbor_logical_ghost_zone_coords = get_logical_coords(
     197              :                 element_map, neighbor_grid_ghost_zone_coords);
     198              : 
     199              :         // We need to check if the neighbor's ghost zone coordinates
     200              :         // are in the same element as the current element. If not, we
     201              :         // need to extend the mesh in the direction of the ghost zone
     202              :         // coordinates.
     203              :         // Note, we only need to do this check if we enable extending
     204              :         // the mesh to avoid extrapolation. If not, we simply
     205              :         // allow the extrapolation to happen.
     206              : 
     207              :         // 'needs_extension' is set to true if the neighbor's ghost zone
     208              :         // coordinates are outside the current element's mesh and if
     209              :         // we enable extending the mesh.
     210              :         bool needs_extension = false;
     211              :         std::optional<Direction<Dim>> direction_to_extend;
     212              :         if (enable_extension_directions) {
     213              :           for (size_t d = 0; d < Dim; ++d) {
     214              :             // small epsilon of 1e-10 used to ensure we are not unncessarily
     215              :             // flagging as problematic
     216              :             const double ext = 1. - (1. / my_fd_mesh.extents(d)) + 1.e-10;
     217              :             const auto& coords = neighbor_logical_ghost_zone_coords.get(d);
     218              : 
     219              :             for (size_t i = 0; i < coords.size(); ++i) {
     220              :               if (std::abs(coords[i]) > ext) {
     221              :                 needs_extension = true;
     222              :                 Direction<Dim> new_direction = Direction<Dim>{
     223              :                     d, coords[i] > 0 ? Side::Upper : Side::Lower};
     224              : 
     225              :                 if (!direction_to_extend.has_value()) {
     226              :                   direction_to_extend = new_direction;
     227              :                 } else if (direction_to_extend.value() != new_direction) {
     228              :                   ERROR("Multiple directions to extend: existing = "
     229              :                         << direction_to_extend.value()
     230              :                         << ", new = " << new_direction);
     231              :                 }
     232              :                 break;  // no reason to check remaining coords.
     233              :               }
     234              :             }
     235              :           }
     236              :         }
     237              : 
     238              :         if (needs_extension) {
     239              :           if (!direction_to_extend.has_value()) {
     240              :             ERROR(
     241              :                 "Should have direction to extend if flagged "
     242              :                 "as problematic!");
     243              :           }
     244              :           const auto& external_boundaries = element.external_boundaries();
     245              :           if (external_boundaries.find(direction_to_extend.value()) !=
     246              :               external_boundaries.end()) {
     247              :             ERROR(
     248              :                 "Direction to extend is toward the "
     249              :                 "faces of the Element that are external boundaries.");
     250              :           }
     251              : 
     252              :           auto new_basis = make_array<Dim>(my_fd_mesh.basis(0));
     253              :           auto new_extents = make_array<Dim>(my_fd_mesh.extents(0));
     254              :           auto new_quads = make_array<Dim>(my_fd_mesh.quadrature(0));
     255              : 
     256              :           const size_t problematic_dim =
     257              :               direction_to_extend.value().dimension();
     258              : 
     259              :           // note we are extending our current volume by including its own ghost
     260              :           // points in the problematic direction (direction to extend)
     261              :           // which means the logical coordinates of the ghost (to be sent)
     262              :           // must be transformed to accommodate the extended mesh in
     263              :           // direction to extend.
     264              : 
     265              :           const double rescale_factor =
     266              :               static_cast<double>(my_fd_mesh.extents(problematic_dim)) /
     267              :               (my_fd_mesh.extents(problematic_dim) + number_of_ghost_zones);
     268              :           double translation =
     269              :               static_cast<double>(number_of_ghost_zones) /
     270              :               (my_fd_mesh.extents(problematic_dim) + number_of_ghost_zones);
     271              :           // translation above is based on extending to Upper Side.
     272              :           if (direction_to_extend.value().side() == Side::Lower) {
     273              :             translation *= -1.;
     274              :           }
     275              :           auto new_neighbor_logical_ghost_zone_coords =
     276              :               neighbor_logical_ghost_zone_coords;
     277              :           for (size_t i = 0;
     278              :                i < new_neighbor_logical_ghost_zone_coords[0].size(); ++i) {
     279              :             new_neighbor_logical_ghost_zone_coords.get(problematic_dim)[i] *=
     280              :                 rescale_factor;
     281              :             new_neighbor_logical_ghost_zone_coords.get(problematic_dim)[i] -=
     282              :                 translation;
     283              :           }
     284              : 
     285              :           for (size_t d = 0; d < Dim; ++d) {
     286              :             gsl::at(new_basis, d) = my_fd_mesh.basis(d);
     287              :             gsl::at(new_quads, d) = my_fd_mesh.quadrature(d);
     288              :             if (d == problematic_dim) {
     289              :               gsl::at(new_extents, d) =
     290              :                   my_fd_mesh.extents(d) + number_of_ghost_zones;
     291              :             } else {
     292              :               gsl::at(new_extents, d) = my_fd_mesh.extents(d);
     293              :             }
     294              :           }
     295              :           const Mesh<Dim> new_mesh{new_extents, new_basis, new_quads};
     296              :           (*interpolators_fd_to_neighbor_fd_ptr)[DirectionalId<Dim>{
     297              :               direction, neighbor_id}] = intrp::Irregular<Dim>{
     298              :               new_mesh, new_neighbor_logical_ghost_zone_coords,
     299              :               subcell_options.get_fd_to_fd_interp_order()};
     300              :           (*extension_direction_ptr)[direction] =
     301              :               interpolators_detail::ExtensionDirection<Dim>{
     302              :                   direction_to_extend.value()};
     303              :         } else {
     304              :           (*interpolators_fd_to_neighbor_fd_ptr)[DirectionalId<Dim>{
     305              :               direction, neighbor_id}] = intrp::Irregular<Dim>{
     306              :               my_fd_mesh, neighbor_logical_ghost_zone_coords,
     307              :               subcell_options.get_fd_to_fd_interp_order()};
     308              :         }
     309              :         // Set up interpolators for our local element to our neighbor's
     310              :         // ghost zones.
     311              :         (*interpolators_dg_to_neighbor_fd_ptr)[DirectionalId<Dim>{
     312              :             direction, neighbor_id}] = intrp::Irregular<Dim>{
     313              :             my_dg_mesh, neighbor_logical_ghost_zone_coords};
     314              : 
     315              :         // InterpolatorsFromNeighborDgToFd: the interpolation from our
     316              :         // neighbor's DG grid to our FD ghost zones.
     317              :         //
     318              :         // 1. Compute the grid coordinates of my ghost zones.
     319              :         // 2. Compute neighbor's element logical coordinates of my ghost
     320              :         //    zones
     321              :         // 3. Create interpolator for InterpolatorsFromNeighborDgToFd
     322              :         const tnsr::I<DataVector, Dim, Frame::ElementLogical>
     323              :             my_logical_coords = logical_coordinates(my_fd_mesh);
     324              :         tnsr::I<DataVector, Dim, Frame::ElementLogical>
     325              :             my_logical_ghost_zone_coords =
     326              :                 evolution::dg::subcell::slice_tensor_for_subcell(
     327              :                     my_logical_coords, neighbor_fd_mesh.extents(),
     328              :                     number_of_ghost_zones, direction,
     329              :                     // We want to _set_ the interpolators, so just do a simple
     330              :                     // slice.
     331              :                     {});
     332              :         const double delta_xi =
     333              :             get<0>(my_logical_coords)[1] - get<0>(my_logical_coords)[0];
     334              :         // The sign accounts for whether we are shift along the
     335              :         // positive or negative axis.
     336              :         const double coordinate_shift =
     337              :             direction.sign() * delta_xi * number_of_ghost_zones;
     338              :         my_logical_ghost_zone_coords.get(direction.dimension()) +=
     339              :             coordinate_shift;
     340              :         const tnsr::I<DataVector, Dim, Frame::Grid> my_grid_ghost_zone_coords =
     341              :             element_map(my_logical_ghost_zone_coords);
     342              :         if (neighbor_block.is_time_dependent()) {
     343              :           const ElementMap neighbor_element_map(
     344              :               neighbor_id,
     345              :               neighbor_block.moving_mesh_logical_to_grid_map().get_clone());
     346              :           (*interpolators_neighbor_dg_to_fd_ptr)[DirectionalId<Dim>{
     347              :               direction, neighbor_id}] = intrp::Irregular<Dim>{
     348              :               neighbor_dg_mesh, get_logical_coords(neighbor_element_map,
     349              :                                                    my_grid_ghost_zone_coords)};
     350              :         } else {
     351              :           const ElementMap neighbor_element_map(
     352              :               neighbor_id, neighbor_block.stationary_map().get_clone());
     353              :           const tnsr::I<DataVector, Dim, Frame::Inertial>
     354              :               view_my_grid_ghost_zone_coords{};
     355              :           for (size_t i = 0; i < Dim; ++i) {
     356              :             make_const_view(make_not_null(&view_my_grid_ghost_zone_coords[i]),
     357              :                             my_grid_ghost_zone_coords[i], 0,
     358              :                             my_grid_ghost_zone_coords[i].size());
     359              :           }
     360              :           (*interpolators_neighbor_dg_to_fd_ptr)[DirectionalId<Dim>{
     361              :               direction, neighbor_id}] = intrp::Irregular<Dim>{
     362              :               neighbor_dg_mesh,
     363              :               get_logical_coords(neighbor_element_map,
     364              :                                  view_my_grid_ghost_zone_coords)};
     365              :         }
     366              :       }
     367              :     }
     368              :   }
     369              : };
     370              : }  // namespace evolution::dg::subcell
        

Generated by: LCOV version 2.0-1