SpECTRE Documentation Coverage Report
Current view: top level - Domain/CoordinateMaps - Composition.hpp Coverage Total Hit
Commit: 0d1d717af84a81fb1284a20c4013708fe85cc2f0 Lines: 33.3 % 3 1
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 <memory>
       8              : #include <pup.h>
       9              : #include <tuple>
      10              : #include <type_traits>
      11              : #include <unordered_map>
      12              : #include <unordered_set>
      13              : #include <utility>
      14              : #include <vector>
      15              : 
      16              : #include "DataStructures/Tensor/Tensor.hpp"
      17              : #include "Domain/CoordinateMaps/CoordinateMap.hpp"
      18              : #include "Domain/FunctionsOfTime/FunctionOfTime.hpp"
      19              : #include "Utilities/Serialization/CharmPupable.hpp"
      20              : #include "Utilities/TMPL.hpp"
      21              : #include "Utilities/Autodiff/Autodiff.hpp"
      22              : #include "Utilities/Simd/Simd.hpp"
      23              : 
      24              : namespace domain::CoordinateMaps {
      25              : 
      26              : /*!
      27              :  * \brief A composition of coordinate maps at runtime
      28              :  *
      29              :  * Composes a sequence of `domain::CoordinateMapBase` that step through the
      30              :  * `Frames`. The result is another `domain::CoordinateMapBase`. This is
      31              :  * different to `domain::CoordinateMap`, which does the composition at compile
      32              :  * time. The use cases are different:
      33              :  *
      34              :  * - Use `domain::CoordinateMap` to compose maps at compile time to go from one
      35              :  *   (named) frame to another using any number of coordinate transformation. The
      36              :  *   coordinate transformations are concatenated to effectively form a single
      37              :  *   map, and intermediate frames have no meaning. This has the performance
      38              :  *   benefit that looking up pointers and calling into virtual member functions
      39              :  *   of intermediate maps is avoided, and it has the semantic benefit that
      40              :  *   intermediate frames without meaning are not named or even accessible.
      41              :  *   **Example:** A static BlockLogical -> Grid map that deforms the logical
      42              :  *   cube to a wedge, applies a radial redistribution of grid points, and
      43              :  *   translates + rotates the wedge.
      44              :  * - Use `domain::CoordinateMaps::Composition` (this class) to compose maps at
      45              :  *   runtime to step through a sequence of (named) frames.
      46              :  *   **Example:** A time-dependent ElementLogical -> BlockLogical -> Grid ->
      47              :  *   Inertial map that applies an affine transformation between the
      48              :  *   ElementLogical and BlockLogical frames (see
      49              :  *   domain::element_to_block_logical_map), then deforms the logical cube to a
      50              :  *   wedge using the map described above (Grid frame), and then rotates the grid
      51              :  *   with a time-dependent rotation map (Inertial frame).
      52              :  *
      53              :  * \warning Think about performance implications before using this Composition
      54              :  * class. In an evolution it's usually advantageous to keep at least some of the
      55              :  * maps separate, e.g. to avoid reevaluating the static maps in every time step.
      56              :  * You can also access individual components of the composition in this class.
      57              :  *
      58              :  * \tparam Frames The list of frames in the composition, as a tmpl::list<>.
      59              :  * The first entry in the list is the source frame, and the last entry is the
      60              :  * target frame of the composition. Maps in the composition step through the
      61              :  * `Frames`. For example, if `Frames = tmpl::list<Frame::ElementLogical,
      62              :  * Frame::BlockLogical, Frame::Inertial>`, then the composition has two maps:
      63              :  * ElementLogical -> BlockLogical and BlockLogical -> Inertial.
      64              :  *
      65              :  * ## Note on inverse Hessian computation
      66              :  * Below we work out the algebra for computing the inverse Hessian
      67              :  * used in this struct (if SPECTRE_AUTODIFF=ON). Suppose we have
      68              :  * a composition of maps \f$ \xi^i \longrightarrow \cdots \longrightarrow x^i
      69              :  * \longrightarrow y^i \f$, where \f$ \xi^i \f$ are the source coordinates, \f$
      70              :  * x^i \f$ are some intermediate coordinates, and \f$ y^i \f$ are the final
      71              :  * target coordinates,  We compute inverse Hessian by first
      72              :  * propagating autodiff dual types through the composed `call_impl` function to
      73              :  * automatically get the Hessian \f$ \frac{\partial^2 y^i}{\partial\xi^j
      74              :  * \partial\xi^k} \f$ Then the inverse Hessian is computed by the identity
      75              :  * \f[ \frac{\partial^2\xi^i}{\partial y^m \partial y^n} =
      76              :  * -\frac{\partial\xi^i}{\partial y^j}\frac{\partial\xi^k}{\partial y^m}
      77              :  * \frac{\partial\xi^l}{\partial y^n}\frac{\partial^2 y^j}{\partial\xi^k
      78              :  * \partial\xi^l}, \f] where the inverse Jacobian is passed in as a function
      79              :  * argument.
      80              :  *
      81              :  * See Test_Composition.cpp for a different implementation. The current
      82              :  * implementation is chosen in production as it is faster when we have
      83              :  * the inverse Jacobian already, and in most cases we do.
      84              :  *
      85              :  * ## Note on auto differentiation
      86              :  * We use forward mode autodiff here as it is simpler to implement and
      87              :  * has better optimization than the reverse mode. Reverse mode in
      88              :  * the [Autodiff](https://github.com/autodiff/autodiff) library
      89              :  * has higher cost per propagation and does not support taping.
      90              :  * Also see this
      91              :  * [github issue](https://github.com/autodiff/autodiff/issues/332).
      92              :  */
      93              : template <typename Frames, size_t Dim,
      94              :           typename Is = std::make_index_sequence<tmpl::size<Frames>::value - 1>>
      95            1 : struct Composition;
      96              : 
      97              : template <typename Frames, size_t Dim, size_t... Is>
      98              : struct Composition<Frames, Dim, std::index_sequence<Is...>>
      99              :     : public CoordinateMapBase<tmpl::front<Frames>, tmpl::back<Frames>, Dim> {
     100              :   using frames = Frames;
     101              :   using SourceFrame = tmpl::front<Frames>;
     102              :   using TargetFrame = tmpl::back<Frames>;
     103              :   static constexpr size_t num_frames = tmpl::size<Frames>::value;
     104              :   using Base = CoordinateMapBase<SourceFrame, TargetFrame, Dim>;
     105              :   using FuncOfTimeMap = std::unordered_map<
     106              :       std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>;
     107              :   using Base::operator();
     108              : 
     109              :   Composition() = default;
     110              :   Composition(const Composition& rhs) { *this = rhs; }
     111              :   Composition& operator=(const Composition& rhs);
     112              :   Composition(Composition&& /*rhs*/) = default;
     113              :   Composition& operator=(Composition&& /*rhs*/) = default;
     114              :   ~Composition() override = default;
     115              : 
     116              :   std::unique_ptr<Base> get_clone() const override {
     117              :     return std::make_unique<Composition>(*this);
     118              :   }
     119              : 
     120              :   /// \cond
     121              :   explicit Composition(CkMigrateMessage* /*m*/) {}
     122              :   using PUP::able::register_constructor;
     123              :   WRAPPED_PUPable_decl_template(Composition);  // NOLINT
     124              :   /// \endcond
     125              : 
     126              :   Composition(std::unique_ptr<CoordinateMapBase<
     127              :                   tmpl::at<frames, tmpl::size_t<Is>>,
     128              :                   tmpl::at<frames, tmpl::size_t<Is + 1>>, Dim>>... maps);
     129              : 
     130              :   const std::tuple<std::unique_ptr<
     131              :       CoordinateMapBase<tmpl::at<frames, tmpl::size_t<Is>>,
     132              :                         tmpl::at<frames, tmpl::size_t<Is + 1>>, Dim>>...>&
     133              :   maps() const {
     134              :     return maps_;
     135              :   }
     136              : 
     137              :   template <typename LocalSourceFrame, typename LocalTargetFrame>
     138              :   const CoordinateMapBase<LocalSourceFrame, LocalTargetFrame, Dim>&
     139              :   get_component() const {
     140              :     return *get<std::unique_ptr<
     141              :         CoordinateMapBase<LocalSourceFrame, LocalTargetFrame, Dim>>>(maps_);
     142              :   }
     143              : 
     144              :   bool is_identity() const override;
     145              : 
     146              :   bool inv_jacobian_is_time_dependent() const override;
     147              : 
     148              :   bool jacobian_is_time_dependent() const override;
     149              : 
     150              :   const std::unordered_set<std::string>& function_of_time_names()
     151              :       const override {
     152              :     return function_of_time_names_;
     153              :   }
     154              : 
     155              :   bool supports_hessian() const override;
     156              : 
     157              :   tnsr::I<double, Dim, TargetFrame> operator()(
     158              :       tnsr::I<double, Dim, SourceFrame> source_point,
     159              :       double time = std::numeric_limits<double>::signaling_NaN(),
     160              :       const FuncOfTimeMap& functions_of_time = {}) const override;
     161              : 
     162              :   tnsr::I<DataVector, Dim, TargetFrame> operator()(
     163              :       tnsr::I<DataVector, Dim, SourceFrame> source_point,
     164              :       double time = std::numeric_limits<double>::signaling_NaN(),
     165              :       const FuncOfTimeMap& functions_of_time = {}) const override;
     166              : 
     167              :   std::optional<tnsr::I<double, Dim, SourceFrame>> inverse(
     168              :       tnsr::I<double, Dim, TargetFrame> target_point,
     169              :       double time = std::numeric_limits<double>::signaling_NaN(),
     170              :       const FuncOfTimeMap& functions_of_time = {}) const override;
     171              : 
     172              :   InverseJacobian<double, Dim, SourceFrame, TargetFrame> inv_jacobian(
     173              :       tnsr::I<double, Dim, SourceFrame> source_point,
     174              :       double time = std::numeric_limits<double>::signaling_NaN(),
     175              :       const FuncOfTimeMap& functions_of_time = {}) const override;
     176              : 
     177              :   InverseJacobian<DataVector, Dim, SourceFrame, TargetFrame> inv_jacobian(
     178              :       tnsr::I<DataVector, Dim, SourceFrame> source_point,
     179              :       double time = std::numeric_limits<double>::signaling_NaN(),
     180              :       const FuncOfTimeMap& functions_of_time = {}) const override;
     181              : 
     182              : #ifdef SPECTRE_AUTODIFF
     183              :   InverseJacobian<autodiff::HigherOrderDual<2, double>, Dim, SourceFrame,
     184              :                   TargetFrame>
     185              :   inv_jacobian(tnsr::I<autodiff::HigherOrderDual<2, double>, Dim, SourceFrame>
     186              :                    source_point,
     187              :                double time = std::numeric_limits<double>::signaling_NaN(),
     188              :                const FuncOfTimeMap& functions_of_time = {}) const override;
     189              : 
     190              :   // Compute inverse hessian by calling operator()
     191              :   InverseHessian<double, Dim, SourceFrame, TargetFrame> inv_hessian(
     192              :       tnsr::I<double, Dim, SourceFrame> source_point,
     193              :       const InverseJacobian<double, Dim, SourceFrame, TargetFrame>& inverse_jac,
     194              :       double time = std::numeric_limits<double>::signaling_NaN(),
     195              :       const FuncOfTimeMap& functions_of_time = {}) const override;
     196              : 
     197              :   // Compute inverse hessian by calling operator()
     198              :   InverseHessian<DataVector, Dim, SourceFrame, TargetFrame> inv_hessian(
     199              :       tnsr::I<DataVector, Dim, SourceFrame> source_point,
     200              :       const InverseJacobian<DataVector, Dim, SourceFrame, TargetFrame>&
     201              :           inverse_jac,
     202              :       double time = std::numeric_limits<double>::signaling_NaN(),
     203              :       const FuncOfTimeMap& functions_of_time = {}) const override;
     204              : #endif  // SPECTRE_AUTODIFF
     205              : 
     206              :   Jacobian<double, Dim, SourceFrame, TargetFrame> jacobian(
     207              :       tnsr::I<double, Dim, SourceFrame> source_point,
     208              :       double time = std::numeric_limits<double>::signaling_NaN(),
     209              :       const FuncOfTimeMap& functions_of_time = {}) const override;
     210              : 
     211              :   Jacobian<DataVector, Dim, SourceFrame, TargetFrame> jacobian(
     212              :       tnsr::I<DataVector, Dim, SourceFrame> source_point,
     213              :       double time = std::numeric_limits<double>::signaling_NaN(),
     214              :       const FuncOfTimeMap& functions_of_time = {}) const override;
     215              : 
     216              :   [[noreturn]] std::tuple<
     217              :       tnsr::I<double, Dim, TargetFrame>,
     218              :       InverseJacobian<double, Dim, SourceFrame, TargetFrame>,
     219              :       Jacobian<double, Dim, SourceFrame, TargetFrame>,
     220              :       tnsr::I<double, Dim, TargetFrame>>
     221              :   coords_frame_velocity_jacobians(
     222              :       tnsr::I<double, Dim, SourceFrame> source_point,
     223              :       double time = std::numeric_limits<double>::signaling_NaN(),
     224              :       const FuncOfTimeMap& functions_of_time = {}) const override;
     225              : 
     226              :   [[noreturn]] std::tuple<
     227              :       tnsr::I<DataVector, Dim, TargetFrame>,
     228              :       InverseJacobian<DataVector, Dim, SourceFrame, TargetFrame>,
     229              :       Jacobian<DataVector, Dim, SourceFrame, TargetFrame>,
     230              :       tnsr::I<DataVector, Dim, TargetFrame>>
     231              :   coords_frame_velocity_jacobians(
     232              :       tnsr::I<DataVector, Dim, SourceFrame> source_point,
     233              :       double time = std::numeric_limits<double>::signaling_NaN(),
     234              :       const FuncOfTimeMap& functions_of_time = {}) const override;
     235              : 
     236              :   [[noreturn]] std::unique_ptr<CoordinateMapBase<SourceFrame, Frame::Grid, Dim>>
     237              :   get_to_grid_frame() const override;
     238              : 
     239              :   // NOLINTNEXTLINE(google-runtime-references)
     240              :   void pup(PUP::er& p) override;
     241              : 
     242              :  private:
     243              :   template <typename DataType>
     244              :   tnsr::I<DataType, Dim, TargetFrame> call_impl(
     245              :       tnsr::I<DataType, Dim, SourceFrame> source_point,
     246              :       double time = std::numeric_limits<double>::signaling_NaN(),
     247              :       const FuncOfTimeMap& functions_of_time = {}) const;
     248              : 
     249              :   template <typename DataType>
     250              :   std::optional<tnsr::I<DataType, Dim, SourceFrame>> inverse_impl(
     251              :       tnsr::I<DataType, Dim, TargetFrame> target_point,
     252              :       double time = std::numeric_limits<double>::signaling_NaN(),
     253              :       const FuncOfTimeMap& functions_of_time = {}) const;
     254              : 
     255              :   template <typename DataType>
     256              :   InverseJacobian<DataType, Dim, SourceFrame, TargetFrame> inv_jacobian_impl(
     257              :       tnsr::I<DataType, Dim, SourceFrame> source_point,
     258              :       double time = std::numeric_limits<double>::signaling_NaN(),
     259              :       const FuncOfTimeMap& functions_of_time = {}) const;
     260              : 
     261              :   template <typename DataType>
     262              :   InverseHessian<DataType, Dim, SourceFrame, TargetFrame> inv_hessian_impl(
     263              :       tnsr::I<DataType, Dim, SourceFrame> source_point,
     264              :       const ::InverseJacobian<DataType, Dim, SourceFrame, TargetFrame>&
     265              :           inverse_jac,
     266              :       double time = std::numeric_limits<double>::signaling_NaN(),
     267              :       const FuncOfTimeMap& functions_of_time = {}) const;
     268              : 
     269              :   template <typename DataType>
     270              :   Jacobian<DataType, Dim, SourceFrame, TargetFrame> jacobian_impl(
     271              :       tnsr::I<DataType, Dim, SourceFrame> source_point,
     272              :       double time = std::numeric_limits<double>::signaling_NaN(),
     273              :       const FuncOfTimeMap& functions_of_time = {}) const;
     274              : 
     275              :   bool is_equal_to(const CoordinateMapBase<SourceFrame, TargetFrame, Dim>&
     276              :                        other) const override;
     277              : 
     278              :   std::tuple<std::unique_ptr<
     279              :       CoordinateMapBase<tmpl::at<frames, tmpl::size_t<Is>>,
     280              :                         tmpl::at<frames, tmpl::size_t<Is + 1>>, Dim>>...>
     281              :       maps_;
     282              :   std::unordered_set<std::string> function_of_time_names_;
     283              : };
     284              : 
     285              : // Template deduction guide
     286              : template <typename FirstMap, typename... Maps>
     287            0 : Composition(std::unique_ptr<FirstMap>, std::unique_ptr<Maps>... maps)
     288              :     -> Composition<tmpl::list<typename FirstMap::source_frame,
     289              :                               typename FirstMap::target_frame,
     290              :                               typename Maps::target_frame...>,
     291              :                    FirstMap::dim>;
     292              : 
     293              : /// \cond
     294              : template <typename Frames, size_t Dim, size_t... Is>
     295              : PUP::able::PUP_ID
     296              :     Composition<Frames, Dim, std::index_sequence<Is...>>::my_PUP_ID = 0;
     297              : /// \endcond
     298              : 
     299              : }  // namespace domain::CoordinateMaps
        

Generated by: LCOV version 2.0-1