SpECTRE Documentation Coverage Report
Current view: top level - Domain/CoordinateMaps/TimeDependent - SphericalCompression.hpp Coverage Total Hit
Commit: 0d1d717af84a81fb1284a20c4013708fe85cc2f0 Lines: 9.5 % 21 2
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 <limits>
       9              : #include <memory>
      10              : #include <optional>
      11              : #include <string>
      12              : #include <unordered_map>
      13              : #include <unordered_set>
      14              : 
      15              : #include "DataStructures/Tensor/TypeAliases.hpp"
      16              : 
      17              : /// \cond
      18              : namespace domain::FunctionsOfTime {
      19              : class FunctionOfTime;
      20              : }  // namespace domain::FunctionsOfTime
      21              : namespace PUP {
      22              : class er;
      23              : }  // namespace PUP
      24              : /// \endcond
      25              : 
      26              : namespace domain::CoordinateMaps::TimeDependent {
      27              : /*!
      28              :  * \ingroup CoordMapsTimeDependentGroup
      29              :  * \brief Time-dependent compression of a finite 3D spherical volume.
      30              :  *
      31              :  * \details Let \f$\xi^i\f$ be the unmapped coordinates, and let \f$\rho\f$ be
      32              :  * the Euclidean radius corresponding to these coordinates with respect to
      33              :  * some center \f$C^i\f$. The transformation implemented by this map is
      34              :  * equivalent to the following transformation: at each point, the mapped
      35              :  * coordinates are the same as the unmapped coordinates, except in
      36              :  * a spherical region \f$\rho \leq \rho_{\rm max}\f$, where instead coordinates
      37              :  * are mapped using a compression that is spherically symmetric about the center
      38              :  * \f$C^i\f$. The amount of compression decreases linearly from a maximum at
      39              :  * \f$\rho = \rho_{\rm min}\f$ to zero at \f$\rho = \rho_{\rm max}\f$. A
      40              :  * scalar domain::FunctionsOfTime::FunctionOfTime \f$\lambda_{00}(t)\f$ controls
      41              :  * the amount of compression.
      42              :  *
      43              :  * The mapped coordinates are a continuous function of the unmapped
      44              :  * coordinates, but the Jacobians are not continuous at \f$\rho_{\rm min}\f$
      45              :  * and \f$\rho_{\rm max}\f$. Therefore, \f$\rho_{\rm min}\f$ and \f$\rho_{\rm
      46              :  * max}\f$ should both be surfaces corresponding to block boundaries. Therefore,
      47              :  * this class implements the transformation described above as follows: the
      48              :  * if the template parameter `InteriorMap` is true, the map is the one
      49              :  * appropriate for \f$\rho < \rho_{\rm min}\f$, while if `InteriorMap` is false,
      50              :  * the map is the one appropriate for \f$\rho_{\rm min} \leq \rho \leq \rho_{\rm
      51              :  * max}\f$. To use this map, add it to the blocks where the transformation is
      52              :  * not the identity, using the appropriate template parameter, depending on
      53              :  * which region the block is in.
      54              :  *
      55              :  * \note This map performs a only a spherical compression. A
      56              :  * generalization of this map that changes the region's shape as well as
      57              :  * its size, by including more terms than the spherically symmetric
      58              :  * term included here, can be found in the
      59              :  * domain::CoordinateMaps::TimeDependent::Shape map.
      60              :  *
      61              :  * \note The quantity stored in the FunctionOfTime is really
      62              :  * the spherical-harmonic coefficient \f$\lambda_{00}(t)\f$.  This is
      63              :  * different from the Shape map, which stores ylm::Spherepack coefficients
      64              :  * \f$a_{lm}(t)\f$ and \f$b_{lm}(t)\f$ instead of \f$\lambda_{lm}(t)\f$.
      65              :  * See domain::CoordinateMaps::TimeDependent::Shape for more details.
      66              :  *
      67              :  * ### Mapped coordinates
      68              :  *
      69              :  * The mapped coordinates
      70              :  * \f$x^i\f$ are related to the unmapped coordinates \f$\xi^i\f$
      71              :  * as follows:
      72              :  * \f{align}{
      73              :  * x^i &= \left\{\begin{array}{ll}\xi^i - \frac{\lambda_{00}(t)}{\sqrt{4\pi}}
      74              :  * \frac{\rho^i}{\rho_{\rm min}}, & \rho < \rho_{\rm min}, \\
      75              :  * \xi^i - \frac{\lambda_{00}(t)}{\sqrt{4\pi}}
      76              :  * \frac{\rho_{\rm max} / \rho - 1}{\rho_{\rm max} - \rho_{\rm min}} \rho^i, &
      77              :  * \rho_{\rm min} \leq \rho \leq \rho_{\rm max}, \\
      78              :  * \xi^i, & \rho_{\rm max} < \rho,\end{array}\right.
      79              :  * \f}
      80              :  * where \f$\rho^i = \xi^i - C^i\f$ is the Euclidean radial position vector in
      81              :  * the unmapped coordinates with respect to the center \f$C^i\f$, \f$\rho =
      82              :  * \sqrt{\delta_{kl}\left(\xi^k - C^l\right)\left(\xi^l - C^l\right)}\f$ is the
      83              :  * Euclidean magnitude of \f$\rho^i\f$, and \f$\rho_j = \delta_{ij} \rho^i\f$.
      84              :  *
      85              :  * ### Frame velocity
      86              :  *
      87              :  * The frame velocity \f$v^i \equiv dx^i/dt\f$ is then
      88              :  * \f{align}{
      89              :  * v^i &= \left\{\begin{array}{ll} - \frac{\lambda_{00}^\prime(t)}{\sqrt{4\pi}}
      90              :  * \frac{\rho^i}{\rho_{\rm min}}, & \rho < \rho_{\rm min}, \\
      91              :  * - \frac{\lambda_{00}^\prime(t)}{\sqrt{4\pi}}
      92              :  * \frac{\rho_{\rm max} / \rho - 1}{\rho_{\rm max} - \rho_{\rm min}} \rho^i,
      93              :  * & \rho_{\rm min} \leq \rho \leq \rho_{\rm max}, \\
      94              :  * 0, & \rho_{\rm max} < \rho,\end{array}\right.
      95              :  * \f} where \f$\lambda_{00}^\prime(t) \equiv d\lambda_{00}/dt\f$.
      96              :  *
      97              :  * ### Jacobian
      98              :  *
      99              :  * Differentiating the equations for \f$x^i\f$ gives the Jacobian
     100              :  * \f$\partial x^i / \partial \xi^j\f$. Using the result
     101              :  * \f{align}{
     102              :  * \frac{\partial \rho^i}{\partial \xi^j} &= \frac{\partial}{\partial \xi^j}
     103              :  * \left(\xi^i - C^i\right) = \frac{\partial \xi^i}{\partial \xi^j}
     104              :  * = \delta^i_{j}
     105              :  * \f}
     106              :  * and taking the derivatives yields
     107              :  * \f{align}{
     108              :  * \frac{\partial x^i}{\partial \xi^j} &= \left\{\begin{array}{ll}
     109              :  * \delta^i_j \left(1
     110              :  * - \frac{\lambda_{00}(t)}{\sqrt{4\pi}} \frac{1}{\rho_{\rm min}}\right),
     111              :  * & \rho < \rho_{\rm min},\\
     112              :  * \delta^i_j
     113              :  * \left(1 - \frac{\lambda_{00}(t)}{\sqrt{4\pi}}
     114              :  * \frac{\rho_{\rm max} / \rho - 1}{\rho_{\rm max} - \rho_{\rm min}}\right)
     115              :  * - \rho^i \frac{\lambda_{00}(t)}{\sqrt{4\pi}}
     116              :  * \frac{\partial}{\partial \xi^j}\left(
     117              :  * \frac{\rho_{\rm max} / \rho - 1}{\rho_{\rm max} - \rho_{\rm min}}\right),
     118              :  * & \rho_{\rm min} \leq \rho < \rho_{\rm max},\\
     119              :  * \delta^i_j, & \rho_{\rm max} < \rho.\end{array}\right.
     120              :  * \f}
     121              :  * Inserting
     122              :  * \f{align}{
     123              :  * \frac{\partial}{\partial \xi^j}\left(
     124              :  * \frac{\rho_{\rm max} / \rho - 1}{\rho_{\rm max} - \rho_{\rm min}}\right)
     125              :  * &= \frac{\rho_{\rm max}}{\rho_{\rm max} - \rho_{\rm min}}
     126              :  * \frac{\partial}{\partial \xi^j}\left(\frac{1}{\rho}\right)
     127              :  * = - \frac{\rho_{\rm max}}{\rho_{\rm max} - \rho_{\rm min}} \frac{1}{\rho^2}
     128              :  * \frac{\partial \rho}{\partial \xi^j}
     129              :  * \f}
     130              :  * and
     131              :  * \f{align}{
     132              :  * \frac{\partial \rho}{\partial \xi^j} &= \frac{\rho_j}{\rho}.
     133              :  * \f}
     134              :  * into the Jacobian yields
     135              :  * \f{align}{
     136              :  * \frac{\partial x^i}{\partial \xi^j} &= \left\{\begin{array}{ll}
     137              :  * \delta^i_j \left(1
     138              :  * - \frac{\lambda_{00}(t)}{\sqrt{4\pi}} \frac{1}{\rho_{\rm min}}\right),
     139              :  * & \rho < \rho_{\rm min},\\
     140              :  * \delta^i_j
     141              :  * \left(1 - \frac{\lambda_{00}(t)}{\sqrt{4\pi}}
     142              :  * \frac{\rho_{\rm max} / \rho - 1}{\rho_{\rm max} - \rho_{\rm min}}\right)
     143              :  * + \rho^i \rho_j \frac{\lambda_{00}(t)}{\sqrt{4\pi}}
     144              :  * \frac{\rho_{\rm max}}{\rho_{\rm max} - \rho_{\rm min}}\frac{1}{\rho^3},
     145              :  * & \rho_{\rm min} \leq \rho < \rho_{\rm max},\\
     146              :  * \delta^i_j, & \rho_{\rm max} < \rho.\end{array}\right.
     147              :  * \f}
     148              :  *
     149              :  * ### Inverse Jacobian
     150              :  *
     151              :  * This map finds the inverse Jacobian by first finding the Jacobian and then
     152              :  * numerically inverting it.
     153              :  *
     154              :  * ### Inverse map
     155              :  *
     156              :  * For \f$\lambda_{00}(t)\f$ that satisfy
     157              :  * \f{align}{
     158              :  * \rho_{\rm min} - \rho_{\rm max} < \lambda_{00}(t) / \sqrt{4\pi} <
     159              :  * \rho_{\rm min},
     160              :  * \f}
     161              :  * the map will be invertible and nonsingular. For simplicity, here we
     162              :  * enforce this condition, even though perhaps the map might be generalized to
     163              :  * handle cases that are still invertible but violate this condition. This
     164              :  * avoids the need to specially handle the cases
     165              :  * \f$\lambda_{00}(t) / \sqrt{4\pi} = \rho_{\rm min} - \rho_{\rm max}\f$
     166              :  * and \f$\lambda_{00}(t) / \sqrt{4\pi} = \rho_{\rm min}\f$, both of which
     167              :  * yield a singular map, and it also avoids cases where the map behaves
     168              :  * in undesirable ways (such as a larger \f$\lambda_{00}(t)\f$ leading to
     169              :  * an expansion and a coordinate inversion instead of a compression).
     170              :  *
     171              :  * After
     172              :  * requiring the above inequality to be satisfied, however, the inverse mapping
     173              :  * can be derived as follows. Let \f$r^i \equiv x^i - C^i\f$. In terms of
     174              :  * \f$r^i\f$, the map is \f{align}{ r^i &= \left\{\begin{array}{ll}\rho^i
     175              :  * \left(1 - \frac{\lambda_{00}(t)}{\sqrt{4\pi}}
     176              :  * \frac{1}{\rho_{\rm min}}\right), & \rho < \rho_{\rm min}, \\
     177              :  * \rho^i\left(1 - \frac{\lambda_{00}(t)}{\sqrt{4\pi}}
     178              :  * \frac{\rho_{\rm max} / \rho - 1}{\rho_{\rm max} - \rho_{\rm min}}\right),
     179              :  * & \rho_{\rm min} \leq \rho \leq \rho_{\rm max}, \\
     180              :  * \rho^i, & \rho_{\rm max} < \rho.\end{array}\right.
     181              :  * \f}
     182              :  *
     183              :  * Taking the Euclidean magnitude of both sides and simplifying yields
     184              :  * \f{align}{
     185              :  * \frac{r}{\rho} &= \left\{\begin{array}{ll}
     186              :  * 1 - \frac{\lambda_{00}(t)}{\sqrt{4\pi}}
     187              :  * \frac{1}{\rho_{\rm min}}, & \rho < \rho_{\rm min}, \\
     188              :  * 1 - \frac{\lambda_{00}(t)}{\sqrt{4\pi}}
     189              :  * \frac{\rho_{\rm max}/\rho - 1}{\rho_{\rm max} - \rho_{\rm min}},
     190              :  * & \rho_{\rm min} \leq \rho \leq \rho_{\rm max}, \\
     191              :  * 1, & \rho_{\rm max} < \rho,\end{array}\right.
     192              :  * \f}
     193              :  * which implies
     194              :  * \f{align}{
     195              :  * r^i = \rho^i \frac{r}{\rho} \Rightarrow \rho^i = r^i \frac{\rho}{r}.
     196              :  * \f}
     197              :  *
     198              :  * Inserting \f$\rho_{\rm min}\f$ or \f$\rho_{\rm max}\f$ then gives the
     199              :  * corresponding bounds in the mapped coordinates: \f{align}{
     200              :  * r_{\rm min} &= \rho_{\rm min} - \frac{\lambda_{00}(t)}{\sqrt{4\pi}},\\
     201              :  * r_{\rm max} &= \rho_{\rm max}.
     202              :  * \f}
     203              :  *
     204              :  * In the regime \f$\rho_{\rm min} \leq \rho < \rho_{\rm max}\f$, rearranging
     205              :  * yields a linear relationship between \f$\rho\f$ and \f$r\f$, which
     206              :  * can then be solved for \f$\rho(r)\f$:
     207              :  * \f{align}{
     208              :  * r &= \rho - \frac{\lambda_{00}(t)}{\sqrt{4\pi}}
     209              :  * \frac{\rho_{\rm max} - \rho}{\rho_{\rm max} - \rho_{\rm min}}\\
     210              :  * \Rightarrow r &= \rho \left(1 + \frac{\lambda_{00}(t)}{\sqrt{4\pi}}
     211              :  * \frac{1}{\rho_{\rm max} - \rho_{\rm min}}\right)
     212              :  * - \frac{\lambda_{00}(t)}{\sqrt{4\pi}}
     213              :  * \frac{\rho_{\rm max}}{\rho_{\rm max} - \rho_{\rm min}}.
     214              :  * \f}
     215              :  * Solving this linear equation for \f$\rho\f$ yields
     216              :  * \f{align}{
     217              :  * \rho &= \left(r+\frac{\lambda_{00}(t)}{\sqrt{4\pi}}\frac{\rho_{\rm
     218              :  * max}}{\rho_{\rm max}-\rho_{\rm min}}\right)
     219              :  * \left(1 + \frac{\lambda_{00}(t)}{\sqrt{4\pi}}
     220              :  * \frac{1}{\rho_{\rm max} - \rho_{\rm min}}\right)^{-1}.
     221              :  * \f}
     222              :  *
     223              :  * Inserting the expressions for \f$\rho\f$ into the equation
     224              :  * \f{align}{
     225              :  * \rho^i = r^i \frac{\rho}{r}
     226              :  * \f}
     227              :  * then gives
     228              :  * \f{align}{
     229              :  * \rho^i &= \left\{\begin{array}{ll}
     230              :  * r^i\left(1 - \frac{\lambda_{00}(t)}{\sqrt{4\pi}}
     231              :  * \frac{1}{\rho_{\rm min}}\right)^{-1},
     232              :  * & r < \rho_{\rm min} - \frac{\lambda_{00}(t)}{\sqrt{4\pi}},\\
     233              :  * r^i
     234              :  * \left(1+\frac{1}{r}\frac{\lambda_{00}(t)}{\sqrt{4\pi}}\frac{\rho_{\rm
     235              :  * max}}{\rho_{\rm max}-\rho_{\rm min}}\right)\left(1 +
     236              :  * \frac{\lambda_{00}(t)}{\sqrt{4\pi}} \frac{1}{\rho_{\rm max} - \rho_{\rm
     237              :  * min}}\right)^{-1}, & \rho_{\rm min} - \frac{\lambda_{00}(t)}{\sqrt{4\pi}}
     238              :  * \leq r
     239              :  * \leq \rho_{\rm max},\\
     240              :  * r^i, & \rho_{\rm max} < r.\end{array}\right.
     241              :  * \f}
     242              :  * Finally, inserting \f$\rho^i = \xi^i - C^i\f$ yields the inverse map:
     243              :  * \f{align}{
     244              :  * \xi^i &= \left\{\begin{array}{ll}
     245              :  * r^i\left(1 - \frac{\lambda_{00}(t)}{\sqrt{4\pi}}
     246              :  * \frac{1}{\rho_{\rm min}}\right)^{-1} + C^i,
     247              :  * & r < \rho_{\rm min} - \frac{\lambda_{00}(t)}{\sqrt{4\pi}},\\
     248              :  * r^i
     249              :  * \left(1+\frac{1}{r}\frac{\lambda_{00}(t)}{\sqrt{4\pi}}\frac{\rho_{\rm
     250              :  * max}}{\rho_{\rm max}-\rho_{\rm min}}\right)\left(1 +
     251              :  * \frac{\lambda_{00}(t)}{\sqrt{4\pi}} \frac{1}{\rho_{\rm max} - \rho_{\rm
     252              :  * min}}\right)^{-1} + C^i, & \rho_{\rm min} -
     253              :  * \frac{\lambda_{00}(t)}{\sqrt{4\pi}} \leq r
     254              :  * \leq \rho_{\rm max},\\
     255              :  * r^i + C^i = x^i, & \rho_{\rm max} < r.\end{array}\right.
     256              :  * \f}
     257              :  *
     258              :  */
     259              : template <bool InteriorMap>
     260            1 : class SphericalCompression {
     261              :  public:
     262            0 :   static constexpr size_t dim = 3;
     263              : 
     264            0 :   explicit SphericalCompression(std::string function_of_time_name,
     265              :                                 double min_radius, double max_radius,
     266              :                                 const std::array<double, 3>& center);
     267            0 :   SphericalCompression() = default;
     268              : 
     269              :   template <typename T>
     270            0 :   std::array<T, 3> operator()(
     271              :       const std::array<T, 3>& source_coords, double time,
     272              :       const std::unordered_map<
     273              :           std::string,
     274              :           std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&
     275              :           functions_of_time) const;
     276              : 
     277              :   /// The inverse function is only callable with doubles because the inverse
     278              :   /// might fail if called for a point out of range, and it is unclear
     279              :   /// what should happen if the inverse were to succeed for some points in a
     280              :   /// DataVector but fail for other points.
     281            1 :   std::optional<std::array<double, 3>> inverse(
     282              :       const std::array<double, 3>& target_coords, double time,
     283              :       const std::unordered_map<
     284              :           std::string,
     285              :           std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&
     286              :           functions_of_time) const;
     287              : 
     288              :   template <typename T>
     289            0 :   std::array<T, 3> frame_velocity(
     290              :       const std::array<T, 3>& source_coords, double time,
     291              :       const std::unordered_map<
     292              :           std::string,
     293              :           std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&
     294              :           functions_of_time) const;
     295              : 
     296              :   template <typename T>
     297            0 :   tnsr::Ij<T, 3, Frame::NoFrame> jacobian(
     298              :       const std::array<T, 3>& source_coords, double time,
     299              :       const std::unordered_map<
     300              :           std::string,
     301              :           std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&
     302              :           functions_of_time) const;
     303              : 
     304              :   template <typename T>
     305            0 :   tnsr::Ij<T, 3, Frame::NoFrame> inv_jacobian(
     306              :       const std::array<T, 3>& source_coords, double time,
     307              :       const std::unordered_map<
     308              :           std::string,
     309              :           std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&
     310              :           functions_of_time) const;
     311              : 
     312              :   // NOLINTNEXTLINE(google-runtime-references)
     313            0 :   void pup(PUP::er& p);
     314              : 
     315            0 :   static bool is_identity() { return false; }
     316              : 
     317            0 :   static constexpr bool supports_hessian{false};
     318              : 
     319            0 :   const std::unordered_set<std::string>& function_of_time_names() const {
     320              :     return f_of_t_names_;
     321              :   }
     322              : 
     323              :  private:
     324            0 :   friend bool operator==(const SphericalCompression& lhs,
     325              :                          const SphericalCompression& rhs) {
     326              :     return lhs.f_of_t_name_ == rhs.f_of_t_name_ and
     327              :            lhs.min_radius_ == rhs.min_radius_ and
     328              :            lhs.max_radius_ == rhs.max_radius_ and lhs.center_ == rhs.center_;
     329              :   }
     330            0 :   std::string f_of_t_name_;
     331            0 :   std::unordered_set<std::string> f_of_t_names_;
     332            0 :   double min_radius_ = std::numeric_limits<double>::signaling_NaN();
     333            0 :   double max_radius_ = std::numeric_limits<double>::signaling_NaN();
     334            0 :   std::array<double, 3> center_;
     335              : };
     336              : 
     337              : template <bool InteriorMap>
     338            0 : bool operator!=(const SphericalCompression<InteriorMap>& lhs,
     339              :                 const SphericalCompression<InteriorMap>& rhs) {
     340              :   return not(lhs == rhs);
     341              : }
     342              : }  // namespace domain::CoordinateMaps::TimeDependent
        

Generated by: LCOV version 2.0-1