Can you make a list of first spin-2 spherical harm...
Created on: July 31, 2025
Created on: July 31, 2025
Can you make a list of first spin-2 spherical harmonics up to l=3? What formula did you use to construct these functions?
pythonimport sympy as sp theta, phi = sp.symbols('theta phi', real=True) l=3 m=2 s=2 # sympy wigner d function small d maybe sp.functions.special.tensor_functions.wigner_d? dir(sp.functions.special) ['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__', 'bessel', 'beta_functions', 'bsplines', 'delta_functions', 'elliptic_integrals', 'error_functions', 'gamma_functions', 'hyper', 'mathieu_functions', 'polynomials', 'singularity_functions', 'spherical_harmonics', 'tensor_functions', 'zeta_functions']
pythondir(sp.functions.special.tensor_functions) ['Eijk', 'Function', 'Integer', 'KroneckerDelta', 'LeviCivita', 'S', 'SYMPY_INTS', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'default_sort_key', 'eval_levicivita', 'fuzzy_not', 'has_dups', 'prod']
pythondir(sp.functions.special.spherical_harmonics) ['Abs', 'ArgumentIndexError', 'Dummy', 'Function', 'I', 'S', 'Ynm', 'Ynm_c', 'Znm', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', '_x', 'assoc_legendre', 'cos', 'cot', 'exp', 'factorial', 'pi', 'sin', 'sqrt', 'sympify']
pythonimport sympy as sp theta, phi = sp.symbols('theta phi', real=True) def Y(l,m): return sp.functions.special.spherical_harmonics.Ynm(l,m,theta,phi) # complex spherical harmonic def eth(f, s_in): sin = sp.sin(theta) return - (sin**s_in) * ( sp.diff( (sin**(-s_in)) * f, theta) + sp.I/sp.sin(theta) * sp.diff( (sin**(-s_in))*f, phi) ) def sY(l,m,s): # for s>=0 integer if s<0: raise NotImplementedError f = Y(l,m) s_in = 0 for k in range(s): f = eth(f, s_in) s_in += 1 norm = sp.sqrt(sp.factorial(l - s)/sp.factorial(l + s)) return sp.simplify(norm * f) sY22 = sp.simplify(sY(2,2,2)) sY21 = sp.simplify(sY(2,1,2)) sY20 = sp.simplify(sY(2,0,2)) sY2m1 = sp.simplify(sY(2,-1,2)) sY2m2 = sp.simplify(sY(2,-2,2)) sY22 sqrt(6)*(cos(theta) - 1)**2*Ynm(2, 2, theta, phi)/(6*sin(theta)**2)
pythonimport sympy as sp theta, phi = sp.symbols('theta phi', real=True) def wigner_d(l,m,mp,theta): # small-d Wigner k=sp.symbols('k', integer=True) # pre-factor sqrt factorials pref = sp.sqrt(sp.factorial(l+m)*sp.factorial(l-m)*sp.factorial(l+mp)*sp.factorial(l-mp)) expr = 0 # determine k limits such that factorial args nonnegative # constraints: l+m-k>=0 => k<=l+m # k>=0 # m'-m + k >= 0 => k >= m - m' # l - m' - k >=0 => k <= l - m' kmin = max(0, m - mp) kmax = min(l+m, l - mp) for ki in range(kmin, kmax+1): term = ((-1)**(ki-m+mp) * pref / (sp.factorial(l+m-ki)*sp.factorial(ki)*sp.factorial(mp-m+ki)*sp.factorial(l-mp-ki)) * sp.cos(theta/2)**(2*l + m - mp - 2*ki) * sp.sin(theta/2)**(mp - m + 2*ki)) expr += term return sp.simplify(expr) def sY_by_wigner(l,m,s=2): d = wigner_d(l,m,-s,theta) return sp.simplify(sp.sqrt((2*l+1)/(4*sp.pi)) * d * sp.exp(sp.I*m*phi)) for L in (2,3): res = {} for m in range(-L, L+1): res[m] = sp.simplify(sp.together(sY_by_wigner(L,m,2))) print(L, res[2]) print(L, res[1]) print(L, res[0])
pythonfrom sympy import sin, cos, exp, I, pi, sqrt def simplify_half_angle(expr): # Replace sin(θ/2)^odd * cos(θ/2)^odd patterns etc expr = sp.expand(expr) # We'll rewrite powers explicitly expr = sp.together(expr) expr = sp.simplify(expr) # Replace sin(θ/2)*cos(θ/2) -> sin θ /2 expr = expr.replace(lambda x: x.is_Pow and x.base==sin(theta/2) and x.exp==1, sin(theta/2)) expr = expr.replace(lambda x: x.is_Pow and x.base==cos(theta/2) and x.exp==1, cos(theta/2)) # Use identities: sin(θ/2)^2 = (1 - cosθ)/2, cos(θ/2)^2 = (1 + cosθ)/2 expr = expr.rewrite(sp.cos) expr = sp.simplify(expr) # Replace sin(θ/2)*cos(θ/2) with sinθ/2 expr = expr.subs(sp.sin(theta/2)*sp.cos(theta/2), sp.sin(theta)/2) # Now replace even powers expr = expr.subs(sp.sin(theta/2)**2, (1-sp.cos(theta))/2) expr = expr.subs(sp.cos(theta/2)**2, (1+sp.cos(theta))/2) # Higher powers: reduce using above repeatedly expr = sp.simplify(sp.expand(expr)) return expr L=3 for m in range(-L,L+1): expr = sY_by_wigner(L,m,2) print(m, sp.simplify(sp.expand_trig(expr.rewrite(sp.cos))))
pythonimport random import cmath import math def sY2_formula_l2(m,theta,phi): if m==2: return math.sqrt(5/(64*math.pi))* (1-math.cos(theta))**2 * cmath.exp(2j*phi) if m==1: return math.sqrt(5/(16*math.pi))* math.sin(theta)*(1-math.cos(theta)) * cmath.exp(1j*phi) if m==0: return math.sqrt(15/(32*math.pi))* (math.sin(theta)**2) if m==-1: return -math.sqrt(5/(16*math.pi))* math.sin(theta)*(1+math.cos(theta)) * cmath.exp(-1j*phi) if m==-2: return math.sqrt(5/(64*math.pi))* (1+math.cos(theta))**2 * cmath.exp(-2j*phi) raise def sY_wigner(l,m,theta,phi): # from earlier sympy result but numeric version using that function is heavy; let's just evaluate sympy expression we computed for given l,m expr = sY_by_wigner(l,m,2) f = sp.lambdify((theta,phi), expr, 'numpy') import numpy as np return complex(f(theta,phi)) theta_val = 1.1 phi_val = 0.7 for m in range(-2,3): a = sY2_formula_l2(m,theta_val,phi_val) b = sY_wigner(2,m,theta_val,phi_val) print(m, a, b, abs(a-b))
pythondef sY_wigner_numeric(l,m,theta_val,phi_val): expr = sY_by_wigner(l,m,2) f = sp.lambdify((theta,phi), expr, 'numpy') import numpy as np return complex(f(theta_val,phi_val)) theta_val = 1.1 phi_val = 0.7 for m in range(-2,3): a = sY2_formula_l2(m,theta_val,phi_val) b = sY_wigner_numeric(2,m,theta_val,phi_val) print(m, abs(a-b))
pythondef sY2_formula_l2_correct(m,theta,phi): if m==2: return math.sqrt(5/(64*math.pi))* (1-math.cos(theta))**2 * cmath.exp(2j*phi) if m==1: return math.sqrt(5/(16*math.pi))* math.sin(theta)*(1-math.cos(theta)) * cmath.exp(1j*phi) if m==0: return math.sqrt(15/(32*math.pi))* (math.sin(theta)**2) if m==-1: return math.sqrt(5/(16*math.pi))* math.sin(theta)*(1+math.cos(theta)) * cmath.exp(-1j*phi) if m==-2: return math.sqrt(5/(64*math.pi))* (1+math.cos(theta))**2 * cmath.exp(-2j*phi) for m in range(-2,3): a = sY2_formula_l2_correct(m,theta_val,phi_val) b = sY_wigner_numeric(2,m,theta_val,phi_val) print(m, abs(a-b))
pythondef sY2_formula_l3(m,theta,phi): if m==3: return math.sqrt(42)/(16*math.sqrt(math.pi)) * math.sin(theta) * (1-math.cos(theta))**2 * cmath.exp(3j*phi) if m==2: return math.sqrt(7)/(8*math.sqrt(math.pi)) * (3*math.cos(theta)+2) * (1-math.cos(theta))**2 * cmath.exp(2j*phi) if m==1: return math.sqrt(70)/(16*math.sqrt(math.pi)) * (3*math.cos(theta)+1) * math.sin(theta) * (1-math.cos(theta)) * cmath.exp(1j*phi) if m==0: return math.sqrt(210)/(8*math.sqrt(math.pi))* (math.sin(theta)**2) * math.cos(theta) if m==-1: return math.sqrt(70)/(16*math.sqrt(math.pi)) * (3*math.cos(theta)-1) * math.sin(theta) * (1+math.cos(theta)) * cmath.exp(-1j*phi) if m==-2: return math.sqrt(7)/(8*math.sqrt(math.pi)) * (3*math.cos(theta)-2) * (1+math.cos(theta))**2 * cmath.exp(-2j*phi) if m==-3: return - math.sqrt(42)/(16*math.sqrt(math.pi)) * math.sin(theta) * (1+math.cos(theta))**2 * cmath.exp(-3j*phi) for m in range(-3,4): a = sY2_formula_l3(m,theta_val,phi_val) b = sY_wigner_numeric(3,m,theta_val,phi_val) print(m, abs(a-b))
Below are the (spin-weight s = 2) spherical harmonics for the first allowed multipoles, ℓ = 2 and ℓ = 3. I use the standard Condon–Shortley phase and the usual normalization
Angles satisfy .
(As a check, note the symmetry .)
(A) Wigner-d definition
where is the (real) Wigner small- matrix element.
I generated the explicit forms above by evaluating and simplifying.
(B) Spin-raising (eth) operator
Starting from the scalar and applying twice gives
which yields the same expressions as above.
If you’d like, I can also list the versions (often used in gravitational-wave work) or provide these in half-angle form.