# Localized spectral analysis on the sphere¶

A localised spectrum estimate is the spectrum of a global function multiplied by a localization window. In a 2D Cartesian coordinate system, this multiplication becomes a convolution in the Fourier domain. As a result of the convolution, a localized spectral estimate for a given wavelength depends upon a range of coefficients of both the global function and localization window. One says that the window couples the global Fourier coefficients to the localized coefficients.

Windowing on the sphere leads to similar effects: Multiplication of a global function by a window leads to coupling of the spherical harmonic coefficients of the globally defined function to the localized spectrum. In the case of an isotropic and stationary stochastic function, a coupling matrix describes the relation between the global power and the expectation of the localized power, and at high degrees, this matrix operation also resembles a convolution.

In :
from __future__ import print_function # only necessary if using Python 2.x
%matplotlib inline

import matplotlib.pyplot as plt
import numpy as np
import pyshtools

In :
pyshtools.utils.figstyle(rel_width=0.75)
# %config InlineBackend.figure_format = 'retina'  # if you are using a retina display, uncomment this line


## Generate a random model using the SHCoeffs class interface¶

We first generate a random model using the SHCoeffs class interface. This class simplifies coefficient generation and provides a simple access to most SHTOOLS functions. We start by defining a global power spectrum that follows a power-law-like decay:

In :
lmax = 100
a = 30  # scale length
degrees = np.arange(lmax+1, dtype=np.float)
power = 1. / (1. + (degrees / a) ** 2) ** 1.5


Next, we generate random coefficients from this input power spectrum, plot the power spectrum of the realization, and expand the coeffificients on a grid:

In :
coeffs_global = pyshtools.SHCoeffs.from_random(power, seed=12345)

power_global = coeffs_global.spectrum()
fig, ax = coeffs_global.plot_spectrum(unit='per_dlogl')

grid_global = coeffs_global.expand(grid='DH2')
fig2, ax2 = grid_global.plot(colorbar=True)

/usr/local/lib/python3.7/site-packages/matplotlib/figure.py:457: UserWarning: matplotlib is currently using a non-GUI backend, so cannot show the figure
"matplotlib is currently using a non-GUI backend, "  ## Generate a box window function¶

We next generate a window function that picks a few local regions from the globally defined model. In this example, the window contains sharp boundaries, so the spherical harmonic bandwidth of the function is infinite. In general, this could severely bias the localized power spectrum away from its global value, especially when the power spectrum follows a power law. To combat this negative characteristic, we will show in a different tutorial how to construct windows with a specified spherical harmonic bandwith that are optimally concentrated within the provided region.

This example makes use of the class SHGrid, which is the counterpart to SHCoeffs. Let's start by making an arbitrary mask:

In :
latgrid, longrid = np.meshgrid(grid_global.lats(), grid_global.lons(), indexing='ij')

window = (-40 < latgrid) & (latgrid < -30) & (10 < longrid) & (longrid < 30)
window += (0 < latgrid) & (latgrid < 30) & (60 < longrid) & (longrid < 80)
window += (-70 < latgrid) & (latgrid < -40) & (130 < longrid) & (longrid < 150)
window += (20 < latgrid) & (latgrid < 40) & (125 < longrid) & (longrid < 145)
window += (10 < latgrid) & (latgrid < 30) & (220 < longrid) & (longrid < 250)


Next, generate an SHGrid instance from the input array, plot the grid, expand it in spherical harmonics, and calculate and plot the power spectrum. Note that the first element of the grid corresponds to 0 degrees east and 90 degrees north.

In :
grid_window = pyshtools.SHGrid.from_array(window.astype(np.float64))
fig, ax = grid_window.plot(colorbar=True)

coeffs_window = grid_window.expand()
fig2, ax2 = coeffs_window.plot_spectrum(unit='per_dlogl')

power_window = coeffs_window.spectrum()

/usr/local/lib/python3.7/site-packages/matplotlib/figure.py:457: UserWarning: matplotlib is currently using a non-GUI backend, so cannot show the figure
"matplotlib is currently using a non-GUI backend, "  ## Multiply the random model by the window¶

Multiplication of the global model with the window function localizes the data, and its expansion in spherical harmonics gives a local power spectrum estimate. The interaction of the window function with the random model distorts the spectrum. In particular, the ouput spectrum at degree l is influenced by the input spherical harmonic degrees from l - Lwin to l + Lwin, where Lwin in the spherical harmonic bandwidth of the windowing function.

Let's multiply the data by the window, expand the result in spherical harmonics, and plot the resulting localized power spectrum:

In :
grid_local = grid_global * grid_window
grid_local.plot(colorbar=True)

coeffs_local = grid_local.expand()
coeffs_local.plot_spectrum(unit='per_dlogl')

power_local = coeffs_local.spectrum()

/usr/local/lib/python3.7/site-packages/matplotlib/figure.py:457: UserWarning: matplotlib is currently using a non-GUI backend, so cannot show the figure
"matplotlib is currently using a non-GUI backend, "  ## Compute the coupling matrix¶

The expected distortion of the global spectrum due to the windowing operation can be described by a coupling matrix under the assumption that the model is stochastic, isotropic and stationary. This means that the local (expected) spectra are independent of the position and orientation of the windows. In this case, the expected local spectrum depends only on the power spectrum of the global data and window.

The coupling matrix describes how input power maps to output power. At high degrees, it is approximately symmetric around the diagonal and resembles a convolution in spherical harmonic degree l. At low degrees, however, input power is spread more unevenly into output power. For example, input degree 0 maps preferentially to output degree 4, which is off-diagonal. A similar bias can be seen when describing coupling in 2D Fourier space between coefficients with similar |k|.

Here, we calculate and plot the couplting matrix using the function SHMTCouplingMatrix():

In :
coupling_matrix = pyshtools.spectralanalysis.SHMTCouplingMatrix(lmax, power_window)

fig, (col1, col2) = plt.subplots(1, 2)
col1.imshow(coupling_matrix)
col1.set(xlabel='Input degree', ylabel='Output degree', xlim=(-0.5, 10.5), ylim=(10.5, -0.5),
title='Zoomed coupling matrix');
col2.imshow(coupling_matrix)
col2.set(xlabel='Input degree', ylabel='Output degree', title='Full coupling matrix'); ## Predict the biased spectrum¶

Finally, the coupling matrix can be used to predict the spectrum of the windowed function. SHTOOLS provides also the functions SHBiasK and SHBias that can compute directly the biased spectrum without explicitely computing the coupling matrix. Under some assumptions, the coupling matrix can be inverted allowing the global input power spectrum to be estimated from the localized spectrum. This has to be done with care because as a smoothing operation, windowing leads to loss of information (less resolution in degree l space) and has to be regularized to stabilize the inversion.

Here. we compute the biased spectrum using two methods, mutliplication of the global spectrum by the coupling matrix, and the function SHBias, and then plot the results.

In :
power_local2 = np.dot(coupling_matrix, power)
power_local3 = pyshtools.spectralanalysis.SHBias(power_window, power)

fig, ax = plt.subplots(1, 1)
ax.plot(degrees, power_global, label='Global spectrum')
ax.plot(degrees, power_local, label='Localized spectrum')
ax.plot(power_local2, label='Localized expectation (CouplingMatrix)')
ax.plot(power_local3, 'x', label='Localized expectation (SHBias)')
ax.legend(loc=3)
ax.set(yscale='log', xscale='log', ylabel='Power', xlabel='Spherical harmonic degree'); In [ ]: