Working with spherical harmonics
20/09/22
This notebook gives a brief introduction to some of the conventions and functionality for handling spherical harmonics and related functions, using low-level ePSproc routines. Note some higher-level routines are available in the base class.
Expansions in (complex) spherical harmonics
In general, expansions in complex harmonics are used, with expansion parameters \(\beta_{L,M}\). A given function is then written as:
For additional control and conversion, the SHtools library can also be used - this includes some useful utility functions including converting between different forms (e.g. real and complex forms), and basic plotters.
For use of real spherical harmonics, see the ‘working with real harmonics notebook’.
Expansions in Legendre polynomials
For cylindrically-symmetric cases (\(m=0\)), general expansions in Legendre polynomials are also sufficient and often used:
Where the “Legendre” and “spherical harmonic” expansion parameters for the harmonics defined above can be related by:
Expansions in symmetrized harmonics
In some case, symmetrized or generalised harmonics are useful. These are defined similarly, but with expansion coeffs additionally set by (point group) symmetry. A basic implementation can be found in the PEMtk package, defined as:
Where the \(b_{hl\lambda}^{\Gamma\mu}\) are defined by symmetry. General function expansions can then be written as a set of spherical harmonics including symmetrization, or an equivalent expansion in symmetrized harmonics (here not all indices may be necessary):
Numerical implementation
Various tools are currently implemented in ePSProc (as of Sept. 2022), and are illustrated below. In particular:
ep.sphCalccontains the base routines for generation of harmonics.ep.sphPlotimplements basic plotting routines.ep.sphFuncs.sphConvimplements additional handling and tools, including conversion routines.
The base class implements some more sophisticated plotting options.
The default routines in ePSproc make use of scipy.special.sph_harm as the default calculation routine. Note the \((\theta, \phi)\) definition, and normalisation, corresponding to the usual “physics” convention (\(\theta\) defined from the z-axis, includes the Condon-Shortley phase, and orthonormalised):
For more details, see also Wikipaedia, which has matching defintions plus further discussion. Note that in the Scipy routines the Condon-Shortley phase term, \((-1)^m\), is actually included in the associated Legendre polynomial function \(P^m_l\), but is written explicitly above.
Conjugates and \(\pm m\) sign swaps are implemented in ep.sphFuncs.sphConv.sphConj as per Blum:
Imports
[1]:
import xarray as xr
import pandas as pd
import numpy as np
import epsproc as ep
# Set compact XR repr
xr.set_options(display_expand_data = False)
OMP: Info #273: omp_set_nested routine deprecated, please use omp_set_max_active_levels instead.
* sparse not found, sparse matrix forms not available.
* natsort not found, some sorting functions not available.
* Hvplot not found, some hvPlotters may not be available. See https://hvplot.holoviz.org/user_guide/Gridded_Data.html for package details.
* Setting plotter defaults with epsproc.basicPlotters.setPlotters(). Run directly to modify, or change options in local env.
* Set Holoviews with bokeh.
* pyevtk not found, VTK export not available.
[1]:
<xarray.core.options.set_options at 0x7fe60c370b50>
Computing spherical harmonics on a grid
A basic wrapper for the backends is provided by ep.sphCalc. This computes spherical harmonics for all orders up to Lmax, for a given angular resolution or set of angles, and returns an Xarray.
Details of the nature of the harmonics is output in the Xarray, as self.attrs['harmonics']. In most cases this will be used by other functions as required, or can be overridden at the function call.
[2]:
# Compute harmonics for 50x50 (theta,phi) grid
Isph = ep.sphCalc(Lmax = 2, res = 50)
Isph
[2]:
<xarray.DataArray 'YLM' (LM: 9, Phi: 50, Theta: 50)>
(0.28209479177387814+0j) (0.28209479177387814+0j) ... 0j
Coordinates:
* LM (LM) object MultiIndex
* l (LM) int64 0 1 1 1 2 2 2 2 2
* m (LM) int64 0 -1 0 1 -2 -1 0 1 2
* Phi (Phi) float64 0.0 0.1282 0.2565 0.3847 ... 5.899 6.027 6.155 6.283
* Theta (Theta) float64 0.0 0.06411 0.1282 0.1923 ... 3.013 3.077 3.142
Attributes:
dataType: YLM
long_name: Spherical harmonics
harmonics: {'dtype': 'Complex harmonics', 'kind': 'complex', 'normType':...
units: arb[3]:
# Compute harmonics for 50x25 (theta,phi) grid
I = ep.sphCalc(Lmax = 2, res = [50,25])
I
[3]:
<xarray.DataArray 'YLM' (LM: 9, Phi: 25, Theta: 50)>
(0.28209479177387814+0j) (0.28209479177387814+0j) ... 0j
Coordinates:
* LM (LM) object MultiIndex
* l (LM) int64 0 1 1 1 2 2 2 2 2
* m (LM) int64 0 -1 0 1 -2 -1 0 1 2
* Phi (Phi) float64 0.0 0.2618 0.5236 0.7854 ... 5.498 5.76 6.021 6.283
* Theta (Theta) float64 0.0 0.06411 0.1282 0.1923 ... 3.013 3.077 3.142
Attributes:
dataType: YLM
long_name: Spherical harmonics
harmonics: {'dtype': 'Complex harmonics', 'kind': 'complex', 'normType':...
units: arb[4]:
# The sum of these can be plotted directly... although may not be very useful
ep.sphSumPlotX(I)
*** WARNING: plot dataset has min value < 0, min = (-0.8400465549302628-0.43869471033269675j). This may be unphysical and/or result in plotting issues.
Sph plots:
Plotting with facetDims=Eke, pType=a with backend=mpl.
[4]:
[<Figure size 720x480 with 1 Axes>]
[5]:
# To plot components subselect on l first
# Set backend to Plotly for interactive panel plot
ep.sphSumPlotX(I.sel(l=2), facetDim = 'm', backend='pl');
*** WARNING: plot dataset has min value < 0, min = (-0.38607574059609784-9.456128398989102e-17j). This may be unphysical and/or result in plotting issues.
Sph plots:
Plotting with facetDims=m, pType=a with backend=pl.
*** Plotting for [1,1,0]
*** Plotting for [1,2,1]
*** Plotting for [1,3,2]
*** Plotting for [1,4,3]
*** Plotting for [1,5,4]