Line data Source code
1 1 : // Distributed under the MIT License. 2 : // See LICENSE.txt for details. 3 : 4 : /// \file 5 : /// Defines the class Equiangular. 6 : 7 : #pragma once 8 : 9 : #include <array> 10 : #include <cmath> 11 : #include <cstddef> 12 : #include <optional> 13 : 14 : #include "DataStructures/Tensor/TypeAliases.hpp" 15 : 16 : /// \cond 17 : namespace PUP { 18 : class er; 19 : } // namespace PUP 20 : /// \endcond 21 : 22 : namespace domain { 23 : namespace CoordinateMaps { 24 : 25 : /*! 26 : * \ingroup CoordinateMapsGroup 27 : * \brief Non-linear map from \f$\xi \in [A, B]\rightarrow x \in [a, b]\f$. 28 : * 29 : * The formula for the mapping is: 30 : * \f{align} 31 : * x &= \frac{a}{2} \left(1-\mathrm{tan}\left( 32 : * \frac{\pi(2\xi-B-A)}{4(B-A)}\right)\right) + 33 : * \frac{b}{2} \left(1+\mathrm{tan}\left( 34 : * \frac{\pi(2\xi-B-A)}{4(B-A)}\right)\right)\\ 35 : * \xi &= \frac{A}{2} \left(1-\frac{4}{\pi}\mathrm{arctan}\left( 36 : * \frac{2x-a-b}{b-a}\right)\right)+ 37 : * \frac{B}{2} \left(1+\frac{4}{\pi}\mathrm{arctan}\left( 38 : * \frac{2x-a-b}{b-a}\right)\right) 39 : * \f} 40 : * 41 : * \note The intermediate step in which a tangent map is applied can be more 42 : * clearly understood if we define the coordinates: 43 : * \f{align} 44 : * \xi_{logical} &:= \frac{2\xi-B-A}{B-A} \in [-1, 1]\\ 45 : * \Xi &:= \mathrm{tan}\left(\frac{\pi\xi_{logical}}{4}\right) \in [-1, 1] 46 : * \f} 47 : * 48 : * This map is intended to be used with the `Wedge` map when equiangular 49 : * coordinates are chosen for those maps. For more information on this choice 50 : * of coordinates, see the documentation for `Wedge`. 51 : */ 52 1 : class Equiangular { 53 : public: 54 0 : static constexpr size_t dim = 1; 55 : 56 0 : Equiangular(double A, double B, double a, double b); 57 : 58 0 : Equiangular() = default; 59 0 : ~Equiangular() = default; 60 0 : Equiangular(const Equiangular&) = default; 61 0 : Equiangular(Equiangular&&) = default; 62 0 : Equiangular& operator=(const Equiangular&) = default; 63 0 : Equiangular& operator=(Equiangular&&) = default; 64 : 65 : template <typename T> 66 0 : std::array<T, 1> operator()(const std::array<T, 1>& source_coords) const; 67 : 68 : /// The inverse function is only callable with doubles because the inverse 69 : /// might fail if called for a point out of range, and it is unclear 70 : /// what should happen if the inverse were to succeed for some points in a 71 : /// DataVector but fail for other points. 72 1 : std::optional<std::array<double, 1>> inverse( 73 : const std::array<double, 1>& target_coords) const; 74 : 75 : template <typename T> 76 0 : tnsr::Ij<T, 1, Frame::NoFrame> jacobian( 77 : const std::array<T, 1>& source_coords) const; 78 : 79 : template <typename T> 80 0 : tnsr::Ij<T, 1, Frame::NoFrame> inv_jacobian( 81 : const std::array<T, 1>& source_coords) const; 82 : 83 : // NOLINTNEXTLINE(google-runtime-references) 84 0 : void pup(PUP::er& p); 85 : 86 0 : static bool is_identity() { return false; } 87 : 88 0 : static constexpr bool supports_hessian{true}; 89 : 90 : private: 91 0 : friend bool operator==(const Equiangular& lhs, const Equiangular& rhs); 92 : 93 0 : double A_{-1.0}; 94 0 : double B_{1.0}; 95 0 : double a_{-1.0}; 96 0 : double b_{1.0}; 97 0 : double length_of_domain_over_m_pi_4_{(B_ - A_) / M_PI_4}; // 4(B-A)/\pi 98 0 : double length_of_range_{2.0}; // b-a 99 0 : double m_pi_4_over_length_of_domain_{M_PI_4 / (B_ - A_)}; 100 0 : double one_over_length_of_range_{0.5}; 101 : // The jacobian for the affine map with the same parameters. 102 0 : double linear_jacobian_times_m_pi_4_{length_of_range_ / 103 : length_of_domain_over_m_pi_4_}; 104 : // The inverse jacobian for the affine map with the same parameters. 105 0 : double linear_inverse_jacobian_over_m_pi_4_{length_of_domain_over_m_pi_4_ / 106 : length_of_range_}; 107 : }; 108 : 109 0 : inline bool operator!=(const CoordinateMaps::Equiangular& lhs, 110 : const CoordinateMaps::Equiangular& rhs) { 111 : return not(lhs == rhs); 112 : } 113 : 114 : } // namespace CoordinateMaps 115 : } // namespace domain