SpECTRE Documentation Coverage Report
Current view: top level - NumericalAlgorithms/DiscontinuousGalerkin - ProjectToBoundary.hpp Coverage Total Hit
Commit: 0d1d717af84a81fb1284a20c4013708fe85cc2f0 Lines: 80.0 % 5 4
Test Date: 2026-10-22 17:29:42
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 <type_traits>
       8              : 
       9              : #include "DataStructures/ApplyMatrices.hpp"
      10              : #include "DataStructures/DataVector.hpp"
      11              : #include "DataStructures/Matrix.hpp"
      12              : #include "DataStructures/SliceIterator.hpp"
      13              : #include "DataStructures/Variables.hpp"
      14              : #include "Domain/Structure/Direction.hpp"
      15              : #include "NumericalAlgorithms/Spectral/BoundaryInterpolationMatrices.hpp"
      16              : #include "NumericalAlgorithms/Spectral/Mesh.hpp"
      17              : #include "NumericalAlgorithms/Spectral/Quadrature.hpp"
      18              : #include "Utilities/ErrorHandling/Assert.hpp"
      19              : #include "Utilities/Gsl.hpp"
      20              : #include "Utilities/MakeArray.hpp"
      21              : #include "Utilities/TMPL.hpp"
      22              : 
      23              : namespace dg {
      24              : namespace detail {
      25              : template <typename TagsList>
      26              : struct NumberOfIndependentComponents;
      27              : 
      28              : template <typename... Tags>
      29              : struct NumberOfIndependentComponents<tmpl::list<Tags...>> {
      30              :   static constexpr size_t value = (... + Tags::type::size());
      31              : };
      32              : }  // namespace detail
      33              : 
      34              : /*!
      35              :  * \brief Projects a `Variables` of volume data to a contiguous subset of
      36              :  * a boundary `Variables`
      37              :  *
      38              :  * The `volume_fields` are all projected into the `face_fields` in the direction
      39              :  * `direction`. The tags in `VolumeVarsTagsList` must be a contiguous subset of
      40              :  * the tags in `FaceVarsTagsList`. That is, `FaceVarsTagsList` must be
      41              :  * equivalent to `tmpl::append<Before, VolumeVarsTagsList, After>` where
      42              :  * `Before` and `After` are `tmpl::list`s of arbitrary size. This is because the
      43              :  * projection is applied on all of the tensor components of the volume variables
      44              :  * and is written into contiguous memory on the boundary.
      45              :  *
      46              :  * In general, this function will be used for projecting all the evolved
      47              :  * variables or all the volume fluxes to the faces. The function
      48              :  * `dg::project_tensors_to_boundary()` should be used for projecting
      49              :  * individual tensors to the face.
      50              :  *
      51              :  * \note This function works for both Gauss and Gauss-Lobatto uniform meshes.
      52              :  */
      53              : template <typename VolumeVarsTagsList, typename FaceVarsTagsList, size_t Dim>
      54            1 : void project_contiguous_data_to_boundary(
      55              :     const gsl::not_null<Variables<FaceVarsTagsList>*> face_fields,
      56              :     const Variables<VolumeVarsTagsList>& volume_fields,
      57              :     const Mesh<Dim>& volume_mesh, const Direction<Dim>& direction) {
      58              :   static_assert(tmpl::size<VolumeVarsTagsList>::value != 0,
      59              :                 "Must have non-zero number of volume fields");
      60              :   static_assert(tmpl::size<FaceVarsTagsList>::value >=
      61              :                     tmpl::size<VolumeVarsTagsList>::value,
      62              :                 "There must not be more volume tags than there are face tags.");
      63              :   static_assert(
      64              :       tmpl::list_contains_v<FaceVarsTagsList, tmpl::front<VolumeVarsTagsList>>,
      65              :       "The first tag of VolumeVarsTagsList is not in the face tags. The "
      66              :       "VolumeVarsTagsList must be a subset of the FaceVarsTagsList");
      67              :   static_assert(
      68              :       tmpl::list_contains_v<FaceVarsTagsList, tmpl::back<VolumeVarsTagsList>>,
      69              :       "The last tag of VolumeVarsTagsList is not in the face tags. The "
      70              :       "VolumeVarsTagsList must be a subset of the FaceVarsTagsList");
      71              :   using face_vars_excluding_extras_at_end = tmpl::front<
      72              :       tmpl::split_at<FaceVarsTagsList,
      73              :                      tmpl::next<tmpl::index_of<
      74              :                          FaceVarsTagsList, tmpl::back<VolumeVarsTagsList>>>>>;
      75              :   using front_face_vars_split = tmpl::split_at<
      76              :       face_vars_excluding_extras_at_end,
      77              :       tmpl::index_of<FaceVarsTagsList, tmpl::front<VolumeVarsTagsList>>>;
      78              :   using volume_vars_face_subset_list = tmpl::back<front_face_vars_split>;
      79              :   static_assert(
      80              :       std::is_same_v<volume_vars_face_subset_list, VolumeVarsTagsList>,
      81              :       "The VolumeVarsTagsList must be a subset of the FaceVarsTagsList.");
      82              :   constexpr const size_t number_of_independent_components =
      83              :       Variables<VolumeVarsTagsList>::number_of_independent_components;
      84              :   using first_volume_tag = tmpl::front<VolumeVarsTagsList>;
      85              :   using VectorType = typename Variables<FaceVarsTagsList>::vector_type;
      86              :   using ValueType = typename VectorType::value_type;
      87              :   const size_t sliced_dim = direction.dimension();
      88              :   if (volume_mesh.quadrature(sliced_dim) == Spectral::Quadrature::Gauss) {
      89              :     const Matrix identity{};
      90              :     auto interpolation_matrices = make_array<Dim>(std::cref(identity));
      91              :     const auto& matrix = Spectral::boundary_interpolation_matrices(
      92              :         volume_mesh.slice_through(sliced_dim));
      93              :     gsl::at(interpolation_matrices, sliced_dim) =
      94              :         direction.side() == Side::Upper ? matrix.second : matrix.first;
      95              : 
      96              :     auto& first_face_field = get<first_volume_tag>(*face_fields);
      97              :     auto& first_volume_field = get<first_volume_tag>(volume_fields);
      98              : 
      99              :     // The size is the number of tensor components we are projecting times the
     100              :     // number of grid points on the face. Note that this is _not_ equal to the
     101              :     // size of face_fields->size() since face_fields is a superset of the
     102              :     // volume variables.
     103              :     VectorType face_view{
     104              :         first_face_field[0].data(),
     105              :         first_face_field[0].size() * number_of_independent_components};
     106              : 
     107              :     apply_matrices(
     108              :         make_not_null(&face_view), interpolation_matrices,
     109              :         VectorType{
     110              :             // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
     111              :             const_cast<ValueType*>(first_volume_field[0].data()),
     112              :             first_volume_field[0].size() * number_of_independent_components},
     113              :         volume_mesh.extents());
     114              :   } else {
     115              :     ASSERT(volume_mesh.quadrature(sliced_dim) ==
     116              :                    Spectral::Quadrature::GaussLobatto or
     117              :                (volume_mesh.quadrature(sliced_dim) ==
     118              :                     Spectral::Quadrature::GaussRadauUpper and
     119              :                 direction.side() == Side::Upper),
     120              :            "Got quadrature without boundary collocation point at "
     121              :                << direction << " with " << volume_mesh.quadrature(sliced_dim));
     122              :     const size_t fixed_index = direction.side() == Side::Upper
     123              :                                    ? volume_mesh.extents(sliced_dim) - 1
     124              :                                    : 0;
     125              : 
     126              :     const size_t interface_grid_points =
     127              :         volume_mesh.extents().slice_away(sliced_dim).product();
     128              :     const size_t volume_grid_points = volume_mesh.number_of_grid_points();
     129              : 
     130              :     const ValueType* vars_data = volume_fields.data();
     131              :     // Since the face fields are a superset of the volume tags we need to find
     132              :     // the first volume tag on the face and get the pointer for that.
     133              :     ValueType* interface_vars_data =
     134              :         get<first_volume_tag>(*face_fields)[0].data();
     135              : 
     136              :     // The reason we can't use data_on_slice is because the volume and face tags
     137              :     // are not the same, but data_on_slice assumes they are. In general, this
     138              :     // function should replace data_on_slice in the long term since in
     139              :     // additional to supporting different volume and face tags, it also supports
     140              :     // Gauss and Gauss-Lobatto points.
     141              :     //
     142              :     // Run the SliceIterator as the outer-most loop since incrementing the slice
     143              :     // iterator is surprisingly expensive.
     144              :     for (SliceIterator si(volume_mesh.extents(), sliced_dim, fixed_index); si;
     145              :          ++si) {
     146              :       for (size_t i = 0; i < number_of_independent_components; ++i) {
     147              :         // clang-tidy: do not use pointer arithmetic
     148              :         interface_vars_data[si.slice_offset() +                      // NOLINT
     149              :                             i * interface_grid_points] =             // NOLINT
     150              :             vars_data[si.volume_offset() + i * volume_grid_points];  // NOLINT
     151              :       }
     152              :     }
     153              :   }
     154              : }
     155              : 
     156              : /*!
     157              :  * \brief Projects a subset of the tensors in the `volume_fields` onto the face
     158              :  *
     159              :  * The tensors to project are listed in the `TagsToProjectList`.
     160              :  *
     161              :  * \note This function works for both Gauss and Gauss-Lobatto uniform meshes.
     162              :  */
     163              : template <typename TagsToProjectList, typename VolumeVarsTagsList,
     164              :           typename FaceVarsTagsList, size_t Dim>
     165            1 : void project_tensors_to_boundary(
     166              :     const gsl::not_null<Variables<FaceVarsTagsList>*> face_fields,
     167              :     const Variables<VolumeVarsTagsList>& volume_fields,
     168              :     const Mesh<Dim>& volume_mesh, const Direction<Dim>& direction) {
     169              :   static_assert(tmpl::size<VolumeVarsTagsList>::value != 0,
     170              :                 "Must have non-zero number of volume fields");
     171              :   static_assert(
     172              :       tmpl::size<
     173              :           tmpl::list_difference<TagsToProjectList, FaceVarsTagsList>>::value ==
     174              :           0,
     175              :       "All of the tags in TagsToProjectList must be in FaceVarsTagsList");
     176              :   static_assert(
     177              :       tmpl::size<tmpl::list_difference<TagsToProjectList,
     178              :                                        VolumeVarsTagsList>>::value == 0,
     179              :       "All of the tags in TagsToProjectList must be in VolumeVarsTagsList");
     180              :   const size_t sliced_dim = direction.dimension();
     181              :   if (volume_mesh.quadrature(sliced_dim) == Spectral::Quadrature::Gauss) {
     182              :     const Matrix identity{};
     183              :     auto interpolation_matrices = make_array<Dim>(std::cref(identity));
     184              :     const std::pair<Matrix, Matrix>& matrices =
     185              :         Spectral::boundary_interpolation_matrices(
     186              :             volume_mesh.slice_through(sliced_dim));
     187              :     gsl::at(interpolation_matrices, sliced_dim) =
     188              :         direction.side() == Side::Upper ? matrices.second : matrices.first;
     189              :     tmpl::for_each<TagsToProjectList>([&face_fields, &interpolation_matrices,
     190              :                                        &volume_fields,
     191              :                                        &volume_mesh](auto tag_v) {
     192              :       using tag = typename decltype(tag_v)::type;
     193              :       auto& face_field = get<tag>(*face_fields);
     194              :       const auto& volume_field = get<tag>(volume_fields);
     195              :       DataVector face_view{face_field[0].data(),
     196              :                            face_field[0].size() * face_field.size()};
     197              :       apply_matrices(make_not_null(&face_view), interpolation_matrices,
     198              :                      // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
     199              :                      DataVector{const_cast<double*>(volume_field[0].data()),
     200              :                                 volume_field[0].size() * volume_field.size()},
     201              :                      volume_mesh.extents());
     202              :     });
     203              :   } else {
     204              :     ASSERT(volume_mesh.quadrature(sliced_dim) ==
     205              :                    Spectral::Quadrature::GaussLobatto or
     206              :                (volume_mesh.quadrature(sliced_dim) ==
     207              :                     Spectral::Quadrature::GaussRadauUpper and
     208              :                 direction.side() == Side::Upper),
     209              :            "Got quadrature without boundary collocation point at "
     210              :                << direction << " with " << volume_mesh.quadrature(sliced_dim));
     211              :     const size_t fixed_index = direction.side() == Side::Upper
     212              :                                    ? volume_mesh.extents(sliced_dim) - 1
     213              :                                    : 0;
     214              : 
     215              :     const size_t interface_grid_points =
     216              :         volume_mesh.extents().slice_away(sliced_dim).product();
     217              :     const size_t volume_grid_points = volume_mesh.number_of_grid_points();
     218              : 
     219              :     // Run the SliceIterator as the outer-most loop since incrementing the slice
     220              :     // iterator is surprisingly expensive.
     221              :     for (SliceIterator si(volume_mesh.extents(), sliced_dim, fixed_index); si;
     222              :          ++si) {
     223              :       tmpl::for_each<TagsToProjectList>([&face_fields, interface_grid_points,
     224              :                                          &si, &volume_fields,
     225              :                                          volume_grid_points](auto tag_v) {
     226              :         using tag = typename decltype(tag_v)::type;
     227              : 
     228              :         const double* vars_data = get<tag>(volume_fields)[0].data();
     229              :         double* interface_vars_data = get<tag>(*face_fields)[0].data();
     230              :         static constexpr size_t number_of_independent_components_in_tensor =
     231              :             std::decay_t<decltype(get<tag>(volume_fields))>::size();
     232              : 
     233              :         for (size_t i = 0; i < number_of_independent_components_in_tensor;
     234              :              ++i) {
     235              :           // clang-tidy: do not use pointer arithmetic
     236              :           interface_vars_data[si.slice_offset() +                      // NOLINT
     237              :                               i * interface_grid_points] =             // NOLINT
     238              :               vars_data[si.volume_offset() + i * volume_grid_points];  // NOLINT
     239              :         }
     240              :       });
     241              :     }
     242              :   }
     243              : }
     244              : 
     245              : /// @{
     246              : /*!
     247              :  * \brief Projects a tensor to the face
     248              :  *
     249              :  * \note This function works for both Gauss and Gauss-Lobatto uniform meshes.
     250              :  */
     251              : template <typename Symm, typename IndexList, size_t Dim>
     252            1 : void project_tensor_to_boundary(
     253              :     const gsl::not_null<Tensor<DataVector, Symm, IndexList>*> face_field,
     254              :     const Tensor<DataVector, Symm, IndexList>& volume_field,
     255              :     const Mesh<Dim>& volume_mesh, const Direction<Dim>& direction) {
     256              :   const size_t sliced_dim = direction.dimension();
     257              :   if (volume_mesh.quadrature(sliced_dim) == Spectral::Quadrature::Gauss) {
     258              :     const Matrix identity{};
     259              :     auto interpolation_matrices = make_array<Dim>(std::cref(identity));
     260              :     const std::pair<Matrix, Matrix>& matrices =
     261              :         Spectral::boundary_interpolation_matrices(
     262              :             volume_mesh.slice_through(sliced_dim));
     263              :     gsl::at(interpolation_matrices, sliced_dim) =
     264              :         direction.side() == Side::Upper ? matrices.second : matrices.first;
     265              :     for (size_t tensor_storage_index = 0;
     266              :          tensor_storage_index < volume_field.size(); ++tensor_storage_index) {
     267              :       apply_matrices(make_not_null(&(*face_field)[tensor_storage_index]),
     268              :                      interpolation_matrices, volume_field[tensor_storage_index],
     269              :                      volume_mesh.extents());
     270              :     }
     271              :   } else {
     272              :     ASSERT(volume_mesh.quadrature(sliced_dim) ==
     273              :                    Spectral::Quadrature::GaussLobatto or
     274              :                (volume_mesh.quadrature(sliced_dim) ==
     275              :                     Spectral::Quadrature::GaussRadauUpper and
     276              :                 direction.side() == Side::Upper),
     277              :            "Got quadrature without boundary collocation point at "
     278              :                << direction << " with " << volume_mesh.quadrature(sliced_dim));
     279              :     const size_t fixed_index = direction.side() == Side::Upper
     280              :                                    ? volume_mesh.extents(sliced_dim) - 1
     281              :                                    : 0;
     282              : 
     283              :     // Run the SliceIterator as the outer-most loop since incrementing the slice
     284              :     // iterator is surprisingly expensive.
     285              :     for (SliceIterator si(volume_mesh.extents(), sliced_dim, fixed_index); si;
     286              :          ++si) {
     287              :       for (size_t tensor_storage_index = 0;
     288              :            tensor_storage_index < volume_field.size(); ++tensor_storage_index) {
     289              :         (*face_field)[tensor_storage_index][si.slice_offset()] =
     290              :             volume_field[tensor_storage_index][si.volume_offset()];
     291              :       }
     292              :     }
     293              :   }
     294              : }
     295              : 
     296              : template <typename Symm, typename IndexList, size_t Dim>
     297            1 : Tensor<DataVector, Symm, IndexList> project_tensor_to_boundary(
     298              :     const Tensor<DataVector, Symm, IndexList>& volume_field,
     299              :     const Mesh<Dim>& volume_mesh, const Direction<Dim>& direction) {
     300              :   Tensor<DataVector, Symm, IndexList> face_field{
     301              :       volume_mesh.slice_away(direction.dimension()).number_of_grid_points()};
     302              :   project_tensor_to_boundary(make_not_null(&face_field), volume_field,
     303              :                              volume_mesh, direction);
     304              :   return face_field;
     305              : }
     306              : /// @}
     307              : 
     308              : }  // namespace dg
        

Generated by: LCOV version 2.0-1