SpECTRE Documentation Coverage Report
Current view: top level - NumericalAlgorithms/LinearOperators - WeakDivergence.hpp Coverage Total Hit
Commit: 0d1d717af84a81fb1284a20c4013708fe85cc2f0 Lines: 75.0 % 4 3
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/Matrix.hpp"
      10              : #include "DataStructures/Tensor/Tensor.hpp"
      11              : #include "DataStructures/Transpose.hpp"
      12              : #include "DataStructures/Variables.hpp"
      13              : #include "NumericalAlgorithms/LinearOperators/Divergence.hpp"
      14              : #include "NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp"
      15              : #include "NumericalAlgorithms/Spectral/DifferentiationMatrix.hpp"
      16              : #include "NumericalAlgorithms/Spectral/Mesh.hpp"
      17              : #include "Utilities/Blas.hpp"
      18              : #include "Utilities/ContainerHelpers.hpp"
      19              : #include "Utilities/Gsl.hpp"
      20              : #include "Utilities/Literals.hpp"
      21              : #include "Utilities/TMPL.hpp"
      22              : 
      23              : /*!
      24              :  * \ingroup NumericalAlgorithmsGroup
      25              :  * \brief Compute the weak form divergence of fluxes
      26              :  *
      27              :  * In a discontinuous Galerkin scheme we integrate the equations against the
      28              :  * basis functions over the element. For the flux divergence term this gives:
      29              :  *
      30              :  * \f{align*}{
      31              :  *  \int_{\Omega}d^n x \phi_{\breve{\imath}}\partial_i F^i,
      32              :  * \f}
      33              :  *
      34              :  * where the basis functions are denoted by \f$\phi_{\breve{\imath}}\f$.
      35              :  *
      36              :  * Integrating by parts we get
      37              :  *
      38              :  * \f{align*}{
      39              :  *  \int_{\Omega}d^n x\, \phi_{\breve{\imath}}\partial_i F^i
      40              :  *  = -\int_{\Omega}d^n x\,  F^i \partial_i \phi_{\breve{\imath}}
      41              :  *   + \int_{\partial\Omega} d^{(n-1)}\Sigma\, n_i F^i \phi_{\breve{\imath}}
      42              :  * \f}
      43              :  *
      44              :  * Next we expand the flux \f$F^i\f$ in terms of the basis functions, yielding
      45              :  *
      46              :  * \f{align*}{
      47              :  *  - \int_{\Omega}d^n x\,F^i_{\breve{\jmath}} \phi_{\breve{\jmath}} \partial_i
      48              :  *    \phi_{\breve{\imath}}
      49              :  *  + \int_{\partial\Omega} d^{(n-1)}\Sigma\, n_i F^i_{\breve{\jmath}}
      50              :  *    \phi_{\breve{\jmath}} \phi_{\breve{\imath}}
      51              :  * \f}
      52              :  *
      53              :  * This function computes the volume term:
      54              :  *
      55              :  * \f{align*}{
      56              :  * \frac{1}{w_\breve{\imath}} \int_{\Omega}d^n x \,
      57              :  *   F^i_{\breve{\jmath}} \phi_{\breve{\jmath}} \partial_i \phi_{\breve{\imath}}
      58              :  * \f}
      59              :  *
      60              :  * \note When using Gauss-Lobatto points the numerical values of the
      61              :  * divergence are the same for the strong and weak divergence at the interior
      62              :  * points. When using Gauss points they are only the same at the central grid
      63              :  * point and only when an odd number of grid points is used.
      64              :  */
      65              : template <typename... ResultTags, typename... FluxTags, size_t Dim>
      66            1 : void weak_divergence(
      67              :     const gsl::not_null<Variables<tmpl::list<ResultTags...>>*>
      68              :         divergence_of_fluxes,
      69              :     const Variables<tmpl::list<FluxTags...>>& fluxes, const Mesh<Dim>& mesh,
      70              :     const InverseJacobian<DataVector, Dim, Frame::ElementLogical,
      71              :                           Frame::Inertial>& det_jac_times_inverse_jacobian) {
      72              :   if (UNLIKELY(divergence_of_fluxes->number_of_grid_points() !=
      73              :                fluxes.number_of_grid_points())) {
      74              :     divergence_of_fluxes->initialize(fluxes.number_of_grid_points());
      75              :   }
      76              : 
      77              :   using ValueType = typename Variables<tmpl::list<FluxTags...>>::value_type;
      78              : 
      79              :   if constexpr (Dim == 1) {
      80              :     (void)det_jac_times_inverse_jacobian;  // is identically 1.0 in 1d
      81              : 
      82              :     partial_derivatives_detail::apply_matrix_in_first_dim(
      83              :         divergence_of_fluxes->data(), fluxes.data(),
      84              :         Spectral::weak_flux_differentiation_matrix(mesh), fluxes.size(), false);
      85              :   } else {
      86              :     // Multiplies the flux by det_jac_time_inverse_jacobian.
      87              :     const auto transform_to_logical_frame =
      88              :         [&det_jac_times_inverse_jacobian, &fluxes](
      89              :             auto flux_tag_v, auto div_tag_v,
      90              :             const gsl::not_null<Variables<tmpl::list<ResultTags...>>*>
      91              :                 result_buffer,
      92              :             auto logical_index_of_jacobian) {
      93              :           using flux_tag = tmpl::type_from<decltype(flux_tag_v)>;
      94              :           using div_tag = tmpl::type_from<decltype(div_tag_v)>;
      95              :           using TensorIndexType =
      96              :               std::array<size_t, div_tag::type::num_tensor_indices>;
      97              :           using FluxIndexType =
      98              :               std::array<size_t, flux_tag::type::num_tensor_indices>;
      99              : 
     100              :           auto& result = get<div_tag>(*result_buffer);
     101              :           const auto& flux = get<flux_tag>(fluxes);
     102              : 
     103              :           for (size_t result_storage_index = 0;
     104              :                result_storage_index < result.size(); ++result_storage_index) {
     105              :             const TensorIndexType result_tensor_index =
     106              :                 result.get_tensor_index(result_storage_index);
     107              :             const FluxIndexType flux_x_tensor_index =
     108              :                 prepend(result_tensor_index, 0_st);
     109              :             const FluxIndexType flux_y_tensor_index =
     110              :                 prepend(result_tensor_index, 1_st);
     111              :             if constexpr (Dim == 2) {
     112              :               result[result_storage_index] =
     113              :                   get<std::decay_t<decltype(logical_index_of_jacobian)>::value,
     114              :                       0>(det_jac_times_inverse_jacobian) *
     115              :                       flux.get(flux_x_tensor_index) +
     116              :                   get<std::decay_t<decltype(logical_index_of_jacobian)>::value,
     117              :                       1>(det_jac_times_inverse_jacobian) *
     118              :                       flux.get(flux_y_tensor_index);
     119              :             } else {
     120              :               const FluxIndexType flux_z_tensor_index =
     121              :                   prepend(result_tensor_index, 2_st);
     122              :               result[result_storage_index] =
     123              :                   get<std::decay_t<decltype(logical_index_of_jacobian)>::value,
     124              :                       0>(det_jac_times_inverse_jacobian) *
     125              :                       flux.get(flux_x_tensor_index) +
     126              :                   get<std::decay_t<decltype(logical_index_of_jacobian)>::value,
     127              :                       1>(det_jac_times_inverse_jacobian) *
     128              :                       flux.get(flux_y_tensor_index) +
     129              :                   get<std::decay_t<decltype(logical_index_of_jacobian)>::value,
     130              :                       2>(det_jac_times_inverse_jacobian) *
     131              :                       flux.get(flux_z_tensor_index);
     132              :             }
     133              :           }
     134              :         };
     135              : 
     136              :     if constexpr (Dim == 2) {
     137              :       Variables<tmpl::list<ResultTags...>> data_buffer{
     138              :           divergence_of_fluxes->number_of_grid_points()};
     139              : 
     140              :       const Matrix& eta_weak_div_matrix =
     141              :           Spectral::weak_flux_differentiation_matrix(mesh.slice_through(1));
     142              :       const Matrix& xi_weak_div_matrix =
     143              :           Spectral::weak_flux_differentiation_matrix(mesh.slice_through(0));
     144              : 
     145              :       // Compute the eta divergence term. Since that also needs a transpose,
     146              :       // copy into result, then transpose into `data_buffer`
     147              :       EXPAND_PACK_LEFT_TO_RIGHT(transform_to_logical_frame(
     148              :           tmpl::type_<FluxTags>{}, tmpl::type_<ResultTags>{},
     149              :           make_not_null(&data_buffer), std::integral_constant<size_t, 1>{}));
     150              :       ValueType* div_ptr = divergence_of_fluxes->data();
     151              :       raw_transpose(make_not_null(div_ptr), data_buffer.data(),
     152              :                     xi_weak_div_matrix.rows(),
     153              :                     divergence_of_fluxes->size() / xi_weak_div_matrix.rows());
     154              :       partial_derivatives_detail::apply_matrix_in_first_dim(
     155              :           data_buffer.data(), divergence_of_fluxes->data(), eta_weak_div_matrix,
     156              :           data_buffer.size(), false);
     157              : 
     158              :       const size_t chunk_size = Variables<tmpl::list<Tags::div<FluxTags>...>>::
     159              :                                     number_of_independent_components *
     160              :                                 eta_weak_div_matrix.rows();
     161              :       raw_transpose(make_not_null(div_ptr), data_buffer.data(), chunk_size,
     162              :                     data_buffer.size() / chunk_size);
     163              : 
     164              :       // Now compute xi divergence and *add* to eta divergence
     165              :       EXPAND_PACK_LEFT_TO_RIGHT(transform_to_logical_frame(
     166              :           tmpl::type_<FluxTags>{}, tmpl::type_<ResultTags>{},
     167              :           make_not_null(&data_buffer), std::integral_constant<size_t, 0>{}));
     168              :       partial_derivatives_detail::apply_matrix_in_first_dim(
     169              :           divergence_of_fluxes->data(), data_buffer.data(), xi_weak_div_matrix,
     170              :           data_buffer.size(), true);
     171              :     } else if constexpr (Dim == 3) {
     172              :       Variables<tmpl::list<ResultTags...>> data_buffer0{
     173              :           divergence_of_fluxes->number_of_grid_points()};
     174              :       Variables<tmpl::list<ResultTags...>> data_buffer1{
     175              :           divergence_of_fluxes->number_of_grid_points()};
     176              :       constexpr size_t number_of_independent_components =
     177              :           decltype(data_buffer1)::number_of_independent_components;
     178              : 
     179              :       const Matrix& zeta_weak_div_matrix =
     180              :           Spectral::weak_flux_differentiation_matrix(mesh.slice_through(2));
     181              :       const Matrix& eta_weak_div_matrix =
     182              :           Spectral::weak_flux_differentiation_matrix(mesh.slice_through(1));
     183              :       const Matrix& xi_weak_div_matrix =
     184              :           Spectral::weak_flux_differentiation_matrix(mesh.slice_through(0));
     185              : 
     186              :       // Compute the zeta divergence term. Since that also needs a transpose,
     187              :       // copy into data_buffer0, then transpose into `data_buffer1`.
     188              :       EXPAND_PACK_LEFT_TO_RIGHT(transform_to_logical_frame(
     189              :           tmpl::type_<FluxTags>{}, tmpl::type_<ResultTags>{},
     190              :           make_not_null(&data_buffer0), std::integral_constant<size_t, 2>{}));
     191              :       size_t chunk_size =
     192              :           xi_weak_div_matrix.rows() * eta_weak_div_matrix.rows();
     193              :       ValueType* result_ptr = data_buffer1.data();
     194              :       raw_transpose(make_not_null(result_ptr), data_buffer0.data(), chunk_size,
     195              :                     data_buffer0.size() / chunk_size);
     196              :       partial_derivatives_detail::apply_matrix_in_first_dim(
     197              :           data_buffer0.data(), data_buffer1.data(), zeta_weak_div_matrix,
     198              :           data_buffer0.size(), false);
     199              :       chunk_size =
     200              :           number_of_independent_components * zeta_weak_div_matrix.rows();
     201              :       result_ptr = divergence_of_fluxes->data();
     202              :       raw_transpose(make_not_null(result_ptr), data_buffer0.data(), chunk_size,
     203              :                     data_buffer1.size() / chunk_size);
     204              : 
     205              :       // Compute the eta divergence term. Since that also needs a transpose,
     206              :       // copy into data_buffer0, then transpose into `data_buffer1`.
     207              :       EXPAND_PACK_LEFT_TO_RIGHT(transform_to_logical_frame(
     208              :           tmpl::type_<FluxTags>{}, tmpl::type_<ResultTags>{},
     209              :           make_not_null(&data_buffer0), std::integral_constant<size_t, 1>{}));
     210              :       chunk_size = xi_weak_div_matrix.rows();
     211              :       result_ptr = data_buffer1.data();
     212              :       raw_transpose(make_not_null(result_ptr), data_buffer0.data(), chunk_size,
     213              :                     data_buffer1.size() / chunk_size);
     214              :       partial_derivatives_detail::apply_matrix_in_first_dim(
     215              :           data_buffer0.data(), data_buffer1.data(), eta_weak_div_matrix,
     216              :           data_buffer0.size(), false);
     217              :       chunk_size = number_of_independent_components *
     218              :                    eta_weak_div_matrix.rows() * zeta_weak_div_matrix.rows();
     219              :       result_ptr = data_buffer1.data();
     220              :       raw_transpose(make_not_null(result_ptr), data_buffer0.data(), chunk_size,
     221              :                     data_buffer0.size() / chunk_size);
     222              :       *divergence_of_fluxes += data_buffer1;
     223              : 
     224              :       // Now compute xi divergence and *add* to eta divergence
     225              :       EXPAND_PACK_LEFT_TO_RIGHT(transform_to_logical_frame(
     226              :           tmpl::type_<FluxTags>{}, tmpl::type_<ResultTags>{},
     227              :           make_not_null(&data_buffer0), std::integral_constant<size_t, 0>{}));
     228              :       partial_derivatives_detail::apply_matrix_in_first_dim(
     229              :           divergence_of_fluxes->data(), data_buffer0.data(), xi_weak_div_matrix,
     230              :           data_buffer0.size(), true);
     231              :     } else {
     232              :       static_assert(Dim == 1 or Dim == 2 or Dim == 3,
     233              :                     "Weak divergence only implemented in 1d, 2d, and 3d.");
     234              :     }
     235              :   }
     236              : }
     237              : 
     238              : /// @{
     239              : /*!
     240              :  * \brief Compute the weak divergence of fluxes in logical coordinates
     241              :  *
     242              :  * Applies the transpose logical differentiation matrix to the fluxes in each
     243              :  * dimension and sums over dimensions.
     244              :  *
     245              :  * \see weak_divergence
     246              :  */
     247              : template <typename ResultTensor, typename FluxTensor, size_t Dim>
     248            1 : void logical_weak_divergence(gsl::not_null<ResultTensor*> div_flux,
     249              :                              const FluxTensor& flux, const Mesh<Dim>& mesh);
     250              : 
     251              : /*!
     252              :  * \param divergence_of_fluxes Result buffer. Will be resized if necessary.
     253              :  * \param fluxes The fluxes to take the divergence of. Their first index must be
     254              :  * an upper spatial index in element-logical coordinates.
     255              :  * \param mesh The element mesh.
     256              :  * \param add_to_buffer If `true`, add the divergence to the existing values in
     257              :  * `divergence_of_fluxes`. Otherwise, overwrite the existing values.
     258              :  */
     259              : template <typename... ResultTags, typename... FluxTags, size_t Dim>
     260            1 : void logical_weak_divergence(
     261              :     const gsl::not_null<Variables<tmpl::list<ResultTags...>>*>
     262              :         divergence_of_fluxes,
     263              :     const Variables<tmpl::list<FluxTags...>>& fluxes, const Mesh<Dim>& mesh,
     264              :     const bool add_to_buffer = false) {
     265              :   static_assert(
     266              :       (... and
     267              :        std::is_same_v<tmpl::front<typename FluxTags::type::index_list>,
     268              :                       SpatialIndex<Dim, UpLo::Up, Frame::ElementLogical>>),
     269              :       "The first index of each flux must be an upper spatial index in "
     270              :       "element-logical coordinates.");
     271              :   static_assert(
     272              :       (... and
     273              :        std::is_same_v<
     274              :            typename ResultTags::type,
     275              :            TensorMetafunctions::remove_first_index<typename FluxTags::type>>),
     276              :       "The result tensors must have the same type as the flux tensors with "
     277              :       "their first index removed.");
     278              :   if (not add_to_buffer) {
     279              :     divergence_of_fluxes->initialize(mesh.number_of_grid_points(), 0.);
     280              :   }
     281              :   EXPAND_PACK_LEFT_TO_RIGHT(logical_weak_divergence(
     282              :       make_not_null(&get<ResultTags>(*divergence_of_fluxes)),
     283              :       get<FluxTags>(fluxes), mesh));
     284              : }
     285              : /// @}
        

Generated by: LCOV version 2.0-1