The harmonic operator of rank takes a unit vector , tensored with
itself times, to its Cartesian harmonic, the Cartesian counterpart of a spherical
harmonic. takes one copy of for each of its Cartesian indices, so the
weight-2 harmonic is act(H, a, a):
import numpy as np
from natto import act, get_harmonic_operator
a = np.array([0.0, 0.0, 1.0])
H = get_harmonic_operator(2)
Y = act(H, a, a)
print(f"H of weight 2: {H}\n")
print(f"H · a a, the weight-2 harmonic of a:\n{Y}")H of weight 2: +3/4 δ_Aa δ_Bb +3/4 δ_Ab δ_Ba -1/2 δ_AB δ_ab
H · a a, the weight-2 harmonic of a:
[[-0.5 0. 0. ]
[ 0. -0.5 0. ]
[ 0. 0. 1. ]]
As with the other operators, act evaluates into an array and contracts it with
its inputs by numpy.einsum. evaluate gives the array and the rule, with one operand
for each copy of :
from natto import evaluate
H_array, rule = evaluate(H)
Y = np.einsum(rule, H_array, a, a)
print(f"einsum rule: {rule}\n")
print(f"the weight-2 harmonic of a:\n{Y}")einsum rule: abAB,...A,...B->...ab
the weight-2 harmonic of a:
[[-0.5 0. 0. ]
[ 0. -0.5 0. ]
[ 0. 0. 1. ]]
By default, normalize="legendre", is scaled so that the harmonic of one unit
vector contracted with the tensored copies of another gives the Legendre polynomial
of the angle between them. normalize="none" leaves it unscaled.