SpECTRE Documentation Coverage Report
Current view: top level - Domain/CoordinateMaps - CoordinateMap.hpp Coverage Total Hit
Commit: 0d1d717af84a81fb1284a20c4013708fe85cc2f0 Lines: 53.7 % 95 51
Test Date: 2026-10-22 17:29:42
Legend: Lines:     hit not hit

            Line data    Source code
       1            1 : // Distributed under the MIT License.
       2              : // See LICENSE.txt for details.
       3              : 
       4              : /// \file
       5              : /// Defines class CoordinateMap
       6              : 
       7              : #pragma once
       8              : 
       9              : #include <cstddef>
      10              : #include <limits>
      11              : #include <memory>
      12              : #include <optional>
      13              : #include <pup.h>
      14              : #include <string>
      15              : #include <tuple>
      16              : #include <type_traits>
      17              : #include <typeinfo>
      18              : #include <unordered_map>
      19              : #include <unordered_set>
      20              : #include <utility>
      21              : #include <vector>
      22              : 
      23              : #include "DataStructures/Tensor/Tensor.hpp"
      24              : #include "Domain/FunctionsOfTime/FunctionOfTime.hpp"
      25              : #include "Utilities/Autodiff/Autodiff.hpp"
      26              : #include "Utilities/PrettyType.hpp"
      27              : #include "Utilities/Serialization/CharmPupable.hpp"
      28              : #include "Utilities/Simd/Simd.hpp"
      29              : #include "Utilities/TMPL.hpp"
      30              : #include "Utilities/TypeTraits/CreateIsCallable.hpp"
      31              : 
      32              : /// \cond
      33              : class DataVector;
      34              : /// \endcond
      35              : 
      36              : namespace domain {
      37              : /// Contains all coordinate maps.
      38              : namespace CoordinateMaps {
      39              : /// Contains the time-dependent coordinate maps
      40            1 : namespace TimeDependent {}
      41              : template <typename FirstMap, typename... Maps>
      42            0 : constexpr size_t map_dim = FirstMap::dim;
      43              : }  // namespace CoordinateMaps
      44              : 
      45              : namespace CoordinateMap_detail {
      46              : CREATE_IS_CALLABLE(function_of_time_names)
      47              : CREATE_IS_CALLABLE_V(function_of_time_names)
      48              : 
      49              : template <typename T>
      50              : struct map_type {
      51              :   using type = T;
      52              : };
      53              : 
      54              : template <typename T>
      55              : struct map_type<std::unique_ptr<T>> {
      56              :   using type = T;
      57              : };
      58              : 
      59              : template <typename T>
      60              : using map_type_t = typename map_type<T>::type;
      61              : 
      62              : template <typename... Maps, size_t... Is>
      63              : std::unordered_set<std::string> initialize_names(
      64              :     const std::tuple<Maps...>& maps, std::index_sequence<Is...> /*meta*/) {
      65              :   std::unordered_set<std::string> function_of_time_names{};
      66              :   const auto add_names = [&function_of_time_names, &maps](auto index) {
      67              :     const auto& map = std::get<decltype(index)::value>(maps);
      68              :     using TupleMap = std::decay_t<decltype(map)>;
      69              :     constexpr bool map_is_unique_ptr = tt::is_a_v<std::unique_ptr, TupleMap>;
      70              :     using Map = map_type_t<TupleMap>;
      71              :     if constexpr (is_function_of_time_names_callable_v<Map>) {
      72              :       if constexpr (map_is_unique_ptr) {
      73              :         const auto& names = map->function_of_time_names();
      74              :         function_of_time_names.insert(names.begin(), names.end());
      75              :       } else {
      76              :         const auto& names = map.function_of_time_names();
      77              :         function_of_time_names.insert(names.begin(), names.end());
      78              :       }
      79              :     } else {
      80              :       (void)function_of_time_names;
      81              :     }
      82              :   };
      83              :   EXPAND_PACK_LEFT_TO_RIGHT(add_names(std::integral_constant<size_t, Is>{}));
      84              : 
      85              :   return function_of_time_names;
      86              : }
      87              : 
      88              : template <typename... Maps>
      89              : std::string get_unsupported_autodiff_maps_error() {
      90              :   std::string unsupported_maps;
      91              :   size_t index = 0;
      92              :   (
      93              :       [&]() {
      94              :         if constexpr (not Maps::supports_hessian) {
      95              :           if (not unsupported_maps.empty()) {
      96              :             unsupported_maps += ", ";
      97              :           }
      98              :           unsupported_maps += "Map " + std::to_string(index) + " (" +
      99              :                               pretty_type::get_name<Maps>() + ")";
     100              :         }
     101              :         ++index;
     102              :       }(),
     103              :       ...);
     104              :   return unsupported_maps;
     105              : }
     106              : 
     107              : }  // namespace CoordinateMap_detail
     108              : 
     109              : /*!
     110              :  * \ingroup CoordinateMapsGroup
     111              :  * \brief Abstract base class for CoordinateMap
     112              :  */
     113              : template <typename SourceFrame, typename TargetFrame, size_t Dim>
     114            1 : class CoordinateMapBase : public PUP::able {
     115              :  public:
     116            0 :   static constexpr size_t dim = Dim;
     117            0 :   using source_frame = SourceFrame;
     118            0 :   using target_frame = TargetFrame;
     119              : 
     120            0 :   WRAPPED_PUPable_abstract(CoordinateMapBase);  // NOLINT
     121              : 
     122            0 :   CoordinateMapBase() = default;
     123            0 :   CoordinateMapBase(const CoordinateMapBase& /*rhs*/) = default;
     124            0 :   CoordinateMapBase& operator=(const CoordinateMapBase& /*rhs*/) = default;
     125            0 :   CoordinateMapBase(CoordinateMapBase&& /*rhs*/) = default;
     126            0 :   CoordinateMapBase& operator=(CoordinateMapBase&& /*rhs*/) = default;
     127            0 :   ~CoordinateMapBase() override = default;
     128              : 
     129              :   virtual std::unique_ptr<CoordinateMapBase<SourceFrame, TargetFrame, Dim>>
     130            0 :   get_clone() const = 0;
     131              : 
     132              :   /// \brief Retrieve the same map but going from `SourceFrame` to
     133              :   /// `Frame::Grid`.
     134              :   ///
     135              :   /// This functionality is needed when composing time-dependent maps with
     136              :   /// time-independent maps, where the target frame of the time-independent map
     137              :   /// is `Frame::Grid`.
     138              :   virtual std::unique_ptr<CoordinateMapBase<SourceFrame, Frame::Grid, Dim>>
     139            1 :   get_to_grid_frame() const = 0;
     140              : 
     141              :   /// Returns `true` if the map is the identity
     142            1 :   virtual bool is_identity() const = 0;
     143              : 
     144              :   /// Returns `true` if the inverse Jacobian depends on time.
     145            1 :   virtual bool inv_jacobian_is_time_dependent() const = 0;
     146              : 
     147              :   /// Returns `true` if the Jacobian depends on time.
     148            1 :   virtual bool jacobian_is_time_dependent() const = 0;
     149              : 
     150              :   /// Get a set of all FunctionOfTime names used in this mapping
     151            1 :   virtual const std::unordered_set<std::string>& function_of_time_names()
     152              :       const = 0;
     153              : 
     154              :   /// Returns `true` if this CoordinateMap supports autodiff
     155            1 :   virtual bool supports_hessian() const = 0;
     156              : 
     157              :   /// @{
     158              :   /// Apply the `Maps` to the point(s) `source_point`
     159            1 :   virtual tnsr::I<double, Dim, TargetFrame> operator()(
     160              :       tnsr::I<double, Dim, SourceFrame> source_point,
     161              :       double time = std::numeric_limits<double>::signaling_NaN(),
     162              :       const FunctionsOfTimeMap& functions_of_time = {}) const = 0;
     163            1 :   virtual tnsr::I<DataVector, Dim, TargetFrame> operator()(
     164              :       tnsr::I<DataVector, Dim, SourceFrame> source_point,
     165              :       double time = std::numeric_limits<double>::signaling_NaN(),
     166              :       const FunctionsOfTimeMap& functions_of_time = {}) const = 0;
     167              :   /// @}
     168              : 
     169              : #ifdef SPECTRE_AUTODIFF
     170              :   /// @{
     171              :   /// Apply the `Maps` to the point(s) `source_point`
     172              :   ///
     173              :   /// \note Require SPECTRE_AUTODIFF=ON.
     174              :   virtual tnsr::I<autodiff::HigherOrderDual<2, double>, Dim, TargetFrame>
     175            1 :   operator()(tnsr::I<autodiff::HigherOrderDual<2, double>, Dim,
     176              :                      SourceFrame> /*source_point*/,
     177              :              double /*time*/ = std::numeric_limits<double>::signaling_NaN(),
     178              :              const FunctionsOfTimeMap& /*functions_of_time*/ = {}) const {
     179              :     ERROR("Call operator for autodiff types must be overriden.");
     180              :   }
     181              :   virtual tnsr::I<autodiff::HigherOrderDual<2, simd::batch<double>>, Dim,
     182              :                   TargetFrame>
     183            1 :   operator()(tnsr::I<autodiff::HigherOrderDual<2, simd::batch<double>>, Dim,
     184              :                      SourceFrame> /*source_point*/,
     185              :              double /*time*/ = std::numeric_limits<double>::signaling_NaN(),
     186              :              const FunctionsOfTimeMap& /*functions_of_time*/ = {}) const {
     187              :     ERROR("Call operator for autodiff types must be overriden.");
     188              :   }
     189              :   /// @}
     190              : #endif  // SPECTRE_AUTODIFF
     191              : 
     192              :   /// @{
     193              :   /// Apply the inverse `Maps` to the point(s) `target_point`.
     194              :   /// The returned std::optional is invalid if the map is not invertible
     195              :   /// at `target_point`, or if `target_point` can be easily determined to not
     196              :   /// make sense for the map.  An example of the latter is passing a
     197              :   /// point with a negative value of z into a positive-z Wedge<3> inverse map.
     198              :   /// The inverse function is only callable with doubles because the inverse
     199              :   /// might fail if called for a point out of range, and it is unclear
     200              :   /// what should happen if the inverse were to succeed for some points in a
     201              :   /// DataVector but fail for other points.
     202            1 :   virtual std::optional<tnsr::I<double, Dim, SourceFrame>> inverse(
     203              :       tnsr::I<double, Dim, TargetFrame> target_point,
     204              :       double time = std::numeric_limits<double>::signaling_NaN(),
     205              :       const FunctionsOfTimeMap& functions_of_time = {}) const = 0;
     206              :   /// @}
     207              : 
     208              :   /// @{
     209              :   /// Compute the inverse Jacobian of the `Maps` at the point(s)
     210              :   /// `source_point`
     211            1 :   virtual InverseJacobian<double, Dim, SourceFrame, TargetFrame> inv_jacobian(
     212              :       tnsr::I<double, Dim, SourceFrame> source_point,
     213              :       double time = std::numeric_limits<double>::signaling_NaN(),
     214              :       const FunctionsOfTimeMap& functions_of_time = {}) const = 0;
     215              :   virtual InverseJacobian<DataVector, Dim, SourceFrame, TargetFrame>
     216            1 :   inv_jacobian(tnsr::I<DataVector, Dim, SourceFrame> source_point,
     217              :                double time = std::numeric_limits<double>::signaling_NaN(),
     218              :                const FunctionsOfTimeMap& functions_of_time = {}) const = 0;
     219              : #ifdef SPECTRE_AUTODIFF
     220              :   virtual InverseJacobian<autodiff::HigherOrderDual<2, double>, Dim,
     221              :                           SourceFrame, TargetFrame>
     222            1 :   inv_jacobian(tnsr::I<autodiff::HigherOrderDual<2, double>, Dim, SourceFrame>
     223              :                    source_point,
     224              :                double time = std::numeric_limits<double>::signaling_NaN(),
     225              :                const FunctionsOfTimeMap& functions_of_time = {}) const = 0;
     226              : #endif  // SPECTRE_AUTODIFF
     227              :   /// @}
     228              : 
     229              : #ifdef SPECTRE_AUTODIFF
     230              :   /// @{
     231              :   /// Compute the inverse Hessian of the `Maps` at the point(s)
     232              :   /// `source_point`
     233              :   ///
     234              :   /// \note Require SPECTRE_AUTODIFF=ON.
     235            1 :   virtual InverseHessian<double, Dim, SourceFrame, TargetFrame> inv_hessian(
     236              :       tnsr::I<double, Dim, SourceFrame> source_point,
     237              :       const InverseJacobian<double, Dim, SourceFrame, TargetFrame>& inverse_jac,
     238              :       double time = std::numeric_limits<double>::signaling_NaN(),
     239              :       const FunctionsOfTimeMap& functions_of_time = {}) const = 0;
     240            1 :   virtual InverseHessian<DataVector, Dim, SourceFrame, TargetFrame> inv_hessian(
     241              :       tnsr::I<DataVector, Dim, SourceFrame> source_point,
     242              :       const InverseJacobian<DataVector, Dim, SourceFrame, TargetFrame>&
     243              :           inverse_jac,
     244              :       double time = std::numeric_limits<double>::signaling_NaN(),
     245              :       const FunctionsOfTimeMap& functions_of_time = {}) const = 0;
     246              :   /// @}
     247              : #endif  // SPECTRE_AUTODIFF
     248              : 
     249              :   /// @{
     250              :   /// Compute the Jacobian of the `Maps` at the point(s) `source_point`
     251            1 :   virtual Jacobian<double, Dim, SourceFrame, TargetFrame> jacobian(
     252              :       tnsr::I<double, Dim, SourceFrame> source_point,
     253              :       double time = std::numeric_limits<double>::signaling_NaN(),
     254              :       const FunctionsOfTimeMap& functions_of_time = {}) const = 0;
     255            1 :   virtual Jacobian<DataVector, Dim, SourceFrame, TargetFrame> jacobian(
     256              :       tnsr::I<DataVector, Dim, SourceFrame> source_point,
     257              :       double time = std::numeric_limits<double>::signaling_NaN(),
     258              :       const FunctionsOfTimeMap& functions_of_time = {}) const = 0;
     259              :   /// @}
     260              : 
     261              :   /// @{
     262              :   /// Compute the mapped coordinates, frame velocity, Jacobian, and inverse
     263              :   /// Jacobian
     264            1 :   virtual auto coords_frame_velocity_jacobians(
     265              :       tnsr::I<double, Dim, SourceFrame> source_point,
     266              :       double time = std::numeric_limits<double>::signaling_NaN(),
     267              :       const FunctionsOfTimeMap& functions_of_time = {}) const
     268              :       -> std::tuple<tnsr::I<double, Dim, TargetFrame>,
     269              :                     InverseJacobian<double, Dim, SourceFrame, TargetFrame>,
     270              :                     Jacobian<double, Dim, SourceFrame, TargetFrame>,
     271              :                     tnsr::I<double, Dim, TargetFrame>> = 0;
     272            1 :   virtual auto coords_frame_velocity_jacobians(
     273              :       tnsr::I<DataVector, Dim, SourceFrame> source_point,
     274              :       double time = std::numeric_limits<double>::signaling_NaN(),
     275              :       const FunctionsOfTimeMap& functions_of_time = {}) const
     276              :       -> std::tuple<tnsr::I<DataVector, Dim, TargetFrame>,
     277              :                     InverseJacobian<DataVector, Dim, SourceFrame, TargetFrame>,
     278              :                     Jacobian<DataVector, Dim, SourceFrame, TargetFrame>,
     279              :                     tnsr::I<DataVector, Dim, TargetFrame>> = 0;
     280              :   /// @}
     281              : 
     282              :  private:
     283            0 :   virtual bool is_equal_to(const CoordinateMapBase& other) const = 0;
     284            0 :   friend bool operator==(const CoordinateMapBase& lhs,
     285              :                          const CoordinateMapBase& rhs) {
     286              :     return typeid(lhs) == typeid(rhs) and lhs.is_equal_to(rhs);
     287              :   }
     288            0 :   friend bool operator!=(const CoordinateMapBase& lhs,
     289              :                          const CoordinateMapBase& rhs) {
     290              :     return not(lhs == rhs);
     291              :   }
     292              : };
     293              : 
     294              : /*!
     295              :  * \ingroup CoordinateMapsGroup
     296              :  * \brief A coordinate map or composition of coordinate maps
     297              :  *
     298              :  * Maps coordinates from the `SourceFrame` to the `TargetFrame` using the
     299              :  * coordinate maps `Maps...`. The individual maps are applied left to right
     300              :  * from the source to the target Frame. The inverse map, as well as Jacobian
     301              :  * and inverse Jacobian are also provided. The `CoordinateMap` class must
     302              :  * be used even if just wrapping a single coordinate map. It is designed to
     303              :  * be an extremely minimal interface to the underlying coordinate maps. For
     304              :  * a list of all coordinate maps see the CoordinateMaps group or namespace.
     305              :  *
     306              :  * Each coordinate map must contain a `static constexpr size_t dim` variable
     307              :  * that is equal to the dimensionality of the map. The Coordinatemap class
     308              :  * contains a member `static constexpr size_t dim`, a type alias `source_frame`,
     309              :  * a type alias `target_frame` and typelist of the `Maps...`.
     310              :  */
     311              : template <typename SourceFrame, typename TargetFrame, typename... Maps>
     312            1 : class CoordinateMap
     313              :     : public CoordinateMapBase<SourceFrame, TargetFrame,
     314              :                                CoordinateMaps::map_dim<Maps...>> {
     315              :   static_assert(sizeof...(Maps) > 0, "Must have at least one map");
     316              :   static_assert(
     317              :       tmpl::all<tmpl::integral_list<size_t, Maps::dim...>,
     318              :                 std::is_same<tmpl::integral_constant<
     319              :                                  size_t, CoordinateMaps::map_dim<Maps...>>,
     320              :                              tmpl::_1>>::value,
     321              :       "All Maps passed to CoordinateMap must be of the same dimensionality.");
     322              : 
     323              :  public:
     324            0 :   static constexpr size_t dim = CoordinateMaps::map_dim<Maps...>;
     325            0 :   using source_frame = SourceFrame;
     326            0 :   using target_frame = TargetFrame;
     327            0 :   using maps_list = tmpl::list<Maps...>;
     328              : 
     329              :   /// Used for Charm++ serialization
     330            1 :   CoordinateMap() = default;
     331              : 
     332            0 :   CoordinateMap(const CoordinateMap& /*rhs*/) = default;
     333            0 :   CoordinateMap& operator=(const CoordinateMap& /*rhs*/) = default;
     334            0 :   CoordinateMap(CoordinateMap&& /*rhs*/) = default;
     335            0 :   CoordinateMap& operator=(CoordinateMap&& /*rhs*/) = default;
     336            0 :   ~CoordinateMap() override = default;
     337              : 
     338            0 :   explicit CoordinateMap(Maps... maps);
     339              : 
     340            0 :   std::unique_ptr<CoordinateMapBase<SourceFrame, TargetFrame, dim>> get_clone()
     341              :       const override {
     342              :     return std::make_unique<CoordinateMap>(*this);
     343              :   }
     344              : 
     345              :   std::unique_ptr<CoordinateMapBase<SourceFrame, Frame::Grid, dim>>
     346            1 :   get_to_grid_frame() const override {
     347              :     return get_to_grid_frame_impl(std::make_index_sequence<sizeof...(Maps)>{});
     348              :   }
     349              : 
     350              :   /// Returns `true` if the map is the identity
     351            1 :   bool is_identity() const override;
     352              : 
     353              :   /// Returns `true` if the inverse Jacobian depends on time.
     354            1 :   bool inv_jacobian_is_time_dependent() const override;
     355              : 
     356              :   /// Returns `true` if the Jacobian depends on time.
     357            1 :   bool jacobian_is_time_dependent() const override;
     358              : 
     359              :   /// Get a set of all FunctionOfTime names from `Maps`
     360            1 :   const std::unordered_set<std::string>& function_of_time_names()
     361              :       const override {
     362              :     return function_of_time_names_;
     363              :   }
     364              : 
     365              :   /// Returns `true` if this coordinate map supports hessian
     366            1 :   bool supports_hessian() const override {
     367              :     return (Maps::supports_hessian && ...);
     368              :   }
     369              : 
     370              :   /// @{
     371              :   /// Apply the `Maps...` to the point(s) `source_point`
     372            1 :   tnsr::I<double, dim, TargetFrame> operator()(
     373              :       tnsr::I<double, dim, SourceFrame> source_point,
     374              :       const double time = std::numeric_limits<double>::signaling_NaN(),
     375              :       const FunctionsOfTimeMap& functions_of_time = {}) const override {
     376              :     return call_impl(std::move(source_point), time, functions_of_time,
     377              :                      std::make_index_sequence<sizeof...(Maps)>{});
     378              :   }
     379            1 :   tnsr::I<DataVector, dim, TargetFrame> operator()(
     380              :       tnsr::I<DataVector, dim, SourceFrame> source_point,
     381              :       const double time = std::numeric_limits<double>::signaling_NaN(),
     382              :       const FunctionsOfTimeMap& functions_of_time = {}) const override {
     383              :     return call_impl(std::move(source_point), time, functions_of_time,
     384              :                      std::make_index_sequence<sizeof...(Maps)>{});
     385              :   }
     386              :   /// @}
     387              : 
     388              : #ifdef SPECTRE_AUTODIFF
     389              :   /// @{
     390              :   /// Apply the `Maps...` to the point(s) `source_point`
     391              :   ///
     392              :   /// \note Require SPECTRE_AUTODIFF=ON.
     393            1 :   tnsr::I<autodiff::HigherOrderDual<2, double>, dim, TargetFrame> operator()(
     394              :       tnsr::I<autodiff::HigherOrderDual<2, double>, dim, SourceFrame>
     395              :           source_point,
     396              :       const double time = std::numeric_limits<double>::signaling_NaN(),
     397              :       const FunctionsOfTimeMap& functions_of_time = {}) const override {
     398              :     if constexpr ((Maps::supports_hessian && ...)) {
     399              :       return call_impl(std::move(source_point), time, functions_of_time,
     400              :                        std::make_index_sequence<sizeof...(Maps)>{});
     401              :     } else {
     402              :       ERROR("At least one of the Maps does not support autodiff: "
     403              :             << CoordinateMap_detail::get_unsupported_autodiff_maps_error<
     404              :                    Maps...>());
     405              :     }
     406              :   }
     407              :   tnsr::I<autodiff::HigherOrderDual<2, simd::batch<double>>, dim, TargetFrame>
     408            1 :   operator()(tnsr::I<autodiff::HigherOrderDual<2, simd::batch<double>>, dim,
     409              :                      SourceFrame>
     410              :                  source_point,
     411              :              const double time = std::numeric_limits<double>::signaling_NaN(),
     412              :              const FunctionsOfTimeMap& functions_of_time = {}) const override {
     413              :     if constexpr ((Maps::supports_hessian && ...)) {
     414              :       return call_impl(std::move(source_point), time, functions_of_time,
     415              :                        std::make_index_sequence<sizeof...(Maps)>{});
     416              :     } else {
     417              :       ERROR("At least one of the Maps does not support autodiff: "
     418              :             << CoordinateMap_detail::get_unsupported_autodiff_maps_error<
     419              :                    Maps...>());
     420              :     }
     421              :   }
     422              :   /// @}
     423              : #endif  // SPECTRE_AUTODIFF
     424              : 
     425              :   /// @{
     426              :   /// Apply the inverse `Maps...` to the point(s) `target_point`
     427            1 :   std::optional<tnsr::I<double, dim, SourceFrame>> inverse(
     428              :       tnsr::I<double, dim, TargetFrame> target_point,
     429              :       const double time = std::numeric_limits<double>::signaling_NaN(),
     430              :       const FunctionsOfTimeMap& functions_of_time = {}) const override {
     431              :     return inverse_impl(std::move(target_point), time, functions_of_time,
     432              :                         std::make_index_sequence<sizeof...(Maps)>{});
     433              :   }
     434              :   /// @}
     435              : 
     436              :   /// @{
     437              :   /// Compute the inverse Jacobian of the `Maps...` at the point(s)
     438              :   /// `source_point`
     439            1 :   InverseJacobian<double, dim, SourceFrame, TargetFrame> inv_jacobian(
     440              :       tnsr::I<double, dim, SourceFrame> source_point,
     441              :       const double time = std::numeric_limits<double>::signaling_NaN(),
     442              :       const FunctionsOfTimeMap& functions_of_time = {}) const override {
     443              :     return inv_jacobian_impl(std::move(source_point), time, functions_of_time);
     444              :   }
     445            1 :   InverseJacobian<DataVector, dim, SourceFrame, TargetFrame> inv_jacobian(
     446              :       tnsr::I<DataVector, dim, SourceFrame> source_point,
     447              :       const double time = std::numeric_limits<double>::signaling_NaN(),
     448              :       const FunctionsOfTimeMap& functions_of_time = {}) const override {
     449              :     return inv_jacobian_impl(std::move(source_point), time, functions_of_time);
     450              :   }
     451              : #ifdef SPECTRE_AUTODIFF
     452              :   InverseJacobian<autodiff::HigherOrderDual<2, double>, dim, SourceFrame,
     453              :                   TargetFrame>
     454            1 :   inv_jacobian(
     455              :       tnsr::I<autodiff::HigherOrderDual<2, double>, dim, SourceFrame>
     456              :           source_point,
     457              :       const double time = std::numeric_limits<double>::signaling_NaN(),
     458              :       const FunctionsOfTimeMap& functions_of_time = {}) const override {
     459              :     if constexpr ((Maps::supports_hessian && ...)) {
     460              :       return inv_jacobian_impl(std::move(source_point), time,
     461              :                                functions_of_time);
     462              :     } else {
     463              :       ERROR("At least one of the Maps does not support autodiff: "
     464              :             << CoordinateMap_detail::get_unsupported_autodiff_maps_error<
     465              :                    Maps...>());
     466              :     }
     467              :   }
     468              : #endif  // SPECTRE_AUTODIFF
     469              :   /// @}
     470              : 
     471              : #ifdef SPECTRE_AUTODIFF
     472              :   /// @{
     473              :   /*!
     474              :    * Compute the inverse Hessian of the `Maps...` at the point(s)
     475              :    * `source_point` by computing the Hessian of the `Maps...` and
     476              :    * compose it with inverse Jacobians of the `Maps...`.
     477              :    *
     478              :    * This function propagates autodiff dual types
     479              :    * through the `call_impl` function to automatically get the
     480              :    * Hessian \f$ \frac{\partial^2 x^i}{\partial\xi^j \partial\xi^k} \f$
     481              :    * where \f$ x^i \f$ are the target coordinates and \f$ \xi^i \f$ are the
     482              :    * source coordinates. Then the inverse Hessian is computed by the
     483              :    * identity
     484              :    * \f[\frac{\partial^2 \xi^i}{\partial x^m \partial x^n} =
     485              :    * -\frac{\partial \xi^i}{\partial x^j} \frac{\partial \xi^k}{\partial x^m}
     486              :    * \frac{\partial \xi^l}{\partial x^n} \frac{\partial^2 x^j}{\partial \xi^k
     487              :    * \partial \xi^l}, \f] where the inverse Jacobian is passed in as a function
     488              :    * argument.
     489              :    *
     490              :    * See Test_CoordinateMap.cpp for a different implementation. The current
     491              :    * implementation is chosen in production as it is faster when we have
     492              :    * the inverse Jacobian already, and in most cases we do.
     493              :    *
     494              :    * We use forward mode autodiff here as it is simpler to implement and
     495              :    * has better optimization than the reverse mode. Reverse mode in
     496              :    * the [Autodiff](https://github.com/autodiff/autodiff) library
     497              :    * has higher cost per propagation and does not support taping.
     498              :    * Also see this
     499              :    * [github issue](https://github.com/autodiff/autodiff/issues/332).
     500              :    *
     501              :    * \note Require SPECTRE_AUTODIFF=ON.
     502              :    */
     503            1 :   InverseHessian<double, dim, SourceFrame, TargetFrame> inv_hessian(
     504              :       tnsr::I<double, dim, SourceFrame> source_point,
     505              :       const ::InverseJacobian<double, dim, SourceFrame, TargetFrame>&
     506              :           inverse_jac,
     507              :       const double time = std::numeric_limits<double>::signaling_NaN(),
     508              :       const FunctionsOfTimeMap& functions_of_time = {}) const override {
     509              :     if constexpr ((Maps::supports_hessian && ...)) {
     510              :       return inv_hessian_impl(std::move(source_point), inverse_jac, time,
     511              :                               functions_of_time);
     512              :     } else {
     513              :       ERROR("At least one of the Maps does not support autodiff: "
     514              :             << CoordinateMap_detail::get_unsupported_autodiff_maps_error<
     515              :                    Maps...>());
     516              :     }
     517              :   }
     518            1 :   InverseHessian<DataVector, dim, SourceFrame, TargetFrame> inv_hessian(
     519              :       tnsr::I<DataVector, dim, SourceFrame> source_point,
     520              :       const ::InverseJacobian<DataVector, dim, SourceFrame, TargetFrame>&
     521              :           inverse_jac,
     522              :       const double time = std::numeric_limits<double>::signaling_NaN(),
     523              :       const FunctionsOfTimeMap& functions_of_time = {}) const override {
     524              :     if constexpr ((Maps::supports_hessian && ...)) {
     525              :       return inv_hessian_impl(std::move(source_point), inverse_jac, time,
     526              :                               functions_of_time);
     527              :     } else {
     528              :       ERROR("At least one of the Maps does not support autodiff: "
     529              :             << CoordinateMap_detail::get_unsupported_autodiff_maps_error<
     530              :                    Maps...>());
     531              :     }
     532              :   }
     533              :   /// @}
     534              : #endif  // SPECTRE_AUTODIFF
     535              : 
     536              :   /// @{
     537              :   /// Compute the Jacobian of the `Maps...` at the point(s) `source_point`
     538            1 :   Jacobian<double, dim, SourceFrame, TargetFrame> jacobian(
     539              :       tnsr::I<double, dim, SourceFrame> source_point,
     540              :       const double time = std::numeric_limits<double>::signaling_NaN(),
     541              :       const FunctionsOfTimeMap& functions_of_time = {}) const override {
     542              :     return jacobian_impl(std::move(source_point), time, functions_of_time);
     543              :   }
     544            1 :   Jacobian<DataVector, dim, SourceFrame, TargetFrame> jacobian(
     545              :       tnsr::I<DataVector, dim, SourceFrame> source_point,
     546              :       const double time = std::numeric_limits<double>::signaling_NaN(),
     547              :       const FunctionsOfTimeMap& functions_of_time = {}) const override {
     548              :     return jacobian_impl(std::move(source_point), time, functions_of_time);
     549              :   }
     550              :   /// @}
     551              : 
     552              :   /// @{
     553              :   /// Compute the mapped coordinates, frame velocity, Jacobian, and inverse
     554              :   /// Jacobian. The inverse Jacobian is computed by numerically inverting the
     555              :   /// Jacobian as this was measured to be quicker than computing it directly for
     556              :   /// more complex map concatenations.
     557            1 :   auto coords_frame_velocity_jacobians(
     558              :       tnsr::I<double, dim, SourceFrame> source_point,
     559              :       const double time = std::numeric_limits<double>::signaling_NaN(),
     560              :       const FunctionsOfTimeMap& functions_of_time = {}) const
     561              :       -> std::tuple<tnsr::I<double, dim, TargetFrame>,
     562              :                     InverseJacobian<double, dim, SourceFrame, TargetFrame>,
     563              :                     Jacobian<double, dim, SourceFrame, TargetFrame>,
     564              :                     tnsr::I<double, dim, TargetFrame>> override {
     565              :     return coords_frame_velocity_jacobians_impl(std::move(source_point), time,
     566              :                                                 functions_of_time);
     567              :   }
     568            1 :   auto coords_frame_velocity_jacobians(
     569              :       tnsr::I<DataVector, dim, SourceFrame> source_point,
     570              :       const double time = std::numeric_limits<double>::signaling_NaN(),
     571              :       const FunctionsOfTimeMap& functions_of_time = {}) const
     572              :       -> std::tuple<tnsr::I<DataVector, dim, TargetFrame>,
     573              :                     InverseJacobian<DataVector, dim, SourceFrame, TargetFrame>,
     574              :                     Jacobian<DataVector, dim, SourceFrame, TargetFrame>,
     575              :                     tnsr::I<DataVector, dim, TargetFrame>> override {
     576              :     return coords_frame_velocity_jacobians_impl(std::move(source_point), time,
     577              :                                                 functions_of_time);
     578              :   }
     579              :   /// @}
     580              : 
     581            0 :   WRAPPED_PUPable_decl_base_template(  // NOLINT
     582              :       SINGLE_ARG(CoordinateMapBase<SourceFrame, TargetFrame, dim>),
     583              :       CoordinateMap);
     584              : 
     585            0 :   explicit CoordinateMap(CkMigrateMessage* /*unused*/) {}
     586              : 
     587              :   // NOLINTNEXTLINE(google-runtime-references)
     588            0 :   void pup(PUP::er& p) override {
     589              :     size_t version = 0;
     590              :     p | version;
     591              :     // Remember to increment the version number when making changes to this
     592              :     // function. Retain support for unpacking data written by previous versions
     593              :     // whenever possible. See `Domain` docs for details.
     594              :     if (version >= 0) {
     595              :       CoordinateMapBase<SourceFrame, TargetFrame, dim>::pup(p);
     596              :       p | maps_;
     597              :     }
     598              : 
     599              :     // No need to pup this because it is uniquely determined by the maps
     600              :     if (p.isUnpacking()) {
     601              :       function_of_time_names_ = CoordinateMap_detail::initialize_names(
     602              :           maps_, std::make_index_sequence<sizeof...(Maps)>{});
     603              :     }
     604              :   }
     605              : 
     606              :  private:
     607            0 :   friend bool operator==(const CoordinateMap& lhs, const CoordinateMap& rhs) {
     608              :     return lhs.maps_ == rhs.maps_;
     609              :   }
     610              : 
     611              :   template <typename NewMap, typename LocalSourceFrame,
     612              :             typename LocalTargetFrame, typename... LocalMaps, size_t... Is>
     613              :   friend CoordinateMap<LocalSourceFrame, LocalTargetFrame, LocalMaps..., NewMap>
     614              :   // NOLINTNEXTLINE(readability-redundant-declaration,-warnings-as-errors)
     615            0 :   push_back_impl(
     616              :       CoordinateMap<LocalSourceFrame, LocalTargetFrame, LocalMaps...>&& old_map,
     617              :       NewMap new_map, std::index_sequence<Is...> /*meta*/);
     618              : 
     619              :   template <typename... NewMaps, typename LocalSourceFrame,
     620              :             typename LocalTargetFrame, typename... LocalMaps, size_t... Is,
     621              :             size_t... Js>
     622              :   friend CoordinateMap<LocalSourceFrame, LocalTargetFrame, LocalMaps...,
     623              :                        NewMaps...>
     624              :   // NOLINTNEXTLINE(readability-redundant-declaration,-warnings-as-errors)
     625            0 :   push_back_impl(
     626              :       CoordinateMap<LocalSourceFrame, LocalTargetFrame, LocalMaps...>&& old_map,
     627              :       CoordinateMap<LocalSourceFrame, LocalTargetFrame, NewMaps...> new_map,
     628              :       std::index_sequence<Is...> /*meta*/, std::index_sequence<Js...> /*meta*/);
     629              : 
     630              :   template <typename NewMap, typename LocalSourceFrame,
     631              :             typename LocalTargetFrame, typename... LocalMaps, size_t... Is>
     632              :   friend CoordinateMap<LocalSourceFrame, LocalTargetFrame, NewMap, LocalMaps...>
     633              :   // NOLINTNEXTLINE(readability-redundant-declaration,-warnings-as-errors)
     634            0 :   push_front_impl(
     635              :       CoordinateMap<LocalSourceFrame, LocalTargetFrame, LocalMaps...>&& old_map,
     636              :       NewMap new_map, std::index_sequence<Is...> /*meta*/);
     637              : 
     638              :   template <size_t... Is>
     639              :   std::unique_ptr<CoordinateMapBase<SourceFrame, Frame::Grid, dim>>
     640            0 :       get_to_grid_frame_impl(std::index_sequence<Is...> /*meta*/) const;
     641              : 
     642            0 :   bool is_equal_to(const CoordinateMapBase<SourceFrame, TargetFrame, dim>&
     643              :                        other) const override {
     644              :     const auto& cast_of_other = dynamic_cast<const CoordinateMap&>(other);
     645              :     return *this == cast_of_other;
     646              :   }
     647              : 
     648            0 :   void check_functions_of_time(
     649              :       const FunctionsOfTimeMap& functions_of_time) const;
     650              : 
     651              :   template <typename T, size_t... Is>
     652            0 :   tnsr::I<T, dim, TargetFrame> call_impl(
     653              :       tnsr::I<T, dim, SourceFrame>&& source_point, double time,
     654              :       const FunctionsOfTimeMap& functions_of_time,
     655              :       std::index_sequence<Is...> /*meta*/) const;
     656              : 
     657              :   template <typename T, size_t... Is>
     658            0 :   std::optional<tnsr::I<T, dim, SourceFrame>> inverse_impl(
     659              :       tnsr::I<T, dim, TargetFrame>&& target_point, double time,
     660              :       const FunctionsOfTimeMap& functions_of_time,
     661              :       std::index_sequence<Is...> /*meta*/) const;
     662              : 
     663              :   template <typename T>
     664            0 :   InverseJacobian<T, dim, SourceFrame, TargetFrame> inv_jacobian_impl(
     665              :       tnsr::I<T, dim, SourceFrame>&& source_point, double time,
     666              :       const FunctionsOfTimeMap& functions_of_time) const;
     667              : 
     668              :   template <typename T>
     669            0 :   InverseHessian<T, dim, SourceFrame, TargetFrame> inv_hessian_impl(
     670              :       tnsr::I<T, dim, SourceFrame>&& source_point,
     671              :       const ::InverseJacobian<T, dim, SourceFrame, TargetFrame>& inverse_jac,
     672              :       double time, const FunctionsOfTimeMap& functions_of_time) const;
     673              : 
     674              :   template <typename T>
     675            0 :   Jacobian<T, dim, SourceFrame, TargetFrame> jacobian_impl(
     676              :       tnsr::I<T, dim, SourceFrame>&& source_point, double time,
     677              :       const FunctionsOfTimeMap& functions_of_time) const;
     678              : 
     679              :   template <typename T>
     680              :   std::tuple<tnsr::I<T, dim, TargetFrame>,
     681              :              InverseJacobian<T, dim, SourceFrame, TargetFrame>,
     682              :              Jacobian<T, dim, SourceFrame, TargetFrame>,
     683              :              tnsr::I<T, dim, TargetFrame>>
     684            0 :   coords_frame_velocity_jacobians_impl(
     685              :       tnsr::I<T, dim, SourceFrame> source_point, double time,
     686              :       const FunctionsOfTimeMap& functions_of_time) const;
     687              : 
     688            0 :   std::tuple<Maps...> maps_;
     689            0 :   std::unordered_set<std::string> function_of_time_names_;
     690              : };
     691              : 
     692              : /// \ingroup ComputationalDomainGroup
     693              : /// \brief Creates a `CoordinateMap` of `maps...`
     694              : template <typename SourceFrame, typename TargetFrame, typename... Maps>
     695            1 : auto make_coordinate_map(Maps&&... maps)
     696              :     -> CoordinateMap<SourceFrame, TargetFrame, std::decay_t<Maps>...>;
     697              : 
     698              : /// \ingroup ComputationalDomainGroup
     699              : /// \brief Creates a `std::unique_ptr<CoordinateMapBase>` of `maps...`
     700              : template <typename SourceFrame, typename TargetFrame, typename... Maps>
     701            1 : auto make_coordinate_map_base(Maps&&... maps)
     702              :     -> std::unique_ptr<CoordinateMapBase<
     703              :         SourceFrame, TargetFrame,
     704              :         CoordinateMap<SourceFrame, TargetFrame, std::decay_t<Maps>...>::dim>>;
     705              : 
     706              : /// \ingroup ComputationalDomainGroup
     707              : /// \brief Creates a `std::vector<std::unique_ptr<CoordinateMapBase>>`
     708              : /// containing the result of `make_coordinate_map_base` applied to each
     709              : /// argument passed in.
     710              : template <typename SourceFrame, typename TargetFrame, typename Arg0,
     711              :           typename... Args>
     712            1 : auto make_vector_coordinate_map_base(Arg0&& arg_0, Args&&... remaining_args)
     713              :     -> std::vector<std::unique_ptr<
     714              :         CoordinateMapBase<SourceFrame, TargetFrame, std::decay_t<Arg0>::dim>>>;
     715              : 
     716              : /// \ingroup ComputationalDomainGroup
     717              : /// \brief Creates a `std::vector<std::unique_ptr<CoordinateMapBase>>`
     718              : /// containing the result of `make_coordinate_map_base` applied to each
     719              : /// element of the vector of maps composed with the rest of the arguments
     720              : /// passed in.
     721              : template <typename SourceFrame, typename TargetFrame, size_t Dim, typename Map,
     722              :           typename... Maps>
     723            1 : auto make_vector_coordinate_map_base(std::vector<Map> maps,
     724              :                                      const Maps&... remaining_maps)
     725              :     -> std::vector<
     726              :         std::unique_ptr<CoordinateMapBase<SourceFrame, TargetFrame, Dim>>>;
     727              : 
     728              : /// \ingroup ComputationalDomainGroup
     729              : /// \brief Creates a `CoordinateMap` by appending the new map to the end of the
     730              : /// old maps
     731              : template <typename SourceFrame, typename TargetFrame, typename... Maps,
     732              :           typename NewMap>
     733            1 : CoordinateMap<SourceFrame, TargetFrame, Maps..., NewMap> push_back(
     734              :     CoordinateMap<SourceFrame, TargetFrame, Maps...> old_map, NewMap new_map);
     735              : 
     736              : /// \ingroup ComputationalDomainGroup
     737              : /// \brief Creates a `CoordinateMap` by prepending the new map to the beginning
     738              : /// of the old maps
     739              : template <typename SourceFrame, typename TargetFrame, typename... Maps,
     740              :           typename NewMap>
     741            1 : CoordinateMap<SourceFrame, TargetFrame, NewMap, Maps...> push_front(
     742              :     CoordinateMap<SourceFrame, TargetFrame, Maps...> old_map, NewMap new_map);
     743              : 
     744              : /// \cond
     745              : template <typename SourceFrame, typename TargetFrame, typename... Maps>
     746              : PUP::able::PUP_ID
     747              :     CoordinateMap<SourceFrame, TargetFrame, Maps...>::my_PUP_ID =  // NOLINT
     748              :     0;
     749              : /// \endcond
     750              : }  // namespace domain
        

Generated by: LCOV version 2.0-1