$s$-SHT Transformations
One important capability of this package is the transformation between the two representations of a spin-weighted spherical function:
- Values
f
of the function evaluated on a set of points or "pixels" in the domain of the function. - Values
fΜ
of the mode weights (coefficients) of an expansion in the standard spin-weighted spherical-harmonic basis.
In the literature, the transformation f
β¦ fΜ
is usually called "analysis" or map2salm
, while the inverse transformation f
β¦ fΜ
is called "synthesis" or salm2map
. These are both referred to as spin-spherical-harmonic transforms, or $s$-SHTs.
To describe the values of a spin-$s$ function up to some maximum angular resolution $\ell_\mathrm{max}$, we need $(\ell_\mathrm{max}+1)^2 - s^2$ mode weights. We assume throughout that the values fΜ
are stored as
fΜ = [mode_weight(β, m) for β β abs(s):ββββ for m β -β:β]
(Here, mode_weight
is a made-up function intended to provide a schematic.) In particular, the $m$ index varies most rapidly, and the $\ell$ index varies most slowly. Correspondingly, there must be at least $(\ell_\mathrm{max}+1)^2 - s^2$ function values f
. However, some $s$-SHT algorithms require more function values β usually by a factor of 2 or 4 β trading off between speed and memory usage.
The SSHT
object implements these transformations, storing pre-computed constants and pre-allocated workspace for the transformations. The interface is designed to be similar to that of FFTW.jl
, whereby an SSHT
object π―
can be used to perform the transformation as either
f = π― * fΜ
or
fΜ = π― \ f
Currently, there are three algorithms implemented, each having different advantages and disadvantages:
- The "Direct" algorithm (introduced here for the first time), which is the default, but should only be used up to $\ell_\mathrm{max} \lesssim 50$ because its intermediate storage requirements scale as $\ell_\mathrm{max}^4$. This algorithm is the fastest for small $\ell_\mathrm{max}$, it can be used with arbitrary (non-degenerate) pixelizations, and achieves optimal dimensionality.
- The "Minimal" algorithm due to Elahi et al. [2], with some minor improvements. This algorithm is fast and β as the name implies β also achieves optimal dimensionality, and its storage scales as $\ell_\mathrm{max}^3$. However, its pixelization is restricted, and its accuracy at very high $\ell_\mathrm{max}$ is not as good as the "RS" algorithm. The algorithm itself is not actually fully specified by Elahi et al., and leaves out some relatively simple improvements, so I have had to take some liberties with my interpretation.
- The "RS" algorithm due to Reinecke and Seljebotn [3]. This forms the basis for the
libsharp
andducc.sht
packages. It requires pixelizations on "iso-latitude rings", and does not achieve optimal dimensionality. However, it is very fast, and its accuracy is excellent at extremely high $\ell_\mathrm{max}$.
SSHT
objects
SphericalFunctions.SSHT
β TypeSupertype of storage for spin-spherical-harmonic transforms
SphericalFunctions.SSHT
β MethodSSHT(s, ββββ; [method="Direct"], [T=Float64], [kwargs...])
Construct an SSHT
object to transform between spin-weighted spherical-harmonic mode weights and function values β performing an $s$-SHT.
This object behaves similarly to an AbstractFFTs.Plan
object β specifically in the ability to use the semantics of algebra to perform transforms. For example, if the function values are stored as a vector f
, the mode weights as fΜ
, and the SSHT
as π―
, then we can compute the function values from the mode weights as
f = π― * fΜ
or solve for the mode weights from the function values as
fΜ = π― \ f
The first dimensions of fΜ
must index the mode weights (as usual, for ββabs(s):ββββ
and mβ-β:β
) and the first index of f
must index the locations at which the function is evaluated. Any following dimensions will be broadcast over. Note that certain types will broadcast using Julia threads, while others will broadcast using BLAS threads. The relevant number of threads must be set appropriately.
Certain SSHT
types (currently, only Minimal
and Direct
) also have an option to always act in place β meaning that they simply re-use the input storage, even when used in an expression like π― \ f
. The option must be passed as the inplace
argument to the constructors, and part of the type of the resulting object. Regardless of the value of that option, for those types where the option exists, it is also possible to use mul!
and ldiv!
from the LinearAlgebra
package to force operation in place.
Note that different algorithms require different "pixelizations", or sets of Rotor
s on which to evaluate the function. These can be obtained from the SSHT
object using the pixels
and rotors
functions.
SphericalFunctions.pixels
β Functionpixels(π―)
Return the spherical coordinates (ΞΈ, Ο) on which the spin-weighted spherical harmonics are evaluated. See also rotors
, which provides the actual Rotor
s on which they are evaluated.
SphericalFunctions.rotors
β Functionrotors(π―)
Return the Rotor
s on which the spin-weighted spherical harmonics are evaluated. See also pixels
, which provides the corresponding spherical coordinates.
SphericalFunctions.SSHTDirect
β TypeSSHTDirect(s, ββββ; decomposition=LinearAlgebra.qr, T=Float64, RΞΈΟ=golden_ratio_spiral_rotors(s, ββββ, T), inplace=true)
Construct an $s$-SHT object that uses the "Direct" method; see βπ
for details about the method and optional arguments. Also see SSHT
for general information about how to use these objects.
By default, this uses precisely optimal sampling β meaning that the number of points on which the function is evaluated, represented by RΞΈΟ
, is equal to the number of modes. However, it is equally possible to evaluate on more points than there are modes. This can be useful, for example, when processing multiple fields with different spin weights; the function could be evaluated on points appropriate for the lowest value of $|s|$, and therefore could also be used to solve for fields of all other spin weights.
Note that in-place operation is possible for this type when the length of the input RΞΈΟ
is equal to the number of modes given s
and ββββ
β and is the default behavior when possible. See SSHT
for description of in-place operation.
This method is typically better than other current implementations for $ββββ β² 24$, both in terms of speed and accuracy. However, this advantage quickly falls away. A warning will be issued if ββββ
is greater than about 64, because this method is not likely to be the most efficient or most accurate choice.
SphericalFunctions.SSHTMinimal
β TypeStorage for Minimal spin-spherical-harmonic transform
The Minimal algorithm was described by Elahi et al., and allows for the minimal number of function samples.
SphericalFunctions.SSHTMinimal
β MethodSSHTMinimal(s, ββββ; kwargs...)
Construct a SSHTMinimal
object directly. This may also be achieved by calling the main SSHT
function with the same keywords, along with method="Minimal"
.
This object uses the algorithm described by Elahi et al.
The basic floating-point number type may be adjusted with the keyword argument T
, which defaults to Float64
.
The SSHs are evaluated on a series of "rings" at constant colatitude. Their locations are specified by the ΞΈ
keyword argument, which defaults to sorted_rings(s, ββββ, T)
. The first element of ΞΈ
is the colatitude of the smallest ring (containing $2s+1$ elements), and so on to the last element of ΞΈ
, which is the colatitude of the largest ring (containing $2ββββ+1$ elements).
Whenever T
is either Float64
or Float32
, the keyword arguments plan_fft_flags
and plan_fft_timelimit
may also be useful for obtaining more efficient FFTs. They default to FFTW.ESTIMATE
and Inf
, respectively. They are passed to AbstractFFTs.plan_fft
.
Note that, because this algorithm achieves optimal dimensionality, the transformation will be performed in place by default. If this is not desired, pass the keyword argument inplace=false
. This will cause the algorithm to copy the input and perform in-place transformation on that copy.
SphericalFunctions.SSHTRS
β TypeStorage for spin-spherical-harmonic transform
The algorithm was described in by Reinecke and Seljebotn.
SphericalFunctions.SSHTRS
β MethodSSHTRS(s, ββββ; kwargs...)
Construct a SSHTRS
object directly. This may also be achieved by calling the main SSHT
function with the same keywords, along with method="RS"
.
This object uses the algorithm described by Reinecke and Seljebotn.
The basic floating-point number type may be adjusted with the keyword argument T
, which defaults to Float64
.
The SSHs are evaluated on a series of "rings" at constant colatitude. Their locations are specified by the ΞΈ
keyword argument, which defaults to fejer1_rings(2ββββ+1, T)
. If this is changed, the user should also provide the corresponding quadrature_weights
argument β the default being fejer1(length(ΞΈ), T)
.
On each of these rings, an FFT is performed. To reach the band limit of $m = Β± ββββ$, the number of points along each ring must therefore be at least $2ββββ+1$, but may be greater. For example, if $2ββββ+1$ does not factorize neatly into a product of small primes, it may be preferable to use $2ββββ+2$ points along each ring. (In that case, whenever ββββ
is 1 less than a power of 2, the number of points will be exactly a power of 2, which is usually particularly efficient.) The number of points on each ring can be modified independently, if given as a vector with the same length as ΞΈ
, or as a single number which is assumed to be the same for all rings.
Whenever T
is either Float64
or Float32
, the keyword arguments plan_fft_flags
and plan_fft_timelimit
may also be useful for obtaining more efficient FFTs. They default to FFTW.ESTIMATE
and Inf
, respectively. They are passed to AbstractFFTs.plan_fft
.
Pixelizations
The algorithms implemented here require pixelizations. While the "Direct" algorithm can be used with arbitrary pixelizations, the "Minimal" and "RS" algorithms require more specific choices, as noted in their docstrings.
Typically, "pixelization" refers exclusively to a choice of points on the sphere πΒ² at which to compute function values. Of course, as mentioned elsewhere, it is not technically possible to define spin-weighted functions as functions of a point on πΒ² alone; we also need some sense of reference direction in the tangent space. Quite generally, we can define spin-weighted functions on the group ππ(3) or ππ©π’π§(3), so we will also refer to a choice of a set of points in ππ©π’π§(3) (which is essentially the group of unit quaternions) as a "pixelization". However, assuming spherical coordinates, a choice of coordinates on the sphere almost everywhere induces a choice of the reference direction in the tangent space, so it is almost possible to define pixelizations just in terms of points on πΒ². But using spherical coordinates is actually enough to fully specify the pixelization, because the degeneracies at the poles also allow us to define the reference direction.
In principle, we could be concerned about the choice of reference direction in the tangent space. That is, we might expect to care about pixelizations over πΒ³. However, we are dealing with spin-weighted functions, which are eigenfunctions of a final rotation about the reference direction. This means that once we choose any reference direction at each point, we know the function values for any other reference direction at those points. In particular, an important property of a pixelization is the condition number of the transformation matrix between the function values and the mode weights. If we rotate the reference direction at a single point, this is equivalent to multiplying the matrix by a diagonal matrix with entries of 1 everywhere except the entry corresponding to that point, where the entry is some complex phase. This does not change the condition number of the matrix, so we can ignore the choice of reference direction at every point. For other situations, where we might care about the choice of reference direction, it might be interesting to consider this work by Marc Alexa, and references therein.
Interesting discussions of various pixelizations and metrics can be found in Saff and Kuijlaars (1997) and Brauchart and Grabner (2015), as well as blog posts here and here. Note that the "equal-area" pixelizations of Healpix are very restrictiveβonly being available for very specific numbers of pointsβand do not provide any obvious advantages over the more flexible pixelizations available here.
The various pixelizations may be computed as follows:
SphericalFunctions.clenshaw_curtis_rings
β Methodclenshaw_curtis_rings(N, [T=Float64])
Values of the colatitude coordinate ($ΞΈ$) appropriate for quadrature by the Clenshaw-Curtis rule, using weights provided by clenshaw_curtis
.
Note that the first argument to this function is N
, rather than the ββββ
used in some other functions. For spin-weighted spherical harmonics, you may want to use N=2ββββ+1
.
SphericalFunctions.fejer1_rings
β Methodfejer1_rings(N, [T=Float64])
Values of the colatitude coordinate ($ΞΈ$) appropriate for quadrature by FejΓ©r's first rule, using weights provided by fejer1
.
Note that the first argument to this function is N
, rather than the ββββ
used in some other functions. For spin-weighted spherical harmonics, you may want to use N=2ββββ+1
.
SphericalFunctions.fejer2_rings
β Methodfejer2_rings(N, [T=Float64])
Values of the colatitude coordinate ($ΞΈ$) appropriate for quadrature by FejΓ©r's second rule, using weights provided by fejer2
.
Note that the first argument to this function is N
, rather than the ββββ
used in some other functions. For spin-weighted spherical harmonics, you may want to use N=2ββββ+1
.
SphericalFunctions.golden_ratio_spiral_pixels
β Methodgolden_ratio_spiral_pixels(s, ββββ, [T=Float64])
Cover the sphere πΒ² with pixels generated by the golden-ratio spiral. Successive pixels are separated by the azimuthal angle $ΞΟ = 2Ο(2-Ο)$, and are uniformly distributed in $\cos ΞΈ$.
This is also known as the "Fibonacci sphere" or "Fibonacci lattice".
Visually, this is a very reasonable-looking pixelization, with fairly uniform distance between neighbors, and approximate isotropy. No two pixels will share the same values of either $ΞΈ$ or $Ο$. Also note that no point is present on either the North or South poles.
The returned quantity is a vector of 2-SVectors providing the spherical coordinates of each pixel. See also golden_ratio_spiral_rotors
for the corresponding Rotor
s.
SphericalFunctions.golden_ratio_spiral_rotors
β Methodgolden_ratio_spiral_rotors(s, ββββ, [T=Float64])
Cover the sphere πΒ² with pixels generated by the golden-ratio spiral.
See golden_ratio_spiral_pixels
for more detailed explanation. The quantity returned by this function is a vector of Rotor
s providing each pixel.
SphericalFunctions.sorted_ring_pixels
β Methodsorted_ring_pixels(s, ββββ, [T=Float64])
Cover the sphere πΒ² with $(ββββ+1)Β²-sΒ²$ pixels distributed in rings provided by sorted_rings
; see that function's documentation for more description.
The returned quantity is a vector of 2-SVectors containing the spherical coordinates of each pixel. See also sorted_ring_rotors
for the corresponding Rotor
s.
SphericalFunctions.sorted_ring_rotors
β Methodsorted_ring_rotors(s, ββββ, [T=Float64])
Cover the sphere πΒ² with $(ββββ+1)Β²-sΒ²$ pixels distributed in rings provided by sorted_rings
; see that function's documentation for more description.
The returned quantity is a vector of Rotor
s. See also sorted_ring_rotors
for the corresponding spherical coordinates.
SphericalFunctions.sorted_rings
β Methodsorted_rings(s, ββββ, [T=Float64])
Compute locations of a series of rings labelled by $j β |s|:ββββ$ (analogous to $β$), where each ring will contain $k = 2j+1$ (analogous to $m$) pixels distributed evenly around the ring. These rings are then sorted, so that the ring with the most pixels ($j = ββββ$) is closest to the equator, and the next-largest ring is placed just above or below the equator (depending on the sign of $s$), the next just below or above, and so on. This is generally a fairly good first guess when minimizing the condition number of matrices used to solve for mode weights from function values. In particular, I use this to initialize the Minimal algorithm, which is then fed into an optimizer to fine-tune the positions of the rings.
This function does not provide the individual pixels; it just provides the colatitude values of the rings on which the pixels will be placed. The pixels themselves are provided by sorted_ring_pixels
.
Quadrature weights
The "RS" algorithm requires quadrature weights corresponding to the input pixelization. Though there is a working default choice, it is possible to use others. There are several that are currently implemented, along with their corresponding pixelizations:
SphericalFunctions.clenshaw_curtis
β Methodclenshaw_curtis(n, [T])
Compute n
weights for the Clenshaw-Curtis rule, corresponding to n
evenly spaced nodes from 0 to Ο inclusive. That is, the nodes are located at
\[\theta_k = k \frac{\pi}{n-1} \quad k=0, \ldots, n-1.\]
This function uses Waldvogel's method.
The type T
may be any AbstractFloat
, but defaults to Float64
.
SphericalFunctions.fejer1
β Methodfejer1(n, [T])
Compute n
weights for FejΓ©r's first rule, corresponding to n
evenly spaced nodes from 0 to Ο inclusive. That is, the nodes are located at
\[\theta_k = k \frac{\pi}{n-1} \quad k=0, \ldots, n-1.\]
This function uses Waldvogel's method.
The type T
may be any AbstractFloat
, but defaults to Float64
.
SphericalFunctions.fejer2
β Methodfejer2(n, [T])
Compute n
weights for FejΓ©r's second rule, corresponding to n
evenly spaced nodes between 0 and Ο exclusive. That is, the nodes are located at
\[\theta_k = k \frac{\pi}{n+1} \quad k=1, \ldots, n.\]
This function uses Waldvogel's method. However, contrary to Waldvogel's notation, this routine does not include the weight corresponding to the Ο=0 or Ο nodes, which both have weight 0.
The type T
may be any AbstractFloat
, but defaults to Float64
.