SpECTRE Documentation Coverage Report
Current view: top level - Evolution/DgSubcell - RdmpTci.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 <algorithm>
       7              : #include <cstddef>
       8              : 
       9              : #include "DataStructures/DataVector.hpp"
      10              : #include "DataStructures/Tensor/Tensor.hpp"
      11              : #include "DataStructures/Variables.hpp"
      12              : #include "Evolution/DgSubcell/Tags/Inactive.hpp"
      13              : #include "Utilities/ErrorHandling/Assert.hpp"
      14              : #include "Utilities/TMPL.hpp"
      15              : 
      16              : namespace evolution::dg::subcell {
      17              : /*!
      18              :  * \brief Troubled cell indicator using a relaxed discrete maximum principle,
      19              :  * comparing the candidate solution with the past solution in the element and
      20              :  * its neighbors.
      21              :  *
      22              :  * Let the candidate solution be denoted by \f$u^\star_{\alpha}(t^{n+1})\f$.
      23              :  * Then the RDMP requires that
      24              :  *
      25              :  * \f{align*}{
      26              :  *   \min_{\forall\mathcal{N}}\left(u_{\alpha}(t^n)\right)
      27              :  *   - \delta_\alpha
      28              :  *   \le
      29              :  *   u^\star_{\alpha}(t^{n+1})
      30              :  *   \le
      31              :  *   \max_{\forall\mathcal{N}}
      32              :  *   \left(u_{\alpha}(t^n)\right) + \delta_\alpha
      33              :  * \f}
      34              :  *
      35              :  * where \f$\mathcal{N}\f$ are either the Neumann or Voronoi neighbors and the
      36              :  * element itself,  and \f$\delta_\alpha\f$ is a parameter defined below that
      37              :  * relaxes the discrete maximum principle (DMP). When computing
      38              :  * \f$\max(u_\alpha)\f$ and \f$\min(u_\alpha)\f$ over a DG element that is not
      39              :  * using subcells we first project the DG solution to the subcells and then
      40              :  * compute the maximum and minimum over *both* the DG grid and the subcell grid.
      41              :  * However, when a DG element is using subcells we compute the maximum and
      42              :  * minimum of \f$u_\alpha(t^n)\f$ over the subcells only. Note that the maximum
      43              :  * and minimum values of \f$u^\star_\alpha\f$ are always computed over both the
      44              :  * DG and the subcell grids, even when using the RDMP to check if the
      45              :  * reconstructed DG solution would be admissible.
      46              :  *
      47              :  * The parameter \f$\delta_\alpha\f$ is given by:
      48              :  *
      49              :  * \f{align*}{
      50              :  *   \delta_\alpha =
      51              :  *   \max\left(\delta_{0},\epsilon
      52              :  *   \left(\max_{\forall\mathcal{N}}\left(u_{\alpha}(t^n)\right)
      53              :  *   - \min_{\forall\mathcal{N}}\left(u_{\alpha}(t^n)\right)\right)
      54              :  *   \right),
      55              :  * \f}
      56              :  *
      57              :  * where we typically take \f$\delta_{0}=10^{-4}\f$ and \f$\epsilon=10^{-3}\f$.
      58              :  *
      59              :  * If all checks are passed and cell is not troubled, returns an integer `0`.
      60              :  * Otherwise returns 1-based index of the tag in the input Variables that fails
      61              :  * the check. For instance, if we have following two Variables objects as
      62              :  * candidate solutions on active and inactive grids
      63              :  *
      64              :  *  - `Variables<tmpl::list<DgVar1, DgVar2, DgVar3>>`
      65              :  *  - `Variables<tmpl::list<SubVar1, SubVar2, SubVar3>>`
      66              :  *
      67              :  * and TCI flags the second pair `DgVar2` and `SubVar2` not satisfying two-mesh
      68              :  * RDMP criteria, returned value is `2` since the second pair of tags failed the
      69              :  * check.
      70              :  *
      71              :  * \note Once a single pair of tags fails to satisfy the check, checks for the
      72              :  * remaining part of the input variables are skipped. In the example above, for
      73              :  * instance if the second pair (`DgVar2`,`SubVar2`) is flagged as troubled, the
      74              :  * third pair (`DgVar3`,`SubVar3`) is ignored and not checked.
      75              :  *
      76              :  */
      77              : template <typename... EvolvedVarsTags>
      78            1 : int rdmp_tci(const Variables<tmpl::list<EvolvedVarsTags...>>&
      79              :                  active_grid_candidate_evolved_vars,
      80              :              const Variables<tmpl::list<Tags::Inactive<EvolvedVarsTags>...>>&
      81              :                  inactive_grid_candidate_evolved_vars,
      82              :              const DataVector& max_of_past_variables,
      83              :              const DataVector& min_of_past_variables, const double rdmp_delta0,
      84              :              const double rdmp_epsilon) {
      85              :   bool cell_is_troubled = false;
      86              :   int rdmp_tci_status = 0;
      87              :   size_t component_index = 0;
      88              : 
      89              :   tmpl::for_each<tmpl::list<EvolvedVarsTags...>>(
      90              :       [&active_grid_candidate_evolved_vars, &cell_is_troubled, &component_index,
      91              :        &inactive_grid_candidate_evolved_vars, &max_of_past_variables,
      92              :        &min_of_past_variables, &rdmp_tci_status, rdmp_delta0,
      93              :        rdmp_epsilon](auto tag_v) {
      94              :         if (cell_is_troubled) {
      95              :           return;
      96              :         }
      97              :         using std::max;
      98              :         using std::min;
      99              : 
     100              :         using tag = tmpl::type_from<decltype(tag_v)>;
     101              :         using inactive_tag = Tags::Inactive<tag>;
     102              :         const auto& active_var = get<tag>(active_grid_candidate_evolved_vars);
     103              :         const auto& inactive_var =
     104              :             get<inactive_tag>(inactive_grid_candidate_evolved_vars);
     105              :         const size_t number_of_components = active_var.size();
     106              :         ASSERT(number_of_components == inactive_var.size(),
     107              :                "The active and inactive vars must have the same type of tensor "
     108              :                "and therefore the same number of components.");
     109              : 
     110              :         for (size_t tensor_storage_index = 0;
     111              :              tensor_storage_index < number_of_components;
     112              :              ++tensor_storage_index) {
     113              :           ASSERT(not cell_is_troubled,
     114              :                  "If a cell has already been marked as troubled during the "
     115              :                  "RDMP TCI, we should not be continuing to check other "
     116              :                  "variables.");
     117              :           const double max_active = max(active_var[tensor_storage_index]);
     118              :           const double min_active = min(active_var[tensor_storage_index]);
     119              :           const double max_inactive = max(inactive_var[tensor_storage_index]);
     120              :           const double min_inactive = min(inactive_var[tensor_storage_index]);
     121              :           const double delta =
     122              :               max(rdmp_delta0,
     123              :                   rdmp_epsilon * (max_of_past_variables[component_index] -
     124              :                                   min_of_past_variables[component_index]));
     125              :           cell_is_troubled =
     126              :               max(max_active, max_inactive) >
     127              :                   max_of_past_variables[component_index] + delta or
     128              :               min(min_active, min_inactive) <
     129              :                   min_of_past_variables[component_index] - delta;
     130              :           if (cell_is_troubled) {
     131              :             rdmp_tci_status = static_cast<int>(component_index + 1);
     132              :             return;
     133              :           }
     134              :           ++component_index;
     135              :         }
     136              :       });
     137              :   return rdmp_tci_status;
     138              : }
     139              : 
     140              : /*!
     141              :  * \brief get the max and min of each component of the active and inactive
     142              :  * variables. If `include_inactive_grid` is `false` then only the max over the
     143              :  * `active_grid_evolved_vars` for each component is returned.
     144              :  */
     145              : template <typename... EvolvedVarsTags>
     146            1 : std::pair<DataVector, DataVector> rdmp_max_min(
     147              :     const Variables<tmpl::list<EvolvedVarsTags...>>& active_grid_evolved_vars,
     148              :     const Variables<tmpl::list<Tags::Inactive<EvolvedVarsTags>...>>&
     149              :         inactive_grid_evolved_vars,
     150              :     const bool include_inactive_grid) {
     151              :   DataVector max_of_vars{
     152              :       active_grid_evolved_vars.number_of_independent_components,
     153              :       std::numeric_limits<double>::min()};
     154              :   DataVector min_of_vars{
     155              :       active_grid_evolved_vars.number_of_independent_components,
     156              :       std::numeric_limits<double>::max()};
     157              :   size_t component_index = 0;
     158              :   tmpl::for_each<tmpl::list<EvolvedVarsTags...>>(
     159              :       [&active_grid_evolved_vars, &component_index, &inactive_grid_evolved_vars,
     160              :        &include_inactive_grid, &max_of_vars, &min_of_vars](auto tag_v) {
     161              :         using std::max;
     162              :         using std::min;
     163              : 
     164              :         using tag = tmpl::type_from<decltype(tag_v)>;
     165              :         const auto& active_var = get<tag>(active_grid_evolved_vars);
     166              :         const size_t number_of_components_in_tensor = active_var.size();
     167              :         for (size_t tensor_storage_index = 0;
     168              :              tensor_storage_index < number_of_components_in_tensor;
     169              :              ++tensor_storage_index) {
     170              :           ASSERT(component_index < max_of_vars.size() and
     171              :                      component_index < min_of_vars.size(),
     172              :                  "The component index into the variables is out of bounds.");
     173              :           max_of_vars[component_index] = max(active_var[tensor_storage_index]);
     174              :           min_of_vars[component_index] = min(active_var[tensor_storage_index]);
     175              :           if (include_inactive_grid) {
     176              :             using inactive_tag = Tags::Inactive<tag>;
     177              :             const auto& inactive_var =
     178              :                 get<inactive_tag>(inactive_grid_evolved_vars);
     179              :             max_of_vars[component_index] =
     180              :                 max(max_of_vars[component_index],
     181              :                     max(inactive_var[tensor_storage_index]));
     182              :             min_of_vars[component_index] =
     183              :                 min(min_of_vars[component_index],
     184              :                     min(inactive_var[tensor_storage_index]));
     185              :           }
     186              :           ++component_index;
     187              :         }
     188              :       });
     189              :   return {std::move(max_of_vars), std::move(min_of_vars)};
     190              : }
     191              : 
     192              : /*!
     193              :  * \brief Check if the current variables satisfy the RDMP. Returns an integer
     194              :  * `0` if cell is not troubled and an integer `i+1` if the `[i]`-th element of
     195              :  * the input vector is responsible for failing the RDMP.
     196              :  *
     197              :  * Let the candidate solution be denoted by \f$u^\star_{\alpha}(t^{n+1})\f$.
     198              :  * Then the RDMP requires that
     199              :  *
     200              :  * \f{align*}{
     201              :  *   \min_{\forall\mathcal{N}}\left(u_{\alpha}(t^n)\right)
     202              :  *   - \delta_\alpha
     203              :  *   \le
     204              :  *   u^\star_{\alpha}(t^{n+1})
     205              :  *   \le
     206              :  *   \max_{\forall\mathcal{N}}
     207              :  *   \left(u_{\alpha}(t^n)\right) + \delta_\alpha
     208              :  * \f}
     209              :  *
     210              :  * where \f$\mathcal{N}\f$ are either the Neumann or Voronoi neighbors and the
     211              :  * element itself,  and \f$\delta_\alpha\f$ is a parameter defined below that
     212              :  * relaxes the discrete maximum principle (DMP). When computing
     213              :  * \f$\max(u_\alpha)\f$ and \f$\min(u_\alpha)\f$ over a DG element that is not
     214              :  * using subcells we first project the DG solution to the subcells and then
     215              :  * compute the maximum and minimum over *both* the DG grid and the subcell grid.
     216              :  * However, when a DG element is using subcells we compute the maximum and
     217              :  * minimum of \f$u_\alpha(t^n)\f$ over the subcells only. Note that the maximum
     218              :  * and minimum values of \f$u^\star_\alpha\f$ are always computed over both the
     219              :  * DG and the subcell grids, even when using the RDMP to check if the
     220              :  * reconstructed DG solution would be admissible.
     221              :  *
     222              :  * The parameter \f$\delta_\alpha\f$ is given by:
     223              :  *
     224              :  * \f{align*}{
     225              :  *   \delta_\alpha =
     226              :  *   \max\left(\delta_{0},\epsilon
     227              :  *   \left(\max_{\forall\mathcal{N}}\left(u_{\alpha}(t^n)\right)
     228              :  *   - \min_{\forall\mathcal{N}}\left(u_{\alpha}(t^n)\right)\right)
     229              :  *   \right),
     230              :  * \f}
     231              :  *
     232              :  * where we typically take \f$\delta_{0}=10^{-4}\f$ and \f$\epsilon=10^{-3}\f$.
     233              :  *
     234              :  * If all checks are passed and cell is not troubled, returns an integer `0`.
     235              :  * Otherwise returns an 1-based index of the element in the input
     236              :  * `DataVector` that fails the check.
     237              :  *
     238              :  * e.g. Suppose we have three variables to check RDMP so that
     239              :  * `max_of_current_variables.size() == 3`. If RDMP TCI flags
     240              :  * `max_of_current_variables[1]`, `min_of_current_variables[1]`, .. (and so on)
     241              :  * as troubled, returned integer value is `2`.
     242              :  *
     243              :  * Once cell is marked as troubled, checks for the remaining part of the input
     244              :  * `std::vector`s are skipped. In the example above, for instance if `[1]`-th
     245              :  * component of inputs is flagged as troubled, checking the remaining index
     246              :  * `[2]` is skipped.
     247              :  *
     248              :  */
     249            1 : int rdmp_tci(const DataVector& max_of_current_variables,
     250              :              const DataVector& min_of_current_variables,
     251              :              const DataVector& max_of_past_variables,
     252              :              const DataVector& min_of_past_variables, double rdmp_delta0,
     253              :              double rdmp_epsilon);
     254              : }  // namespace evolution::dg::subcell
        

Generated by: LCOV version 2.0-1