SpECTRE Documentation Coverage Report
Current view: top level - NumericalAlgorithms/LinearOperators - WeakDivergence.hpp Hit Total Coverage
Commit: 1f2210958b4f38fdc0400907ee7c6d5af5111418 Lines: 3 4 75.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/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 1.14