SpECTRE Documentation Coverage Report
Current view: top level - Evolution/DiscontinuousGalerkin/Limiters - Krivodonova.hpp Hit Total Coverage
Commit: 361cb8d8406bb752684a5f31c27320ec444a50e3 Lines: 4 40 10.0 %
Date: 2025-11-09 02:02:04
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 <boost/functional/hash.hpp>
       8             : #include <cmath>
       9             : #include <cstddef>
      10             : #include <limits>
      11             : #include <ostream>
      12             : #include <pup.h>
      13             : #include <type_traits>
      14             : #include <unordered_map>
      15             : #include <utility>
      16             : 
      17             : #include "DataStructures/DataVector.hpp"
      18             : #include "DataStructures/Index.hpp"
      19             : #include "DataStructures/ModalVector.hpp"
      20             : #include "DataStructures/Tags.hpp"
      21             : #include "DataStructures/Tensor/Metafunctions.hpp"
      22             : #include "DataStructures/Tensor/Tensor.hpp"
      23             : #include "DataStructures/Variables.hpp"
      24             : #include "Domain/Structure/Direction.hpp"
      25             : #include "Domain/Structure/DirectionalId.hpp"
      26             : #include "Domain/Structure/Element.hpp"
      27             : #include "Domain/Structure/ElementId.hpp"
      28             : #include "Domain/Structure/OrientationMap.hpp"
      29             : #include "Domain/Structure/OrientationMapHelpers.hpp"
      30             : #include "Domain/Tags.hpp"
      31             : #include "NumericalAlgorithms/LinearOperators/CoefficientTransforms.hpp"
      32             : #include "NumericalAlgorithms/Spectral/MaximumNumberOfPoints.hpp"
      33             : #include "NumericalAlgorithms/Spectral/Mesh.hpp"
      34             : #include "Options/Context.hpp"
      35             : #include "Options/ParseError.hpp"
      36             : #include "Options/String.hpp"
      37             : #include "Utilities/Algorithm.hpp"
      38             : #include "Utilities/ErrorHandling/Error.hpp"
      39             : #include "Utilities/Gsl.hpp"
      40             : #include "Utilities/MakeArray.hpp"
      41             : #include "Utilities/Math.hpp"
      42             : #include "Utilities/TMPL.hpp"
      43             : 
      44             : /// \cond
      45             : namespace Limiters {
      46             : template <size_t VolumeDim, typename TagsToLimit>
      47             : class Krivodonova;
      48             : }  // namespace Limiters
      49             : /// \endcond
      50             : 
      51             : namespace Limiters {
      52             : /*!
      53             :  * \ingroup LimitersGroup
      54             :  * \brief An implementation of the Krivodonova limiter.
      55             :  *
      56             :  * The limiter is described in \cite Krivodonova2007. The Krivodonova limiter
      57             :  * works by limiting the highest derivatives/modal coefficients using an
      58             :  * aggressive minmod approach, decreasing in derivative/modal coefficient order
      59             :  * until no more limiting is necessary. In 3d, the function being limited is
      60             :  * expanded as:
      61             :  *
      62             :  * \f{align}{
      63             :  * u^{l,m,n}=\sum_{i,j,k=0,0,0}^{N_i,N_j,N_k}c^{l,m,n}_{i,j,k}
      64             :  *  P_{i}(\xi)P_{j}(\eta)P_{k}(\zeta)
      65             :  * \f}
      66             :  *
      67             :  * where \f$\left\{\xi, \eta, \zeta\right\}\f$ are the logical coordinates,
      68             :  * \f$P_{i}\f$ are the Legendre polynomials, the superscript \f$\{l,m,n\}\f$
      69             :  * represents the element indexed by \f$l,m,n\f$, and \f$N_i,N_j\f$ and
      70             :  * \f$N_k\f$ are the number of collocation points minus one in the
      71             :  * \f$\xi,\eta,\f$ and \f$\zeta\f$ direction, respectively. The coefficients are
      72             :  * limited according to:
      73             :  *
      74             :  * \f{align}{
      75             :  * \tilde{c}^{l,m,n}_{i,j,k}=\mathrm{minmod}
      76             :  *   &\left(c_{i,j,k}^{l,m,n},
      77             :  *          \alpha_i\left(c^{l+1,m,n}_{i-1,j,k}-c^{l,m,n}_{i-1,j,k}\right),
      78             :  *          \alpha_i\left(c^{l,m,n}_{i-1,j,k}-c^{l-1,m,n}_{i-1,j,k}\right),
      79             :  *     \right.\notag \\
      80             :  *   &\;\;\;\;
      81             :  *          \alpha_j\left(c^{l,m+1,n}_{i,j-1,k}-c^{l,m,n}_{i,j-1,k}\right),
      82             :  *          \alpha_j\left(c^{l,m,n}_{i,j-1,k}-c^{l,m-1,n}_{i,j-1,k}\right),
      83             :  *     \notag \\
      84             :  *   &\;\;\;\;\left.
      85             :  *          \alpha_k\left(c^{l,m,n+1}_{i,j,k-1}-c^{l,m,n}_{i,j,k-1}\right),
      86             :  *          \alpha_k\left(c^{l,m,n}_{i,j,k-1}-c^{l,m,n-1}_{i,j,k-1}\right)
      87             :  *     \right),
      88             :  * \label{eq:krivodonova 3d minmod}
      89             :  * \f}
      90             :  *
      91             :  * where \f$\mathrm{minmod}\f$ is the minmod function defined as
      92             :  *
      93             :  * \f{align}{
      94             :  *  \mathrm{minmod}(a,b,c,\ldots)=
      95             :  *  \left\{
      96             :  *  \begin{array}{ll}
      97             :  *    \mathrm{sgn}(a)\min(\lvert a\rvert, \lvert b\rvert,
      98             :  *    \lvert c\rvert, \ldots) & \mathrm{if} \;
      99             :  *    \mathrm{sgn}(a)=\mathrm{sgn}(b)=\mathrm{sgn}(c)=\mathrm{sgn}(\ldots) \\
     100             :  *    0 & \mathrm{otherwise}
     101             :  *  \end{array}\right.
     102             :  * \f}
     103             :  *
     104             :  * Krivodonova \cite Krivodonova2007 requires \f$\alpha_i\f$ to be in the range
     105             :  *
     106             :  * \f{align*}{
     107             :  * \frac{1}{2(2i-1)}\le \alpha_i \le 1
     108             :  * \f}
     109             :  *
     110             :  * where the lower bound comes from finite differencing the coefficients between
     111             :  * neighbor elements when using Legendre polynomials (see \cite Krivodonova2007
     112             :  * for details). Note that we normalize our Legendre polynomials by \f$P_i(1) =
     113             :  * 1\f$; this is the normalization \cite Krivodonova2007 uses in 1D, (but not in
     114             :  * 2D), which is why our bounds on \f$\alpha_i\f$ match Eq. 14 of
     115             :  * \cite Krivodonova2007 (but not Eq. 23). We relax the lower bound:
     116             :  *
     117             :  * \f{align*}{
     118             :  * 0 \le \alpha_i \le 1
     119             :  * \f}
     120             :  *
     121             :  * to allow different basis functions (e.g. Chebyshev polynomials) and to allow
     122             :  * the limiter to be more dissipative if necessary. The same \f$\alpha_i\f$s are
     123             :  * used in all dimensions.
     124             :  *
     125             :  * \note The only place where the specific choice of 1d basis
     126             :  * comes in is the lower bound for the \f$\alpha_i\f$s, and so in general the
     127             :  * limiter can be applied to any 1d or tensor product of 1d basis functions.
     128             :  *
     129             :  * The limiting procedure must be applied from the highest derivatives to the
     130             :  * lowest, i.e. the highest coefficients to the lowest. Let us consider a 3d
     131             :  * element with \f$N+1\f$ coefficients in each dimension and denote the
     132             :  * coefficients as \f$c_{i,j,k}\f$. Then the limiting procedure starts at
     133             :  * \f$c_{N,N,N}\f$, followed by \f$c_{N,N,N-1}\f$, \f$c_{N,N-1,N}\f$, and
     134             :  * \f$c_{N-1,N,N}\f$. A detailed example is given below. Limiting is stopped if
     135             :  * all symmetric pairs of coefficients are left unchanged, i.e.
     136             :  * \f$c_{i,j,k}=\tilde{c}_{i,j,k}\f$. By all symmetric coefficients we mean
     137             :  * that, for example, \f$c_{N-i,N-j,N-k}\f$, \f$c_{N-j,N-i,N-k}\f$,
     138             :  * \f$c_{N-k,N-j,N-i}\f$, \f$c_{N-j,N-k,N-i}\f$, \f$c_{N-i,N-k,N-j}\f$, and
     139             :  * \f$c_{N-k,N-i,N-j}\f$ are not limited. As a concrete example, consider a 3d
     140             :  * element with 3 collocation points per dimension. Each limited coefficient is
     141             :  * defined as (though only computed if needed):
     142             :  *
     143             :  * \f{align*}{
     144             :  * \tilde{c}^{l,m,n}_{2,2,2}=\mathrm{minmod}
     145             :  *   &\left(c_{2,2,2}^{l,m,n},
     146             :  *          \alpha_2\left(c^{l+1,m,n}_{1,2,2}-c^{l,m,n}_{1,2,2}\right),
     147             :  *          \alpha_2\left(c^{l,m,n}_{1,2,2}-c^{l-1,m,n}_{1,2,2}\right),
     148             :  *     \right.\\
     149             :  *   &\;\;\;\;
     150             :  *          \alpha_2\left(c^{l,m+1,n}_{2,1,2}-c^{l,m,n}_{2,1,2}\right),
     151             :  *          \alpha_2\left(c^{l,m,n}_{2,1,2}-c^{l,m-1,n}_{2,1,2}\right),\\
     152             :  *   &\;\;\;\;\left.
     153             :  *          \alpha_2\left(c^{l,m,n+1}_{2,2,1}-c^{l,m,n}_{2,2,1}\right),
     154             :  *          \alpha_2\left(c^{l,m,n}_{2,2,1}-c^{l,m,n-1}_{2,2,1}\right)
     155             :  *     \right),\\
     156             :  * \tilde{c}^{l,m,n}_{2,2,1}=\mathrm{minmod}
     157             :  *   &\left(c_{2,2,1}^{l,m,n},
     158             :  *          \alpha_2\left(c^{l+1,m,n}_{1,2,1}-c^{l,m,n}_{1,2,1}\right),
     159             :  *          \alpha_2\left(c^{l,m,n}_{1,2,1}-c^{l-1,m,n}_{1,2,1}\right),
     160             :  *     \right.\\
     161             :  *   &\;\;\;\;
     162             :  *          \alpha_2\left(c^{l,m+1,n}_{2,1,1}-c^{l,m,n}_{2,1,1}\right),
     163             :  *          \alpha_2\left(c^{l,m,n}_{2,1,1}-c^{l,m-1,n}_{2,1,1}\right),\\
     164             :  *   &\;\;\;\;\left.
     165             :  *          \alpha_1\left(c^{l,m,n+1}_{2,2,0}-c^{l,m,n}_{2,2,0}\right),
     166             :  *          \alpha_1\left(c^{l,m,n}_{2,2,0}-c^{l,m,n-1}_{2,2,0}\right)
     167             :  *     \right),\\
     168             :  * \tilde{c}^{l,m,n}_{2,1,2}=\mathrm{minmod}
     169             :  *   &\left(c_{2,1,2}^{l,m,n},
     170             :  *          \alpha_2\left(c^{l+1,m,n}_{1,1,2}-c^{l,m,n}_{1,1,2}\right),
     171             :  *          \alpha_2\left(c^{l,m,n}_{1,1,2}-c^{l-1,m,n}_{1,1,2}\right),
     172             :  *     \right.\\
     173             :  *   &\;\;\;\;
     174             :  *          \alpha_1\left(c^{l,m+1,n}_{2,0,2}-c^{l,m,n}_{2,0,2}\right),
     175             :  *          \alpha_1\left(c^{l,m,n}_{2,0,2}-c^{l,m-1,n}_{2,0,2}\right),\\
     176             :  *   &\;\;\;\;\left.
     177             :  *          \alpha_2\left(c^{l,m,n+1}_{2,1,1}-c^{l,m,n}_{2,1,1}\right),
     178             :  *          \alpha_2\left(c^{l,m,n}_{2,1,1}-c^{l,m,n-1}_{2,1,1}\right)
     179             :  *     \right),\\
     180             :  * \tilde{c}^{l,m,n}_{1,2,2}=\mathrm{minmod}
     181             :  *   &\left(c_{1,2,2}^{l,m,n},
     182             :  *          \alpha_1\left(c^{l+1,m,n}_{0,2,2}-c^{l,m,n}_{0,2,2}\right),
     183             :  *          \alpha_1\left(c^{l,m,n}_{0,2,2}-c^{l-1,m,n}_{0,2,2}\right),
     184             :  *     \right.\\
     185             :  *   &\;\;\;\;
     186             :  *          \alpha_2\left(c^{l,m+1,n}_{1,1,2}-c^{l,m,n}_{1,1,2}\right),
     187             :  *          \alpha_2\left(c^{l,m,n}_{1,1,2}-c^{l,m-1,n}_{1,1,2}\right),\\
     188             :  *   &\;\;\;\;\left.
     189             :  *          \alpha_2\left(c^{l,m,n+1}_{1,2,1}-c^{l,m,n}_{1,2,1}\right),
     190             :  *          \alpha_2\left(c^{l,m,n}_{1,2,1}-c^{l,m,n-1}_{1,2,1}\right)
     191             :  *     \right),\\
     192             :  * \tilde{c}^{l,m,n}_{2,2,0}=\mathrm{minmod}
     193             :  *   &\left(c_{2,2,0}^{l,m,n},
     194             :  *          \alpha_2\left(c^{l+1,m,n}_{1,2,0}-c^{l,m,n}_{1,2,0}\right),
     195             :  *          \alpha_2\left(c^{l,m,n}_{1,2,0}-c^{l-1,m,n}_{1,2,0}\right),
     196             :  *     \right.\\
     197             :  *   &\;\;\;\;\left.
     198             :  *          \alpha_2\left(c^{l,m+1,n}_{2,1,0}-c^{l,m,n}_{2,1,0}\right),
     199             :  *          \alpha_2\left(c^{l,m,n}_{2,1,0}-c^{l,m-1,n}_{2,1,0}\right)
     200             :  *     \right),\\
     201             :  * \tilde{c}^{l,m,n}_{2,0,2}=\mathrm{minmod}
     202             :  *   &\left(c_{2,0,2}^{l,m,n},
     203             :  *          \alpha_2\left(c^{l+1,m,n}_{1,0,2}-c^{l,m,n}_{1,0,2}\right),
     204             :  *          \alpha_2\left(c^{l,m,n}_{1,0,2}-c^{l-1,m,n}_{1,0,2}\right),
     205             :  *     \right.\\
     206             :  *   &\;\;\;\;\left.
     207             :  *          \alpha_2\left(c^{l,m,n+1}_{2,0,1}-c^{l,m,n}_{2,0,1}\right),
     208             :  *          \alpha_2\left(c^{l,m,n}_{2,0,1}-c^{l,m,n-1}_{2,0,1}\right)
     209             :  *     \right),\\
     210             :  * \tilde{c}^{l,m,n}_{0,2,2}=\mathrm{minmod}
     211             :  *   &\left(c_{0,2,2}^{l,m,n},
     212             :  *          \alpha_2\left(c^{l,m+1,n}_{0,1,2}-c^{l,m,n}_{0,1,2}\right),
     213             :  *          \alpha_2\left(c^{l,m,n}_{0,1,2}-c^{l,m-1,n}_{0,1,2}\right),
     214             :  *     \right.\\
     215             :  *   &\;\;\;\;\left.
     216             :  *          \alpha_2\left(c^{l,m,n+1}_{0,2,1}-c^{l,m,n}_{0,2,1}\right),
     217             :  *          \alpha_2\left(c^{l,m,n}_{0,2,1}-c^{l,m,n-1}_{0,2,1}\right)
     218             :  *     \right),\\
     219             :  * \tilde{c}^{l,m,n}_{2,1,1}=\mathrm{minmod}
     220             :  *   &\left(c_{2,1,1}^{l,m,n},
     221             :  *          \alpha_2\left(c^{l+1,m,n}_{1,1,1}-c^{l,m,n}_{1,1,1}\right),
     222             :  *          \alpha_2\left(c^{l,m,n}_{1,1,1}-c^{l-1,m,n}_{1,1,1}\right),
     223             :  *     \right.\\
     224             :  *   &\;\;\;\;
     225             :  *          \alpha_1\left(c^{l,m+1,n}_{2,0,1}-c^{l,m,n}_{2,0,1}\right),
     226             :  *          \alpha_1\left(c^{l,m,n}_{2,0,1}-c^{l,m-1,n}_{2,0,1}\right),\\
     227             :  *   &\;\;\;\;\left.
     228             :  *          \alpha_1\left(c^{l,m,n+1}_{2,1,0}-c^{l,m,n}_{2,1,0}\right),
     229             :  *          \alpha_1\left(c^{l,m,n}_{2,1,0}-c^{l,m,n-1}_{2,1,0}\right)
     230             :  *     \right),\\
     231             :  * \tilde{c}^{l,m,n}_{1,2,1}=\mathrm{minmod}
     232             :  *   &\left(c_{1,2,1}^{l,m,n},
     233             :  *          \alpha_1\left(c^{l+1,m,n}_{0,2,1}-c^{l,m,n}_{0,2,1}\right),
     234             :  *          \alpha_1\left(c^{l,m,n}_{0,2,1}-c^{l-1,m,n}_{0,2,1}\right),
     235             :  *     \right.\\
     236             :  *   &\;\;\;\;
     237             :  *          \alpha_2\left(c^{l,m+1,n}_{1,1,1}-c^{l,m,n}_{1,1,1}\right),
     238             :  *          \alpha_2\left(c^{l,m,n}_{1,1,1}-c^{l,m-1,n}_{1,1,1}\right),\\
     239             :  *   &\;\;\;\;\left.
     240             :  *          \alpha_1\left(c^{l,m,n+1}_{1,2,0}-c^{l,m,n}_{1,2,0}\right),
     241             :  *          \alpha_1\left(c^{l,m,n}_{1,2,0}-c^{l,m,n-1}_{1,2,0}\right)
     242             :  *     \right),\\
     243             :  * \tilde{c}^{l,m,n}_{1,1,2}=\mathrm{minmod}
     244             :  *   &\left(c_{1,1,2}^{l,m,n},
     245             :  *          \alpha_1\left(c^{l+1,m,n}_{0,1,2}-c^{l,m,n}_{0,1,2}\right),
     246             :  *          \alpha_1\left(c^{l,m,n}_{0,1,2}-c^{l-1,m,n}_{0,1,2}\right),
     247             :  *     \right.\\
     248             :  *   &\;\;\;\;
     249             :  *          \alpha_1\left(c^{l,m+1,n}_{1,0,2}-c^{l,m,n}_{1,0,2}\right),
     250             :  *          \alpha_1\left(c^{l,m,n}_{1,0,2}-c^{l,m-1,n}_{1,0,2}\right),\\
     251             :  *   &\;\;\;\;\left.
     252             :  *          \alpha_2\left(c^{l,m,n+1}_{1,1,1}-c^{l,m,n}_{1,1,1}\right),
     253             :  *          \alpha_2\left(c^{l,m,n}_{1,1,1}-c^{l,m,n-1}_{1,1,1}\right)
     254             :  *     \right),
     255             :  * \f}
     256             :  * \f{align*}{
     257             :  * \tilde{c}^{l,m,n}_{2,1,0}=\mathrm{minmod}
     258             :  *   &\left(c_{2,1,0}^{l,m,n},
     259             :  *          \alpha_2\left(c^{l+1,m,n}_{1,1,0}-c^{l,m,n}_{1,1,0}\right),
     260             :  *          \alpha_2\left(c^{l,m,n}_{1,1,0}-c^{l-1,m,n}_{1,1,0}\right),
     261             :  *     \right.\\
     262             :  *   &\;\;\;\;\left.
     263             :  *          \alpha_1\left(c^{l,m+1,n}_{2,0,0}-c^{l,m,n}_{2,0,0}\right),
     264             :  *          \alpha_1\left(c^{l,m,n}_{2,0,0}-c^{l,m-1,n}_{2,0,0}\right)
     265             :  *     \right),\\
     266             :  * \tilde{c}^{l,m,n}_{2,0,1}=\mathrm{minmod}
     267             :  *   &\left(c_{2,0,1}^{l,m,n},
     268             :  *          \alpha_2\left(c^{l+1,m,n}_{1,0,1}-c^{l,m,n}_{1,0,1}\right),
     269             :  *          \alpha_2\left(c^{l,m,n}_{1,0,1}-c^{l-1,m,n}_{1,0,1}\right),
     270             :  *     \right.\\
     271             :  *   &\;\;\;\;\left.
     272             :  *          \alpha_1\left(c^{l,m,n+1}_{2,0,0}-c^{l,m,n}_{2,0,0}\right),
     273             :  *          \alpha_1\left(c^{l,m,n}_{2,0,0}-c^{l,m,n-1}_{2,0,0}\right)
     274             :  *     \right),\\
     275             :  * \tilde{c}^{l,m,n}_{1,2,0}=\mathrm{minmod}
     276             :  *   &\left(c_{1,2,0}^{l,m,n},
     277             :  *          \alpha_1\left(c^{l+1,m,n}_{0,2,0}-c^{l,m,n}_{0,2,0}\right),
     278             :  *          \alpha_1\left(c^{l,m,n}_{0,2,0}-c^{l-1,m,n}_{0,2,0}\right),
     279             :  *     \right.\\
     280             :  *   &\;\;\;\;\left.
     281             :  *          \alpha_2\left(c^{l,m+1,n}_{1,1,0}-c^{l,m,n}_{1,1,0}\right),
     282             :  *          \alpha_2\left(c^{l,m,n}_{1,1,0}-c^{l,m-1,n}_{1,1,0}\right)
     283             :  *     \right),\\
     284             :  * \tilde{c}^{l,m,n}_{1,0,2}=\mathrm{minmod}
     285             :  *   &\left(c_{1,0,2}^{l,m,n},
     286             :  *          \alpha_1\left(c^{l+1,m,n}_{0,0,2}-c^{l,m,n}_{0,0,2}\right),
     287             :  *          \alpha_1\left(c^{l,m,n}_{0,0,2}-c^{l-1,m,n}_{0,0,2}\right),
     288             :  *     \right.\\
     289             :  *   &\;\;\;\;\left.
     290             :  *          \alpha_2\left(c^{l,m,n+1}_{1,0,1}-c^{l,m,n}_{1,0,1}\right),
     291             :  *          \alpha_2\left(c^{l,m,n}_{1,0,1}-c^{l,m,n-1}_{1,0,1}\right)
     292             :  *     \right),\\
     293             :  * \tilde{c}^{l,m,n}_{0,1,2}=\mathrm{minmod}
     294             :  *   &\left(c_{0,1,2}^{l,m,n},
     295             :  *          \alpha_1\left(c^{l,m+1,n}_{0,0,2}-c^{l,m,n}_{0,0,2}\right),
     296             :  *          \alpha_1\left(c^{l,m,n}_{0,0,2}-c^{l,m-1,n}_{0,0,2}\right),
     297             :  *   \right. \\
     298             :  *   &\;\;\;\;\left.
     299             :  *          \alpha_2\left(c^{l,m,n+1}_{0,1,1}-c^{l,m,n}_{0,1,1}\right),
     300             :  *          \alpha_2\left(c^{l,m,n}_{0,1,1}-c^{l,m,n-1}_{0,1,1}\right)
     301             :  *     \right),\\
     302             :  * \tilde{c}^{l,m,n}_{0,2,1}=\mathrm{minmod}
     303             :  *   &\left(c_{0,2,1}^{l,m,n},
     304             :  *          \alpha_2\left(c^{l,m+1,n}_{0,1,1}-c^{l,m,n}_{0,1,1}\right),
     305             :  *          \alpha_2\left(c^{l,m,n}_{0,1,1}-c^{l,m-1,n}_{0,1,1}\right),
     306             :  *   \right.\\
     307             :  *   &\;\;\;\;\left.
     308             :  *          \alpha_1\left(c^{l,m,n+1}_{0,2,0}-c^{l,m,n}_{0,2,0}\right),
     309             :  *          \alpha_1\left(c^{l,m,n}_{0,2,0}-c^{l,m,n-1}_{0,2,0}\right)
     310             :  *     \right),
     311             :  * \f}
     312             :  * \f{align*}{
     313             :  * \tilde{c}^{l,m,n}_{2,0,0}=\mathrm{minmod}
     314             :  *   &\left(c_{2,0,0}^{l,m,n},
     315             :  *          \alpha_2\left(c^{l+1,m,n}_{1,0,0}-c^{l,m,n}_{1,0,0}\right),
     316             :  *          \alpha_2\left(c^{l,m,n}_{1,0,0}-c^{l-1,m,n}_{1,0,0}\right)
     317             :  *     \right),\\
     318             :  * \tilde{c}^{l,m,n}_{0,2,0}=\mathrm{minmod}
     319             :  *   &\left(c_{0,2,0}^{l,m,n},
     320             :  *          \alpha_2\left(c^{l,m+1,n}_{0,1,0}-c^{l,m,n}_{0,1,0}\right),
     321             :  *          \alpha_2\left(c^{l,m,n}_{0,1,0}-c^{l,m-1,n}_{0,1,0}\right)
     322             :  *     \right),\\
     323             :  * \tilde{c}^{l,m,n}_{0,0,2}=\mathrm{minmod}
     324             :  *   &\left(c_{0,0,2}^{l,m,n},
     325             :  *          \alpha_2\left(c^{l,m,n+1}_{0,0,1}-c^{l,m,n}_{0,0,1}\right),
     326             :  *          \alpha_2\left(c^{l,m,n}_{0,0,1}-c^{l,m,n-1}_{0,0,1}\right)
     327             :  *     \right),\\
     328             :  * \tilde{c}^{l,m,n}_{1,1,1}=\mathrm{minmod}
     329             :  *   &\left(c_{1,1,1}^{l,m,n},
     330             :  *          \alpha_1\left(c^{l+1,m,n}_{0,1,1}-c^{l,m,n}_{0,1,1}\right),
     331             :  *          \alpha_1\left(c^{l,m,n}_{0,1,1}-c^{l-1,m,n}_{0,1,1}\right),
     332             :  *     \right.\\
     333             :  *   &\;\;\;\;
     334             :  *          \alpha_1\left(c^{l,m+1,n}_{1,0,1}-c^{l,m,n}_{1,0,1}\right),
     335             :  *          \alpha_1\left(c^{l,m,n}_{1,0,1}-c^{l,m-1,n}_{1,0,1}\right),\\
     336             :  *   &\;\;\;\;\left.
     337             :  *          \alpha_1\left(c^{l,m,n+1}_{1,1,0}-c^{l,m,n}_{1,1,0}\right),
     338             :  *          \alpha_1\left(c^{l,m,n}_{1,1,0}-c^{l,m,n-1}_{1,1,0}\right)
     339             :  *     \right),\\
     340             :  * \tilde{c}^{l,m,n}_{1,1,0}=\mathrm{minmod}
     341             :  *   &\left(c_{1,1,0}^{l,m,n},
     342             :  *          \alpha_1\left(c^{l+1,m,n}_{0,1,0}-c^{l,m,n}_{0,1,0}\right),
     343             :  *          \alpha_1\left(c^{l,m,n}_{0,1,0}-c^{l-1,m,n}_{0,1,0}\right),
     344             :  *     \right.\\
     345             :  *   &\;\;\;\;\left.
     346             :  *          \alpha_1\left(c^{l,m+1,n}_{1,0,0}-c^{l,m,n}_{1,0,0}\right),
     347             :  *          \alpha_1\left(c^{l,m,n}_{1,0,0}-c^{l,m-1,n}_{1,0,0}\right),
     348             :  *     \right),\\
     349             :  * \tilde{c}^{l,m,n}_{1,0,1}=\mathrm{minmod}
     350             :  *   &\left(c_{1,0,1}^{l,m,n},
     351             :  *          \alpha_1\left(c^{l+1,m,n}_{0,0,1}-c^{l,m,n}_{0,0,1}\right),
     352             :  *          \alpha_1\left(c^{l,m,n}_{0,0,1}-c^{l-1,m,n}_{0,0,1}\right),
     353             :  *     \right.\\
     354             :  *   &\;\;\;\;\left.
     355             :  *          \alpha_1\left(c^{l,m,n+1}_{1,0,0}-c^{l,m,n}_{1,0,0}\right),
     356             :  *          \alpha_1\left(c^{l,m,n}_{1,0,0}-c^{l,m,n-1}_{1,0,0}\right)
     357             :  *     \right),\\
     358             :  * \tilde{c}^{l,m,n}_{0,1,1}=\mathrm{minmod}
     359             :  *   &\left(c_{0,1,1}^{l,m,n},
     360             :  *          \alpha_1\left(c^{l,m+1,n}_{0,0,1}-c^{l,m,n}_{0,0,1}\right),
     361             :  *          \alpha_1\left(c^{l,m,n}_{0,0,1}-c^{l,m-1,n}_{0,0,1}\right),
     362             :  *   \right.\\
     363             :  *   &\;\;\;\;\left.
     364             :  *          \alpha_1\left(c^{l,m,n+1}_{0,1,0}-c^{l,m,n}_{0,1,0}\right),
     365             :  *          \alpha_1\left(c^{l,m,n}_{0,1,0}-c^{l,m,n-1}_{0,1,0}\right)
     366             :  *     \right),\\
     367             :  * \tilde{c}^{l,m,n}_{1,0,0}=\mathrm{minmod}
     368             :  *   &\left(c_{1,0,0}^{l,m,n},
     369             :  *          \alpha_1\left(c^{l+1,m,n}_{0,0,0}-c^{l,m,n}_{0,0,0}\right),
     370             :  *          \alpha_1\left(c^{l,m,n}_{0,0,0}-c^{l-1,m,n}_{0,0,0}\right)
     371             :  *     \right),\\
     372             :  * \tilde{c}^{l,m,n}_{0,1,0}=\mathrm{minmod}
     373             :  *   &\left(c_{0,1,0}^{l,m,n},
     374             :  *          \alpha_1\left(c^{l,m+1,n}_{0,0,0}-c^{l,m,n}_{0,0,0}\right),
     375             :  *          \alpha_1\left(c^{l,m,n}_{0,0,0}-c^{l,m-1,n}_{0,0,0}\right)
     376             :  *     \right),\\
     377             :  * \tilde{c}^{l,m,n}_{0,0,1}=\mathrm{minmod}
     378             :  *   &\left(c_{0,0,1}^{l,m,n},
     379             :  *          \alpha_1\left(c^{l,m,n+1}_{0,0,0}-c^{l,m,n}_{0,0,0}\right),
     380             :  *          \alpha_1\left(c^{l,m,n}_{0,0,0}-c^{l,m,n-1}_{0,0,0}\right)
     381             :  *     \right),
     382             :  * \f}
     383             :  *
     384             :  *
     385             :  * The algorithm to perform the limiting is as follows:
     386             :  *
     387             :  * - limit \f$c_{2,2,2}\f$ (i.e. \f$c_{2,2,2}\leftarrow\tilde{c}_{2,2,2}\f$), if
     388             :  *   not changed, stop
     389             :  * - limit \f$c_{2,2,1}\f$, \f$c_{2,1,2}\f$, and \f$c_{1,2,2}\f$, if all not
     390             :  * changed, stop
     391             :  * - limit \f$c_{2,2,0}\f$, \f$c_{2,0,2}\f$, and \f$c_{0,2,2}\f$, if all not
     392             :  * changed, stop
     393             :  * - limit \f$c_{2,1,1}\f$, \f$c_{1,2,1}\f$, and \f$c_{1,1,2}\f$, if all not
     394             :  * changed, stop
     395             :  * - limit \f$c_{2,1,0}\f$, \f$c_{2,0,1}\f$, \f$c_{1,2,0}\f$, \f$c_{1,0,2}\f$,
     396             :  * \f$c_{0,1,2}\f$, and \f$c_{0,2,1}\f$, if all not changed, stop
     397             :  * - limit \f$c_{2,0,0}\f$, \f$c_{0,2,0}\f$, and \f$c_{0,0,2}\f$, if all not
     398             :  * changed, stop
     399             :  * - limit \f$c_{1,1,1}\f$, if not changed, stop
     400             :  * - limit \f$c_{1,1,0}\f$, \f$c_{1,0,1}\f$, and \f$c_{0,1,1}\f$, if all not
     401             :  * changed, stop
     402             :  * - limit \f$c_{1,0,0}\f$, \f$c_{0,1,0}\f$, and \f$c_{0,0,1}\f$, if all not
     403             :  * changed, stop
     404             :  *
     405             :  * The 1d and 2d implementations are straightforward restrictions of the
     406             :  * described algorithm.
     407             :  *
     408             :  * #### Limitations:
     409             :  *
     410             :  * - We currently recompute the spectral coefficients for local use, after
     411             :  *   having computed these same coefficients for sending to the neighbors. We
     412             :  *   should be able to avoid this either by storing the coefficients in the
     413             :  *   DataBox or by allowing the limiters' `packaged_data` function to return an
     414             :  *   object to be passed as an additional argument to the `operator()` (still
     415             :  *   would need to be stored in the DataBox).
     416             :  * - We cannot handle the case where neighbors have more/fewer
     417             :  *   coefficients. In the case that the neighbor has more coefficients we could
     418             :  *   just ignore the higher coefficients. In the case that the neighbor has
     419             :  *   fewer coefficients we have a few choices.
     420             :  * - Having a different number of collocation points in different directions is
     421             :  *   not supported. However, it is straightforward to handle this case. The
     422             :  *   outermost loop should be in the direction with the most collocation points,
     423             :  *   while the inner most loop should be over the direction with the fewest
     424             :  *   collocation points. The highest to lowest coefficients can then be limited
     425             :  *   appropriately again.
     426             :  * - h-refinement is not supported, but there is one reasonably straightforward
     427             :  *   implementation that may work. In this case we would ignore refinement that
     428             :  *   is not in the direction of the neighbor, treating the element as
     429             :  *   simply having multiple neighbors in that direction. The only change would
     430             :  *   be accounting for different refinement in the direction of the neighor,
     431             :  *   which should be easy to add since the differences in coefficients in
     432             :  *   Eq.\f$\ref{eq:krivodonova 3d minmod}\f$ will just be multiplied by
     433             :  *   non-unity factors.
     434             :  */
     435             : template <size_t VolumeDim, typename... Tags>
     436           1 : class Krivodonova<VolumeDim, tmpl::list<Tags...>> {
     437             :  public:
     438             :   /*!
     439             :    * \brief The \f$\alpha_i\f$ values in the Krivodonova algorithm.
     440             :    */
     441           1 :   struct Alphas {
     442           0 :     using type = std::array<
     443             :         double, Spectral::maximum_number_of_points<Spectral::Basis::Legendre>>;
     444           0 :     static constexpr Options::String help = {
     445             :         "The alpha parameters of the Krivodonova limiter"};
     446             :   };
     447             :   /*!
     448             :    * \brief Turn the limiter off
     449             :    *
     450             :    * This option exists to temporarily disable the limiter for debugging
     451             :    * purposes. For problems where limiting is not needed, the preferred
     452             :    * approach is to not compile the limiter into the executable.
     453             :    */
     454           1 :   struct DisableForDebugging {
     455           0 :     using type = bool;
     456           0 :     static type suggested_value() { return false; }
     457           0 :     static constexpr Options::String help = {"Disable the limiter"};
     458             :   };
     459             : 
     460           0 :   using options = tmpl::list<Alphas, DisableForDebugging>;
     461           0 :   static constexpr Options::String help = {
     462             :       "The hierarchical limiter of Krivodonova.\n\n"
     463             :       "This limiter works by limiting the highest modal "
     464             :       "coefficients/derivatives using an aggressive minmod approach, "
     465             :       "decreasing in modal coefficient order until no more limiting is "
     466             :       "necessary."};
     467             : 
     468           0 :   explicit Krivodonova(
     469             :       std::array<double,
     470             :                  Spectral::maximum_number_of_points<Spectral::Basis::Legendre>>
     471             :           alphas,
     472             :       bool disable_for_debugging = false, const Options::Context& context = {});
     473             : 
     474           0 :   Krivodonova() = default;
     475           0 :   Krivodonova(const Krivodonova&) = default;
     476           0 :   Krivodonova& operator=(const Krivodonova&) = default;
     477           0 :   Krivodonova(Krivodonova&&) = default;
     478           0 :   Krivodonova& operator=(Krivodonova&&) = default;
     479           0 :   ~Krivodonova() = default;
     480             : 
     481             :   // NOLINTNEXTLINE(google-runtime-references)
     482           0 :   void pup(PUP::er& p);
     483             : 
     484           0 :   bool operator==(const Krivodonova& rhs) const;
     485             : 
     486           0 :   struct PackagedData {
     487           0 :     Variables<tmpl::list<::Tags::Modal<Tags>...>> modal_volume_data;
     488           0 :     Mesh<VolumeDim> mesh;
     489             : 
     490             :     // NOLINTNEXTLINE(google-runtime-references)
     491           0 :     void pup(PUP::er& p) {
     492             :       p | modal_volume_data;
     493             :       p | mesh;
     494             :     }
     495             :   };
     496             : 
     497           0 :   using package_argument_tags =
     498             :       tmpl::list<Tags..., domain::Tags::Mesh<VolumeDim>>;
     499             : 
     500             :   /// \brief Package data for sending to neighbor elements.
     501           1 :   void package_data(gsl::not_null<PackagedData*> packaged_data,
     502             :                     const typename Tags::type&... tensors,
     503             :                     const Mesh<VolumeDim>& mesh,
     504             :                     const OrientationMap<VolumeDim>& orientation_map) const;
     505             : 
     506           0 :   using limit_tags = tmpl::list<Tags...>;
     507           0 :   using limit_argument_tags = tmpl::list<domain::Tags::Element<VolumeDim>,
     508             :                                          domain::Tags::Mesh<VolumeDim>>;
     509             : 
     510           0 :   bool operator()(
     511             :       const gsl::not_null<std::add_pointer_t<typename Tags::type>>... tensors,
     512             :       const Element<VolumeDim>& element, const Mesh<VolumeDim>& mesh,
     513             :       const std::unordered_map<DirectionalId<VolumeDim>, PackagedData,
     514             :                                boost::hash<DirectionalId<VolumeDim>>>&
     515             :           neighbor_data) const;
     516             : 
     517             :  private:
     518             :   template <typename Tag>
     519           0 :   char limit_one_tensor(
     520             :       gsl::not_null<Variables<tmpl::list<::Tags::Modal<Tags>...>>*> coeffs_self,
     521             :       gsl::not_null<bool*> limited_any_component, const Mesh<1>& mesh,
     522             :       const std::unordered_map<DirectionalId<1>, PackagedData,
     523             :                                boost::hash<DirectionalId<1>>>& neighbor_data)
     524             :       const;
     525             :   template <typename Tag>
     526           0 :   char limit_one_tensor(
     527             :       gsl::not_null<Variables<tmpl::list<::Tags::Modal<Tags>...>>*> coeffs_self,
     528             :       gsl::not_null<bool*> limited_any_component, const Mesh<2>& mesh,
     529             :       const std::unordered_map<DirectionalId<2>, PackagedData,
     530             :                                boost::hash<DirectionalId<2>>>& neighbor_data)
     531             :       const;
     532             :   template <typename Tag>
     533           0 :   char limit_one_tensor(
     534             :       gsl::not_null<Variables<tmpl::list<::Tags::Modal<Tags>...>>*> coeffs_self,
     535             :       gsl::not_null<bool*> limited_any_component, const Mesh<3>& mesh,
     536             :       const std::unordered_map<DirectionalId<3>, PackagedData,
     537             :                                boost::hash<DirectionalId<3>>>& neighbor_data)
     538             :       const;
     539             : 
     540             :   template <typename Tag, size_t Dim>
     541           0 :   char fill_variables_tag_with_spectral_coeffs(
     542             :       gsl::not_null<Variables<tmpl::list<::Tags::Modal<Tags>...>>*>
     543             :           modal_coeffs,
     544             :       const typename Tag::type& nodal_tensor, const Mesh<Dim>& mesh) const;
     545             : 
     546             :   std::array<double,
     547             :              Spectral::maximum_number_of_points<Spectral::Basis::Legendre>>
     548           0 :       alphas_ = make_array<
     549             :           Spectral::maximum_number_of_points<Spectral::Basis::Legendre>>(
     550             :           std::numeric_limits<double>::signaling_NaN());
     551           0 :   bool disable_for_debugging_{false};
     552             : };
     553             : 
     554             : template <size_t VolumeDim, typename... Tags>
     555             : Krivodonova<VolumeDim, tmpl::list<Tags...>>::Krivodonova(
     556             :     std::array<double,
     557             :                Spectral::maximum_number_of_points<Spectral::Basis::Legendre>>
     558             :         alphas,
     559             :     bool disable_for_debugging, const Options::Context& context)
     560             :     : alphas_(alphas), disable_for_debugging_(disable_for_debugging) {
     561             :   // See the main documentation for an explanation of why these bounds are
     562             :   // different from those of Krivodonova 2007
     563             :   if (alg::any_of(alphas_,
     564             :                   [](const double t) { return t > 1.0 or t <= 0.0; })) {
     565             :     PARSE_ERROR(context,
     566             :                 "The alphas in the Krivodonova limiter must be in the range "
     567             :                 "(0,1].");
     568             :   }
     569             : }
     570             : 
     571             : template <size_t VolumeDim, typename... Tags>
     572             : void Krivodonova<VolumeDim, tmpl::list<Tags...>>::package_data(
     573             :     const gsl::not_null<PackagedData*> packaged_data,
     574             :     const typename Tags::type&... tensors, const Mesh<VolumeDim>& mesh,
     575             :     const OrientationMap<VolumeDim>& orientation_map) const {
     576             :   if (UNLIKELY(disable_for_debugging_)) {
     577             :     // Do not initialize packaged_data
     578             :     return;
     579             :   }
     580             : 
     581             :   packaged_data->modal_volume_data.initialize(mesh.number_of_grid_points());
     582             :   // perform nodal coefficients to modal coefficients transformation on each
     583             :   // tensor component
     584             :   expand_pack(fill_variables_tag_with_spectral_coeffs<Tags>(
     585             :       &(packaged_data->modal_volume_data), tensors, mesh)...);
     586             : 
     587             :   packaged_data->modal_volume_data = orient_variables(
     588             :       packaged_data->modal_volume_data, mesh.extents(), orientation_map);
     589             : 
     590             :   // This doesn't currently do anything because the mesh is assumed to be the
     591             :   // same in all dimensions
     592             :   packaged_data->mesh = orientation_map(mesh);
     593             : }
     594             : 
     595             : template <size_t VolumeDim, typename... Tags>
     596             : bool Krivodonova<VolumeDim, tmpl::list<Tags...>>::operator()(
     597             :     const gsl::not_null<std::add_pointer_t<typename Tags::type>>... tensors,
     598             :     const Element<VolumeDim>& element, const Mesh<VolumeDim>& mesh,
     599             :     const std::unordered_map<DirectionalId<VolumeDim>, PackagedData,
     600             :                              boost::hash<DirectionalId<VolumeDim>>>&
     601             :         neighbor_data) const {
     602             :   if (UNLIKELY(disable_for_debugging_)) {
     603             :     // Do not modify input tensors
     604             :     return false;
     605             :   }
     606             :   if (UNLIKELY(mesh != Mesh<VolumeDim>(mesh.extents()[0], mesh.basis()[0],
     607             :                                        mesh.quadrature()[0]))) {
     608             :     ERROR(
     609             :         "The Krivodonova limiter does not yet support non-uniform number of "
     610             :         "collocation points, bases, and quadrature in each direction. The "
     611             :         "mesh is: "
     612             :         << mesh);
     613             :   }
     614             :   if (UNLIKELY(
     615             :           alg::any_of(element.neighbors(), [](const auto& direction_neighbors) {
     616             :             return direction_neighbors.second.size() != 1;
     617             :           }))) {
     618             :     ERROR("The Krivodonova limiter does not yet support h-refinement");
     619             :   }
     620             :   alg::for_each(neighbor_data, [&mesh](const auto& id_packaged_data) {
     621             :     if (UNLIKELY(id_packaged_data.second.mesh != mesh)) {
     622             :       ERROR(
     623             :           "The Krivodonova limiter does not yet support differing meshes "
     624             :           "between neighbors. Self mesh is: "
     625             :           << mesh << " neighbor mesh is: " << id_packaged_data.second.mesh);
     626             :     }
     627             :   });
     628             : 
     629             :   // Compute local modal coefficients
     630             :   Variables<tmpl::list<::Tags::Modal<Tags>...>> coeffs_self(
     631             :       mesh.number_of_grid_points(), 0.0);
     632             :   expand_pack(fill_variables_tag_with_spectral_coeffs<Tags>(&coeffs_self,
     633             :                                                             *tensors, mesh)...);
     634             : 
     635             :   // Perform the limiting on the modal coefficients
     636             :   bool limited_any_component = false;
     637             :   expand_pack(limit_one_tensor<::Tags::Modal<Tags>>(
     638             :       make_not_null(&coeffs_self), make_not_null(&limited_any_component), mesh,
     639             :       neighbor_data)...);
     640             : 
     641             :   // transform back to nodal coefficients
     642             :   const auto wrap_copy_nodal_coeffs =
     643             :       [&mesh, &coeffs_self](auto tag, const auto tensor) {
     644             :         auto& coeffs_tensor = get<decltype(tag)>(coeffs_self);
     645             :         auto tensor_it = tensor->begin();
     646             :         for (auto coeffs_it = coeffs_tensor.begin();
     647             :              coeffs_it != coeffs_tensor.end();
     648             :              (void)++coeffs_it, (void)++tensor_it) {
     649             :           to_nodal_coefficients(make_not_null(&*tensor_it), *coeffs_it, mesh);
     650             :         }
     651             :         return '0';
     652             :       };
     653             :   expand_pack(wrap_copy_nodal_coeffs(::Tags::Modal<Tags>{}, tensors)...);
     654             : 
     655             :   return limited_any_component;
     656             : }
     657             : 
     658             : template <size_t VolumeDim, typename... Tags>
     659             : template <typename Tag>
     660           0 : char Krivodonova<VolumeDim, tmpl::list<Tags...>>::limit_one_tensor(
     661             :     const gsl::not_null<Variables<tmpl::list<::Tags::Modal<Tags>...>>*>
     662             :         coeffs_self,
     663             :     const gsl::not_null<bool*> limited_any_component, const Mesh<1>& mesh,
     664             :     const std::unordered_map<DirectionalId<1>, PackagedData,
     665             :                              boost::hash<DirectionalId<1>>>& neighbor_data)
     666             :     const {
     667             :   using tensor_type = typename Tag::type;
     668             :   for (size_t storage_index = 0; storage_index < tensor_type::size();
     669             :        ++storage_index) {
     670             :     // for each coefficient...
     671             :     for (size_t i = mesh.extents()[0] - 1; i > 0; --i) {
     672             :       auto& self_coeffs = get<Tag>(*coeffs_self)[storage_index];
     673             :       double min_abs_coeff = std::abs(self_coeffs[i]);
     674             :       const double sgn_of_coeff = sgn(self_coeffs[i]);
     675             :       bool sgns_all_equal = true;
     676             :       for (const auto& kv : neighbor_data) {
     677             :         const auto& neighbor_coeffs =
     678             :             get<Tag>(kv.second.modal_volume_data)[storage_index];
     679             :         const double tmp = kv.first.direction().sign() * gsl::at(alphas_, i) *
     680             :                            (neighbor_coeffs[i - 1] - self_coeffs[i - 1]);
     681             : 
     682             :         min_abs_coeff = std::min(min_abs_coeff, std::abs(tmp));
     683             :         sgns_all_equal &= sgn(tmp) == sgn_of_coeff;
     684             :         if (not sgns_all_equal) {
     685             :           self_coeffs[i] = 0.0;
     686             :           break;
     687             :         }
     688             :       }
     689             :       if (sgns_all_equal) {
     690             :         const double tmp = sgn_of_coeff * min_abs_coeff;
     691             :         if (tmp == self_coeffs[i]) {
     692             :           break;
     693             :         }
     694             :         *limited_any_component |= true;
     695             :         self_coeffs[i] = tmp;
     696             :       }
     697             :     }
     698             :   }
     699             :   return '0';
     700             : }
     701             : 
     702             : template <size_t VolumeDim, typename... Tags>
     703             : template <typename Tag>
     704           0 : char Krivodonova<VolumeDim, tmpl::list<Tags...>>::limit_one_tensor(
     705             :     const gsl::not_null<Variables<tmpl::list<::Tags::Modal<Tags>...>>*>
     706             :         coeffs_self,
     707             :     const gsl::not_null<bool*> limited_any_component, const Mesh<2>& mesh,
     708             :     const std::unordered_map<DirectionalId<2>, PackagedData,
     709             :                              boost::hash<DirectionalId<2>>>& neighbor_data)
     710             :     const {
     711             :   using tensor_type = typename Tag::type;
     712             :   const auto minmod = [&coeffs_self, &mesh, &neighbor_data, this](
     713             :                           const size_t local_i, const size_t local_j,
     714             :                           const size_t local_tensor_storage_index) {
     715             :     const auto& self_coeffs =
     716             :         get<Tag>(*coeffs_self)[local_tensor_storage_index];
     717             :     double min_abs_coeff = std::abs(
     718             :         self_coeffs[mesh.storage_index(Index<VolumeDim>{local_i, local_j})]);
     719             :     const double sgn_of_coeff = sgn(
     720             :         self_coeffs[mesh.storage_index(Index<VolumeDim>{local_i, local_j})]);
     721             :     for (const auto& kv : neighbor_data) {
     722             :       const Direction<2>& dir = kv.first.direction();
     723             :       const auto& neighbor_coeffs =
     724             :           get<Tag>(kv.second.modal_volume_data)[local_tensor_storage_index];
     725             :       const size_t index_i =
     726             :           dir.axis() == Direction<VolumeDim>::Axis::Xi ? local_i - 1 : local_i;
     727             :       const size_t index_j =
     728             :           dir.axis() == Direction<VolumeDim>::Axis::Eta ? local_j - 1 : local_j;
     729             :       // skip neighbors where we cannot compute a finite difference in that
     730             :       // direction because we are already at the lowest coefficient.
     731             :       if (index_i == std::numeric_limits<size_t>::max() or
     732             :           index_j == std::numeric_limits<size_t>::max()) {
     733             :         continue;
     734             :       }
     735             :       const size_t alpha_index =
     736             :           dir.axis() == Direction<VolumeDim>::Axis::Xi ? local_i : local_j;
     737             :       const double tmp =
     738             :           dir.sign() * gsl::at(alphas_, alpha_index) *
     739             :           (neighbor_coeffs[mesh.storage_index(
     740             :                Index<VolumeDim>{index_i, index_j})] -
     741             :            self_coeffs[mesh.storage_index(Index<VolumeDim>{index_i, index_j})]);
     742             : 
     743             :       min_abs_coeff = std::min(min_abs_coeff, std::abs(tmp));
     744             :       if (sgn(tmp) != sgn_of_coeff) {
     745             :         return 0.0;
     746             :       }
     747             :     }
     748             :     return sgn_of_coeff * min_abs_coeff;
     749             :   };
     750             : 
     751             :   for (size_t tensor_storage_index = 0;
     752             :        tensor_storage_index < tensor_type::size(); ++tensor_storage_index) {
     753             :     // for each coefficient...
     754             :     for (size_t i = mesh.extents()[0] - 1; i > 0; --i) {
     755             :       for (size_t j = i; j < mesh.extents()[1]; --j) {
     756             :         // Check if we are done limiting, and if so we move on to the next
     757             :         // tensor component.
     758             :         auto& self_coeffs = get<Tag>(*coeffs_self)[tensor_storage_index];
     759             :         // We treat the different cases separately to reduce the number of
     760             :         // times we call minmod, not because it is required for correctness.
     761             :         if (UNLIKELY(i == j)) {
     762             :           const double tmp = minmod(i, j, tensor_storage_index);
     763             :           if (tmp == self_coeffs[mesh.storage_index(Index<VolumeDim>{i, j})]) {
     764             :             goto next_tensor_index;
     765             :           }
     766             :           *limited_any_component |= true;
     767             :           self_coeffs[mesh.storage_index(Index<VolumeDim>{i, j})] = tmp;
     768             :         } else {
     769             :           const double tmp_ij = minmod(i, j, tensor_storage_index);
     770             :           const double tmp_ji = minmod(j, i, tensor_storage_index);
     771             :           if (tmp_ij ==
     772             :                   self_coeffs[mesh.storage_index(Index<VolumeDim>{i, j})] and
     773             :               tmp_ji ==
     774             :                   self_coeffs[mesh.storage_index(Index<VolumeDim>{j, i})]) {
     775             :             goto next_tensor_index;
     776             :           }
     777             :           *limited_any_component |= true;
     778             :           self_coeffs[mesh.storage_index(Index<VolumeDim>{i, j})] = tmp_ij;
     779             :           self_coeffs[mesh.storage_index(Index<VolumeDim>{j, i})] = tmp_ji;
     780             :         }
     781             :       }
     782             :     }
     783             :   next_tensor_index:
     784             :     continue;
     785             :   }
     786             :   return '0';
     787             : }
     788             : 
     789             : template <size_t VolumeDim, typename... Tags>
     790             : template <typename Tag>
     791           0 : char Krivodonova<VolumeDim, tmpl::list<Tags...>>::limit_one_tensor(
     792             :     const gsl::not_null<Variables<tmpl::list<::Tags::Modal<Tags>...>>*>
     793             :         coeffs_self,
     794             :     const gsl::not_null<bool*> limited_any_component, const Mesh<3>& mesh,
     795             :     const std::unordered_map<DirectionalId<3>, PackagedData,
     796             :                              boost::hash<DirectionalId<3>>>& neighbor_data)
     797             :     const {
     798             :   using tensor_type = typename Tag::type;
     799             :   const auto minmod = [&coeffs_self, &mesh, &neighbor_data, this](
     800             :                           const size_t local_i, const size_t local_j,
     801             :                           const size_t local_k,
     802             :                           const size_t local_tensor_storage_index) {
     803             :     const auto& self_coeffs =
     804             :         get<Tag>(*coeffs_self)[local_tensor_storage_index];
     805             :     double min_abs_coeff = std::abs(self_coeffs[mesh.storage_index(
     806             :         Index<VolumeDim>{local_i, local_j, local_k})]);
     807             :     const double sgn_of_coeff = sgn(self_coeffs[mesh.storage_index(
     808             :         Index<VolumeDim>{local_i, local_j, local_k})]);
     809             :     for (const auto& kv : neighbor_data) {
     810             :       const Direction<3>& dir = kv.first.direction();
     811             :       const auto& neighbor_coeffs =
     812             :           get<Tag>(kv.second.modal_volume_data)[local_tensor_storage_index];
     813             :       const size_t index_i =
     814             :           dir.axis() == Direction<VolumeDim>::Axis::Xi ? local_i - 1 : local_i;
     815             :       const size_t index_j =
     816             :           dir.axis() == Direction<VolumeDim>::Axis::Eta ? local_j - 1 : local_j;
     817             :       const size_t index_k = dir.axis() == Direction<VolumeDim>::Axis::Zeta
     818             :                                  ? local_k - 1
     819             :                                  : local_k;
     820             :       // skip neighbors where we cannot compute a finite difference in that
     821             :       // direction because we are already at the lowest coefficient.
     822             :       if (index_i == std::numeric_limits<size_t>::max() or
     823             :           index_j == std::numeric_limits<size_t>::max() or
     824             :           index_k == std::numeric_limits<size_t>::max()) {
     825             :         continue;
     826             :       }
     827             :       const size_t alpha_index =
     828             :           dir.axis() == Direction<VolumeDim>::Axis::Xi
     829             :               ? local_i
     830             :               : dir.axis() == Direction<VolumeDim>::Axis::Eta ? local_j
     831             :                                                               : local_k;
     832             :       const double tmp = dir.sign() * gsl::at(alphas_, alpha_index) *
     833             :                          (neighbor_coeffs[mesh.storage_index(
     834             :                               Index<VolumeDim>{index_i, index_j, index_k})] -
     835             :                           self_coeffs[mesh.storage_index(
     836             :                               Index<VolumeDim>{index_i, index_j, index_k})]);
     837             : 
     838             :       min_abs_coeff = std::min(min_abs_coeff, std::abs(tmp));
     839             :       if (sgn(tmp) != sgn_of_coeff) {
     840             :         return 0.0;
     841             :       }
     842             :     }
     843             :     return sgn_of_coeff * min_abs_coeff;
     844             :   };
     845             : 
     846             :   for (size_t tensor_storage_index = 0;
     847             :        tensor_storage_index < tensor_type::size(); ++tensor_storage_index) {
     848             :     // for each coefficient...
     849             :     for (size_t i = mesh.extents()[0] - 1; i > 0; --i) {
     850             :       for (size_t j = i; j < mesh.extents()[1]; --j) {
     851             :         for (size_t k = j; k < mesh.extents()[2]; --k) {
     852             :           // Check if we are done limiting, and if so we move on to the next
     853             :           // tensor component.
     854             :           auto& self_coeffs = get<Tag>(*coeffs_self)[tensor_storage_index];
     855             :           // We treat the different cases separately to reduce the number of
     856             :           // times we call minmod, not because it is required for correctness.
     857             :           //
     858             :           // Note that the case `i == k and i != j` cannot be encountered since
     859             :           // the loop bounds are `i >= j >= k`.
     860             :           if (UNLIKELY(i == j and i == k)) {
     861             :             const double tmp = minmod(i, j, k, tensor_storage_index);
     862             :             if (tmp ==
     863             :                 self_coeffs[mesh.storage_index(Index<VolumeDim>{i, j, k})]) {
     864             :               goto next_tensor_index;
     865             :             }
     866             :             *limited_any_component |= true;
     867             :             self_coeffs[mesh.storage_index(Index<VolumeDim>{i, j, k})] = tmp;
     868             :           } else if (i == j and i != k) {
     869             :             const double tmp_ijk = minmod(i, j, k, tensor_storage_index);
     870             :             const double tmp_ikj = minmod(i, k, j, tensor_storage_index);
     871             :             const double tmp_kij = minmod(k, i, j, tensor_storage_index);
     872             :             if (tmp_ijk == self_coeffs[mesh.storage_index(
     873             :                                Index<VolumeDim>{i, j, k})] and
     874             :                 tmp_ikj == self_coeffs[mesh.storage_index(
     875             :                                Index<VolumeDim>{i, k, j})] and
     876             :                 tmp_kij == self_coeffs[mesh.storage_index(
     877             :                                Index<VolumeDim>{k, i, j})]) {
     878             :               goto next_tensor_index;
     879             :             }
     880             :             *limited_any_component |= true;
     881             :             self_coeffs[mesh.storage_index(Index<VolumeDim>{i, j, k})] =
     882             :                 tmp_ijk;
     883             :             self_coeffs[mesh.storage_index(Index<VolumeDim>{i, k, j})] =
     884             :                 tmp_ikj;
     885             :             self_coeffs[mesh.storage_index(Index<VolumeDim>{k, i, j})] =
     886             :                 tmp_kij;
     887             :           } else if (i != j and j == k) {
     888             :             const double tmp_ijk = minmod(i, j, k, tensor_storage_index);
     889             :             const double tmp_kij = minmod(k, i, j, tensor_storage_index);
     890             :             const double tmp_kji = minmod(k, j, i, tensor_storage_index);
     891             :             if (tmp_ijk == self_coeffs[mesh.storage_index(
     892             :                                Index<VolumeDim>{i, j, k})] and
     893             :                 tmp_kij == self_coeffs[mesh.storage_index(
     894             :                                Index<VolumeDim>{k, i, j})] and
     895             :                 tmp_kji == self_coeffs[mesh.storage_index(
     896             :                                Index<VolumeDim>{k, j, i})]) {
     897             :               goto next_tensor_index;
     898             :             }
     899             :             *limited_any_component |= true;
     900             :             self_coeffs[mesh.storage_index(Index<VolumeDim>{i, j, k})] =
     901             :                 tmp_ijk;
     902             :             self_coeffs[mesh.storage_index(Index<VolumeDim>{k, i, j})] =
     903             :                 tmp_kij;
     904             :             self_coeffs[mesh.storage_index(Index<VolumeDim>{k, j, i})] =
     905             :                 tmp_kji;
     906             :           } else {
     907             :             const double tmp_ijk = minmod(i, j, k, tensor_storage_index);
     908             :             const double tmp_jik = minmod(j, i, k, tensor_storage_index);
     909             :             const double tmp_ikj = minmod(i, k, j, tensor_storage_index);
     910             :             const double tmp_jki = minmod(j, k, i, tensor_storage_index);
     911             :             const double tmp_kij = minmod(k, i, j, tensor_storage_index);
     912             :             const double tmp_kji = minmod(k, j, i, tensor_storage_index);
     913             :             if (tmp_ijk == self_coeffs[mesh.storage_index(
     914             :                                Index<VolumeDim>{i, j, k})] and
     915             :                 tmp_jik == self_coeffs[mesh.storage_index(
     916             :                                Index<VolumeDim>{j, i, k})] and
     917             :                 tmp_ikj == self_coeffs[mesh.storage_index(
     918             :                                Index<VolumeDim>{i, k, j})] and
     919             :                 tmp_jki == self_coeffs[mesh.storage_index(
     920             :                                Index<VolumeDim>{j, k, i})] and
     921             :                 tmp_kij == self_coeffs[mesh.storage_index(
     922             :                                Index<VolumeDim>{k, i, j})] and
     923             :                 tmp_kji == self_coeffs[mesh.storage_index(
     924             :                                Index<VolumeDim>{k, j, i})]) {
     925             :               goto next_tensor_index;
     926             :             }
     927             :             *limited_any_component |= true;
     928             :             self_coeffs[mesh.storage_index(Index<VolumeDim>{i, j, k})] =
     929             :                 tmp_ijk;
     930             :             self_coeffs[mesh.storage_index(Index<VolumeDim>{j, i, k})] =
     931             :                 tmp_jik;
     932             :             self_coeffs[mesh.storage_index(Index<VolumeDim>{i, k, j})] =
     933             :                 tmp_ikj;
     934             :             self_coeffs[mesh.storage_index(Index<VolumeDim>{j, k, i})] =
     935             :                 tmp_jki;
     936             :             self_coeffs[mesh.storage_index(Index<VolumeDim>{k, i, j})] =
     937             :                 tmp_kij;
     938             :             self_coeffs[mesh.storage_index(Index<VolumeDim>{k, j, i})] =
     939             :                 tmp_kji;
     940             :           }
     941             :         }
     942             :       }
     943             :     }
     944             :   next_tensor_index:
     945             :     continue;
     946             :   }
     947             :   return '0';
     948             : }
     949             : 
     950             : template <size_t VolumeDim, typename... Tags>
     951             : template <typename Tag, size_t Dim>
     952           0 : char Krivodonova<VolumeDim, tmpl::list<Tags...>>::
     953             :     fill_variables_tag_with_spectral_coeffs(
     954             :         const gsl::not_null<Variables<tmpl::list<::Tags::Modal<Tags>...>>*>
     955             :             modal_coeffs,
     956             :         const typename Tag::type& nodal_tensor, const Mesh<Dim>& mesh) const {
     957             :   auto& coeffs_tensor = get<::Tags::Modal<Tag>>(*modal_coeffs);
     958             :   auto tensor_it = nodal_tensor.begin();
     959             :   for (auto coeffs_it = coeffs_tensor.begin(); coeffs_it != coeffs_tensor.end();
     960             :        (void)++coeffs_it, (void)++tensor_it) {
     961             :     to_modal_coefficients(make_not_null(&*coeffs_it), *tensor_it, mesh);
     962             :   }
     963             :   return '0';
     964             : }
     965             : 
     966             : template <size_t VolumeDim, typename... Tags>
     967             : void Krivodonova<VolumeDim, tmpl::list<Tags...>>::pup(PUP::er& p) {
     968             :   p | alphas_;
     969             :   p | disable_for_debugging_;
     970             : }
     971             : 
     972             : template <size_t VolumeDim, typename... Tags>
     973             : bool Krivodonova<VolumeDim, tmpl::list<Tags...>>::operator==(
     974             :     const Krivodonova<VolumeDim, tmpl::list<Tags...>>& rhs) const {
     975             :   return alphas_ == rhs.alphas_ and
     976             :          disable_for_debugging_ == rhs.disable_for_debugging_;
     977             : }
     978             : 
     979             : template <size_t VolumeDim, typename... Tags>
     980           0 : bool operator!=(const Krivodonova<VolumeDim, tmpl::list<Tags...>>& lhs,
     981             :                 const Krivodonova<VolumeDim, tmpl::list<Tags...>>& rhs) {
     982             :   return not(lhs == rhs);
     983             : }
     984             : }  // namespace Limiters

Generated by: LCOV version 1.14