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 <optional>
9 :
10 : #include "DataStructures/Tensor/TypeAliases.hpp"
11 : #include "Domain/CoordinateMaps/Distribution.hpp"
12 : #include "Utilities/Serialization/PupStlCpp17.hpp"
13 :
14 : /// \cond
15 : namespace PUP {
16 : class er;
17 : } // namespace PUP
18 : /// \endcond
19 :
20 : namespace domain::CoordinateMaps {
21 :
22 : /*!
23 : * \ingroup CoordinateMapsGroup
24 : * \brief Maps \f$\xi\f$ in the 1D interval \f$[A, B]\f$ to \f$x\f$ in the
25 : * interval \f$[a, b]\f$ according to a `domain::CoordinateMaps::Distribution`.
26 : *
27 : * \details The mapping takes a `domain::CoordinateMaps::Distribution` and
28 : * distributes the grid points accordingly.
29 : *
30 : * The formula for the mapping is, in case of a `Linear` distribution
31 : * \f{align}
32 : * x &= \frac{b}{B-A} (\xi-A) + \frac{a}{B-A} (B-\xi)\\
33 : * \xi &=\frac{B}{b-a} (x-a) + \frac{A}{b-a} (b-x)
34 : * \f}
35 : *
36 : * For every other distribution we use this linear mapping to map onto an
37 : * interval \f$[-1, 1]\f$, so we define:
38 : * \f{align}
39 : * f(\xi) &:= \frac{A+B-2\xi}{A-B} \in [-1, 1]\\
40 : * g(x) &:= \frac{a+b-2x}{a-b} \in [-1, 1]
41 : * \f}
42 : *
43 : * With this an `Equiangular` distribution is described by
44 : *
45 : * \f{align}
46 : * x &= \frac{a}{2} \left(1-\mathrm{tan}\left(\frac{\pi}{4}f(\xi)\right)\right)
47 : * + \frac{b}{2} \left(1+\mathrm{tan}\left(\frac{\pi}{4}f(\xi)\right)\right)\\
48 : * \xi &=
49 : * \frac{A}{2} \left(1-\frac{4}{\pi}\mathrm{arctan}\left(g(x)\right)\right) +
50 : * \frac{B}{2} \left(1+\frac{4}{\pi}\mathrm{arctan}\left(g(x)\right)\right)
51 : * \f}
52 : *
53 : * \note The equiangular distribution is intended to be used with the `Wedge`
54 : * map when equiangular coordinates are chosen for those maps. For more
55 : * information on this choice of coordinates, see the documentation for `Wedge`.
56 : *
57 : *
58 : * For both the `Logarithmic` and `Inverse` distribution, we first specify a
59 : * position for the singularity \f$c:=\f$`singularity_pos` outside the target
60 : * interval \f$[a, b]\f$.
61 : *
62 : * The `Logarithmic` distribution further requires the introduction of a
63 : * variable \f$\sigma\f$ dependent on whether \f$c\f$ is left (\f$ \sigma =
64 : * 1\f$) or right (\f$ \sigma = -1\f$) of the target interval. With this, the
65 : * `Logarithmic` distribution is described by
66 : *
67 : * \f{align}
68 : * x &= \sigma\, {\rm exp}\left(\frac{\ln(b-c)+\ln(a-c)}{2} + f(\xi)
69 : * \frac{\ln(b-c)-\ln(a-c)}{2}\right) + c\\
70 : * \xi &= \frac{B-A}{2}\frac{2\ln(\sigma [x-c]) - \ln(b-c) - \ln(a-c)}{\ln(b-c)
71 : * - \ln(a-c)} + \frac{B+A}{2} \f}
72 : *
73 : * and the `Inverse` distribution by
74 : *
75 : * \f{align}
76 : * x &= \frac{2(a-c)(b-c)}{a+b-2c+(a-b)f(\xi)} + c\\
77 : * \xi &= -\frac{A-B}{2(a-b)} \left(\frac{2(a-c)(b-c)}{x-c} - (a-c) -
78 : * (b-c)\right) + \frac{A+B}{2}
79 : * \f}
80 : */
81 1 : class Interval {
82 : public:
83 0 : static constexpr size_t dim = 1;
84 :
85 0 : Interval(double A, double B, double a, double b, Distribution distribution,
86 : std::optional<double> singularity_pos = std::nullopt);
87 :
88 0 : Interval() = default;
89 :
90 : template <typename T>
91 0 : std::array<T, 1> operator()(const std::array<T, 1>& source_coords) const;
92 :
93 0 : std::optional<std::array<double, 1>> inverse(
94 : const std::array<double, 1>& target_coords) const;
95 :
96 : template <typename T>
97 0 : tnsr::Ij<T, 1, Frame::NoFrame> jacobian(
98 : const std::array<T, 1>& source_coords) const;
99 :
100 : template <typename T>
101 0 : tnsr::Ij<T, 1, Frame::NoFrame> inv_jacobian(
102 : const std::array<T, 1>& source_coords) const;
103 :
104 : // NOLINTNEXTLINE(google-runtime-references)
105 0 : void pup(PUP::er& p);
106 :
107 0 : bool is_identity() const { return is_identity_; }
108 :
109 0 : static constexpr bool supports_hessian{true};
110 :
111 : private:
112 0 : friend bool operator==(const Interval& lhs, const Interval& rhs);
113 :
114 0 : double A_{std::numeric_limits<double>::signaling_NaN()};
115 0 : double B_{std::numeric_limits<double>::signaling_NaN()};
116 0 : double a_{std::numeric_limits<double>::signaling_NaN()};
117 0 : double b_{std::numeric_limits<double>::signaling_NaN()};
118 0 : Distribution distribution_{Distribution::Linear};
119 0 : std::optional<double> singularity_pos_{std::nullopt};
120 0 : bool is_identity_{false};
121 : };
122 :
123 0 : inline bool operator!=(const CoordinateMaps::Interval& lhs,
124 : const CoordinateMaps::Interval& rhs) {
125 : return not(lhs == rhs);
126 : }
127 :
128 : } // namespace domain::CoordinateMaps
|