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

Generated by: LCOV version 1.14