SpECTRE Documentation Coverage Report
Current view: top level - NumericalAlgorithms/FiniteDifference - AoWeno.hpp Hit Total Coverage
Commit: 3c072f0ce967e2e56649d3fa12aa2a0e4fe2a42e Lines: 3 4 75.0 %
Date: 2024-04-23 20:50:18
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 <cmath>
       8             : #include <cstddef>
       9             : #include <utility>
      10             : 
      11             : #include "NumericalAlgorithms/FiniteDifference/Reconstruct.hpp"
      12             : #include "Utilities/ConstantExpressions.hpp"
      13             : #include "Utilities/ForceInline.hpp"
      14             : #include "Utilities/Gsl.hpp"
      15             : #include "Utilities/Math.hpp"
      16             : #include "Utilities/TMPL.hpp"
      17             : 
      18             : /// \cond
      19             : template <size_t Dim>
      20             : class Direction;
      21             : template <size_t Dim>
      22             : class Index;
      23             : /// \endcond
      24             : 
      25           1 : namespace fd::reconstruction {
      26             : namespace detail {
      27             : template <size_t NonlinearWeightExponent>
      28             : struct AoWeno53Reconstructor {
      29             :   SPECTRE_ALWAYS_INLINE static std::array<double, 2> pointwise(
      30             :       const double* const u, const int stride, const double gamma_hi,
      31             :       const double gamma_lo, const double epsilon) {
      32             :     ASSERT(gamma_hi <= 1.0 and gamma_hi >= 0.0,
      33             :            "gamma_hi must be in [0.0, 1.0] but is " << gamma_hi);
      34             :     ASSERT(gamma_lo <= 1.0 and gamma_lo >= 0.0,
      35             :            "gamma_lo must be in [0.0, 1.0] but is " << gamma_lo);
      36             :     ASSERT(epsilon > 0.0,
      37             :            "epsilon must be greater than zero but is " << epsilon);
      38             : 
      39             :     const std::array moments_sr3_1{
      40             :         1.041666666666666 * u[0] - 0.08333333333333333 * u[-stride] +
      41             :             0.04166666666666666 * u[-2 * stride],
      42             :         0.5 * u[-2 * stride] - 2.0 * u[-stride] + 1.5 * u[0],
      43             :         0.5 * u[-2 * stride] - u[-stride] + 0.5 * u[0]};
      44             :     const std::array moments_sr3_2{0.04166666666666666 * u[stride] +
      45             :                                        0.9166666666666666 * u[0] +
      46             :                                        0.04166666666666666 * u[-stride],
      47             :                                    0.5 * (u[stride] - u[-stride]),
      48             :                                    0.5 * u[-stride] - u[0] + 0.5 * u[stride]};
      49             :     const std::array moments_sr3_3{
      50             :         0.04166666666666666 * u[2 * stride] - 0.08333333333333333 * u[stride] +
      51             :             1.04166666666666666 * u[0],
      52             :         -1.5 * u[0] + 2.0 * u[stride] - 0.5 * u[2 * stride],
      53             :         0.5 * u[0] - u[stride] + 0.5 * u[2 * stride]};
      54             :     const std::array moments_sr5{-2.95138888888888881e-03 * u[-2 * stride] +
      55             :                                      5.34722222222222196e-02 * u[-stride] +
      56             :                                      8.98958333333333304e-01 * u[0] +
      57             :                                      5.34722222222222196e-02 * u[stride] +
      58             :                                      -2.95138888888888881e-03 * u[2 * stride],
      59             :                                  7.08333333333333315e-02 * u[-2 * stride] +
      60             :                                      -6.41666666666666718e-01 * u[-stride] +
      61             :                                      6.41666666666666718e-01 * u[stride] +
      62             :                                      -7.08333333333333315e-02 * u[2 * stride],
      63             :                                  -3.27380952380952397e-02 * u[-2 * stride] +
      64             :                                      6.30952380952380931e-01 * u[-stride] +
      65             :                                      -1.19642857142857140e+00 * u[0] +
      66             :                                      6.30952380952380931e-01 * u[stride] +
      67             :                                      -3.27380952380952397e-02 * u[2 * stride],
      68             :                                  -8.33333333333333287e-02 * u[-2 * stride] +
      69             :                                      1.66666666666666657e-01 * u[-stride] +
      70             :                                      -1.66666666666666657e-01 * u[stride] +
      71             :                                      8.33333333333333287e-02 * u[2 * stride],
      72             :                                  4.16666666666666644e-02 * u[-2 * stride] +
      73             :                                      -1.66666666666666657e-01 * u[-stride] +
      74             :                                      2.50000000000000000e-01 * u[0] +
      75             :                                      -1.66666666666666657e-01 * u[stride] +
      76             :                                      4.16666666666666644e-02 * u[2 * stride]};
      77             : 
      78             :     // These are the "first alternative" oscillation indicators
      79             :     constexpr double beta_r3_factor = 37.0 / 3.0;
      80             :     const std::array beta_r3{
      81             :         square(moments_sr3_1[1]) + beta_r3_factor * square(moments_sr3_1[2]),
      82             :         square(moments_sr3_2[1]) + beta_r3_factor * square(moments_sr3_2[2]),
      83             :         square(moments_sr3_3[1]) + beta_r3_factor * square(moments_sr3_3[2])};
      84             :     const double beta_sr5 = square(moments_sr5[1]) +
      85             :                             61.0 / 5.0 * moments_sr5[1] * moments_sr5[3] +
      86             :                             37.0 / 3.0 * square(moments_sr5[2]) +
      87             :                             1538.0 / 7.0 * moments_sr5[2] * moments_sr5[4] +
      88             :                             8973.0 / 50.0 * square(moments_sr5[3]) +
      89             :                             167158.0 / 49.0 * square(moments_sr5[4]);
      90             : 
      91             :     // Compute linear and normalized nonlinear weights
      92             :     const std::array linear_weights{
      93             :         gamma_hi, 0.5 * (1.0 - gamma_hi) * (1.0 - gamma_lo),
      94             :         (1.0 - gamma_hi) * gamma_lo, 0.5 * (1.0 - gamma_hi) * (1.0 - gamma_lo)};
      95             :     std::array nonlinear_weights{
      96             :         linear_weights[0] / pow<NonlinearWeightExponent>(beta_sr5 + epsilon),
      97             :         linear_weights[1] / pow<NonlinearWeightExponent>(beta_r3[0] + epsilon),
      98             :         linear_weights[2] / pow<NonlinearWeightExponent>(beta_r3[1] + epsilon),
      99             :         linear_weights[3] / pow<NonlinearWeightExponent>(beta_r3[2] + epsilon)};
     100             :     const double normalization = nonlinear_weights[0] + nonlinear_weights[1] +
     101             :                                  nonlinear_weights[2] + nonlinear_weights[3];
     102             :     for (double& nw : nonlinear_weights) {
     103             :       nw /= normalization;
     104             :     }
     105             : 
     106             :     const std::array<double, 5> moments{
     107             :         {nonlinear_weights[0] / linear_weights[0] *
     108             :                  (moments_sr5[0] - linear_weights[1] * moments_sr3_1[0] -
     109             :                   linear_weights[2] * moments_sr3_2[0] -
     110             :                   linear_weights[3] * moments_sr3_3[0]) +
     111             :              nonlinear_weights[1] * moments_sr3_1[0] +
     112             :              nonlinear_weights[2] * moments_sr3_2[0] +
     113             :              nonlinear_weights[3] * moments_sr3_3[0],
     114             :          nonlinear_weights[0] / linear_weights[0] *
     115             :                  (moments_sr5[1] - linear_weights[1] * moments_sr3_1[1] -
     116             :                   linear_weights[2] * moments_sr3_2[1] -
     117             :                   linear_weights[3] * moments_sr3_3[1]) +
     118             :              nonlinear_weights[1] * moments_sr3_1[1] +
     119             :              nonlinear_weights[2] * moments_sr3_2[1] +
     120             :              nonlinear_weights[3] * moments_sr3_3[1],
     121             :          nonlinear_weights[0] / linear_weights[0] *
     122             :                  (moments_sr5[2] - linear_weights[1] * moments_sr3_1[2] -
     123             :                   linear_weights[2] * moments_sr3_2[2] -
     124             :                   linear_weights[3] * moments_sr3_3[2]) +
     125             :              nonlinear_weights[1] * moments_sr3_1[2] +
     126             :              nonlinear_weights[2] * moments_sr3_2[2] +
     127             :              nonlinear_weights[3] * moments_sr3_3[2],
     128             :          nonlinear_weights[0] / linear_weights[0] * moments_sr5[3],
     129             :          nonlinear_weights[0] / linear_weights[0] * moments_sr5[4]}};
     130             : 
     131             :     // The polynomial values (excluding the L_0(x)) at the lower/upper faces.
     132             :     // The polynomials are (x in [-0.5,0.5]):
     133             :     // L0(x)=1.0 (not in array)
     134             :     // L1(x)=x
     135             :     // L2(x)=x^2-1/12
     136             :     // L3(x)=x^3 - (3/20) x
     137             :     // L4(x)=x^4 - (3/14) x^2 + 3/560
     138             :     const std::array polys_at_plus_half{0.5, 0.16666666666666666, 0.05,
     139             :                                         0.014285714285714289};
     140             : 
     141             :     return {{moments[0] - polys_at_plus_half[0] * moments[1] +
     142             :                  polys_at_plus_half[1] * moments[2] -
     143             :                  polys_at_plus_half[2] * moments[3] +
     144             :                  polys_at_plus_half[3] * moments[4],
     145             :              moments[0] + polys_at_plus_half[0] * moments[1] +
     146             :                  polys_at_plus_half[1] * moments[2] +
     147             :                  polys_at_plus_half[2] * moments[3] +
     148             :                  polys_at_plus_half[3] * moments[4]}};
     149             :   }
     150             : 
     151             :   SPECTRE_ALWAYS_INLINE static constexpr size_t stencil_width() { return 5; }
     152             : };
     153             : }  // namespace detail
     154             : 
     155             : /*!
     156             :  * \ingroup FiniteDifferenceGroup
     157             :  * \brief Performs an adaptive order (AO) WENO reconstruction using a single 5th
     158             :  * order stencil and a 3rd-order CWENO scheme.
     159             :  *
     160             :  * The AO-WENO(5,3) scheme is based on the scheme presented in
     161             :  * \cite Balsara2016780 but adjusted to do reconstruction on variables instead
     162             :  * of fluxes. The Legendre basis functions on the domain \f$\xi\in[-1/2,1/2]\f$
     163             :  * are given by:
     164             :  *
     165             :  * \f{align*}{
     166             :  * L_0(\xi) &= 1 \\
     167             :  * L_1(\xi) &= \xi \\
     168             :  * L_2(\xi) &= \xi^2-\frac{1}{12} \\
     169             :  * L_3(\xi) &= \xi^3 - \frac{3}{20} \xi \\
     170             :  * L_4(\xi) &= \xi^4 - \frac{3}{14} \xi^2 + \frac{3}{560}
     171             :  * \f}
     172             :  *
     173             :  * The oscillation indicators are given by
     174             :  *
     175             :  * \f{align*}{
     176             :  * \beta_l = \sum_{k=1}^{p}\sum_{m=1}^{p}\sigma_{km} u_{k,l} u_{m,l}
     177             :  * \f}
     178             :  *
     179             :  * where \f$p\f$ is the maximum degree of the basis function used for the
     180             :  * stencil \f$l\f$, and
     181             :  *
     182             :  * \f{align*}{
     183             :  * \sigma_{km}=\sum_{i=1}^{p}\int_{-1/2}^{1/2}
     184             :  * \frac{d^i L_k(\xi)}{d\xi^i}\frac{d^i L_m(\xi)}{d\xi^i}d\xi
     185             :  * \f}
     186             :  *
     187             :  * We write the 3rd-order reconstructed polynomial \f$P^{r3}_l(\xi)\f$
     188             :  * associated with the stencil \f$S^{r3}_l\f$ as
     189             :  *
     190             :  * \f{align*}{
     191             :  * P^{r3}_l(\xi) = u_0 +u_{\xi} L_1(\xi) + u_{\xi 2} L_2(\xi)
     192             :  * \f}
     193             :  *
     194             :  * For the stencil \f$S^{r3}_1\f$ we get
     195             :  *
     196             :  * \f{align*}{
     197             :  *  u_0 &= \frac{25}{24} u_{j} -\frac{1}{12} u_{j-1} + \frac{1}{24} u_{j-2} \\
     198             :  *  u_{\xi} &= \frac{1}{2}u_{j-2} - 2 u_{j-1} + \frac{3}{2} u_j \\
     199             :  *  u_{\xi2} &= \frac{1}{2} u_{j-2} - u_{j-1} + \frac{1}{2} u_j
     200             :  * \f}
     201             :  *
     202             :  * For the stencil \f$S^{r3}_2\f$ we get
     203             :  *
     204             :  * \f{align*}{
     205             :  *  u_0 &= \frac{1}{24} u_{j-1} + \frac{11}{12} u_{j} + \frac{1}{24} u_{j+1} \\
     206             :  *  u_{\xi} &= \frac{1}{2}(u_{j+1} - u_{j-1}) \\
     207             :  *  u_{\xi2} &= \frac{1}{2} u_{j-1} - u_{j} + \frac{1}{2} u_{j+1}
     208             :  * \f}
     209             :  *
     210             :  * For the stencil \f$S^{r3}_3\f$ we get
     211             :  *
     212             :  * \f{align*}{
     213             :  *  u_0 &= \frac{25}{24} u_{j} -\frac{1}{12} u_{j+1} + \frac{1}{24} u_{j+2} \\
     214             :  *  u_{\xi} &= -\frac{1}{2}u_{j+2} + 2 u_{j+1} - \frac{3}{2} u_j \\
     215             :  *  u_{\xi2} &= \frac{1}{2} u_{j+2} - u_{j+1} + \frac{1}{2} u_j
     216             :  * \f}
     217             :  *
     218             :  * The oscillation indicator for the 3rd-order stencils is given by
     219             :  *
     220             :  * \f{align*}{
     221             :  *  \beta^{r3}_l = \left(u_{\xi}\right)^2+\frac{13}{3}\left(u_{\xi2}\right)^2
     222             :  * \f}
     223             :  *
     224             :  * We write the 5th-order reconstructed polynomial \f$P^{r5}(\xi)\f$ as
     225             :  *
     226             :  * \f{align*}{
     227             :  * P^{r5}_l(\xi) = u_0 +u_{\xi} L_1(\xi) + u_{\xi 2} L_2(\xi)
     228             :  *  + u_{\xi3} L_3(\xi) + u_{\xi4} L_4(\xi)
     229             :  * \f}
     230             :  *
     231             :  * with
     232             :  *
     233             :  * \f{align*}{
     234             :  * u_0 &= -\frac{17}{5760} u_{j-2} + \frac{77}{1440} u_{j-1} +
     235             :  *        \frac{863}{960} u_{j} + \frac{77}{1440} u_{j+1} -
     236             :  *        \frac{17}{5760} u_{j+2} \\
     237             :  * u_{\xi} &= \frac{17}{240} u_{j-2} - \frac{77}{120} u_{j-1} +
     238             :  *           \frac{77}{120} u_{j+1} - \frac{17}{240} u_{j+2} \\
     239             :  * u_{\xi2} &= -\frac{11}{336} u_{j-2} + \frac{53}{84} u_{j-1} -
     240             :  *            \frac{67}{56} u_{j} + \frac{53}{84} u_{j+1} -
     241             :  *            \frac{11}{336} u_{j+2} \\
     242             :  * u_{\xi3} &= -\frac{1}{12} u_{j-2} + \frac{1}{6} u_{j-1}
     243             :  *            -\frac{1}{6} u_{j+1} + \frac{1}{12} u_{j+2} \\
     244             :  * u_{\xi4} &= \frac{1}{24} u_{j-2} -\frac{1}{6} u_{j-1}
     245             :  *           +\frac{1}{4} u_{j} - \frac{1}{6}u_{j+1}+\frac{1}{24}u_{j+2}
     246             :  * \f}
     247             :  *
     248             :  * The oscillation indicator is given by
     249             :  *
     250             :  * \f{align*}{
     251             :  *  \beta^{r5}&=\left(u_{\xi}+\frac{1}{10}u_{\xi3}\right)^2 \\
     252             :  *             &+\frac{13}{3}\left(u_{\xi2}+\frac{123}{455}u_{\xi4}\right)^2\\
     253             :  *             &+\frac{781}{20}(u_{\xi3})^2+\frac{1421461}{2275}(u_{\xi4})^2
     254             :  * \f}
     255             :  *
     256             :  * There are two linear weights \f$\gamma_{\mathrm{hi}}\f$ and
     257             :  * \f$\gamma_{\mathrm{lo}}\f$. \f$\gamma_{\mathrm{hi}}\f$ controls how much of
     258             :  * the 5th-order stencil is used in smooth regions, while
     259             :  * \f$\gamma_{\mathrm{lo}}\f$ controls the linear weight of the central
     260             :  * 3rd-order stencil. For larger \f$\gamma_{\mathrm{lo}}\f$, the 3rd-order
     261             :  * method is more centrally weighted. The linear weights for the stencils
     262             :  * are given by
     263             :  *
     264             :  * \f{align*}{
     265             :  *  \gamma^{r5}&=\gamma_{\mathrm{hi}} \\
     266             :  *  \gamma^{r3}_1& = (1-\gamma_{\mathrm{hi}})(1-\gamma_{\mathrm{lo}})/2 \\
     267             :  *  \gamma^{r3}_2& = (1-\gamma_{\mathrm{hi}})\gamma_{\mathrm{lo}} \\
     268             :  *  \gamma^{r3}_3& = (1-\gamma_{\mathrm{hi}})(1-\gamma_{\mathrm{lo}})/2
     269             :  * \f}
     270             :  *
     271             :  * We use the standard nonlinear weights instead of the "Z" weights of
     272             :  * \cite Borges20083191
     273             :  *
     274             :  * \f{align*}{
     275             :  *  w^{r5}&=\frac{\gamma^{r5}}{(\beta^{r5}+\epsilon)^q} \\
     276             :  *  w^{r3}_l&=\frac{\gamma^{r3}_l}{(\beta^{r3}_l+\epsilon)^q}
     277             :  * \f}
     278             :  *
     279             :  * where \f$\epsilon\f$ is a small number used to avoid division by zero. The
     280             :  * normalized nonlinear weights are denoted by \f$\bar{w}^{r5}\f$ and
     281             :  * \f$\bar{w}_l^{r3}\f$. The final reconstructed polynomial \f$P(\xi)\f$ is
     282             :  * given by
     283             :  *
     284             :  * \f{align*}{
     285             :  *  P(\xi)&=\frac{\bar{w}^{r5}}{\gamma^{r5}}
     286             :  *   \left(P^{r5}(\xi)-\gamma^{r3}_1P^{r3}_1(\xi)
     287             :  *         -\gamma^{r3}_2P^{r3}_2(\xi)-\gamma^{r3}_3P^{r3}_3(\xi)\right) \\
     288             :  *  &+\bar{w}^{r3}_1P^{r3}_1(\xi)+\bar{w}^{r3}_2P^{r3}_2(\xi)
     289             :  *  +\bar{w}^{r3}_3P^{r3}_3(\xi)
     290             :  * \f}
     291             :  *
     292             :  * The weights \f$\gamma_{\mathrm{hi}}\f$ and \f$\gamma_{\mathrm{lo}}\f$
     293             :  * are typically chosen to be in the range \f$[0.85,0.95]\f$.
     294             :  *
     295             :  * ### First alternative oscillation indicators
     296             :  *
     297             :  * Instead of integrating over just the cell, we can instead integrate the basis
     298             :  * functions over the entire fit interval, \f$[-5/2,5/2]\f$. Using this interval
     299             :  * for the \f$S^{r3}_l\f$ and the \f$S^{r5}\f$ stencils we get
     300             :  *
     301             :  * \f{align*}{
     302             :  *  \beta^{r3}_l&=(u_{\xi})^2 + \frac{37}{3} (u_{\xi2})^2 \\
     303             :  *  \beta^{r5}  &=\left(u_{\xi}+\frac{61}{10}u_{\xi3}\right)^2
     304             :  *   + \frac{569}{4}u_{\xi3}^2 \\
     305             :  *  &+ \frac{1}{8190742}\left(5383 u_{\xi2} + 167158 u_{\xi4}\right)^2
     306             :  *  +\frac{4410763}{501474}(u_{\xi2})^2 \\
     307             :  * &=u_{\xi}^2 + \frac{61}{5}u_{\xi}u_{\xi3} + \frac{37}{3}u_{\xi2}^2
     308             :  *  + \frac{1538}{7}u_{\xi2}u_{\xi4} \\
     309             :  * &+ \frac{8973}{50}u_{\xi3}^2 + \frac{167158}{49}u_{\xi4}^2
     310             :  * \f}
     311             :  *
     312             :  * Note that the indicator is manifestly non-negative, a required property of
     313             :  * oscillation indicators. These indicators weight high modes more, which means
     314             :  * the scheme is more sensitive to high-frequency features in the solution.
     315             :  *
     316             :  * \note currently it is the alternative indicators that are used. However, an
     317             :  * option to control which are used can readily be added, probably best done
     318             :  * as a template parameter with `if constexpr` to avoid conditionals inside
     319             :  * tight loops.
     320             :  */
     321             : template <size_t NonlinearWeightExponent, size_t Dim>
     322           1 : void aoweno_53(
     323             :     const gsl::not_null<std::array<gsl::span<double>, Dim>*>
     324             :         reconstructed_upper_side_of_face_vars,
     325             :     const gsl::not_null<std::array<gsl::span<double>, Dim>*>
     326             :         reconstructed_lower_side_of_face_vars,
     327             :     const gsl::span<const double>& volume_vars,
     328             :     const DirectionMap<Dim, gsl::span<const double>>& ghost_cell_vars,
     329             :     const Index<Dim>& volume_extents, const size_t number_of_variables,
     330             :     const double gamma_hi, const double gamma_lo, const double epsilon) {
     331             :   detail::reconstruct<detail::AoWeno53Reconstructor<NonlinearWeightExponent>>(
     332             :       reconstructed_upper_side_of_face_vars,
     333             :       reconstructed_lower_side_of_face_vars, volume_vars, ghost_cell_vars,
     334             :       volume_extents, number_of_variables, gamma_hi, gamma_lo, epsilon);
     335             : }
     336             : 
     337             : /*!
     338             :  * \brief Returns function pointers to the `aoweno_53` function, lower neighbor
     339             :  * reconstruction, and upper neighbor reconstruction.
     340             :  *
     341             :  * This is useful for controlling template parameters like the
     342             :  * `NonlinearWeightExponent` from an input file by setting a function pointer.
     343             :  * Note that the reason the reconstruction functions instead of say the
     344             :  * `pointwise` member function is returned is to avoid function pointers inside
     345             :  * tight loops.
     346             :  */
     347             : template <size_t Dim>
     348           1 : auto aoweno_53_function_pointers(size_t nonlinear_weight_exponent)
     349             :     -> std::tuple<void (*)(gsl::not_null<std::array<gsl::span<double>, Dim>*>,
     350             :                            gsl::not_null<std::array<gsl::span<double>, Dim>*>,
     351             :                            const gsl::span<const double>&,
     352             :                            const DirectionMap<Dim, gsl::span<const double>>&,
     353             :                            const Index<Dim>&, size_t, double, double, double),
     354             :                   void (*)(gsl::not_null<DataVector*>, const DataVector&,
     355             :                            const DataVector&, const Index<Dim>&,
     356             :                            const Index<Dim>&, const Direction<Dim>&,
     357             :                            const double&, const double&, const double&),
     358             :                   void (*)(gsl::not_null<DataVector*>, const DataVector&,
     359             :                            const DataVector&, const Index<Dim>&,
     360             :                            const Index<Dim>&, const Direction<Dim>&,
     361             :                            const double&, const double&, const double&)>;
     362             : }  // namespace fd::reconstruction

Generated by: LCOV version 1.14