Line data Source code
1 0 : // Distributed under the MIT License.
2 : // See LICENSE.txt for details.
3 :
4 : #pragma once
5 :
6 : #include <complex>
7 : #include <cstddef>
8 :
9 : #include "DataStructures/ComplexDataVector.hpp"
10 : #include "DataStructures/ComplexDiagonalModalOperator.hpp"
11 : #include "DataStructures/ComplexModalVector.hpp"
12 : #include "DataStructures/DataVector.hpp"
13 : #include "DataStructures/SpinWeighted.hpp"
14 : #include "DataStructures/Tags/TempTensor.hpp"
15 : #include "DataStructures/TempBuffer.hpp"
16 : #include "NumericalAlgorithms/SpinWeightedSphericalHarmonics/ComplexDataView.hpp"
17 : #include "NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCoefficients.hpp"
18 : #include "NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp"
19 : #include "NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshSettings.hpp"
20 : #include "NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTags.hpp"
21 : #include "NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTransform.hpp"
22 : #include "Utilities/ForceInline.hpp"
23 : #include "Utilities/Gsl.hpp"
24 : #include "Utilities/TMPL.hpp"
25 :
26 : namespace Spectral {
27 : namespace Swsh {
28 : namespace detail {
29 :
30 : // Factors that appear in the modal representation of spin-weighted angular
31 : // derivatives, needed for compute_coefficients_of_derivative
32 : template <typename DerivativeKind>
33 : SPECTRE_ALWAYS_INLINE std::complex<double> derivative_factor(int l, int s);
34 :
35 : template <>
36 : SPECTRE_ALWAYS_INLINE std::complex<double> derivative_factor<Tags::Eth>(
37 : const int l, const int s) {
38 : return sqrt(static_cast<std::complex<double>>((l - s) * (l + s + 1)));
39 : }
40 :
41 : template <>
42 : SPECTRE_ALWAYS_INLINE std::complex<double> derivative_factor<Tags::Ethbar>(
43 : const int l, const int s) {
44 : return -sqrt(static_cast<std::complex<double>>((l + s) * (l - s + 1)));
45 : }
46 :
47 : template <>
48 : SPECTRE_ALWAYS_INLINE std::complex<double> derivative_factor<Tags::EthEth>(
49 : const int l, const int s) {
50 : return sqrt(static_cast<std::complex<double>>((l - s - 1) * (l + s + 2) *
51 : (l - s) * (l + s + 1)));
52 : }
53 :
54 : template <>
55 : SPECTRE_ALWAYS_INLINE std::complex<double>
56 : derivative_factor<Tags::EthbarEthbar>(const int l, const int s) {
57 : return sqrt(static_cast<std::complex<double>>((l + s - 1) * (l - s + 2) *
58 : (l + s) * (l - s + 1)));
59 : }
60 :
61 : template <>
62 : SPECTRE_ALWAYS_INLINE std::complex<double> derivative_factor<Tags::EthbarEth>(
63 : const int l, const int s) {
64 : return static_cast<std::complex<double>>(-(l - s) * (l + s + 1));
65 : }
66 :
67 : template <>
68 : SPECTRE_ALWAYS_INLINE std::complex<double> derivative_factor<Tags::EthEthbar>(
69 : const int l, const int s) {
70 : return static_cast<std::complex<double>>(-(l + s) * (l - s + 1));
71 : }
72 :
73 : template <>
74 : SPECTRE_ALWAYS_INLINE std::complex<double> derivative_factor<Tags::InverseEth>(
75 : const int l, const int s) {
76 : return (l - s + 1) * (l + s) == 0
77 : ? 0.0
78 : : 1.0 / sqrt(static_cast<std::complex<double>>((l - s + 1) *
79 : (l + s)));
80 : }
81 :
82 : template <>
83 : SPECTRE_ALWAYS_INLINE std::complex<double>
84 : derivative_factor<Tags::InverseEthbar>(const int l, const int s) {
85 : return (l + s + 1) * (l - s) == 0
86 : ? 0.0
87 : : 1.0 / -sqrt(static_cast<std::complex<double>>((l + s + 1) *
88 : (l - s)));
89 : }
90 :
91 : // For a particular derivative represented by `DerivativeKind` and input spin
92 : // `Spin`, multiplies the derivative spectral factors with
93 : // `pre_derivative_modes`, returning by pointer via parameter `derivative_modes`
94 : template <typename DerivativeKind, int Spin>
95 : void compute_coefficients_of_derivative(
96 : gsl::not_null<
97 : SpinWeighted<ComplexModalVector,
98 : Spin + Tags::derivative_spin_weight<DerivativeKind>>*>
99 : derivative_modes,
100 : gsl::not_null<SpinWeighted<ComplexModalVector, Spin>*> pre_derivative_modes,
101 : size_t l_max, size_t number_of_radial_points);
102 :
103 : // Helper function for dealing with the parameter packs in the utilities which
104 : // evaluate several spin-weighted derivatives at once. The `apply` function of
105 : // this struct locates the appropriate mode buffer in the input tuple
106 : // `pre_derivative_mode_tuple`, and calls `compute_coefficients_of_derivative`,
107 : // deriving the coefficients for `DerivativeTag` and returning by pointer.
108 : template <typename DerivativeTag, typename PreDerivativeTagList>
109 : struct dispatch_to_compute_coefficients_of_derivative {
110 : template <int Spin, typename... ModalTypes>
111 : static void apply(const gsl::not_null<SpinWeighted<ComplexModalVector, Spin>*>
112 : derivative_modes,
113 : const std::tuple<ModalTypes...>& pre_derivative_mode_tuple,
114 : const size_t l_max, const size_t number_of_radial_points) {
115 : compute_coefficients_of_derivative<typename DerivativeTag::derivative_kind>(
116 : derivative_modes,
117 : get<tmpl::index_of<PreDerivativeTagList,
118 : typename DerivativeTag::derivative_of>::value>(
119 : pre_derivative_mode_tuple),
120 : l_max, number_of_radial_points);
121 : }
122 : };
123 :
124 : // Helper function for dealing with the parameter packs in the utilities which
125 : // evaluate several spin-weighted derivatives at once. The `apply` function of
126 : // this struct locates the like-spin set of paired modes and nodes, and calls
127 : // the member function of `SwshTransform` or `InverseSwshTransform` to perform
128 : // the spin-weighted transform. This function is a friend of both
129 : // `SwshTransform` and `InverseSwshTransform` to gain access to the private
130 : // `apply_to_vectors`.
131 : // The calling code will pass as tuples a superset of the quantities to be
132 : // transformed, in the same order as the tags provided to `TagList`. The
133 : // `modal_tuple` must be the storage destinations of the transforms in the
134 : // same order as the `nodal_tuple`. The set of nodal tags to be transformed are
135 : // the `TransformTags...` determined by the `SwshTransform` or
136 : // `InverseSwshTransform`. The reason for using `tuple`s rather than
137 : // `TaggedTuple`s or similar is to pass around spin-weighted vectors directly,
138 : // rather than `Scalar`s, which allows for more condensed forwarding code.
139 : template <typename Transform, typename TagList>
140 : struct dispatch_to_transform;
141 :
142 : template <ComplexRepresentation Representation, typename... TransformTags,
143 : typename TagList>
144 : struct dispatch_to_transform<
145 : SwshTransform<tmpl::list<TransformTags...>, Representation>, TagList> {
146 : template <typename... ModalTypes, typename... NodalTypes>
147 : static void apply(const gsl::not_null<std::tuple<ModalTypes...>*> modal_tuple,
148 : const std::tuple<NodalTypes...>& nodal_tuple,
149 : const size_t l_max, const size_t number_of_radial_points) {
150 : SwshTransform<tmpl::list<TransformTags...>, Representation>::
151 : apply_to_vectors(
152 : get<tmpl::index_of<TagList, TransformTags>::value>(*modal_tuple)...,
153 : get<tmpl::index_of<TagList, TransformTags>::value>(nodal_tuple)...,
154 : l_max, number_of_radial_points);
155 : }
156 : };
157 :
158 : template <ComplexRepresentation Representation, typename... TransformTags,
159 : typename TagList>
160 : struct dispatch_to_transform<
161 : InverseSwshTransform<tmpl::list<TransformTags...>, Representation>,
162 : TagList> {
163 : template <typename... NodalTypes, typename... ModalTypes>
164 : static void apply(const gsl::not_null<std::tuple<NodalTypes...>*> nodal_tuple,
165 : const std::tuple<ModalTypes...>& modal_tuple,
166 : const size_t l_max, const size_t number_of_radial_points) {
167 : InverseSwshTransform<tmpl::list<TransformTags...>, Representation>::
168 : apply_to_vectors(
169 : get<tmpl::index_of<TagList, TransformTags>::value>(*nodal_tuple)...,
170 : *get<tmpl::index_of<TagList, TransformTags>::value>(modal_tuple)...,
171 : l_max, number_of_radial_points);
172 : }
173 : };
174 :
175 : // template 'implementation' for the DataBox mutate-compatible interface to
176 : // spin-weighted derivative evaluation. This impl version is needed to have easy
177 : // access to the `UniqueDifferentiatedFromTagList` as a parameter pack
178 : template <typename DerivativeTagList, typename UniqueDifferentiatedFromTagList,
179 : ComplexRepresentation Representation>
180 : struct AngularDerivativesImpl;
181 :
182 : template <typename... DerivativeTags, typename... UniqueDifferentiatedFromTags,
183 : ComplexRepresentation Representation>
184 : struct AngularDerivativesImpl<tmpl::list<DerivativeTags...>,
185 : tmpl::list<UniqueDifferentiatedFromTags...>,
186 : Representation> {
187 : using return_tags =
188 : tmpl::list<DerivativeTags..., Tags::SwshTransform<DerivativeTags>...,
189 : Tags::SwshTransform<UniqueDifferentiatedFromTags>...>;
190 : using argument_tags =
191 : tmpl::list<UniqueDifferentiatedFromTags..., Tags::LMaxBase,
192 : Tags::NumberOfRadialPointsBase>;
193 :
194 : static void apply(
195 : const gsl::not_null<typename DerivativeTags::type*>... derivative_scalars,
196 : const gsl::not_null<typename Tags::SwshTransform<
197 : DerivativeTags>::type*>... transform_of_derivative_scalars,
198 : const gsl::not_null<typename Tags::SwshTransform<
199 : UniqueDifferentiatedFromTags>::type*>... transform_of_input_scalars,
200 : const typename UniqueDifferentiatedFromTags::type&... input_scalars,
201 : const size_t l_max, const size_t number_of_radial_points) {
202 : apply_to_vectors(make_not_null(&get(*transform_of_derivative_scalars))...,
203 : make_not_null(&get(*transform_of_input_scalars))...,
204 : make_not_null(&get(*derivative_scalars))...,
205 : get(input_scalars)..., l_max, number_of_radial_points);
206 : }
207 :
208 : template <ComplexRepresentation FriendRepresentation,
209 : typename... DerivativeKinds, typename... ArgumentTypes,
210 : size_t... Is>
211 : // NOLINTNEXTLINE(readability-redundant-declaration)
212 : friend void angular_derivatives_impl(const std::tuple<ArgumentTypes...>&,
213 : size_t, size_t,
214 : std::index_sequence<Is...>,
215 : tmpl::list<DerivativeKinds...>,
216 : std::bool_constant<true>);
217 :
218 : private:
219 : // note inputs reordered to accommodate the alternative tag-free functions
220 : // which call into this function.
221 : static void apply_to_vectors(
222 : const gsl::not_null<typename Tags::SwshTransform<
223 : DerivativeTags>::type::type*>... transform_of_derivatives,
224 : const gsl::not_null<typename Tags::SwshTransform<
225 : UniqueDifferentiatedFromTags>::type::type*>... transform_of_inputs,
226 : const gsl::not_null<typename DerivativeTags::type::type*>... derivatives,
227 : const typename UniqueDifferentiatedFromTags::type::type&... inputs,
228 : const size_t l_max, const size_t number_of_radial_points) {
229 : // perform the forward transform on the minimal set of input nodal
230 : // quantities to obtain all of the requested derivatives
231 : using ForwardTransformList =
232 : make_transform_list_from_derivative_tags<Representation,
233 : tmpl::list<DerivativeTags...>>;
234 :
235 : tmpl::for_each<ForwardTransformList>(
236 : [&number_of_radial_points, &l_max, &inputs...,
237 : &transform_of_inputs...](auto transform_v) {
238 : using transform = typename decltype(transform_v)::type;
239 : auto input_transforms = std::make_tuple(transform_of_inputs...);
240 : dispatch_to_transform<transform,
241 : tmpl::list<UniqueDifferentiatedFromTags...>>::
242 : apply(make_not_null(&input_transforms),
243 : std::forward_as_tuple(inputs...), l_max,
244 : number_of_radial_points);
245 : });
246 :
247 : // apply the modal derivative factors and place the result in the
248 : // `transform_of_derivatives`
249 : EXPAND_PACK_LEFT_TO_RIGHT(
250 : dispatch_to_compute_coefficients_of_derivative<
251 : DerivativeTags, tmpl::list<UniqueDifferentiatedFromTags...>>::
252 : apply(transform_of_derivatives,
253 : std::make_tuple(transform_of_inputs...), l_max,
254 : number_of_radial_points));
255 :
256 : // perform the inverse transform on the derivative results, placing the
257 : // result in the nodal `derivatives` passed by pointer.
258 : using InverseTransformList =
259 : make_inverse_transform_list<Representation,
260 : tmpl::list<DerivativeTags...>>;
261 :
262 : tmpl::for_each<InverseTransformList>([&number_of_radial_points, &l_max,
263 : &derivatives...,
264 : &transform_of_derivatives...](
265 : auto transform_v) {
266 : using transform = typename decltype(transform_v)::type;
267 : auto derivative_tuple = std::make_tuple(derivatives...);
268 : dispatch_to_transform<transform, tmpl::list<DerivativeTags...>>::apply(
269 : make_not_null(&derivative_tuple),
270 : std::make_tuple(transform_of_derivatives...), l_max,
271 : number_of_radial_points);
272 : });
273 : }
274 : };
275 :
276 : // metafunction for determining the tags needed to evaluate the derivative tags
277 : // in `DerivativeTagList`, removing all duplicate tags.
278 : template <typename DerivativeTagList>
279 : struct unique_derived_from_list;
280 :
281 : template <typename... DerivativeTags>
282 : struct unique_derived_from_list<tmpl::list<DerivativeTags...>> {
283 : using type = tmpl::remove_duplicates<
284 : tmpl::list<typename DerivativeTags::derivative_of...>>;
285 : };
286 : } // namespace detail
287 :
288 : /*!
289 : * \ingroup SpectralGroup
290 : * \brief A \ref DataBoxGroup mutate-compatible computational struct for
291 : * computing a set of spin-weighted spherical harmonic derivatives by
292 : * grouping and batch-computing spin-weighted spherical harmonic transforms.
293 : *
294 : * \details A derivative is evaluated for each tag in `DerivativeTagList`. All
295 : * entries in `DerivativeTagList` must be the tag
296 : * `Spectral::Swsh::Tags::Derivative<Tag, DerivativeKind>` prefixing the `Tag`
297 : * to be differentiated, and indicating the spin-weighted derivative
298 : * `DerivativeKind` to be taken. A \ref DataBoxGroup on which this struct is
299 : * invoked must contain:
300 : * - each of the tags in `DerivativeTagList` (the results of the computation)
301 : * - each of the tags `Tag` prefixed by `Spectral::Swsh::Tags::Derivative` in
302 : * `DerivativeTagList` (the inputs of the computation).
303 : * - each of the tags `Spectral::Swsh::Tags::SwshTransform<DerivativeTag>` for
304 : * `DerivativeTag`in `DerivativeTagList` (the buffers for the derivative
305 : * applied to the modes)
306 : * - each of the tags `Spectral::Swsh::Tags::SwshTransform<Tag>` for `Tag`
307 : * prefixed by any `DerivativeTag` in `DerivativeTagList` (the buffers for the
308 : * transforms of the input data).
309 : *
310 : * This function optimizes the derivative taking process by clustering like
311 : * spins of tags, forward-transforming each spin cluster together, applying the
312 : * factor for the derivative to each modal vector, re-clustering according to
313 : * the new spin weights (the derivatives alter the spin weights), and finally
314 : * inverse-transforming in clusters.
315 : */
316 : template <typename DerivativeTagList, ComplexRepresentation Representation =
317 : ComplexRepresentation::Interleaved>
318 1 : using AngularDerivatives = detail::AngularDerivativesImpl<
319 : DerivativeTagList,
320 : typename detail::unique_derived_from_list<DerivativeTagList>::type,
321 : Representation>;
322 :
323 : /*!
324 : * \ingroup SpectralGroup
325 : * \brief Produces a `SpinWeighted<ComplexModalVector, Spin>` of the appropriate
326 : * size to be used as a modal buffer for `Spectral::Swsh::AngularDerivatives` or
327 : * `Spectral::Swsh::angular_derivatives`.
328 : *
329 : * \details The `Spectral::Swsh::angular_derivatives` and
330 : * `Spectral::Swsh::AngularDerivatives` interfaces require that calling code
331 : * provides a buffer for the intermediate transform results, to ensure that
332 : * callers are aware of the allocations and can suitably reuse buffers if
333 : * possible. This utility eases the creation of those buffers.
334 : */
335 : template <int Spin>
336 1 : auto swsh_buffer(const size_t l_max, const size_t number_of_radial_points) {
337 : return SpinWeighted<ComplexModalVector, Spin>{
338 : size_of_libsharp_coefficient_vector(l_max) * number_of_radial_points};
339 : }
340 :
341 : namespace detail {
342 : // template 'implementation' for the `angular_derivatives` function below which
343 : // evaluates an arbitrary number of derivatives, and places them in the set of
344 : // nodal containers passed by pointer.
345 : template <ComplexRepresentation Representation, typename... DerivativeKinds,
346 : typename... ArgumentTypes, size_t... Is>
347 : void angular_derivatives_impl(
348 : const std::tuple<ArgumentTypes...>& argument_tuple, const size_t l_max,
349 : const size_t number_of_radial_points, std::index_sequence<Is...> /*meta*/,
350 : tmpl::list<DerivativeKinds...> /*meta*/,
351 : std::bool_constant<true> /*buffers_included_in_arguments*/) {
352 : AngularDerivatives<
353 : tmpl::list<Tags::Derivative<
354 : ::Tags::SpinWeighted<
355 : ::Tags::TempScalar<Is, ComplexDataVector>,
356 : std::integral_constant<
357 : int, std::decay_t<decltype(get<Is + 3 * sizeof...(Is)>(
358 : argument_tuple))>::spin>>,
359 : DerivativeKinds>...>,
360 : Representation>::apply_to_vectors(get<Is>(argument_tuple)...,
361 : get<Is + sizeof...(Is)>(
362 : argument_tuple)...,
363 : get<Is + 2 * sizeof...(Is)>(
364 : argument_tuple)...,
365 : get<Is + 3 * sizeof...(Is)>(
366 : argument_tuple)...,
367 : l_max, number_of_radial_points);
368 : }
369 :
370 : template <ComplexRepresentation Representation, typename... DerivativeKinds,
371 : typename... ArgumentTypes, size_t... Is>
372 : void angular_derivatives_impl(
373 : const std::tuple<ArgumentTypes...>& argument_tuple, const size_t l_max,
374 : const size_t number_of_radial_points,
375 : std::index_sequence<Is...> index_sequence,
376 : tmpl::list<DerivativeKinds...> derivative_kinds,
377 : std::bool_constant<false> /*buffers_included_in_arguments*/) {
378 : auto derivative_buffer_tuple = std::make_tuple(
379 : swsh_buffer<std::decay_t<decltype(*get<Is>(argument_tuple))>::spin>(
380 : l_max, number_of_radial_points)...);
381 : auto input_buffer_tuple = std::make_tuple(
382 : swsh_buffer<std::decay_t<decltype(get<Is + sizeof...(Is)>(
383 : argument_tuple))>::spin>(l_max, number_of_radial_points)...);
384 : angular_derivatives_impl<Representation>(
385 : std::forward_as_tuple(make_not_null(&get<Is>(derivative_buffer_tuple))...,
386 : make_not_null(&get<Is>(input_buffer_tuple))...,
387 : get<Is>(argument_tuple)...,
388 : get<Is + sizeof...(Is)>(argument_tuple)...),
389 : l_max, number_of_radial_points, index_sequence, derivative_kinds,
390 : std::bool_constant<true>{});
391 : }
392 : } // namespace detail
393 :
394 : /*!
395 : * \ingroup SpectralGroup
396 : * \brief Evaluate all of the spin-weighted derivatives in `DerivKindList` on
397 : * input `SpinWeighted<ComplexDataVector, Spin>` collocation data, returning by
398 : * pointer.
399 : *
400 : * \details This function provides two interfaces, one in which the caller
401 : * provides the intermediate coefficient buffers needed during the computation
402 : * of the derivatives, and one in which those buffers are temporarily allocated
403 : * during the derivative function calls.
404 : *
405 : * For the interface in which the caller does not provide buffers, the arguments
406 : * must take the following structure (enforced by internal function calls):
407 : *
408 : * - `size_t l_max` : angular resolution for the spherical representation
409 : * - `size_t number_of_radial_points` : radial resolution (number of consecutive
410 : * blocks to evaluate derivatives, for each input vector )
411 : * - for each `DerivKind` in `DerivKindList`, a
412 : * `gsl::not_null<SpinWeighted<ComplexDataVector, Spin +
413 : * Tags::derivative_spin_weight<DerivKind>>>` : the output of the derivative
414 : * evaluation
415 : * - for each `DerivKind` in `DerivKindList`, a `const
416 : * SpinWeighted<ComplexDataVector, Spin>&` (where the `Spin` for these arguments
417 : * matches the corresponding vector from the previous set) : the input to the
418 : * derivative evaluation.
419 : *
420 : * For the interface in which the caller does provide buffers, the arguments
421 : * must take the following structure (enforced by internal function calls):
422 : *
423 : * - `size_t l_max` : angular resolution for the spherical representation
424 : * - `size_t number_of_radial_points` : radial resolution (number of consecutive
425 : * blocks to evaluate derivatives, for each input vector )
426 : * - for each `DerivKind` in `DerivKindList`, a
427 : * `gsl::not_null<SpinWeighted<ComplexModalVector, Spin +
428 : * Tags::derivative_spin_weight<DerivKind>>>` : the buffer for the spin-weighted
429 : * spherical harmonic modes of the derivative quantities.
430 : * - for each `DerivKind` in `DerivKindList`, a `const
431 : * SpinWeighted<ComplexModalVector, Spin>` (where the `Spin` for these arguments
432 : * matches the corresponding vector from the previous set) : the buffer for the
433 : * spin-weighted spherical harmonic modes of the input quantities.
434 : * - for each `DerivKind` in `DerivKindList`, a
435 : * `gsl::not_null<SpinWeighted<ComplexDataVector, Spin +
436 : * Tags::derivative_spin_weight<DerivKind>>>` : the output of the derivative
437 : * evaluation
438 : * - for each `DerivKind` in `DerivKindList`, a `const
439 : * SpinWeighted<ComplexDataVector, Spin>` (where the `Spin` for these arguments
440 : * matches the corresponding vector from the previous set) : the input to the
441 : * derivative evaluation.
442 : *
443 : * The function `swsh_buffer` assists in generating the modal buffers of
444 : * appropriate size.
445 : */
446 : template <
447 : typename DerivativeKindList,
448 : ComplexRepresentation Representation = ComplexRepresentation::Interleaved,
449 : typename... ArgumentTypes>
450 1 : void angular_derivatives(const size_t l_max, // NOLINT
451 : const size_t number_of_radial_points, // NOLINT
452 : const ArgumentTypes&... arguments) {
453 : static_assert(
454 : tmpl::size<DerivativeKindList>::value * 2 == sizeof...(ArgumentTypes) or
455 : tmpl::size<DerivativeKindList>::value * 4 == sizeof...(ArgumentTypes),
456 : "When using the tagless `angular_derivatives` interface, you must "
457 : "provide either one nodal input and one nodal output per derivative "
458 : "or one nodal input, one nodal output, and two appropriate "
459 : "intermediate transform buffers per derivative.");
460 :
461 : detail::angular_derivatives_impl<Representation>(
462 : std::forward_as_tuple(arguments...), l_max, number_of_radial_points,
463 : std::make_index_sequence<tmpl::size<DerivativeKindList>::value>{},
464 : DerivativeKindList{},
465 : std::bool_constant<tmpl::size<DerivativeKindList>::value * 4 ==
466 : sizeof...(ArgumentTypes)>{});
467 : }
468 :
469 : /*!
470 : * \ingroup SpectralGroup
471 : * \brief Evaluate the spin-weighted derivative `DerivKind` on the provided
472 : * `SpinWeighted<ComplexDataVector, Spin>` collocation data, returning by value.
473 : */
474 : template <
475 : typename DerivKind,
476 : ComplexRepresentation Representation = ComplexRepresentation::Interleaved,
477 : int Spin>
478 : SpinWeighted<ComplexDataVector, Tags::derivative_spin_weight<DerivKind> + Spin>
479 1 : angular_derivative(
480 : size_t l_max, size_t number_of_radial_points,
481 : const SpinWeighted<ComplexDataVector, Spin>& to_differentiate);
482 : } // namespace Swsh
483 : } // namespace Spectral
|