SpECTRE Documentation Coverage Report
Current view: top level - NumericalAlgorithms/LinearOperators/Filters - HollowCylinder.hpp Coverage Total Hit
Commit: 0d1d717af84a81fb1284a20c4013708fe85cc2f0 Lines: 26.0 % 77 20
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 <cstddef>
       7              : #include <optional>
       8              : #include <string>
       9              : #include <unordered_map>
      10              : #include <unordered_set>
      11              : #include <vector>
      12              : 
      13              : #include "DataStructures/Matrix.hpp"
      14              : #include "DataStructures/Tensor/TypeAliases.hpp"
      15              : #include "NumericalAlgorithms/LinearOperators/Filters/Filter.hpp"
      16              : #include "Options/Auto.hpp"
      17              : #include "Options/Context.hpp"
      18              : #include "Options/String.hpp"
      19              : #include "Utilities/Serialization/CharmPupable.hpp"
      20              : #include "Utilities/TMPL.hpp"
      21              : 
      22              : /// \cond
      23              : class DataVector;
      24              : template <size_t Dim>
      25              : class Mesh;
      26              : template <typename TagsList>
      27              : class Variables;
      28              : namespace PUP {
      29              : class er;
      30              : }  // namespace PUP
      31              : namespace gsl {
      32              : template <typename T>
      33              : class not_null;
      34              : }  // namespace gsl
      35              : /// \endcond
      36              : 
      37              : namespace Filters {
      38              : /*!
      39              :  * \ingroup DiscontinuousGalerkinGroup
      40              :  * \brief A modal filter for hollow-cylinder elements: independent exponential
      41              :  * roll-offs in the radial, angular (Fourier), and axial directions plus an
      42              :  * optional top-mode Heaviside cutoff in the angular direction.
      43              :  *
      44              :  * Concrete implementation of `Filters::Filter` for the hollow-cylinder shells
      45              :  * created by `domain::creators::AngularCylinder`, driven by the DG filtering
      46              :  * action. See `Filters::Filter` for the framing of volume vs. boundary
      47              :  * application, the substep / every-N-steps cadence controls, and the
      48              :  * `blocks_to_filter` semantics.
      49              :  *
      50              :  * A hollow-cylinder shell element uses a Legendre/Chebyshev basis in the
      51              :  * radial direction (logical dimension 0), a Fourier basis in the angular
      52              :  * direction (logical dimension 1), and a Legendre/Chebyshev basis in the axial
      53              :  * \f$z\f$ direction (logical dimension 2).
      54              :  *
      55              :  * For each component of the tensors in `TagList`, the filter rescales the 1-D
      56              :  * modal coefficients in a given logical direction by a smooth exponential
      57              :  * roll-off. For the radial and axial (Legendre/Chebyshev) directions the
      58              :  * coefficient \f$c_i\f$ of the \f$i\f$-th modal basis function is rescaled as
      59              :  *
      60              :  * \f{align*}{
      61              :  *  c_i \to c_i \exp\!\left[-36 \left(\frac{i}{N}\right)^{2m}\right],
      62              :  * \f}
      63              :  *
      64              :  * where \f$N\f$ is the basis degree (number of grid points per element per
      65              :  * dimension minus one) and \f$m\f$ is the half-power option for that direction
      66              :  * (`RadialHalfPower` or `ZHalfPower`). For the angular (Fourier) direction the
      67              :  * roll-off is applied in the angular mode number \f$k\f$ instead, with the
      68              :  * maximum resolved mode \f$K = (N_\mathrm{pts} - 1) / 2\f$ taking the role of
      69              :  * \f$N\f$:
      70              :  *
      71              :  * \f{align*}{
      72              :  *  c_k \to c_k \exp\!\left[-36 \left(\frac{k}{K}\right)^{2m}\right],
      73              :  * \f}
      74              :  *
      75              :  * where \f$m\f$ is `AngularHalfPower` and both the \f$\cos\f$ and \f$\sin\f$
      76              :  * contributions to a given mode \f$k\f$ are weighted identically. Any direction
      77              :  * whose half-power is `None` is left untouched. When `NumModesToKill` is
      78              :  * nonzero, the top `NumModesToKill` angular modes are additionally set to zero
      79              :  * by a Heaviside cutoff.
      80              :  *
      81              :  * \note The angular (Fourier) direction is periodic and therefore has no
      82              :  * boundary faces: a hollow-cylinder element only has radial and axial
      83              :  * boundaries. Consequently `apply_on_boundary` is only ever valid on a face
      84              :  * obtained by slicing away the radial direction (a `(angular, z)` face) or the
      85              :  * axial direction (a `(radial, angular)` face); both of these retain the
      86              :  * angular direction. A face that sliced away the angular direction would have
      87              :  * no Fourier direction and cannot occur, so `apply_on_boundary` errors if
      88              :  * handed one.
      89              :  */
      90              : template <typename TagList>
      91            1 : class HollowCylinder : public Filter<3, TagList> {
      92              :  public:
      93              :   /// \brief The number of top angular (Fourier) \f$m\f$-modes to set to zero.
      94            1 :   struct NumModesToKill {
      95            0 :     using type = size_t;
      96            0 :     static constexpr Options::String help =
      97              :         "The number of top angular (Fourier) m-modes to set to zero.";
      98              :   };
      99              : 
     100              :   /*!
     101              :    * \brief Half of the exponent \f$m\f$ in the smooth exponential roll-off
     102              :    * applied to the angular (Fourier) modal coefficients.
     103              :    *
     104              :    * \f{align*}{
     105              :    *  c_k \to c_k \exp\left[-36 \left(\frac{k}{K}\right)^{2m}\right]
     106              :    * \f}
     107              :    *
     108              :    * Here \f$k\f$ is the angular mode number and \f$K = (N_\mathrm{pts} -
     109              :    * 1)/2\f$ is the maximum resolved mode; the \f$\cos\f$ and \f$\sin\f$ parts
     110              :    * of each mode \f$k\f$ are weighted identically. If `None`, only the top-mode
     111              :    * Heaviside cutoff (if any) is applied to the angular modes.
     112              :    */
     113            1 :   struct AngularHalfPower {
     114            0 :     using type = Options::Auto<size_t, Options::AutoLabel::None>;
     115            0 :     static constexpr Options::String help =
     116              :         "The half-power for the angular (Fourier) exponential roll-off. If "
     117              :         "None, only the top-mode cutoff is applied to the angular modes.";
     118              :   };
     119              : 
     120              :   /*!
     121              :    * \brief Half of the exponent \f$m\f$ in the smooth exponential roll-off
     122              :    * applied to the radial modal coefficients (logical dimension 0). If `None`,
     123              :    * the radial direction is not filtered.
     124              :    */
     125            1 :   struct RadialHalfPower {
     126            0 :     using type = Options::Auto<size_t, Options::AutoLabel::None>;
     127            0 :     static constexpr Options::String help =
     128              :         "The half-power for the radial exponential filter. If None, no radial "
     129              :         "filtering is applied.";
     130              :   };
     131              : 
     132              :   /*!
     133              :    * \brief Half of the exponent \f$m\f$ in the smooth exponential roll-off
     134              :    * applied to the axial \f$z\f$ modal coefficients (logical dimension 2). If
     135              :    * `None`, the axial direction is not filtered.
     136              :    */
     137            1 :   struct ZHalfPower {
     138            0 :     using type = Options::Auto<size_t, Options::AutoLabel::None>;
     139            0 :     static constexpr Options::String help =
     140              :         "The half-power for the axial (z) exponential filter. If None, no "
     141              :         "axial filtering is applied.";
     142              :   };
     143              : 
     144              :   /// \brief Enable (true) or disable (false) the filter
     145            1 :   struct Enable {
     146            0 :     using type = bool;
     147            0 :     static constexpr Options::String help = {"Enable the filter"};
     148              :   };
     149              : 
     150              :   /// \brief Which blocks the filter should be applied to.
     151            1 :   struct BlocksToFilter {
     152            0 :     using type =
     153              :         Options::Auto<std::vector<std::string>, Options::AutoLabel::All>;
     154            0 :     static constexpr Options::String help = {
     155              :         "List of blocks or block groups to apply filtering to. All other "
     156              :         "blocks will have no filtering. You can also specify 'All' to do "
     157              :         "filtering in all blocks of the domain that are hollow cylinders."};
     158              :   };
     159              : 
     160              :   /// \brief Apply the volume filter inside every Runge-Kutta substep
     161              :   /// instead of only at whole-step boundaries.
     162            1 :   struct VolumeFilterOnSubstep {
     163            0 :     using type = bool;
     164            0 :     static constexpr Options::String help = {
     165              :         "Enable the volume filter on every substep."};
     166              :   };
     167              : 
     168              :   /// \brief Apply the boundary correction filter inside every Runge-Kutta
     169              :   /// substep instead of only at whole-step boundaries.
     170            1 :   struct BoundaryCorrectionFilterOnSubstep {
     171            0 :     using type = bool;
     172            0 :     static constexpr Options::String help = {
     173              :         "Enable the boundary filter on every substep."};
     174              :   };
     175              : 
     176              :   /// \brief Apply the volume filter once every `N` steps. `None`
     177              :   /// (`std::nullopt`) disables the every-N-steps trigger.
     178              :   ///
     179              :   /// \note Currently the check for whether to filter on every `N` steps is done
     180              :   /// relative to the start of the current Slab. This means that for GTS,
     181              :   /// independent of the value of `N` for every `N` steps, every step has a
     182              :   /// filter applied since GTS has one step per slab.
     183            1 :   struct VolumeFilterEveryNSteps {
     184            0 :     using type = Options::Auto<size_t, Options::AutoLabel::None>;
     185            0 :     static constexpr Options::String help = {
     186              :         "Enable the volume filter on every N steps. 'None' to disable."};
     187              :   };
     188              : 
     189              :   /// \brief Apply the boundary correction filter once every `N` steps. `None`
     190              :   /// (`std::nullopt`) disables the every-N-steps trigger.
     191              :   ///
     192              :   /// \note Currently the check for whether to filter on every `N` steps is done
     193              :   /// relative to the start of the current Slab. This means that for GTS,
     194              :   /// independent of the value of `N` for every `N` steps, every step has a
     195              :   /// filter applied since GTS has one step per slab.
     196            1 :   struct BoundaryCorrectionFilterEveryNSteps {
     197            0 :     using type = Options::Auto<size_t, Options::AutoLabel::None>;
     198            0 :     static constexpr Options::String help = {
     199              :         "Enable the boundary filter on every N steps. 'None' to disable."};
     200              :   };
     201              : 
     202            0 :   using options =
     203              :       tmpl::list<NumModesToKill, AngularHalfPower, RadialHalfPower, ZHalfPower,
     204              :                  Enable, BlocksToFilter, VolumeFilterOnSubstep,
     205              :                  BoundaryCorrectionFilterOnSubstep, VolumeFilterEveryNSteps,
     206              :                  BoundaryCorrectionFilterEveryNSteps>;
     207              : 
     208            0 :   static constexpr Options::String help = {
     209              :       "A hollow-cylinder filter applying independent exponential roll-offs in "
     210              :       "the radial, angular (Fourier), and axial directions with an optional "
     211              :       "top-mode Heaviside cutoff in the angular direction."};
     212              : 
     213            0 :   HollowCylinder() = default;
     214              : 
     215            0 :   HollowCylinder(
     216              :       size_t num_modes_to_kill, std::optional<size_t> angular_half_power,
     217              :       std::optional<size_t> radial_half_power,
     218              :       std::optional<size_t> z_half_power, bool enable,
     219              :       const std::optional<std::vector<std::string>>& blocks_to_filter,
     220              :       bool volume_filter_on_substep, bool boundary_filter_on_substep,
     221              :       std::optional<size_t> volume_filter_every_n_steps,
     222              :       std::optional<size_t> boundary_filter_every_n_steps,
     223              :       const Options::Context& context = {});
     224              : 
     225            0 :   WRAPPED_PUPable_decl_base_template(  // NOLINT
     226              :       SINGLE_ARG(Filter<3, TagList>), HollowCylinder);
     227            0 :   explicit HollowCylinder(CkMigrateMessage* msg) : Filter<3, TagList>(msg) {}
     228              : 
     229              :   // NOLINTNEXTLINE(google-runtime-references)
     230            0 :   void pup(PUP::er& p) override;
     231              : 
     232            1 :   std::unique_ptr<Filter<3, TagList>> get_clone() const override;
     233              : 
     234            1 :   bool apply_volume_filter_on_substep() const override;
     235            1 :   bool apply_volume_filter_on_this_step(size_t step_number) const override;
     236              : 
     237            1 :   bool apply_boundary_filter_on_substep() const override;
     238            1 :   bool apply_boundary_filter_on_this_step(size_t step_number) const override;
     239              : 
     240            1 :   bool need_jacobians() const override { return false; }
     241              : 
     242            0 :   bool supports_mesh(const Mesh<3>& mesh) const override;
     243              : 
     244            1 :   std::string name() const override { return "HollowCylinder"; }
     245              : 
     246            1 :   const std::optional<std::vector<size_t>>& blocks_to_filter() const override;
     247              : 
     248            1 :   void set_blocks_to_filter(
     249              :       const std::vector<std::string>& all_block_names,
     250              :       const std::unordered_map<std::string, std::unordered_set<std::string>>&
     251              :           block_groups) override;
     252              : 
     253            0 :   void apply_in_volume(
     254              :       gsl::not_null<Variables<TagList>*> vars, const Mesh<3>& mesh,
     255              :       const std::optional<
     256              :           InverseJacobian<DataVector, 3, Frame::Grid, Frame::Inertial>>&
     257              :           inv_jac_grid_to_inertial,
     258              :       const std::optional<
     259              :           Jacobian<DataVector, 3, Frame::Grid, Frame::Inertial>>&
     260              :           jac_grid_to_inertial) const override;
     261              : 
     262            0 :   void apply_on_boundary(
     263              :       gsl::not_null<Variables<TagList>*> vars, const Mesh<2>& mesh,
     264              :       const std::optional<
     265              :           InverseJacobian<DataVector, 3, Frame::Grid, Frame::Inertial>>&
     266              :           inv_jac_grid_to_inertial,
     267              :       const std::optional<
     268              :           Jacobian<DataVector, 3, Frame::Grid, Frame::Inertial>>&
     269              :           jac_grid_to_inertial) const override;
     270              : 
     271            0 :   bool is_equal(const Filter<3, TagList>& other) const override;
     272              : 
     273              :  private:
     274              :   template <typename LocalTagList>
     275              :   // NOLINTNEXTLINE(readability-redundant-declaration)
     276            0 :   friend bool operator==(const HollowCylinder<LocalTagList>& lhs,
     277              :                          const HollowCylinder<LocalTagList>& rhs);
     278              : 
     279              :   // Caches a single filter matrix together with the parameters it was built
     280              :   // from: the 1-D extent, the half-power, and (for the angular direction) the
     281              :   // number of top modes killed. Rebuilt (replacing the previous entry) whenever
     282              :   // queried with any different parameter, keeping the memory footprint bounded
     283              :   // instead of growing with the number of distinct extents encountered.
     284            0 :   struct SingleExtentCache {
     285            0 :     std::optional<size_t> extent{std::nullopt};
     286            0 :     std::optional<size_t> half_power{std::nullopt};
     287            0 :     size_t num_modes_to_kill{0};
     288            0 :     Matrix matrix{};
     289              :   };
     290              : 
     291              :   // Returns the cached radial/axial exponential filter matrix for a Legendre or
     292              :   // Chebyshev direction with the given half-power and 1-D mesh. Returns an
     293              :   // empty (identity) matrix when the half-power is None.
     294            0 :   const Matrix& exponential_filter_matrix(std::optional<size_t> half_power,
     295              :                                           const Mesh<1>& mesh_1d,
     296              :                                           SingleExtentCache& cache) const;
     297              : 
     298              :   // Returns the cached angular (Fourier) filter matrix combining the optional
     299              :   // exponential roll-off and the optional top-mode cutoff. Returns an empty
     300              :   // (identity) matrix when neither is requested.
     301            0 :   const Matrix& angular_filter_matrix(const Mesh<1>& mesh_1d) const;
     302              : 
     303            0 :   size_t num_modes_to_kill_{0};
     304            0 :   std::optional<size_t> angular_half_power_{std::nullopt};
     305            0 :   std::optional<size_t> radial_half_power_{std::nullopt};
     306            0 :   std::optional<size_t> z_half_power_{std::nullopt};
     307            0 :   bool enable_{true};
     308            0 :   std::optional<std::vector<std::string>> blocks_and_groups_to_filter_{};
     309            0 :   std::optional<std::vector<size_t>> blocks_to_filter_{};
     310            0 :   bool volume_filter_on_substep_{false};
     311            0 :   bool boundary_filter_on_substep_{false};
     312            0 :   std::optional<size_t> volume_filter_every_n_steps_{std::nullopt};
     313            0 :   std::optional<size_t> boundary_filter_every_n_steps_{std::nullopt};
     314              : 
     315              :   // One filter matrix per direction, each bound to the single extent it was
     316              :   // built for. These caches serve both the volume filter (`apply_in_volume`,
     317              :   // which filters all three directions) and the boundary filter
     318              :   // (`apply_on_boundary`). The empty matrix is returned (and not cached) when a
     319              :   // direction is not filtered.
     320              :   // NOLINTNEXTLINE(spectre-mutable)
     321            0 :   mutable SingleExtentCache cached_radial_filter_{};
     322              :   // NOLINTNEXTLINE(spectre-mutable)
     323            0 :   mutable SingleExtentCache cached_angular_filter_{};
     324              :   // NOLINTNEXTLINE(spectre-mutable)
     325            0 :   mutable SingleExtentCache cached_z_filter_{};
     326              :   // Returned by const reference to represent the identity for unfiltered
     327              :   // directions; never mutated, so it needs no `mutable`.
     328            0 :   Matrix empty_matrix_{};
     329              : };
     330              : 
     331              : template <typename TagList>
     332            0 : bool operator==(const HollowCylinder<TagList>& lhs,
     333              :                 const HollowCylinder<TagList>& rhs);
     334              : 
     335              : template <typename TagList>
     336            0 : bool operator!=(const HollowCylinder<TagList>& lhs,
     337              :                 const HollowCylinder<TagList>& rhs);
     338              : }  // namespace Filters
        

Generated by: LCOV version 2.0-1