Line data Source code
1 0 : // Distributed under the MIT License.
2 : // See LICENSE.txt for details
3 :
4 : #pragma once
5 :
6 : #include "DataStructures/ComplexDataVector.hpp"
7 : #include "Utilities/ForceInline.hpp"
8 : #include "Utilities/Requires.hpp"
9 : #include "Utilities/TypeTraits.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 1 : struct SpinWeighted;
25 :
26 : template <typename T, int Spin>
27 0 : struct SpinWeighted<T, Spin, false> {
28 0 : using value_type = T;
29 0 : constexpr static int spin = Spin;
30 :
31 0 : SpinWeighted() = default;
32 0 : SpinWeighted(const SpinWeighted&) = default;
33 0 : SpinWeighted(SpinWeighted&&) = default;
34 0 : SpinWeighted& operator=(const SpinWeighted&) = default;
35 0 : SpinWeighted& operator=(SpinWeighted&&) = default;
36 0 : ~SpinWeighted() = 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 0 : SpinWeighted(const SpinWeighted<Rhs, Spin>& rhs) // NOLINT
42 : : data_{rhs.data()} {}
43 :
44 : template <typename Rhs>
45 0 : SpinWeighted(SpinWeighted<Rhs, Spin>&& rhs) // NOLINT
46 : : data_{std::move(rhs.data())} {}
47 :
48 0 : SpinWeighted(const T& rhs) : data_{rhs} {} // NOLINT
49 0 : SpinWeighted(T&& rhs) : data_{std::move(rhs)} {} // NOLINT
50 0 : explicit SpinWeighted(const size_t size) : data_{size} {}
51 : template <typename U>
52 0 : SpinWeighted(const size_t size, const U& val) : data_{size, val} {}
53 :
54 : template <typename Rhs>
55 0 : SpinWeighted& operator=(const SpinWeighted<Rhs, Spin>& rhs) {
56 : data_ = rhs.data();
57 : return *this;
58 : }
59 :
60 : template <typename Rhs>
61 0 : SpinWeighted& operator=(SpinWeighted<Rhs, Spin>&& rhs) {
62 : data_ = std::move(rhs.data());
63 : return *this;
64 : }
65 :
66 0 : SpinWeighted& operator=(const T& rhs) {
67 : data_ = rhs;
68 : return *this;
69 : }
70 0 : SpinWeighted& operator=(T&& rhs) {
71 : data_ = std::move(rhs);
72 : return *this;
73 : }
74 :
75 : template <typename Rhs>
76 0 : auto& operator+=(const SpinWeighted<Rhs, Spin>& rhs) {
77 : data_ += rhs.data();
78 : return *this;
79 : }
80 :
81 0 : auto& operator+=(const T& rhs) {
82 : data_ += rhs;
83 : return *this;
84 : }
85 :
86 : template <typename Rhs>
87 0 : auto& operator-=(const SpinWeighted<Rhs, Spin>& rhs) {
88 : data_ -= rhs.data();
89 : return *this;
90 : }
91 :
92 0 : auto& operator-=(const T& rhs) {
93 : data_ -= rhs;
94 : return *this;
95 : }
96 :
97 0 : T& data() { return data_; }
98 0 : const T& data() const { return data_; }
99 :
100 0 : size_t size() const { return data_.size(); }
101 :
102 : /// Serialization for Charm++
103 : // NOLINTNEXTLINE(google-runtime-references)
104 1 : void pup(PUP::er& p);
105 :
106 : private:
107 0 : T data_;
108 : };
109 :
110 : template <typename T, int Spin>
111 0 : struct SpinWeighted<T, Spin, true> {
112 0 : using value_type = T;
113 0 : constexpr static int spin = Spin;
114 :
115 0 : void set_data_ref(const gsl::not_null<T*> rhs) { data_.set_data_ref(rhs); }
116 :
117 : // needed for invoking the check in `Variables.hpp` that ensures that
118 : // default-constructed `Variables` are never used.
119 0 : void set_data_ref(const std::nullptr_t null, const size_t size) {
120 : data_.set_data_ref(null, size);
121 : }
122 :
123 0 : void set_data_ref(const gsl::not_null<SpinWeighted<T, spin>*> rhs) {
124 : data_.set_data_ref(make_not_null(&(rhs->data_)));
125 : }
126 :
127 : template <typename ValueType>
128 0 : void set_data_ref(ValueType* const start, const size_t set_size) {
129 : data_.set_data_ref(start, set_size);
130 : }
131 :
132 0 : void destructive_resize(const size_t new_size) {
133 : data_.destructive_resize(new_size);
134 : }
135 :
136 0 : SpinWeighted() = default;
137 0 : SpinWeighted(const SpinWeighted&) = default;
138 0 : SpinWeighted(SpinWeighted&&) = default;
139 0 : SpinWeighted& operator=(const SpinWeighted&) = default;
140 0 : SpinWeighted& operator=(SpinWeighted&&) = default;
141 0 : ~SpinWeighted() = default;
142 :
143 : // clang-tidy asks that these be marked explicit, but we actually do not want
144 : // them explicit for use in the math operations below.
145 : template <typename Rhs>
146 0 : SpinWeighted(const SpinWeighted<Rhs, Spin>& rhs) // NOLINT
147 : : data_{rhs.data()} {}
148 :
149 : template <typename Rhs>
150 0 : SpinWeighted(SpinWeighted<Rhs, Spin>&& rhs) // NOLINT
151 : : data_{std::move(rhs.data())} {}
152 :
153 0 : SpinWeighted(const T& rhs) : data_{rhs} {} // NOLINT
154 0 : SpinWeighted(T&& rhs) : data_{std::move(rhs)} {} // NOLINT
155 0 : explicit SpinWeighted(const size_t size) : data_{size} {}
156 : template <typename U>
157 0 : SpinWeighted(const size_t size, const U& val) : data_{size, val} {}
158 :
159 : template <typename Rhs>
160 0 : SpinWeighted& operator=(const SpinWeighted<Rhs, Spin>& rhs) {
161 : data_ = rhs.data();
162 : return *this;
163 : }
164 :
165 : template <typename Rhs>
166 0 : SpinWeighted& operator=(SpinWeighted<Rhs, Spin>&& rhs) {
167 : data_ = std::move(rhs.data());
168 : return *this;
169 : }
170 :
171 0 : SpinWeighted& operator=(const T& rhs) {
172 : data_ = rhs;
173 : return *this;
174 : }
175 0 : SpinWeighted& operator=(T&& rhs) {
176 : data_ = std::move(rhs);
177 : return *this;
178 : }
179 :
180 : template <typename Rhs>
181 0 : auto& operator+=(const SpinWeighted<Rhs, Spin>& rhs) {
182 : data_ += rhs.data();
183 : return *this;
184 : }
185 :
186 0 : auto& operator+=(const T& rhs) {
187 : data_ += rhs;
188 : return *this;
189 : }
190 :
191 : template <typename Rhs>
192 0 : auto& operator-=(const SpinWeighted<Rhs, Spin>& rhs) {
193 : data_ -= rhs.data();
194 : return *this;
195 : }
196 :
197 0 : auto& operator-=(const T& rhs) {
198 : data_ -= rhs;
199 : return *this;
200 : }
201 :
202 0 : T& data() { return data_; }
203 0 : const T& data() const { return data_; }
204 :
205 0 : size_t size() const { return data_.size(); }
206 :
207 : /// Serialization for Charm++
208 : // NOLINTNEXTLINE(google-runtime-references)
209 1 : void pup(PUP::er& p);
210 :
211 : private:
212 0 : T data_;
213 : };
214 : /// @}
215 :
216 : template <typename T, int Spin>
217 : void SpinWeighted<T, Spin, true>::pup(PUP::er& p) {
218 : p | data_;
219 : }
220 :
221 : template <typename T, int Spin>
222 : void SpinWeighted<T, Spin, false>::pup(PUP::er& p) {
223 : p | data_;
224 : }
225 :
226 : /// @{
227 : /// \ingroup TypeTraitsGroup
228 : /// \ingroup DataStructuresGroup
229 : /// This is a `std::true_type` if the provided type is a `SpinWeighted` of any
230 : /// type and spin, otherwise is a `std::false_type`.
231 : template <typename T>
232 1 : struct is_any_spin_weighted : std::false_type {};
233 :
234 : template <typename T, int S>
235 0 : struct is_any_spin_weighted<SpinWeighted<T, S>> : std::true_type {};
236 : /// @}
237 :
238 : template <typename T>
239 0 : constexpr bool is_any_spin_weighted_v = is_any_spin_weighted<T>::value;
240 :
241 : /// @{
242 : /// \ingroup TypeTraitsGroup
243 : /// \ingroup DataStructuresGroup
244 : /// This is a `std::true_type` if the provided type `T` is a `SpinWeighted` of
245 : /// `InternalType` and any spin, otherwise is a `std::false_type`.
246 : template <typename InternalType, typename T>
247 1 : struct is_spin_weighted_of : std::false_type {};
248 :
249 : template <typename InternalType, int S>
250 0 : struct is_spin_weighted_of<InternalType, SpinWeighted<InternalType, S>>
251 : : std::true_type {};
252 : /// @}
253 :
254 : template <typename InternalType, typename T>
255 0 : constexpr bool is_spin_weighted_of_v =
256 : is_spin_weighted_of<InternalType, T>::value;
257 :
258 : /// @{
259 : /// \ingroup TypeTraitsGroup
260 : /// \ingroup DataStructuresGroup
261 : /// This is a `std::true_type` if the provided type `T1` is a `SpinWeighted` and
262 : /// `T2` is a `SpinWeighted`, and both have the same internal type, but any
263 : /// combination of spin weights.
264 : template <typename T1, typename T2>
265 1 : struct is_spin_weighted_of_same_type : std::false_type {};
266 :
267 : template <typename T, int Spin1, int Spin2>
268 0 : struct is_spin_weighted_of_same_type<SpinWeighted<T, Spin1>,
269 : SpinWeighted<T, Spin2>> : std::true_type {
270 : };
271 : /// @}
272 :
273 : template <typename T1, typename T2>
274 0 : constexpr bool is_spin_weighted_of_same_type_v =
275 : is_spin_weighted_of_same_type<T1, T2>::value;
276 :
277 : /// @{
278 : /// \brief Add two spin-weighted quantities if the types are compatible and
279 : /// spins are the same. 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 Spin>
284 : SPECTRE_ALWAYS_INLINE
285 : SpinWeighted<decltype(std::declval<T1>() + std::declval<T2>()), Spin>
286 1 : operator+(const SpinWeighted<T1, Spin>& lhs,
287 : const SpinWeighted<T2, Spin>& rhs) {
288 : return {lhs.data() + rhs.data()};
289 : }
290 : template <typename T>
291 : SPECTRE_ALWAYS_INLINE
292 : SpinWeighted<decltype(std::declval<T>() + std::declval<T>()), 0>
293 1 : operator+(const SpinWeighted<T, 0>& lhs, const T& rhs) {
294 : return {lhs.data() + rhs};
295 : }
296 : template <typename T>
297 : SPECTRE_ALWAYS_INLINE SpinWeighted<
298 : decltype(std::declval<T>() + std::declval<get_vector_element_type_t<T>>()),
299 : 0>
300 1 : operator+(const SpinWeighted<T, 0>& lhs,
301 : const get_vector_element_type_t<T>& rhs) {
302 : return {lhs.data() + rhs};
303 : }
304 : template <typename T>
305 : SPECTRE_ALWAYS_INLINE
306 : SpinWeighted<decltype(std::declval<T>() + std::declval<T>()), 0>
307 1 : operator+(const T& lhs, const SpinWeighted<T, 0>& rhs) {
308 : return {lhs + rhs.data()};
309 : }
310 : template <typename T>
311 : SPECTRE_ALWAYS_INLINE SpinWeighted<
312 : decltype(std::declval<get_vector_element_type_t<T>>() + std::declval<T>()),
313 : 0>
314 1 : operator+(const get_vector_element_type_t<T>& lhs,
315 : const SpinWeighted<T, 0>& rhs) {
316 : return {lhs + rhs.data()};
317 : }
318 : /// @}
319 :
320 : /// @{
321 : /// \brief Subtract two spin-weighted quantities if the types are compatible and
322 : /// spins are the same. 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 Spin>
327 : SPECTRE_ALWAYS_INLINE
328 : SpinWeighted<decltype(std::declval<T1>() - std::declval<T2>()), Spin>
329 1 : operator-(const SpinWeighted<T1, Spin>& lhs,
330 : const SpinWeighted<T2, Spin>& rhs) {
331 : return {lhs.data() - rhs.data()};
332 : }
333 : template <typename T>
334 : SPECTRE_ALWAYS_INLINE
335 : SpinWeighted<decltype(std::declval<T>() - std::declval<T>()), 0>
336 1 : operator-(const SpinWeighted<T, 0>& lhs, const T& rhs) {
337 : return {lhs.data() - rhs};
338 : }
339 : template <typename T>
340 : SPECTRE_ALWAYS_INLINE SpinWeighted<
341 : decltype(std::declval<T>() - std::declval<get_vector_element_type_t<T>>()),
342 : 0>
343 1 : operator-(const SpinWeighted<T, 0>& lhs,
344 : const get_vector_element_type_t<T>& rhs) {
345 : return {lhs.data() - rhs};
346 : }
347 : template <typename T>
348 : SPECTRE_ALWAYS_INLINE
349 : SpinWeighted<decltype(std::declval<T>() - std::declval<T>()), 0>
350 1 : operator-(const T& lhs, const SpinWeighted<T, 0>& rhs) {
351 : return {lhs - rhs.data()};
352 : }
353 : template <typename T>
354 : SPECTRE_ALWAYS_INLINE SpinWeighted<
355 : decltype(std::declval<get_vector_element_type_t<T>>() - std::declval<T>()),
356 : 0>
357 1 : operator-(const get_vector_element_type_t<T>& lhs,
358 : const SpinWeighted<T, 0>& rhs) {
359 : return {lhs - rhs.data()};
360 : }
361 : /// @}
362 :
363 : /// Negation operator preserves spin
364 : template <typename T, int Spin>
365 : SPECTRE_ALWAYS_INLINE SpinWeighted<decltype(-std::declval<T>()), Spin>
366 1 : operator-(const SpinWeighted<T, Spin>& operand) {
367 : return {-operand.data()};
368 : }
369 :
370 : /// Unary `+` operator preserves spin
371 : template <typename T, int Spin>
372 : SPECTRE_ALWAYS_INLINE SpinWeighted<decltype(+std::declval<T>()), Spin>
373 1 : operator+(const SpinWeighted<T, Spin>& operand) {
374 : return {+operand.data()};
375 : }
376 :
377 : /// @{
378 : /// \brief Multiply two spin-weighted quantities if the types are compatible and
379 : /// add the spins. Un-weighted quantities are assumed to be spin 0.
380 : // These overloads are designed to allow SpinWeighted to wrap Blaze expression
381 : // templates to ensure efficient math operations, necessitating the
382 : // `decltype(declval<T>() ...` syntax
383 : template <typename T1, typename T2, int Spin1, int Spin2>
384 : SPECTRE_ALWAYS_INLINE SpinWeighted<
385 : decltype(std::declval<T1>() * std::declval<T2>()), Spin1 + Spin2>
386 1 : operator*(const SpinWeighted<T1, Spin1>& lhs,
387 : const SpinWeighted<T2, Spin2>& rhs) {
388 : return {lhs.data() * rhs.data()};
389 : }
390 : template <typename T, int Spin>
391 : SPECTRE_ALWAYS_INLINE
392 : SpinWeighted<decltype(std::declval<T>() * std::declval<T>()), Spin>
393 1 : operator*(const SpinWeighted<T, Spin>& lhs, const T& rhs) {
394 : return {lhs.data() * rhs};
395 : }
396 : template <typename T, int Spin>
397 : SPECTRE_ALWAYS_INLINE SpinWeighted<
398 : decltype(std::declval<T>() * std::declval<get_vector_element_type_t<T>>()),
399 : Spin>
400 1 : operator*(const SpinWeighted<T, Spin>& lhs,
401 : const get_vector_element_type_t<T>& rhs) {
402 : return {lhs.data() * rhs};
403 : }
404 : template <typename T, int Spin>
405 : SPECTRE_ALWAYS_INLINE
406 : SpinWeighted<decltype(std::declval<T>() * std::declval<T>()), Spin>
407 1 : operator*(const T& lhs, const SpinWeighted<T, Spin>& rhs) {
408 : return {lhs * rhs.data()};
409 : }
410 : template <typename T, int Spin>
411 : SPECTRE_ALWAYS_INLINE SpinWeighted<
412 : decltype(std::declval<get_vector_element_type_t<T>>() * std::declval<T>()),
413 : Spin>
414 1 : operator*(const get_vector_element_type_t<T>& lhs,
415 : const SpinWeighted<T, Spin>& rhs) {
416 : return {lhs * rhs.data()};
417 : }
418 : /// @}
419 :
420 : /// @{
421 : /// \brief Divide two spin-weighted quantities if the types are compatible and
422 : /// subtract the spins. Un-weighted quantities are assumed to be spin 0.
423 : // These overloads are designed to allow SpinWeighted to wrap Blaze expression
424 : // templates to ensure efficient math operations, necessitating the
425 : // `decltype(declval<T>() ...` syntax
426 : template <typename T1, typename T2, int Spin1, int Spin2>
427 : SPECTRE_ALWAYS_INLINE SpinWeighted<
428 : decltype(std::declval<T1>() / std::declval<T2>()), Spin1 - Spin2>
429 1 : operator/(const SpinWeighted<T1, Spin1>& lhs,
430 : const SpinWeighted<T2, Spin2>& rhs) {
431 : return {lhs.data() / rhs.data()};
432 : }
433 : template <typename T, int Spin>
434 : SPECTRE_ALWAYS_INLINE
435 : SpinWeighted<decltype(std::declval<T>() / std::declval<T>()), Spin>
436 1 : operator/(const SpinWeighted<T, Spin>& lhs, const T& rhs) {
437 : return {lhs.data() / rhs};
438 : }
439 : template <typename T, int Spin>
440 : SPECTRE_ALWAYS_INLINE SpinWeighted<
441 : decltype(std::declval<T>() / std::declval<get_vector_element_type_t<T>>()),
442 : Spin>
443 1 : operator/(const SpinWeighted<T, Spin>& lhs,
444 : const get_vector_element_type_t<T>& rhs) {
445 : return {lhs.data() / rhs};
446 : }
447 : template <typename T, int Spin>
448 : SPECTRE_ALWAYS_INLINE
449 : SpinWeighted<decltype(std::declval<T>() / std::declval<T>()), -Spin>
450 1 : operator/(const T& lhs, const SpinWeighted<T, Spin>& rhs) {
451 : return {lhs / rhs.data()};
452 : }
453 : template <typename T, int Spin>
454 : SPECTRE_ALWAYS_INLINE SpinWeighted<
455 : decltype(std::declval<get_vector_element_type_t<T>>() / std::declval<T>()),
456 : -Spin>
457 1 : operator/(const get_vector_element_type_t<T>& lhs,
458 : const SpinWeighted<T, Spin>& rhs) {
459 : return {lhs / rhs.data()};
460 : }
461 : /// @}
462 :
463 : /// conjugate the spin-weighted quantity, inverting the spin
464 : template <typename T, int Spin>
465 : SPECTRE_ALWAYS_INLINE SpinWeighted<decltype(conj(std::declval<T>())), -Spin>
466 1 : conj(const SpinWeighted<T, Spin>& value) {
467 : return {conj(value.data())};
468 : }
469 :
470 : /// Take the exponential of the spin-weighted quantity; only valid for
471 : /// spin-weight = 0
472 : template <typename T>
473 1 : SPECTRE_ALWAYS_INLINE SpinWeighted<decltype(exp(std::declval<T>())), 0> exp(
474 : const SpinWeighted<T, 0>& value) {
475 : return {exp(value.data())};
476 : }
477 :
478 : /// Take the square-root of the spin-weighted quantity; only valid for
479 : /// spin-weight = 0
480 : template <typename T, int Spin>
481 1 : SPECTRE_ALWAYS_INLINE SpinWeighted<decltype(sqrt(std::declval<T>())), 0> sqrt(
482 : const SpinWeighted<T, Spin>& value) {
483 : return {sqrt(value.data())};
484 : }
485 :
486 : /// @{
487 : /// \brief Test equivalence of spin-weighted quantities if the types are
488 : /// compatible and spins are the same. Un-weighted quantities are assumed to
489 : /// be spin 0.
490 : template <typename T1, typename T2, int Spin>
491 1 : SPECTRE_ALWAYS_INLINE bool operator==(const SpinWeighted<T1, Spin>& lhs,
492 : const SpinWeighted<T2, Spin>& rhs) {
493 : return lhs.data() == rhs.data();
494 : }
495 : template <typename T>
496 1 : SPECTRE_ALWAYS_INLINE bool operator==(const SpinWeighted<T, 0>& lhs,
497 : const T& rhs) {
498 : return lhs.data() == rhs;
499 : }
500 : template <typename T>
501 1 : SPECTRE_ALWAYS_INLINE bool operator==(const T& lhs,
502 : const SpinWeighted<T, 0>& rhs) {
503 : return lhs == rhs.data();
504 : }
505 : /// @}
506 :
507 : /// @{
508 : /// \brief Test inequivalence of spin-weighted quantities if the types are
509 : /// compatible and spins are the same. Un-weighted quantities are assumed to be
510 : /// spin 0.
511 : template <typename T1, typename T2, int Spin>
512 1 : SPECTRE_ALWAYS_INLINE bool operator!=(const SpinWeighted<T1, Spin>& lhs,
513 : const SpinWeighted<T2, Spin>& rhs) {
514 : return not(lhs == rhs);
515 : }
516 : template <typename T>
517 1 : SPECTRE_ALWAYS_INLINE bool operator!=(const SpinWeighted<T, 0>& lhs,
518 : const T& rhs) {
519 : return not(lhs == rhs);
520 : }
521 : template <typename T>
522 1 : SPECTRE_ALWAYS_INLINE bool operator!=(const T& lhs,
523 : const SpinWeighted<T, 0>& rhs) {
524 : return not(lhs == rhs);
525 : }
526 : /// @}
527 :
528 : /// \ingroup DataStructuresGroup
529 : /// Make the input `view` a `const` view of the const data `spin_weighted`, at
530 : /// offset `offset` and length `extent`.
531 : ///
532 : /// \warning This DOES modify the (const) input `view`. The reason `view` is
533 : /// taken by const pointer is to try to insist that the object to be a `const`
534 : /// view is actually const. Of course, there are ways of subverting this
535 : /// intended functionality and editing the data pointed into by `view` after
536 : /// this function is called; doing so is highly discouraged and results in
537 : /// undefined behavior.
538 : template <typename SpinWeightedType,
539 : Requires<is_any_spin_weighted_v<SpinWeightedType> and
540 : is_derived_of_vector_impl_v<
541 : typename SpinWeightedType::value_type>> = nullptr>
542 1 : void make_const_view(const gsl::not_null<const SpinWeightedType*> view,
543 : const SpinWeightedType& spin_weighted, const size_t offset,
544 : const size_t extent) {
545 : const_cast<SpinWeightedType*>(view.get()) // NOLINT
546 : ->set_data_ref(const_cast< // NOLINT
547 : typename SpinWeightedType::value_type::value_type*>(
548 : spin_weighted.data().data()) + // NOLINT
549 : offset,
550 : extent);
551 : }
552 :
553 : /// Stream operator simply forwards
554 : template <typename T, int Spin>
555 1 : std::ostream& operator<<(std::ostream& os, const SpinWeighted<T, Spin>& d) {
556 : return os << d.data();
557 : }
558 :
559 : namespace MakeWithValueImpls {
560 : template <int Spin, typename SpinWeightedType>
561 0 : struct NumberOfPoints<SpinWeighted<SpinWeightedType, Spin>> {
562 : static SPECTRE_ALWAYS_INLINE size_t
563 0 : apply(const SpinWeighted<SpinWeightedType, Spin>& input) {
564 : return number_of_points(input.data());
565 : }
566 : };
567 :
568 : template <int Spin, typename SpinWeightedType>
569 0 : struct MakeWithSize<SpinWeighted<SpinWeightedType, Spin>> {
570 : template <typename ValueType>
571 0 : static SPECTRE_ALWAYS_INLINE SpinWeighted<SpinWeightedType, Spin> apply(
572 : const size_t size, const ValueType value) {
573 : return SpinWeighted<SpinWeightedType, Spin>{
574 : make_with_value<SpinWeightedType>(size, value)};
575 : }
576 : };
577 : } // namespace MakeWithValueImpls
|