SpECTRE Documentation Coverage Report
Current view: top level - NumericalAlgorithms/Strahlkorper - Strahlkorper.hpp Coverage Total Hit
Commit: 0d1d717af84a81fb1284a20c4013708fe85cc2f0 Lines: 44.7 % 47 21
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 <array>
       7              : #include <cstddef>
       8              : #include <memory>
       9              : #include <string>
      10              : 
      11              : #include "DataStructures/DataVector.hpp"
      12              : #include "DataStructures/ModalVector.hpp"
      13              : #include "NumericalAlgorithms/SphericalHarmonics/Spherepack.hpp"
      14              : #include "NumericalAlgorithms/Strahlkorper/InitialShape.hpp"
      15              : #include "Options/Context.hpp"
      16              : #include "Options/String.hpp"
      17              : #include "Utilities/ForceInline.hpp"
      18              : #include "Utilities/StdArrayHelpers.hpp"
      19              : #include "Utilities/TMPL.hpp"
      20              : 
      21              : /// \cond
      22              : namespace PUP {
      23              : class er;
      24              : }  // namespace PUP
      25              : 
      26              : namespace Options {
      27              : template <typename... AlternativeLists>
      28              : struct Alternatives;
      29              : }  // namespace Options
      30              : /// \endcond
      31              : 
      32              : namespace ylm {
      33              : /// \ingroup SurfacesGroup
      34              : /// \brief A star-shaped surface expanded in spherical harmonics.
      35              : template <typename Frame>
      36            1 : class Strahlkorper {
      37              :  public:
      38            0 :   struct InitialL {
      39            0 :     using type = size_t;
      40            0 :     static constexpr Options::String help = {
      41              :         "Initial Strahlkorper resolution. Sets both l_max and m_max."};
      42              :   };
      43            0 :   struct InitialShapeOption {
      44            0 :     using type = std::unique_ptr<InitialShape<Frame>>;
      45            0 :     static constexpr Options::String help = {"Initial Strahlkorper shape."};
      46            0 :     static std::string name() { return "InitialShape"; }
      47              :   };
      48            0 :   using options = tmpl::list<InitialL, InitialShapeOption>;
      49              : 
      50            0 :   static constexpr Options::String help{
      51              :       "A star-shaped surface expressed as an expansion in spherical "
      52              :       "harmonics.\n"
      53              :       "A Strahlkorper can be constructed from options by specifying InitialL "
      54              :       "and an InitialShape. InitialL sets both l_max and m_max."};
      55              : 
      56              :   // Pup needs default constructor
      57            0 :   Strahlkorper() = default;
      58            0 :   Strahlkorper(const Strahlkorper&) = default;
      59            0 :   Strahlkorper(Strahlkorper&&) = default;
      60            0 :   Strahlkorper& operator=(const Strahlkorper&) = default;
      61            0 :   Strahlkorper& operator=(Strahlkorper&&) = default;
      62              : 
      63              :   /// Construct a sphere of radius `radius` with a given center.
      64            1 :   Strahlkorper(size_t l_max, size_t m_max, double radius,
      65              :                std::array<double, 3> center);
      66              : 
      67              :   /// Construct a sphere of radius `radius`, setting `m_max`=`l_max`.
      68            1 :   Strahlkorper(size_t l_max, double radius, std::array<double, 3> center)
      69              :       : Strahlkorper(l_max, l_max, radius, center) {}
      70              : 
      71              :   /// Construct from options: either from radius and center, or from file.
      72              :   /// These constructors handle both alternatives specified in the options.
      73            1 :   Strahlkorper(size_t l_max, const std::string& h5_filename,
      74              :                const std::string& subfile_name, double time,
      75              :                double time_epsilon, bool check_frame,
      76              :                const Options::Context& context);
      77              : 
      78              :   /// Construct from options from an initial shape.
      79              :   /// @{
      80            1 :   Strahlkorper(size_t initial_l, const InitialShape<Frame>& initial_shape,
      81              :                const Options::Context& context);
      82            1 :   Strahlkorper(size_t initial_l,
      83              :                const std::unique_ptr<InitialShape<Frame>>& initial_shape,
      84              :                const Options::Context& context);
      85              :   /// @}
      86              : 
      87              :   /// Construct a Strahlkorper from a DataVector containing the radius
      88              :   /// at the collocation points.
      89              :   ///
      90              :   /// \note The collocation points of the constructed Strahlkorper
      91              :   /// will not be exactly `radius_at_collocation_points`.  Instead,
      92              :   /// the constructed Strahlkorper will match the shape given by
      93              :   /// `radius_at_collocation_points` only to order (`l_max`,`m_max`).
      94              :   /// This is because the ylm::Spherepack representation of the
      95              :   /// Strahlkorper has more collocation points than spectral
      96              :   /// coefficients.  Specifically, `radius_at_collocation_points` has
      97              :   /// \f$(l_{\rm max} + 1) (2 m_{\rm max} + 1)\f$ degrees of freedom,
      98              :   /// but because there are only
      99              :   /// \f$m_{\rm max}^2+(l_{\rm max}-m_{\rm max})(2m_{\rm max}+1)\f$
     100              :   /// spectral coefficients, it is not possible to choose spectral
     101              :   /// coefficients to exactly match all points in
     102              :   /// `radius_at_collocation_points`.
     103            1 :   explicit Strahlkorper(size_t l_max, size_t m_max,
     104              :                         const DataVector& radius_at_collocation_points,
     105              :                         std::array<double, 3> center);
     106              : 
     107              :   /// \brief Construct a Strahlkorper from a `ModalVector` containing the
     108              :   /// spectral coefficients
     109              :   ///
     110              :   /// \details The spectral coefficients should be in the form defined by
     111              :   /// `ylm::Strahlkorper::coefficients() const`.
     112            1 :   explicit Strahlkorper(size_t l_max, size_t m_max,
     113              :                         const ModalVector& spectral_coefficients,
     114              :                         std::array<double, 3> center);
     115              : 
     116              :   /// Copies a Strahlkorper from another frame into this Strahlkorper.
     117              :   ///
     118              :   /// \note The `OtherFrame` is ignored.
     119              :   template <typename OtherFrame>
     120            1 :   explicit Strahlkorper(const Strahlkorper<OtherFrame>& another_strahlkorper)
     121              :       : l_max_(another_strahlkorper.l_max()),
     122              :         m_max_(another_strahlkorper.m_max()),
     123              :         ylm_(l_max_, m_max_),
     124              :         center_(another_strahlkorper.expansion_center()),
     125              :         strahlkorper_coefs_(another_strahlkorper.coefficients()) {}
     126              : 
     127              :   /// Prolong or restrict another surface to the given `l_max` and `m_max`.
     128              :   ///
     129              :   /// \note The `OtherFrame` is ignored.
     130              :   template <typename OtherFrame>
     131            1 :   Strahlkorper(size_t l_max, size_t m_max,
     132              :                const Strahlkorper<OtherFrame>& another_strahlkorper)
     133              :       : l_max_(l_max),
     134              :         m_max_(m_max),
     135              :         ylm_(l_max, m_max),
     136              :         center_(another_strahlkorper.expansion_center()),
     137              :         strahlkorper_coefs_(
     138              :             another_strahlkorper.ylm_spherepack().prolong_or_restrict(
     139              :                 another_strahlkorper.coefficients(), ylm_)) {}
     140              : 
     141              :   /// Construct a Strahlkorper from another Strahlkorper,
     142              :   /// but explicitly specifying the coefficients.
     143              :   /// Here coefficients are in the same storage scheme
     144              :   /// as the `coefficients()` member function returns.
     145              :   ///
     146              :   /// \note The `OtherFrame` is ignored.
     147              :   template <typename OtherFrame>
     148            1 :   Strahlkorper(DataVector coefs,
     149              :                const Strahlkorper<OtherFrame>& another_strahlkorper)
     150              :       : l_max_(another_strahlkorper.l_max()),
     151              :         m_max_(another_strahlkorper.m_max()),
     152              :         ylm_(another_strahlkorper.ylm_spherepack()),
     153              :         center_(another_strahlkorper.expansion_center()),
     154              :         strahlkorper_coefs_(std::move(coefs)) {
     155              :     ASSERT(
     156              :         strahlkorper_coefs_.size() ==
     157              :             another_strahlkorper.ylm_spherepack().spectral_size(),
     158              :         "Bad size " << strahlkorper_coefs_.size() << ", expected "
     159              :                     << another_strahlkorper.ylm_spherepack().spectral_size());
     160              :   }
     161              : 
     162              :   /// Serialization for Charm++
     163              :   // NOLINTNEXTLINE(google-runtime-references)
     164            1 :   void pup(PUP::er& p);
     165              : 
     166              :   /*!
     167              :    *  These coefficients are stored as SPHEREPACK coefficients.
     168              :    *  Suppose you represent a set of coefficients \f$F^{lm}\f$ in the expansion
     169              :    *  \f[
     170              :    *  f(\theta,\phi) =
     171              :    *  \sum_{l=0}^{l_{max}} \sum_{m=-l}^{l} F^{lm} Y^{lm}(\theta,\phi)
     172              :    *  \f]
     173              :    *  Here the \f$Y^{lm}(\theta,\phi)\f$ are the usual complex-valued scalar
     174              :    *  spherical harmonics, so \f$F^{lm}\f$ are also complex-valued.
     175              :    *  But here we assume that \f$f(\theta,\phi)\f$ is real, so therefore
     176              :    *  the \f$F^{lm}\f$ obey \f$F^{l-m} = (-1)^m (F^{lm})^\star\f$. So one
     177              :    *  does not need to store both real and imaginary parts for both positive
     178              :    *  and negative \f$m\f$, and the stored coefficients can all be real.
     179              :    *
     180              :    *  So the stored coefficients are:
     181              :    * \f{align}
     182              :    *  \text{coefficients()(l,m)} &= (-1)^m \sqrt{\frac{2}{\pi}}
     183              :    *     \Re(F^{lm}) \quad \text{for} \quad m\ge 0, \\
     184              :    *  \text{coefficients()(l,m)} &= (-1)^m \sqrt{\frac{2}{\pi}}
     185              :    *     \Im(F^{lm}) \quad \text{for} \quad m<0
     186              :    * \f}
     187              :    */
     188            1 :   SPECTRE_ALWAYS_INLINE const DataVector& coefficients() const {
     189              :     return strahlkorper_coefs_;
     190              :   }
     191            0 :   SPECTRE_ALWAYS_INLINE DataVector& coefficients() {
     192              :     return strahlkorper_coefs_;
     193              :   }
     194              : 
     195              :   /// Point about which the spectral basis of the Strahlkorper is expanded.
     196              :   /// The center is given in the frame in which the Strahlkorper is defined.
     197              :   /// This center must be somewhere inside the Strahlkorper, but in principle
     198              :   /// it can be anywhere.  See `physical_center()` for a different measure.
     199            1 :   SPECTRE_ALWAYS_INLINE const std::array<double, 3>& expansion_center() const {
     200              :     return center_;
     201              :   }
     202              : 
     203              :   /// Approximate physical center (determined by \f$l=1\f$ coefficients)
     204              :   /// Implementation of Eqs. (38)-(40) in \cite Hemberger2012jz
     205            1 :   std::array<double, 3> physical_center() const;
     206              : 
     207              :   /// Average radius of the surface (determined by \f$Y_{00}\f$ coefficient)
     208            1 :   double average_radius() const;
     209              : 
     210              :   /// Maximum \f$l\f$ in \f$Y_{lm}\f$ decomposition.
     211            1 :   SPECTRE_ALWAYS_INLINE size_t l_max() const { return l_max_; }
     212              : 
     213              :   /// Maximum \f$m\f$ in \f$Y_{lm}\f$ decomposition.
     214            1 :   SPECTRE_ALWAYS_INLINE size_t m_max() const { return m_max_; }
     215              : 
     216              :   /// Radius at a particular angle \f$(\theta,\phi)\f$.
     217              :   /// This is inefficient if done at multiple points many times.
     218              :   /// See ylm::Spherepack for alternative ways of computing this.
     219            1 :   double radius(double theta, double phi) const;
     220              : 
     221              :   /// Determine if a point `x` is contained inside the surface.
     222              :   /// The point must be given in Cartesian coordinates in the frame in
     223              :   /// which the Strahlkorper is defined.
     224              :   /// This is inefficient if done at multiple points many times.
     225            1 :   bool point_is_contained(const std::array<double, 3>& x) const;
     226              : 
     227            0 :   SPECTRE_ALWAYS_INLINE const ylm::Spherepack& ylm_spherepack() const {
     228              :     return ylm_;
     229              :   }
     230              : 
     231              :  private:
     232            0 :   size_t l_max_{2}, m_max_{2};
     233            0 :   ylm::Spherepack ylm_{2, 2};
     234            0 :   std::array<double, 3> center_{{0.0, 0.0, 0.0}};
     235            0 :   DataVector strahlkorper_coefs_ = DataVector(ylm_.spectral_size(), 0.0);
     236              : };
     237              : 
     238            0 : namespace OptionTags {
     239              : /// \ingroup OptionTagsGroup
     240              : /// \ingroup SurfacesGroup
     241              : /// The input file tag for a Strahlkorper.
     242              : template <typename Frame>
     243            1 : struct Strahlkorper {
     244            0 :   using type = ylm::Strahlkorper<Frame>;
     245            0 :   static constexpr Options::String help{"A star-shaped surface"};
     246              : };
     247              : }  // namespace OptionTags
     248              : 
     249              : template <typename Frame>
     250            0 : bool operator==(const Strahlkorper<Frame>& lhs,
     251              :                 const Strahlkorper<Frame>& rhs) {
     252              :   return lhs.l_max() == rhs.l_max() and lhs.m_max() == rhs.m_max() and
     253              :          lhs.expansion_center() == rhs.expansion_center() and
     254              :          lhs.coefficients() == rhs.coefficients();
     255              : }
     256              : 
     257              : template <typename Frame>
     258            0 : bool operator!=(const Strahlkorper<Frame>& lhs,
     259              :                 const Strahlkorper<Frame>& rhs) {
     260              :   return not(lhs == rhs);
     261              : }
     262              : }  // namespace ylm
        

Generated by: LCOV version 2.0-1