ClimaCoreSpectra.jl
ClimaCoreSpectra.jl provides functionality for calculating kinetic energy spectra using spherical harmonics.
Interface
ClimaCoreSpectra.SpectralSphericalMesh — Type
SpectralSphericalMesh{FT}(nθ, nd)
SpectralSphericalMesh(nθ, nd, ArrType, ComplexType, IntArrType)Spherical mesh data structure for computing spectra on a regular latitude-longitude grid with nθ latitudes, nλ = 2nθ longitudes, and nd vertical levels. The triangular truncation is num_fourier = floor((2nθ - 1) / 3) (e.g. nθ = 32 gives T21).
ArrType, ComplexType, and IntArrType are the array types used for the real, complex, and integer work arrays; the first form uses Arrays of FT.
Fields
num_fourier: number of truncated zonal wavenumbersm.num_spherical: number of total wavenumbersn(num_fourier + 1).nλ,nθ,nd: numbers of longitudes, latitudes, and vertical levels.Δλ: longitude spacing [rad].qwg: Gaussian-weighted associated Legendre polynomials, indexed[m, n, θ].qnm: normalized associated Legendre polynomials, indexed[m, n, θ].wave_numbers: total wavenumbernfor each[m, n]entry.var_grid,var_fourier,var_spherical,var_spectrum: work arrays for the variable on the grid, after the Fourier transform, in spherical-harmonic space, and its power spectrum.
ClimaCoreSpectra.power_spectrum_1d — Function
power_spectrum_1d(FT, var_grid, z, lat, lon, weight)Compute the zonal (1D) power spectrum of the variable var_grid on a (lon, lat, z) grid with a Fourier transform along each latitude circle, weighting each level by weight. The input field must first be interpolated to a regular latitude-longitude grid.
Arguments
FT: float type.var_grid: variable on the(lon, lat, z)grid to be transformed.z: array of vertical levels.lat: array of latitudes [degrees].lon: array of uniformly spaced longitudes [degrees].weight: array with one weight per level, e.g. for mass weighting.
Returns
The tuple (zon_spectrum, freqs) of arrays of shape (num_pfourier, nlat, nlev), where num_pfourier is the number of non-negative Fourier frequencies: the power at each frequency (with the negative-frequency contribution folded in) and the corresponding angular wavenumbers.
ClimaCoreSpectra.power_spectrum_2d — Function
power_spectrum_2d(FT, var_grid, mass_weight)Transform the variable var_grid on a regular latitude-longitude grid into spherical-harmonic space, using an FFT along latitude circles (as for the 1D spectrum) and a Legendre transform along meridians, and compute its 2D power spectrum.
Arguments
FT: float type.var_grid: variable on the(lon, lat, z)grid to be transformed, withnlon = 2 nlat.mass_weight: array with one weight per level, e.g. for mass weighting.
Returns
The tuple (var_spectrum, wave_numbers, var_spherical, mesh): the power spectrum indexed [m, n, k], the total wavenumber of each [m, n] entry, the spherical-harmonic coefficients indexed [m, n, k, θ], and the SpectralSphericalMesh used.
See [55].
ClimaCoreSpectra.compute_gaussian! — Function
compute_gaussian!(FT, n)Compute sin(latitude) at the n Gaussian latitudes and the corresponding weights for Gaussian integration, returned as the tuple (sinθ, wts) of arrays with element type FT. n must be even. The function allocates its results and mutates none of its arguments.
Notes
The roots of the Legendre polynomial $P_n$ are found by Newton iteration from the initial guess $x_i = \cos(π(i - 1/4)/(n + 1/2))$, using the recurrences $n P_n(x) = (2n-1) x P_{n-1}(x) - (n-1) P_{n-2}(x)$ and $P'_n(x) = \frac{n}{x^2 - 1}(x P_n(x) - P_{n-1}(x))$; since $P_n$ is odd, only half of the roots are computed. The weights are $w_i = 2 / ((1 - x_i^2) P'_n(x_i)^2)$. An error is logged if the iteration does not converge. See Ehrendorfer, M. (2011), Spectral Numerical Weather Prediction Models, Appendix B, SIAM.
ClimaCoreSpectra.compute_legendre! — Function
compute_legendre!(FT, num_fourier, num_spherical, sinθ, nθ)Compute the normalized associated Legendre polynomials $P_{l,m}$ at the Gaussian latitudes and return them as an array qnm of shape (num_fourier + 1, num_spherical + 1, nθ), with qnm[m + 1, l + 1, :] holding $P_{l,m}$. The function allocates its result and mutates none of its arguments.
Arguments
FT: float type.num_fourier: number of truncated zonal wavenumbersm.num_spherical: number of total wavenumbersn.sinθ: array ofsin(latitude)at the Gaussian latitudes.nθ: number of Gaussian latitudes.
Notes
Following the notation and equation numbers of Ehrendorfer (2011), Appendix B, with l = 0, 1, … and m = -l, …, l:
P_{0,0} = 1
P_{m,m} = sqrt((2m+1)/2m) cosθ P_{m-1,m-1}
P_{m+1,m} = sqrt(2m+3) sinθ P_{m,m}
sqrt((l²-m²)/(4l²-1)) P_{l,m} = sinθ P_{l-1,m} - sqrt(((l-1)²-m²)/(4(l-1)²-1)) P_{l-2,m}The normalization gives $\frac{1}{2} \int_{-1}^1 P_{l,m}(x) P_{n,m}(x)\, dx = δ_{n,l}$ with $x = \sin θ$.
References: Ehrendorfer, M. (2011), Spectral Numerical Weather Prediction Models, Appendix B, SIAM; Winch, D. (2007), Spherical harmonics, in Encyclopedia of Geomagnetism and Paleomagnetism, Springer.
ClimaCoreSpectra.trans_grid_to_spherical! — Function
trans_grid_to_spherical!(mesh::SpectralSphericalMesh, pfield::AbstractArray)Transform the variable pfield of shape (nλ, nθ) on a Gaussian grid into spherical-harmonic space and return the complex coefficient array of shape (num_fourier + 1, num_spherical + 1, nθ ÷ 2), split by latitude hemisphere pairs.
The transform is a Fourier transform along each latitude circle followed by a Legendre transform using the weighted polynomials mesh.qwg. mesh is read but not mutated; nθ must be even.
Arguments
mesh: mesh information and weighted Legendre polynomials.pfield: variable on the Gaussian grid to be transformed.
Notes
With λ the longitude, θ the latitude, η = sin θ, m the zonal wavenumber, and n the total wavenumber:
var_spherical2d = F_{m,n} # output in spectral space
qwg = P_{m,n}(η) w(η) # weighted Legendre polynomials
var_fourier2d = g_{m,θ} # untruncated Fourier transform
pfield = F(λ, η) # input on the Gaussian gridSee Ehrendorfer, M. (2011), Spectral Numerical Weather Prediction Models, Appendix B, SIAM, and [56].
ClimaCoreSpectra.compute_wave_numbers! — Function
compute_wave_numbers!(wave_numbers, num_fourier::Int, num_spherical::Int)Store the total wavenumber n for each (m, n) entry of the triangular truncation in the matrix wave_numbers, which is mutated in place. Entries with n < m are left unchanged. Returns nothing.
Arguments
wave_numbers: integer matrix of shape(num_fourier + 1, num_spherical + 1).num_fourier: number of truncated zonal wavenumbersm.num_spherical: number of total wavenumbersn.
Examples
lib/ClimaCoreSpectra/test/gcm_visual_test.jl computes the one- and two-dimensional spectra of test fields on a latitude–longitude grid, transforms them back, and plots the input, the spectra, and the reconstruction error. Run it from the repository root with BUILD_DOCS=true set to write the figures:
import ClimaCore
fn = joinpath(pkgdir(ClimaCore), "lib", "ClimaCoreSpectra", "test", "gcm_visual_test.jl")
ENV["BUILD_DOCS"] = true
include(fn)