Getting started#
Installation#
For a release from PyPI:
python -m pip install petal2d
For a development checkout:
python -m pip install -e ".[test,docs]"
Verify the import:
import petal2d
print(petal2d.__version__)
0.1.0
The central idea#
Suppose \(f(x,y)\) is localized around a point \((x_0,y_0)\). Relative to that point,
At every radius, PETAL2D expands the angular dependence as
The integer \(m\) labels an angular harmonic and \(\rho_m(r)\) tells you where radially that angular structure lives. If the analyzed field is a complex wavefunction, the \(e^{im\theta}\) channels are eigenchannels of the planar angular-momentum operator \(L_z\) about the chosen origin. Their integrated power fractions therefore resolve the wavefunction’s \(L_z\)-sector weights on the analyzed disk. For a density or another real scalar observable, the same \(m\) values describe angular shape rather than hidden wavefunction phase.
First decomposition: an isotropic Gaussian#
import numpy as np
from petal2d import PolarDecomposition
x = np.linspace(-6.0, 6.0, 161)
y = np.linspace(-6.0, 6.0, 161)
def field(x, y):
return np.exp(-(x**2 + y**2))
dec = PolarDecomposition(field, x, y)
With the defaults PETAL2D will:
evaluate the field on the supplied Cartesian domain.
determine the \(L^2\) power centroid.
choose the largest centered polar disk fully contained in the rectangle.
evaluate a callable directly on the polar grid, or interpolate sampled arrays.
perform the angular FFT.
compute radial mode powers.
retain the smallest power-ranked set meeting the requested reconstruction error.
compute radial cutoff diagnostics for every retained harmonic.
Inspect the result:
print("origin:", dec.origin)
print("domain consistency:", dec.domain_consistency)
print("selected pairs:", dec.selected_pairs)
print("reconstruction error (%):", dec.recon_error_measured)
dec.print_spectrum()
origin: (3.533949646070574e-17, 5.478458685715792e-19)
domain consistency: 0.9995311180756926
selected pairs: [(0,)]
reconstruction error (%): 1.266726935300768e-14
m power fraction r_power_support r_amplitude_support r_cutoff retained
----------------------------------------------------------------------------------------------------
0 2.498828e-01 1.000000e+00 2.63027 2.62854 2.63027 True
The tiny nonzero coordinates are floating-point noise around the exact origin \((0,0)\). The physically meaningful result is that the isotropic field is entirely in the \(m=0\) channel to numerical precision.
Plot the decomposition#
import matplotlib.pyplot as plt
dec.plot_harmonics_with_hist("Isotropic Gaussian")
dec.plot_original_vs_reconstructions("Isotropic Gaussian")
plt.show()
The retained radial profile and power-ranked angular spectrum. The dashed line marks cutoff_radius for the retained mode.#
Original field and retained-mode reconstruction using the same color normalization.#
The cutoff radius is a diagnostic, not a numerical truncation of the reconstruction. It is the larger of two independently computed radii: one that leaves no more than the requested radial power tail, and one that lies beyond the last sampled radius whose amplitude exceeds the configured relative-amplitude threshold. See Radial cutoff diagnostics for the exact definitions.
Reconstruct the field#
f_adaptive = dec.reconstruct()
f_all = dec.reconstruct("all")
f_m0 = dec.reconstruct([0])
print(dec.reconstruction_error())
print(dec.reconstruction_error("all"))
print(dec.reconstruction_error([0]))
1.266726935300768e-14
1.406744239744267e-14
1.266726935300768e-14
The returned error is the polar-area-weighted relative \(L^2\) error in percent. Here all three reconstructions are effectively exact because the field contains only \(m=0\).
Sampled arrays instead of callables#
PETAL2D also accepts a two-dimensional array sampled on the Cartesian grid:
X, Y = np.meshgrid(x, y, indexing="ij")
sampled = np.exp(-(X**2 + Y**2))
dec_sampled = PolarDecomposition(
sampled,
x,
y,
interp_method="cubic",
)
print(dec_sampled.selected_pairs)
print(dec_sampled.recon_error_measured)
[(0,)]
4.6017946300227716e-05
The sampled version retains the same physically expected \(m=0\) channel. Its small nonzero reconstruction error is controlled by interpolation and finite-grid effects rather than direct callable evaluation.
The array shape must be (len(x), len(y)), and x and y must be strictly increasing and uniformly spaced. Cubic interpolation is the default because validation shows a large accuracy advantage for a modest runtime cost. A more demanding sampled-data example with measured interpolation leakage is developed in Example 4: sampled numerical data.
What to check after every decomposition#
A useful first-pass checklist is:
target_reached: did the admissible angular spectrum satisfyrecon_err_tol?selected_pairs: which adaptive angular units were retained?recon_error_measured: what weighted reconstruction error was measured directly?domain_consistency: does the analyzed polar disk contain essentially the same \(L^2\) weight as the supplied Cartesian domain?print_spectrum(): what are the retained mode powers and radial cutoff diagnostics?
For the Gaussian run above:
print(dec.target_reached)
print(dec.selected_pairs)
print(dec.recon_error_measured)
print(dec.domain_consistency)
True
[(0,)]
1.266726935300768e-14
0.9995311180756926
Continue with Choosing parameters for parameter selection, Guided examples for progressively more demanding examples, and Theory for the derivation.