SpECTRE Documentation Coverage Report
Current view: top level - Evolution/Systems/NewtonianMhd/Limiters - CharacteristicHelpers.hpp Coverage Total Hit
Commit: a37d7b3c1803b88b537d2aef1c27ca5e6b499398 Lines: 75.0 % 8 6
Test Date: 2026-11-12 04:47:19
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 <array>
       7              : #include <cstddef>
       8              : #include <limits>
       9              : #include <string>
      10              : #include <type_traits>
      11              : #include <unordered_map>
      12              : #include <utility>
      13              : 
      14              : #include "DataStructures/TaggedTuple.hpp"
      15              : #include "DataStructures/Tags.hpp"
      16              : #include "DataStructures/Tags/TempTensor.hpp"
      17              : #include "DataStructures/Tensor/Tensor.hpp"
      18              : #include "DataStructures/Variables.hpp"
      19              : #include "Evolution/Systems/NewtonianMhd/Characteristics.hpp"
      20              : #include "Evolution/Systems/NewtonianMhd/SoundSpeedSquared.hpp"
      21              : #include "Evolution/Systems/NewtonianMhd/Tags.hpp"
      22              : #include "NumericalAlgorithms/LinearOperators/MeanValue.hpp"
      23              : #include "NumericalAlgorithms/Spectral/Mesh.hpp"
      24              : #include "PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp"
      25              : #include "Utilities/Gsl.hpp"
      26              : #include "Utilities/TMPL.hpp"
      27              : 
      28            0 : namespace NewtonianMhd::Limiters {
      29              : 
      30              : /*!
      31              :  * \brief Compute the transform matrices between the conserved variables and
      32              :  * the characteristic variables of the NewtonianMhd system.
      33              :  *
      34              :  * Wraps calls to `NewtonianMhd::right_eigenvectors` and
      35              :  * `NewtonianMhd::left_eigenvectors`.
      36              :  */
      37              : template <size_t VolumeDim>
      38            1 : std::pair<Matrix, Matrix> right_and_left_eigenvectors(
      39              :     const Scalar<double>& mean_density,
      40              :     const tnsr::I<double, VolumeDim>& mean_momentum,
      41              :     const Scalar<double>& mean_energy,
      42              :     const EquationsOfState::EquationOfState<false, 2>& equation_of_state,
      43              :     const tnsr::i<double, VolumeDim>& unit_vector,
      44              :     bool compute_char_transformation_numerically = false);
      45              : 
      46              : /// @{
      47              : /// \brief Compute characteristic fields from conserved fields
      48              : ///
      49              : /// Note that these functions apply the same transformation to every grid point
      50              : /// in the element, using the same matrix from `left_eigenvectors`.
      51              : /// This is in contrast to the characteristic transformation used in the
      52              : /// GeneralizedHarmonic upwind flux, which is computed pointwise.
      53              : ///
      54              : /// By using a fixed matrix of eigenvectors computed from the cell-averaged
      55              : /// fields, we ensure we can consistently transform back from the characteristic
      56              : /// variables even after applying the limiter (the limiter changes the pointwise
      57              : /// values but preserves the cell averages).
      58              : template <size_t VolumeDim>
      59            1 : void characteristic_fields(
      60              :     gsl::not_null<tuples::TaggedTuple<
      61              :         ::Tags::Mean<NewtonianMhd::Tags::VMinus>,
      62              :         ::Tags::Mean<NewtonianMhd::Tags::VMomentum<VolumeDim>>,
      63              :         ::Tags::Mean<NewtonianMhd::Tags::VPlus>>*>
      64              :         char_means,
      65              :     const tuples::TaggedTuple<
      66              :         ::Tags::Mean<NewtonianMhd::Tags::MassDensityCons>,
      67              :         ::Tags::Mean<NewtonianMhd::Tags::MomentumDensity<VolumeDim>>,
      68              :         ::Tags::Mean<NewtonianMhd::Tags::EnergyDensity>>& cons_means,
      69              :     const Matrix& left);
      70              : 
      71              : template <size_t VolumeDim>
      72            1 : void characteristic_fields(
      73              :     gsl::not_null<Scalar<DataVector>*> char_v_minus,
      74              :     gsl::not_null<tnsr::I<DataVector, VolumeDim>*> char_v_momentum,
      75              :     gsl::not_null<Scalar<DataVector>*> char_v_plus,
      76              :     const Scalar<DataVector>& cons_mass_density,
      77              :     const tnsr::I<DataVector, VolumeDim>& cons_momentum_density,
      78              :     const Scalar<DataVector>& cons_energy_density, const Matrix& left);
      79              : 
      80              : template <size_t VolumeDim>
      81            1 : void characteristic_fields(
      82              :     gsl::not_null<Variables<tmpl::list<NewtonianMhd::Tags::VMinus,
      83              :                                        NewtonianMhd::Tags::VMomentum<VolumeDim>,
      84              :                                        NewtonianMhd::Tags::VPlus>>*>
      85              :         char_vars,
      86              :     const Variables<tmpl::list<NewtonianMhd::Tags::MassDensityCons,
      87              :                                NewtonianMhd::Tags::MomentumDensity<VolumeDim>,
      88              :                                NewtonianMhd::Tags::EnergyDensity>>& cons_vars,
      89              :     const Matrix& left);
      90              : /// @}
      91              : 
      92              : /// \brief Compute conserved fields from characteristic fields
      93              : ///
      94              : /// Note that this function applies the same transformation to every grid point
      95              : /// in the element, using the same matrix from `right_eigenvectors`.
      96              : /// This is in contrast to the characteristic transformation used in the
      97              : /// GeneralizedHarmonic upwind flux, which is computed pointwise.
      98              : ///
      99              : /// By using a fixed matrix of eigenvectors computed from the cell-averaged
     100              : /// fields, we ensure we can consistently transform back from the characteristic
     101              : /// variables even after applying the limiter (the limiter changes the pointwise
     102              : /// values but preserves the cell averages).
     103              : template <size_t VolumeDim>
     104            1 : void conserved_fields_from_characteristic_fields(
     105              :     gsl::not_null<Scalar<DataVector>*> cons_mass_density,
     106              :     gsl::not_null<tnsr::I<DataVector, VolumeDim>*> cons_momentum_density,
     107              :     gsl::not_null<Scalar<DataVector>*> cons_energy_density,
     108              :     const Scalar<DataVector>& char_v_minus,
     109              :     const tnsr::I<DataVector, VolumeDim>& char_v_momentum,
     110              :     const Scalar<DataVector>& char_v_plus, const Matrix& right);
     111              : 
     112              : /// \brief Apply a limiter to the characteristic fields computed with respect
     113              : /// to each direction in the volume, then take average of results
     114              : ///
     115              : /// When computing the characteristic fields in the volume to pass as inputs to
     116              : /// the limiter, it is not necessarily clear (in more than one dimension) which
     117              : /// unit vector should be used in the characteristic decomposition. This is in
     118              : /// contrast to uses of characteristic fields for, e.g., computing boundary
     119              : /// conditions where the boundary normal provides a clear choice of unit vector.
     120              : //
     121              : /// The common solution to this challenge is to average the results of applying
     122              : /// the limiter with different choices of characteristic decomposition:
     123              : /// - For each direction (e.g. in 3D for each of
     124              : ///   \f$(\hat{x}, \hat{y}, \hat{z})\f$), compute the characteristic fields with
     125              : ///   respect to this direction. This gives `VolumeDim` sets of characteristic
     126              : ///   fields.
     127              : /// - Apply the limiter to each set of characteristic fields, then convert back
     128              : ///   to conserved fields. This gives `VolumeDim` different sets of limited
     129              : ///   conserved fields.
     130              : /// - Average the new conserved fields; this is the result of the limiting
     131              : ///   process.
     132              : ///
     133              : /// This function handles the logic of computing the different characteristic
     134              : /// fields, limiting them, converting back to conserved fields, and averaging.
     135              : ///
     136              : /// The limiter to apply is passed in via a lambda which is responsible for
     137              : /// converting any neighbor data to the characteristic representation, and then
     138              : /// applying the limiter to all NewtonianMhd characteristic fields.
     139              : template <size_t VolumeDim, typename LimiterLambda>
     140            1 : bool apply_limiter_to_characteristic_fields_in_all_directions(
     141              :     const gsl::not_null<Scalar<DataVector>*> mass_density_cons,
     142              :     const gsl::not_null<tnsr::I<DataVector, VolumeDim>*> momentum_density,
     143              :     const gsl::not_null<Scalar<DataVector>*> energy_density,
     144              :     const Mesh<VolumeDim>& mesh,
     145              :     const EquationsOfState::EquationOfState<false, 2>& equation_of_state,
     146              :     const LimiterLambda& prepare_and_apply_limiter,
     147              :     const bool compute_char_transformation_numerically = false) {
     148              :   // Temp variables for calculations
     149              :   // There are quite a few tensors in this allocation because in general we need
     150              :   // to preserve the input (cons field) tensors until they are overwritten at
     151              :   // the very end of the computation... then we need additional buffers for the
     152              :   // char fields, the limited cons fields, and the accumulated cons fields for
     153              :   // averaging.
     154              :   //
     155              :   // Possible optimization: specialize the 1D case which doesn't do average so
     156              :   // doesn't need as many buffers
     157              :   Variables<tmpl::list<
     158              :       NewtonianMhd::Tags::VMinus, NewtonianMhd::Tags::VMomentum<VolumeDim>,
     159              :       NewtonianMhd::Tags::VPlus, ::Tags::TempScalar<0>,
     160              :       ::Tags::TempI<0, VolumeDim>, ::Tags::TempScalar<1>, ::Tags::TempScalar<2>,
     161              :       ::Tags::TempI<1, VolumeDim>, ::Tags::TempScalar<3>>>
     162              :       temp_buffer(mesh.number_of_grid_points());
     163              :   auto& char_v_minus = get<NewtonianMhd::Tags::VMinus>(temp_buffer);
     164              :   auto& char_v_momentum =
     165              :       get<NewtonianMhd::Tags::VMomentum<VolumeDim>>(temp_buffer);
     166              :   auto& char_v_plus = get<NewtonianMhd::Tags::VPlus>(temp_buffer);
     167              :   auto& temp_mass_density_cons = get<::Tags::TempScalar<0>>(temp_buffer);
     168              :   auto& temp_momentum_density = get<::Tags::TempI<0, VolumeDim>>(temp_buffer);
     169              :   auto& temp_energy_density = get<::Tags::TempScalar<1>>(temp_buffer);
     170              :   auto& accumulate_mass_density_cons = get<::Tags::TempScalar<2>>(temp_buffer);
     171              :   auto& accumulate_momentum_density =
     172              :       get<::Tags::TempI<1, VolumeDim>>(temp_buffer);
     173              :   auto& accumulate_energy_density = get<::Tags::TempScalar<3>>(temp_buffer);
     174              : 
     175              :   // Initialize the accumulating tensors
     176              :   get(accumulate_mass_density_cons) = 0.;
     177              :   for (size_t i = 0; i < VolumeDim; ++i) {
     178              :     accumulate_momentum_density.get(i) = 0.;
     179              :   }
     180              :   get(accumulate_energy_density) = 0.;
     181              : 
     182              :   // Cellwise means, used in computing the cons/char transformations
     183              :   const auto mean_density =
     184              :       Scalar<double>{mean_value(get(*mass_density_cons), mesh)};
     185              :   const auto mean_momentum = [&momentum_density, &mesh]() {
     186              :     tnsr::I<double, VolumeDim> result{};
     187              :     for (size_t i = 0; i < VolumeDim; ++i) {
     188              :       result.get(i) = mean_value(momentum_density->get(i), mesh);
     189              :     }
     190              :     return result;
     191              :   }();
     192              :   const auto mean_energy =
     193              :       Scalar<double>{mean_value(get(*energy_density), mesh)};
     194              : 
     195              :   bool some_component_was_limited = false;
     196              : 
     197              :   // Loop over directions, then compute chars w.r.t. this direction and limit
     198              :   for (size_t d = 0; d < VolumeDim; ++d) {
     199              :     const auto unit_vector = [&d]() {
     200              :       auto components = make_array<VolumeDim>(0.);
     201              :       components[d] = 1.;
     202              :       return tnsr::i<double, VolumeDim>(components);
     203              :     }();
     204              :     const auto right_and_left =
     205              :         NewtonianMhd::Limiters::right_and_left_eigenvectors(
     206              :             mean_density, mean_momentum, mean_energy, equation_of_state,
     207              :             unit_vector, compute_char_transformation_numerically);
     208              :     const auto& right = right_and_left.first;
     209              :     const auto& left = right_and_left.second;
     210              : 
     211              :     // Transform tensors to characteristics
     212              :     NewtonianMhd::Limiters::characteristic_fields(
     213              :         make_not_null(&char_v_minus), make_not_null(&char_v_momentum),
     214              :         make_not_null(&char_v_plus), *mass_density_cons, *momentum_density,
     215              :         *energy_density, left);
     216              : 
     217              :     // Transform neighbor data and apply limiter
     218              :     const bool some_component_was_limited_with_this_unit_vector =
     219              :         prepare_and_apply_limiter(make_not_null(&char_v_minus),
     220              :                                   make_not_null(&char_v_momentum),
     221              :                                   make_not_null(&char_v_plus), left);
     222              : 
     223              :     some_component_was_limited =
     224              :         some_component_was_limited_with_this_unit_vector or
     225              :         some_component_was_limited;
     226              : 
     227              :     // Transform back to conserved variables. But skip the transformation if no
     228              :     // limiting occured with this unit vector.
     229              :     if (some_component_was_limited_with_this_unit_vector) {
     230              :       NewtonianMhd::Limiters::conserved_fields_from_characteristic_fields(
     231              :           make_not_null(&temp_mass_density_cons),
     232              :           make_not_null(&temp_momentum_density),
     233              :           make_not_null(&temp_energy_density), char_v_minus, char_v_momentum,
     234              :           char_v_plus, right);
     235              :     } else {
     236              :       temp_mass_density_cons = *mass_density_cons;
     237              :       temp_momentum_density = *momentum_density;
     238              :       temp_energy_density = *energy_density;
     239              :     }
     240              : 
     241              :     // Add to running sum for averaging
     242              :     const double one_over_dim = 1.0 / static_cast<double>(VolumeDim);
     243              :     get(accumulate_mass_density_cons) +=
     244              :         one_over_dim * get(temp_mass_density_cons);
     245              :     for (size_t i = 0; i < VolumeDim; ++i) {
     246              :       accumulate_momentum_density.get(i) +=
     247              :           one_over_dim * temp_momentum_density.get(i);
     248              :     }
     249              :     get(accumulate_energy_density) += one_over_dim * get(temp_energy_density);
     250              :   }  // for loop over dimensions
     251              : 
     252              :   *mass_density_cons = accumulate_mass_density_cons;
     253              :   *momentum_density = accumulate_momentum_density;
     254              :   *energy_density = accumulate_energy_density;
     255              :   return some_component_was_limited;
     256              : }
     257              : 
     258              : }  // namespace NewtonianMhd::Limiters
        

Generated by: LCOV version 2.0-1