SpinWeighted.hpp
1 // Distributed under the MIT License.
2 // See LICENSE.txt for details
3 
4 #pragma once
5 
6 #include "DataStructures/ComplexDataVector.hpp"
8 #include "Utilities/Requires.hpp"
10 
11 // @{
12 /*!
13  * \ingroup DataStructuresGroup
14  * \brief Make a spin-weighted type `T` with spin-weight `Spin`. Mathematical
15  * operators are restricted to addition, subtraction, multiplication and
16  * division, with spin-weights checked for validity.
17  *
18  * \details For a spin-weighted object, we limit operations to those valid for a
19  * pair of spin-weighted quantities - i.e. addition only makes sense when the
20  * two summands possess the same spin weight, and multiplication (or division)
21  * result in a summed (or subtracted) spin weight.
22  */
23 template <typename T, int Spin, bool is_vector = is_derived_of_vector_impl_v<T>>
24 struct SpinWeighted;
25 
26 template <typename T, int Spin>
27 struct SpinWeighted<T, Spin, false> {
28  using value_type = T;
29  constexpr static int spin = Spin;
30 
31  SpinWeighted() = default;
32  SpinWeighted(const SpinWeighted&) noexcept = default;
33  SpinWeighted(SpinWeighted&&) noexcept = default;
34  SpinWeighted& operator=(const SpinWeighted&) noexcept = default;
35  SpinWeighted& operator=(SpinWeighted&&) noexcept = default;
36  ~SpinWeighted() noexcept = default;
37 
38  // clang-tidy asks that these be marked explicit, but we actually do not want
39  // them explicit for use in the math operations below.
40  template <typename Rhs>
41  SpinWeighted(const SpinWeighted<Rhs, Spin>& rhs) noexcept // NOLINT
42  : data_{rhs.data()} {}
43 
44  template <typename Rhs>
45  SpinWeighted(SpinWeighted<Rhs, Spin>&& rhs) noexcept // NOLINT
46  : data_{std::move(rhs.data())} {}
47 
48  SpinWeighted(const T& rhs) noexcept : data_{rhs} {} // NOLINT
49  SpinWeighted(T&& rhs) noexcept : data_{std::move(rhs)} {} // NOLINT
50 
51  template <typename Rhs>
52  SpinWeighted& operator=(const SpinWeighted<Rhs, Spin>& rhs) noexcept {
53  data_ = rhs.data();
54  return *this;
55  }
56 
57  template <typename Rhs>
58  SpinWeighted& operator=(SpinWeighted<Rhs, Spin>&& rhs) noexcept {
59  data_ = std::move(rhs.data());
60  return *this;
61  }
62 
63  SpinWeighted& operator=(const T& rhs) noexcept {
64  data_ = rhs;
65  return *this;
66  }
67  SpinWeighted& operator=(T&& rhs) noexcept {
68  data_ = std::move(rhs);
69  return *this;
70  }
71 
72  T& data() noexcept { return data_; }
73  const T& data() const noexcept { return data_; }
74 
75  private:
76  T data_;
77 };
78 
79 template <typename T, int Spin>
80 struct SpinWeighted<T, Spin, true> {
81  using value_type = T;
82  constexpr static int spin = Spin;
83 
84  void set_data_ref(gsl::not_null<T*> rhs) noexcept { data_.set_data_ref(rhs); }
85 
86  template <typename ValueType>
87  void set_data_ref(ValueType* const start, const size_t set_size) noexcept {
88  data_.set_data_ref(start, set_size);
89  }
90 
91  SpinWeighted() = default;
92  SpinWeighted(const SpinWeighted&) noexcept = default;
93  SpinWeighted(SpinWeighted&&) noexcept = default;
94  SpinWeighted& operator=(const SpinWeighted&) noexcept = default;
95  SpinWeighted& operator=(SpinWeighted&&) noexcept = default;
96  ~SpinWeighted() noexcept = default;
97 
98  // clang-tidy asks that these be marked explicit, but we actually do not want
99  // them explicit for use in the math operations below.
100  template <typename Rhs>
101  SpinWeighted(const SpinWeighted<Rhs, Spin>& rhs) noexcept // NOLINT
102  : data_{rhs.data()} {}
103 
104  template <typename Rhs>
105  SpinWeighted(SpinWeighted<Rhs, Spin>&& rhs) noexcept // NOLINT
106  : data_{std::move(rhs.data())} {}
107 
108  SpinWeighted(const T& rhs) noexcept : data_{rhs} {} // NOLINT
109  SpinWeighted(T&& rhs) noexcept : data_{std::move(rhs)} {} // NOLINT
110 
111  template <typename Rhs>
112  SpinWeighted& operator=(const SpinWeighted<Rhs, Spin>& rhs) noexcept {
113  data_ = rhs.data();
114  return *this;
115  }
116 
117  template <typename Rhs>
118  SpinWeighted& operator=(SpinWeighted<Rhs, Spin>&& rhs) noexcept {
119  data_ = std::move(rhs.data());
120  return *this;
121  }
122 
123  SpinWeighted& operator=(const T& rhs) noexcept {
124  data_ = rhs;
125  return *this;
126  }
127  SpinWeighted& operator=(T&& rhs) noexcept {
128  data_ = std::move(rhs);
129  return *this;
130  }
131 
132  T& data() noexcept { return data_; }
133  const T& data() const noexcept { return data_; }
134 
135  private:
136  T data_;
137 };
138 // @}
139 
140 // @{
141 /// \ingroup TypeTraitsGroup
142 /// \ingroup DataStructuresGroup
143 /// This is a `std::true_type` if the provided type is a `SpinWeighted` of any
144 /// type and spin, otherwise is a `std::false_type`.
145 template <typename T>
147 
148 template <typename T, int S>
150 // @}
151 
152 template <typename T>
153 constexpr bool is_any_spin_weighted_v = is_any_spin_weighted<T>::value;
154 
155 // @{
156 /// \ingroup TypeTraitsGroup
157 /// \ingroup DataStructuresGroup
158 /// This is a `std::true_type` if the provided type `T` is a `SpinWeighted` of
159 /// `InternalType` and any spin, otherwise is a `std::false_type`.
160 template <typename InternalType, typename T>
162 
163 template <typename InternalType, int S>
164 struct is_spin_weighted_of<InternalType, SpinWeighted<InternalType, S>>
165  : std::true_type {};
166 // @}
167 
168 template <typename InternalType, typename T>
169 constexpr bool is_spin_weighted_of_v =
171 
172 // @{
173 /// \ingroup TypeTraitsGroup
174 /// \ingroup DataStructuresGroup
175 /// This is a `std::true_type` if the provided type `T1` is a `SpinWeighted` and
176 /// `T2` is a `SpinWeighted`, and both have the same internal type, but any
177 /// combination of spin weights.
178 template <typename T1, typename T2>
180 
181 template <typename T, int Spin1, int Spin2>
183  SpinWeighted<T, Spin2>> : std::true_type {
184 };
185 // @}
186 
187 template <typename T1, typename T2>
188 constexpr bool is_spin_weighted_of_same_type_v =
190 
191 // {@
192 /// \brief Add two spin-weighted quantities if the types are compatible and
193 /// spins are the same. Un-weighted quantities are assumed to be spin 0.
194 // These overloads are designed to allow SpinWeighted to wrap Blaze expression
195 // templates to ensure efficient math operations, necessitating the
196 // `decltype(declval<T>() ...` syntax
197 template <typename T1, typename T2, int Spin>
199  SpinWeighted<decltype(std::declval<T1>() + std::declval<T2>()), Spin>
200  operator+(const SpinWeighted<T1, Spin>& lhs,
201  const SpinWeighted<T2, Spin>& rhs) noexcept {
202  return {lhs.data() + rhs.data()};
203 }
204 template <typename T>
206  SpinWeighted<decltype(std::declval<T>() + std::declval<T>()), 0>
207  operator+(const SpinWeighted<T, 0>& lhs, const T& rhs) noexcept {
208  return {lhs.data() + rhs};
209 }
210 template <typename T>
212  decltype(std::declval<T>() + std::declval<get_vector_element_type_t<T>>()),
213  0>
214 operator+(const SpinWeighted<T, 0>& lhs,
215  const get_vector_element_type_t<T>& rhs) noexcept {
216  return {lhs.data() + rhs};
217 }
218 template <typename T>
220  SpinWeighted<decltype(std::declval<T>() + std::declval<T>()), 0>
221  operator+(const T& lhs, const SpinWeighted<T, 0>& rhs) noexcept {
222  return {lhs + rhs.data()};
223 }
224 template <typename T>
226  decltype(std::declval<get_vector_element_type_t<T>>() + std::declval<T>()),
227  0>
228 operator+(const get_vector_element_type_t<T>& lhs,
229  const SpinWeighted<T, 0>& rhs) noexcept {
230  return {lhs + rhs.data()};
231 }
232 // @}
233 
234 // @{
235 /// \brief Subtract two spin-weighted quantities if the types are compatible and
236 /// spins are the same. Un-weighted quantities are assumed to be spin 0.
237 // These overloads are designed to allow SpinWeighted to wrap Blaze expression
238 // templates to ensure efficient math operations, necessitating the
239 // `decltype(declval<T>() ...` syntax
240 template <typename T1, typename T2, int Spin>
242  SpinWeighted<decltype(std::declval<T1>() - std::declval<T2>()), Spin>
243  operator-(const SpinWeighted<T1, Spin>& lhs,
244  const SpinWeighted<T2, Spin>& rhs) noexcept {
245  return {lhs.data() - rhs.data()};
246 }
247 template <typename T>
249  SpinWeighted<decltype(std::declval<T>() - std::declval<T>()), 0>
250  operator-(const SpinWeighted<T, 0>& lhs, const T& rhs) noexcept {
251  return {lhs.data() - rhs};
252 }
253 template <typename T>
255  decltype(std::declval<T>() - std::declval<get_vector_element_type_t<T>>()),
256  0>
257 operator-(const SpinWeighted<T, 0>& lhs,
258  const get_vector_element_type_t<T>& rhs) noexcept {
259  return {lhs.data() - rhs};
260 }
261 template <typename T>
263  SpinWeighted<decltype(std::declval<T>() - std::declval<T>()), 0>
264  operator-(const T& lhs, const SpinWeighted<T, 0>& rhs) noexcept {
265  return {lhs - rhs.data()};
266 }
267 template <typename T>
269  decltype(std::declval<get_vector_element_type_t<T>>() - std::declval<T>()),
270  0>
271 operator-(const get_vector_element_type_t<T>& lhs,
272  const SpinWeighted<T, 0>& rhs) noexcept {
273  return {lhs - rhs.data()};
274 }
275 // @}
276 
277 // @{
278 /// \brief Multiply two spin-weighted quantities if the types are compatible and
279 /// add the spins. Un-weighted quantities are assumed to be spin 0.
280 // These overloads are designed to allow SpinWeighted to wrap Blaze expression
281 // templates to ensure efficient math operations, necessitating the
282 // `decltype(declval<T>() ...` syntax
283 template <typename T1, typename T2, int Spin1, int Spin2>
285  decltype(std::declval<T1>() * std::declval<T2>()), Spin1 + Spin2>
287  const SpinWeighted<T2, Spin2>& rhs) noexcept {
288  return {lhs.data() * rhs.data()};
289 }
290 template <typename T, int Spin>
292  SpinWeighted<decltype(std::declval<T>() * std::declval<T>()), Spin>
293  operator*(const SpinWeighted<T, Spin>& lhs, const T& rhs) noexcept {
294  return {lhs.data() * rhs};
295 }
296 template <typename T, int Spin>
298  decltype(std::declval<T>() * std::declval<get_vector_element_type_t<T>>()),
299  Spin>
301  const get_vector_element_type_t<T>& rhs) noexcept {
302  return {lhs.data() * rhs};
303 }
304 template <typename T, int Spin>
306  SpinWeighted<decltype(std::declval<T>() * std::declval<T>()), Spin>
307  operator*(const T& lhs, const SpinWeighted<T, Spin>& rhs) noexcept {
308  return {lhs * rhs.data()};
309 }
310 template <typename T, int Spin>
312  decltype(std::declval<get_vector_element_type_t<T>>() * std::declval<T>()),
313  Spin>
314 operator*(const get_vector_element_type_t<T>& lhs,
315  const SpinWeighted<T, Spin>& rhs) noexcept {
316  return {lhs * rhs.data()};
317 }
318 // @}
319 
320 // @{
321 /// \brief Divide two spin-weighted quantities if the types are compatible and
322 /// subtract the spins. Un-weighted quantities are assumed to be spin 0.
323 // These overloads are designed to allow SpinWeighted to wrap Blaze expression
324 // templates to ensure efficient math operations, necessitating the
325 // `decltype(declval<T>() ...` syntax
326 template <typename T1, typename T2, int Spin1, int Spin2>
328  decltype(std::declval<T1>() / std::declval<T2>()), Spin1 - Spin2>
329 operator/(const SpinWeighted<T1, Spin1>& lhs,
330  const SpinWeighted<T2, Spin2>& rhs) noexcept {
331  return {lhs.data() / rhs.data()};
332 }
333 template <typename T, int Spin>
335  SpinWeighted<decltype(std::declval<T>() / std::declval<T>()), Spin>
336  operator/(const SpinWeighted<T, Spin>& lhs, const T& rhs) noexcept {
337  return {lhs.data() / rhs};
338 }
339 template <typename T, int Spin>
341  decltype(std::declval<T>() / std::declval<get_vector_element_type_t<T>>()),
342  Spin>
343 operator/(const SpinWeighted<T, Spin>& lhs,
344  const get_vector_element_type_t<T>& rhs) noexcept {
345  return {lhs.data() / rhs};
346 }
347 template <typename T, int Spin>
349  SpinWeighted<decltype(std::declval<T>() / std::declval<T>()), -Spin>
350  operator/(const T& lhs, const SpinWeighted<T, Spin>& rhs) noexcept {
351  return {lhs / rhs.data()};
352 }
353 template <typename T, int Spin>
355  decltype(std::declval<get_vector_element_type_t<T>>() / std::declval<T>()),
356  -Spin>
357 operator/(const get_vector_element_type_t<T>& lhs,
358  const SpinWeighted<T, Spin>& rhs) noexcept {
359  return {lhs / rhs.data()};
360 }
361 // @}
362 
363 template <typename T, int Spin>
365 conj(const SpinWeighted<T, Spin> value) noexcept {
366  return {conj(value.data())};
367 }
368 
369 // @{
370 /// \brief Test equivalence of spin-weighted quantities if the types are
371 /// compatible and spins are the same. Un-weighted quantities are assumed to
372 /// be spin 0.
373 template <typename T1, typename T2, int Spin>
374 SPECTRE_ALWAYS_INLINE bool operator==(
375  const SpinWeighted<T1, Spin>& lhs,
376  const SpinWeighted<T2, Spin>& rhs) noexcept {
377  return lhs.data() == rhs.data();
378 }
379 template <typename T>
380 SPECTRE_ALWAYS_INLINE bool operator==(const SpinWeighted<T, 0>& lhs,
381  const T& rhs) noexcept {
382  return lhs.data() == rhs;
383 }
384 template <typename T>
385 SPECTRE_ALWAYS_INLINE bool operator==(const T& lhs,
386  const SpinWeighted<T, 0>& rhs) noexcept {
387  return lhs == rhs.data();
388 }
389 // @}
390 
391 // @{
392 /// \brief Test inequivalence of spin-weighted quantities if the types are
393 /// compatible and spins are the same. Un-weighted quantities are assumed to be
394 /// spin 0.
395 template <typename T1, typename T2, int Spin>
396 SPECTRE_ALWAYS_INLINE bool operator!=(
397  const SpinWeighted<T1, Spin>& lhs,
398  const SpinWeighted<T2, Spin>& rhs) noexcept {
399  return not(lhs == rhs);
400 }
401 template <typename T>
402 SPECTRE_ALWAYS_INLINE bool operator!=(const SpinWeighted<T, 0>& lhs,
403  const T& rhs) noexcept {
404  return not(lhs == rhs);
405 }
406 template <typename T>
407 SPECTRE_ALWAYS_INLINE bool operator!=(const T& lhs,
408  const SpinWeighted<T, 0>& rhs) noexcept {
409  return not(lhs == rhs);
410 }
411 // @}
412 
413 namespace MakeWithValueImpls {
414 template <int Spin1, int Spin2, typename SpinWeightedType1,
415  typename SpinWeightedType2>
416 struct MakeWithValueImpl<SpinWeighted<SpinWeightedType1, Spin1>,
417  SpinWeighted<SpinWeightedType2, Spin2>> {
418  template <typename ValueType>
421  const ValueType value) noexcept {
423  make_with_value<SpinWeightedType1>(input.data(), value)};
424  }
425 };
426 
427 template <int Spin, typename SpinWeightedType, typename MakeWithType>
428 struct MakeWithValueImpl<SpinWeighted<SpinWeightedType, Spin>, MakeWithType> {
429  template <typename ValueType>
431  const MakeWithType& input, const ValueType value) noexcept {
433  make_with_value<SpinWeightedType>(input, value)};
434  }
435 };
436 } // namespace MakeWithValueImpls
Implementations of make_with_value.
Definition: DenseVector.hpp:61
This is a std::true_type if the provided type is a SpinWeighted of any type and spin, otherwise is a std::false_type.
Definition: SpinWeighted.hpp:146
This is a std::true_type if the provided type T1 is a SpinWeighted and T2 is a SpinWeighted, and both have the same internal type, but any combination of spin weights.
Definition: SpinWeighted.hpp:179
auto operator*(const TensorExpression< T1, X, Symm1, IndexList1, Args1 > &t1, const TensorExpression< T2, X, Symm2, IndexList2, Args2 > &t2)
Definition: Product.hpp:89
Defines the type alias Requires.
constexpr auto apply(F &&f, const DataBox< BoxTags > &box, Args &&... args)
Apply the function f with argument Tags TagsList from DataBox box
Definition: DataBox.hpp:1595
Definition: MakeWithValue.hpp:20
Make a spin-weighted type T with spin-weight Spin. Mathematical operators are restricted to addition...
Definition: SpinWeighted.hpp:24
#define SPECTRE_ALWAYS_INLINE
Always inline a function. Only use this if you benchmarked the code.
Definition: ForceInline.hpp:20
This is a std::true_type if the provided type T is a SpinWeighted of InternalType and any spin...
Definition: SpinWeighted.hpp:161
Defines macro to always inline a function.
Defines type traits, some of which are future STL type_traits header.
Require a pointer to not be a nullptr
Definition: ConservativeFromPrimitive.hpp:12