SpECTRE Documentation Coverage Report
Current view: top level - NumericalAlgorithms/DiscontinuousGalerkin - ProjectToBoundary.hpp Hit Total Coverage
Commit: 1f2210958b4f38fdc0400907ee7c6d5af5111418 Lines: 4 5 80.0 %
Date: 2025-12-05 05:03:31
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             :     const size_t fixed_index = direction.side() == Side::Upper
     116             :                                    ? volume_mesh.extents(sliced_dim) - 1
     117             :                                    : 0;
     118             : 
     119             :     const size_t interface_grid_points =
     120             :         volume_mesh.extents().slice_away(sliced_dim).product();
     121             :     const size_t volume_grid_points = volume_mesh.number_of_grid_points();
     122             : 
     123             :     const ValueType* vars_data = volume_fields.data();
     124             :     // Since the face fields are a superset of the volume tags we need to find
     125             :     // the first volume tag on the face and get the pointer for that.
     126             :     ValueType* interface_vars_data =
     127             :         get<first_volume_tag>(*face_fields)[0].data();
     128             : 
     129             :     // The reason we can't use data_on_slice is because the volume and face tags
     130             :     // are not the same, but data_on_slice assumes they are. In general, this
     131             :     // function should replace data_on_slice in the long term since in
     132             :     // additional to supporting different volume and face tags, it also supports
     133             :     // Gauss and Gauss-Lobatto points.
     134             :     //
     135             :     // Run the SliceIterator as the outer-most loop since incrementing the slice
     136             :     // iterator is surprisingly expensive.
     137             :     for (SliceIterator si(volume_mesh.extents(), sliced_dim, fixed_index); si;
     138             :          ++si) {
     139             :       for (size_t i = 0; i < number_of_independent_components; ++i) {
     140             :         // clang-tidy: do not use pointer arithmetic
     141             :         interface_vars_data[si.slice_offset() +                      // NOLINT
     142             :                             i * interface_grid_points] =             // NOLINT
     143             :             vars_data[si.volume_offset() + i * volume_grid_points];  // NOLINT
     144             :       }
     145             :     }
     146             :   }
     147             : }
     148             : 
     149             : /*!
     150             :  * \brief Projects a subset of the tensors in the `volume_fields` onto the face
     151             :  *
     152             :  * The tensors to project are listed in the `TagsToProjectList`.
     153             :  *
     154             :  * \note This function works for both Gauss and Gauss-Lobatto uniform meshes.
     155             :  */
     156             : template <typename TagsToProjectList, typename VolumeVarsTagsList,
     157             :           typename FaceVarsTagsList, size_t Dim>
     158           1 : void project_tensors_to_boundary(
     159             :     const gsl::not_null<Variables<FaceVarsTagsList>*> face_fields,
     160             :     const Variables<VolumeVarsTagsList>& volume_fields,
     161             :     const Mesh<Dim>& volume_mesh, const Direction<Dim>& direction) {
     162             :   static_assert(tmpl::size<VolumeVarsTagsList>::value != 0,
     163             :                 "Must have non-zero number of volume fields");
     164             :   static_assert(
     165             :       tmpl::size<
     166             :           tmpl::list_difference<TagsToProjectList, FaceVarsTagsList>>::value ==
     167             :           0,
     168             :       "All of the tags in TagsToProjectList must be in FaceVarsTagsList");
     169             :   static_assert(
     170             :       tmpl::size<tmpl::list_difference<TagsToProjectList,
     171             :                                        VolumeVarsTagsList>>::value == 0,
     172             :       "All of the tags in TagsToProjectList must be in VolumeVarsTagsList");
     173             :   const size_t sliced_dim = direction.dimension();
     174             :   if (volume_mesh.quadrature(sliced_dim) == Spectral::Quadrature::Gauss) {
     175             :     const Matrix identity{};
     176             :     auto interpolation_matrices = make_array<Dim>(std::cref(identity));
     177             :     const std::pair<Matrix, Matrix>& matrices =
     178             :         Spectral::boundary_interpolation_matrices(
     179             :             volume_mesh.slice_through(sliced_dim));
     180             :     gsl::at(interpolation_matrices, sliced_dim) =
     181             :         direction.side() == Side::Upper ? matrices.second : matrices.first;
     182             :     tmpl::for_each<TagsToProjectList>([&face_fields, &interpolation_matrices,
     183             :                                        &volume_fields,
     184             :                                        &volume_mesh](auto tag_v) {
     185             :       using tag = typename decltype(tag_v)::type;
     186             :       auto& face_field = get<tag>(*face_fields);
     187             :       const auto& volume_field = get<tag>(volume_fields);
     188             :       DataVector face_view{face_field[0].data(),
     189             :                            face_field[0].size() * face_field.size()};
     190             :       apply_matrices(make_not_null(&face_view), interpolation_matrices,
     191             :                      // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
     192             :                      DataVector{const_cast<double*>(volume_field[0].data()),
     193             :                                 volume_field[0].size() * volume_field.size()},
     194             :                      volume_mesh.extents());
     195             :     });
     196             :   } else {
     197             :     const size_t fixed_index = direction.side() == Side::Upper
     198             :                                    ? volume_mesh.extents(sliced_dim) - 1
     199             :                                    : 0;
     200             : 
     201             :     const size_t interface_grid_points =
     202             :         volume_mesh.extents().slice_away(sliced_dim).product();
     203             :     const size_t volume_grid_points = volume_mesh.number_of_grid_points();
     204             : 
     205             :     // Run the SliceIterator as the outer-most loop since incrementing the slice
     206             :     // iterator is surprisingly expensive.
     207             :     for (SliceIterator si(volume_mesh.extents(), sliced_dim, fixed_index); si;
     208             :          ++si) {
     209             :       tmpl::for_each<TagsToProjectList>([&face_fields, interface_grid_points,
     210             :                                          &si, &volume_fields,
     211             :                                          volume_grid_points](auto tag_v) {
     212             :         using tag = typename decltype(tag_v)::type;
     213             : 
     214             :         const double* vars_data = get<tag>(volume_fields)[0].data();
     215             :         double* interface_vars_data = get<tag>(*face_fields)[0].data();
     216             :         static constexpr size_t number_of_independent_components_in_tensor =
     217             :             std::decay_t<decltype(get<tag>(volume_fields))>::size();
     218             : 
     219             :         for (size_t i = 0; i < number_of_independent_components_in_tensor;
     220             :              ++i) {
     221             :           // clang-tidy: do not use pointer arithmetic
     222             :           interface_vars_data[si.slice_offset() +                      // NOLINT
     223             :                               i * interface_grid_points] =             // NOLINT
     224             :               vars_data[si.volume_offset() + i * volume_grid_points];  // NOLINT
     225             :         }
     226             :       });
     227             :     }
     228             :   }
     229             : }
     230             : 
     231             : /// @{
     232             : /*!
     233             :  * \brief Projects a tensor to the face
     234             :  *
     235             :  * \note This function works for both Gauss and Gauss-Lobatto uniform meshes.
     236             :  */
     237             : template <typename Symm, typename IndexList, size_t Dim>
     238           1 : void project_tensor_to_boundary(
     239             :     const gsl::not_null<Tensor<DataVector, Symm, IndexList>*> face_field,
     240             :     const Tensor<DataVector, Symm, IndexList>& volume_field,
     241             :     const Mesh<Dim>& volume_mesh, const Direction<Dim>& direction) {
     242             :   const size_t sliced_dim = direction.dimension();
     243             :   if (volume_mesh.quadrature(sliced_dim) == Spectral::Quadrature::Gauss) {
     244             :     const Matrix identity{};
     245             :     auto interpolation_matrices = make_array<Dim>(std::cref(identity));
     246             :     const std::pair<Matrix, Matrix>& matrices =
     247             :         Spectral::boundary_interpolation_matrices(
     248             :             volume_mesh.slice_through(sliced_dim));
     249             :     gsl::at(interpolation_matrices, sliced_dim) =
     250             :         direction.side() == Side::Upper ? matrices.second : matrices.first;
     251             :     for (size_t tensor_storage_index = 0;
     252             :          tensor_storage_index < volume_field.size(); ++tensor_storage_index) {
     253             :       apply_matrices(make_not_null(&(*face_field)[tensor_storage_index]),
     254             :                      interpolation_matrices, volume_field[tensor_storage_index],
     255             :                      volume_mesh.extents());
     256             :     }
     257             :   } else {
     258             :     const size_t fixed_index = direction.side() == Side::Upper
     259             :                                    ? volume_mesh.extents(sliced_dim) - 1
     260             :                                    : 0;
     261             : 
     262             :     // Run the SliceIterator as the outer-most loop since incrementing the slice
     263             :     // iterator is surprisingly expensive.
     264             :     for (SliceIterator si(volume_mesh.extents(), sliced_dim, fixed_index); si;
     265             :          ++si) {
     266             :       for (size_t tensor_storage_index = 0;
     267             :            tensor_storage_index < volume_field.size(); ++tensor_storage_index) {
     268             :         (*face_field)[tensor_storage_index][si.slice_offset()] =
     269             :             volume_field[tensor_storage_index][si.volume_offset()];
     270             :       }
     271             :     }
     272             :   }
     273             : }
     274             : 
     275             : template <typename Symm, typename IndexList, size_t Dim>
     276           1 : Tensor<DataVector, Symm, IndexList> project_tensor_to_boundary(
     277             :     const Tensor<DataVector, Symm, IndexList>& volume_field,
     278             :     const Mesh<Dim>& volume_mesh, const Direction<Dim>& direction) {
     279             :   Tensor<DataVector, Symm, IndexList> face_field{
     280             :       volume_mesh.slice_away(direction.dimension()).number_of_grid_points()};
     281             :   project_tensor_to_boundary(make_not_null(&face_field), volume_field,
     282             :                              volume_mesh, direction);
     283             :   return face_field;
     284             : }
     285             : /// @}
     286             : 
     287             : }  // namespace dg

Generated by: LCOV version 1.14