4. Rotations and Spherical Harmonics

4.1. Wigner d Matrices

wigner_d(two_j, two_m, two_n, beta) evaluates the real reduced rotation matrix element \(d^j_{mn}(\beta)\). Angular-momentum arguments are doubled integers and beta is in radians.

const double value = wigner::wigner_d(2, 2, 0, beta);

Here the call requests \(d^1_{1,0}(\beta)\). Invalid spin states return zero. For fixed j and beta, the reduced Wigner matrix is real and orthogonal:

\[\sum_{n=-j}^{j} d^j_{mn}(\beta)d^j_{m'n}(\beta) =\delta_{mm'}.\]

Equivalently, its columns obey

\[\sum_{m=-j}^{j} d^j_{mn}(\beta)d^j_{mn'}(\beta) =\delta_{nn'}.\]

The familiar row-normalization relation follows from the first equation by setting \(m'=m\).

4.2. Wigner D Matrices

wigner_D adds the outer Euler-angle phases and returns std::complex<double>:

const auto value = wigner::wigner_D(
    two_j, two_m, two_n,
    alpha, beta, gamma);

The convention is stated explicitly in Conventions. This is important when using these matrices to rotate helicity amplitudes or define subduction coefficients, since active/passive rotation choices can complex-conjugate the result.

The full matrix is unitary rather than merely real-orthogonal:

\[\sum_{n=-j}^{j} D^j_{mn}(\alpha,\beta,\gamma) D^{j*}_{m'n}(\alpha,\beta,\gamma) =\delta_{mm'}.\]

4.3. SU(2) Characters

character(two_j, omega) evaluates

\[\chi^j(\omega)= \frac{\sin[(2j+1)\omega/2]}{\sin(\omega/2)}.\]

The removable limits at zero, \(\pi\), and \(2\pi\) are handled explicitly. Characters are useful for representation checks and group projection formulas without constructing the complete rotation matrix.

4.4. Complex Spherical Harmonics

spherical_harmonic implements standard complex \(Y_l^m(\theta,\phi)\) with the Condon-Shortley phase. Unlike the Wigner symbols, l and m are ordinary integers.

The angular overload accepts theta and phi. The Cartesian overload accepts x, y, and z and uses only the vector’s direction:

const auto angular = wigner::spherical_harmonic(l, m, theta, phi);
const auto cartesian = wigner::spherical_harmonic(l, m, x, y, z);

The complete example evaluates both forms at the same direction:

 1// SPDX-License-Identifier: MIT
 2
 3/**
 4 * @file spherical_harmonics.cpp
 5 * @brief Example evaluations of complex spherical harmonics.
 6 */
 7
 8#include <cmath>
 9#include <complex>
10#include <iostream>
11
12#include <wigner/wigner.hpp>
13
14int main()
15{
16    const double theta = 0.7;
17    const double phi = 1.1;
18    const double r = 2.0;
19    const double x = r * std::sin(theta) * std::cos(phi);
20    const double y = r * std::sin(theta) * std::sin(phi);
21    const double z = r * std::cos(theta);
22
23    const std::complex<double> y00 = wigner::spherical_harmonic(0, 0, theta, phi);
24    const std::complex<double> y10 = wigner::spherical_harmonic(1, 0, theta, phi);
25    const std::complex<double> y11 = wigner::spherical_harmonic(1, 1, theta, phi);
26    const std::complex<double> y11_cartesian = wigner::spherical_harmonic(1, 1, x, y, z);
27    const double gaunt = wigner::gaunt(1, 1, 1, -1, 0, 0);
28
29    std::cout << "theta = " << theta << ", phi = " << phi << '\n';
30    std::cout << "r = (" << x << ", " << y << ", " << z << ")\n";
31    std::cout << "Y_0^0(theta, phi)  = " << y00 << '\n';
32    std::cout << "Y_1^0(theta, phi)  = " << y10 << '\n';
33    std::cout << "Y_1^1(theta, phi)  = " << y11 << '\n';
34    std::cout << "Y_1^1(r)           = " << y11_cartesian << '\n';
35    std::cout << "Gaunt(1,1;1,-1;0,0) = " << gaunt << '\n';
36    std::cout << "-Y_0^0(theta, phi)  = " << -y00 << '\n';
37
38    return 0;
39}

At the zero Cartesian vector, no direction exists; the implementation returns zero rather than choosing an arbitrary axis.

4.5. Gaunt Coefficients

gaunt evaluates the integral of three complex spherical harmonics,

\[\int d\Omega\, Y_{l_1m_1}(\Omega)Y_{l_2m_2}(\Omega)Y_{l_3m_3}(\Omega),\]

through two Wigner 3j symbols. It is nonzero only when the projection sum vanishes, the orbital angular momenta satisfy the triangle condition, and \(l_1+l_2+l_3\) is even.

const double integral = wigner::gaunt(
    l1, m1, l2, m2, l3, m3);

Using gaunt instead of reproducing the formula in client code keeps the spherical-harmonic and Wigner-symbol phase conventions synchronized.