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 = tmpl::list<UniqueDifferentiatedFromTags..., Tags::LMax,
191 : Tags::NumberOfRadialPoints>;
192 :
193 : static void apply(
194 : const gsl::not_null<typename DerivativeTags::type*>... derivative_scalars,
195 : const gsl::not_null<typename Tags::SwshTransform<
196 : DerivativeTags>::type*>... transform_of_derivative_scalars,
197 : const gsl::not_null<typename Tags::SwshTransform<
198 : UniqueDifferentiatedFromTags>::type*>... transform_of_input_scalars,
199 : const typename UniqueDifferentiatedFromTags::type&... input_scalars,
200 : const size_t l_max, const size_t number_of_radial_points) {
201 : apply_to_vectors(make_not_null(&get(*transform_of_derivative_scalars))...,
202 : make_not_null(&get(*transform_of_input_scalars))...,
203 : make_not_null(&get(*derivative_scalars))...,
204 : get(input_scalars)..., l_max, number_of_radial_points);
205 : }
206 :
207 : template <ComplexRepresentation FriendRepresentation,
208 : typename... DerivativeKinds, typename... ArgumentTypes,
209 : size_t... Is>
210 : // NOLINTNEXTLINE(readability-redundant-declaration)
211 : friend void angular_derivatives_impl(const std::tuple<ArgumentTypes...>&,
212 : size_t, size_t,
213 : std::index_sequence<Is...>,
214 : tmpl::list<DerivativeKinds...>,
215 : std::bool_constant<true>);
216 :
217 : private:
218 : // note inputs reordered to accommodate the alternative tag-free functions
219 : // which call into this function.
220 : static void apply_to_vectors(
221 : const gsl::not_null<typename Tags::SwshTransform<
222 : DerivativeTags>::type::type*>... transform_of_derivatives,
223 : const gsl::not_null<typename Tags::SwshTransform<
224 : UniqueDifferentiatedFromTags>::type::type*>... transform_of_inputs,
225 : const gsl::not_null<typename DerivativeTags::type::type*>... derivatives,
226 : const typename UniqueDifferentiatedFromTags::type::type&... inputs,
227 : const size_t l_max, const size_t number_of_radial_points) {
228 : // perform the forward transform on the minimal set of input nodal
229 : // quantities to obtain all of the requested derivatives
230 : using ForwardTransformList =
231 : make_transform_list_from_derivative_tags<Representation,
232 : tmpl::list<DerivativeTags...>>;
233 :
234 : tmpl::for_each<ForwardTransformList>(
235 : [&number_of_radial_points, &l_max, &inputs...,
236 : &transform_of_inputs...](auto transform_v) {
237 : using transform = typename decltype(transform_v)::type;
238 : auto input_transforms = std::make_tuple(transform_of_inputs...);
239 : dispatch_to_transform<transform,
240 : tmpl::list<UniqueDifferentiatedFromTags...>>::
241 : apply(make_not_null(&input_transforms),
242 : std::forward_as_tuple(inputs...), l_max,
243 : number_of_radial_points);
244 : });
245 :
246 : // apply the modal derivative factors and place the result in the
247 : // `transform_of_derivatives`
248 : EXPAND_PACK_LEFT_TO_RIGHT(
249 : dispatch_to_compute_coefficients_of_derivative<
250 : DerivativeTags, tmpl::list<UniqueDifferentiatedFromTags...>>::
251 : apply(transform_of_derivatives,
252 : std::make_tuple(transform_of_inputs...), l_max,
253 : number_of_radial_points));
254 :
255 : // perform the inverse transform on the derivative results, placing the
256 : // result in the nodal `derivatives` passed by pointer.
257 : using InverseTransformList =
258 : make_inverse_transform_list<Representation,
259 : tmpl::list<DerivativeTags...>>;
260 :
261 : tmpl::for_each<InverseTransformList>([&number_of_radial_points, &l_max,
262 : &derivatives...,
263 : &transform_of_derivatives...](
264 : auto transform_v) {
265 : using transform = typename decltype(transform_v)::type;
266 : auto derivative_tuple = std::make_tuple(derivatives...);
267 : dispatch_to_transform<transform, tmpl::list<DerivativeTags...>>::apply(
268 : make_not_null(&derivative_tuple),
269 : std::make_tuple(transform_of_derivatives...), l_max,
270 : number_of_radial_points);
271 : });
272 : }
273 : };
274 :
275 : // metafunction for determining the tags needed to evaluate the derivative tags
276 : // in `DerivativeTagList`, removing all duplicate tags.
277 : template <typename DerivativeTagList>
278 : struct unique_derived_from_list;
279 :
280 : template <typename... DerivativeTags>
281 : struct unique_derived_from_list<tmpl::list<DerivativeTags...>> {
282 : using type = tmpl::remove_duplicates<
283 : tmpl::list<typename DerivativeTags::derivative_of...>>;
284 : };
285 : } // namespace detail
286 :
287 : /*!
288 : * \ingroup SpectralGroup
289 : * \brief A \ref DataBoxGroup mutate-compatible computational struct for
290 : * computing a set of spin-weighted spherical harmonic derivatives by
291 : * grouping and batch-computing spin-weighted spherical harmonic transforms.
292 : *
293 : * \details A derivative is evaluated for each tag in `DerivativeTagList`. All
294 : * entries in `DerivativeTagList` must be the tag
295 : * `Spectral::Swsh::Tags::Derivative<Tag, DerivativeKind>` prefixing the `Tag`
296 : * to be differentiated, and indicating the spin-weighted derivative
297 : * `DerivativeKind` to be taken. A \ref DataBoxGroup on which this struct is
298 : * invoked must contain:
299 : * - each of the tags in `DerivativeTagList` (the results of the computation)
300 : * - each of the tags `Tag` prefixed by `Spectral::Swsh::Tags::Derivative` in
301 : * `DerivativeTagList` (the inputs of the computation).
302 : * - each of the tags `Spectral::Swsh::Tags::SwshTransform<DerivativeTag>` for
303 : * `DerivativeTag`in `DerivativeTagList` (the buffers for the derivative
304 : * applied to the modes)
305 : * - each of the tags `Spectral::Swsh::Tags::SwshTransform<Tag>` for `Tag`
306 : * prefixed by any `DerivativeTag` in `DerivativeTagList` (the buffers for the
307 : * transforms of the input data).
308 : *
309 : * This function optimizes the derivative taking process by clustering like
310 : * spins of tags, forward-transforming each spin cluster together, applying the
311 : * factor for the derivative to each modal vector, re-clustering according to
312 : * the new spin weights (the derivatives alter the spin weights), and finally
313 : * inverse-transforming in clusters.
314 : */
315 : template <typename DerivativeTagList, ComplexRepresentation Representation =
316 : ComplexRepresentation::Interleaved>
317 1 : using AngularDerivatives = detail::AngularDerivativesImpl<
318 : DerivativeTagList,
319 : typename detail::unique_derived_from_list<DerivativeTagList>::type,
320 : Representation>;
321 :
322 : /*!
323 : * \ingroup SpectralGroup
324 : * \brief Produces a `SpinWeighted<ComplexModalVector, Spin>` of the appropriate
325 : * size to be used as a modal buffer for `Spectral::Swsh::AngularDerivatives` or
326 : * `Spectral::Swsh::angular_derivatives`.
327 : *
328 : * \details The `Spectral::Swsh::angular_derivatives` and
329 : * `Spectral::Swsh::AngularDerivatives` interfaces require that calling code
330 : * provides a buffer for the intermediate transform results, to ensure that
331 : * callers are aware of the allocations and can suitably reuse buffers if
332 : * possible. This utility eases the creation of those buffers.
333 : */
334 : template <int Spin>
335 1 : auto swsh_buffer(const size_t l_max, const size_t number_of_radial_points) {
336 : return SpinWeighted<ComplexModalVector, Spin>{
337 : size_of_libsharp_coefficient_vector(l_max) * number_of_radial_points};
338 : }
339 :
340 : namespace detail {
341 : // template 'implementation' for the `angular_derivatives` function below which
342 : // evaluates an arbitrary number of derivatives, and places them in the set of
343 : // nodal containers passed by pointer.
344 : template <ComplexRepresentation Representation, typename... DerivativeKinds,
345 : typename... ArgumentTypes, size_t... Is>
346 : void angular_derivatives_impl(
347 : const std::tuple<ArgumentTypes...>& argument_tuple, const size_t l_max,
348 : const size_t number_of_radial_points, std::index_sequence<Is...> /*meta*/,
349 : tmpl::list<DerivativeKinds...> /*meta*/,
350 : std::bool_constant<true> /*buffers_included_in_arguments*/) {
351 : AngularDerivatives<
352 : tmpl::list<Tags::Derivative<
353 : ::Tags::SpinWeighted<
354 : ::Tags::TempScalar<Is, ComplexDataVector>,
355 : std::integral_constant<
356 : int, std::decay_t<decltype(get<Is + 3 * sizeof...(Is)>(
357 : argument_tuple))>::spin>>,
358 : DerivativeKinds>...>,
359 : Representation>::apply_to_vectors(get<Is>(argument_tuple)...,
360 : get<Is + sizeof...(Is)>(
361 : argument_tuple)...,
362 : get<Is + 2 * sizeof...(Is)>(
363 : argument_tuple)...,
364 : get<Is + 3 * sizeof...(Is)>(
365 : argument_tuple)...,
366 : l_max, number_of_radial_points);
367 : }
368 :
369 : template <ComplexRepresentation Representation, typename... DerivativeKinds,
370 : typename... ArgumentTypes, size_t... Is>
371 : void angular_derivatives_impl(
372 : const std::tuple<ArgumentTypes...>& argument_tuple, const size_t l_max,
373 : const size_t number_of_radial_points,
374 : std::index_sequence<Is...> index_sequence,
375 : tmpl::list<DerivativeKinds...> derivative_kinds,
376 : std::bool_constant<false> /*buffers_included_in_arguments*/) {
377 : auto derivative_buffer_tuple = std::make_tuple(
378 : swsh_buffer<std::decay_t<decltype(*get<Is>(argument_tuple))>::spin>(
379 : l_max, number_of_radial_points)...);
380 : auto input_buffer_tuple = std::make_tuple(
381 : swsh_buffer<std::decay_t<decltype(get<Is + sizeof...(Is)>(
382 : argument_tuple))>::spin>(l_max, number_of_radial_points)...);
383 : angular_derivatives_impl<Representation>(
384 : std::forward_as_tuple(make_not_null(&get<Is>(derivative_buffer_tuple))...,
385 : make_not_null(&get<Is>(input_buffer_tuple))...,
386 : get<Is>(argument_tuple)...,
387 : get<Is + sizeof...(Is)>(argument_tuple)...),
388 : l_max, number_of_radial_points, index_sequence, derivative_kinds,
389 : std::bool_constant<true>{});
390 : }
391 : } // namespace detail
392 :
393 : /*!
394 : * \ingroup SpectralGroup
395 : * \brief Evaluate all of the spin-weighted derivatives in `DerivKindList` on
396 : * input `SpinWeighted<ComplexDataVector, Spin>` collocation data, returning by
397 : * pointer.
398 : *
399 : * \details This function provides two interfaces, one in which the caller
400 : * provides the intermediate coefficient buffers needed during the computation
401 : * of the derivatives, and one in which those buffers are temporarily allocated
402 : * during the derivative function calls.
403 : *
404 : * For the interface in which the caller does not provide buffers, the arguments
405 : * must take the following structure (enforced by internal function calls):
406 : *
407 : * - `size_t l_max` : angular resolution for the spherical representation
408 : * - `size_t number_of_radial_points` : radial resolution (number of consecutive
409 : * blocks to evaluate derivatives, for each input vector )
410 : * - for each `DerivKind` in `DerivKindList`, a
411 : * `gsl::not_null<SpinWeighted<ComplexDataVector, Spin +
412 : * Tags::derivative_spin_weight<DerivKind>>>` : the output of the derivative
413 : * evaluation
414 : * - for each `DerivKind` in `DerivKindList`, a `const
415 : * SpinWeighted<ComplexDataVector, Spin>&` (where the `Spin` for these arguments
416 : * matches the corresponding vector from the previous set) : the input to the
417 : * derivative evaluation.
418 : *
419 : * For the interface in which the caller does provide buffers, the arguments
420 : * must take the following structure (enforced by internal function calls):
421 : *
422 : * - `size_t l_max` : angular resolution for the spherical representation
423 : * - `size_t number_of_radial_points` : radial resolution (number of consecutive
424 : * blocks to evaluate derivatives, for each input vector )
425 : * - for each `DerivKind` in `DerivKindList`, a
426 : * `gsl::not_null<SpinWeighted<ComplexModalVector, Spin +
427 : * Tags::derivative_spin_weight<DerivKind>>>` : the buffer for the spin-weighted
428 : * spherical harmonic modes of the derivative quantities.
429 : * - for each `DerivKind` in `DerivKindList`, a `const
430 : * SpinWeighted<ComplexModalVector, Spin>` (where the `Spin` for these arguments
431 : * matches the corresponding vector from the previous set) : the buffer for the
432 : * spin-weighted spherical harmonic modes of the input quantities.
433 : * - for each `DerivKind` in `DerivKindList`, a
434 : * `gsl::not_null<SpinWeighted<ComplexDataVector, Spin +
435 : * Tags::derivative_spin_weight<DerivKind>>>` : the output of the derivative
436 : * evaluation
437 : * - for each `DerivKind` in `DerivKindList`, a `const
438 : * SpinWeighted<ComplexDataVector, Spin>` (where the `Spin` for these arguments
439 : * matches the corresponding vector from the previous set) : the input to the
440 : * derivative evaluation.
441 : *
442 : * The function `swsh_buffer` assists in generating the modal buffers of
443 : * appropriate size.
444 : */
445 : template <
446 : typename DerivativeKindList,
447 : ComplexRepresentation Representation = ComplexRepresentation::Interleaved,
448 : typename... ArgumentTypes>
449 1 : void angular_derivatives(const size_t l_max, // NOLINT
450 : const size_t number_of_radial_points, // NOLINT
451 : const ArgumentTypes&... arguments) {
452 : static_assert(
453 : tmpl::size<DerivativeKindList>::value * 2 == sizeof...(ArgumentTypes) or
454 : tmpl::size<DerivativeKindList>::value * 4 == sizeof...(ArgumentTypes),
455 : "When using the tagless `angular_derivatives` interface, you must "
456 : "provide either one nodal input and one nodal output per derivative "
457 : "or one nodal input, one nodal output, and two appropriate "
458 : "intermediate transform buffers per derivative.");
459 :
460 : detail::angular_derivatives_impl<Representation>(
461 : std::forward_as_tuple(arguments...), l_max, number_of_radial_points,
462 : std::make_index_sequence<tmpl::size<DerivativeKindList>::value>{},
463 : DerivativeKindList{},
464 : std::bool_constant<tmpl::size<DerivativeKindList>::value * 4 ==
465 : sizeof...(ArgumentTypes)>{});
466 : }
467 :
468 : /*!
469 : * \ingroup SpectralGroup
470 : * \brief Evaluate the spin-weighted derivative `DerivKind` on the provided
471 : * `SpinWeighted<ComplexDataVector, Spin>` collocation data, returning by value.
472 : */
473 : template <
474 : typename DerivKind,
475 : ComplexRepresentation Representation = ComplexRepresentation::Interleaved,
476 : int Spin>
477 : SpinWeighted<ComplexDataVector, Tags::derivative_spin_weight<DerivKind> + Spin>
478 1 : angular_derivative(
479 : size_t l_max, size_t number_of_radial_points,
480 : const SpinWeighted<ComplexDataVector, Spin>& to_differentiate);
481 : } // namespace Swsh
482 : } // namespace Spectral
|