Polar decomposition#

class petal2d.PolarDecomposition(f_xy, x, y, Nr=None, Ntheta=512, rmax=None, m_abs_max=None, recon_err_tol=1.0, radial_power_tail_fraction=1e-06, radial_relative_amplitude_threshold=0.001, origin='centroid', normalize=False, interp_method='cubic')[source]#

Bases: Mapping

Polar angular-harmonic decomposition of a localized 2D scalar field.

PolarDecomposition converts a scalar field from Cartesian coordinates to a polar representation about a chosen origin and expands the angular dependence at every radius as

\[f(r,\theta) = \sum_m \rho_m(r)e^{im\theta}.\]

PETAL2D defines the radial power of angular harmonic m as

\[P_m = \int_0^{r_{\max}} |\rho_m(r)|^2 r\,dr.\]

The angular FFT is computed first for the complete discrete spectrum. Adaptive truncation then ranks admissible angular channels by power and retains the smallest set that satisfies recon_err_tol. For real input, nonzero conjugate harmonics (+m, -m) are selected together so the automatic reconstruction remains real-valued.

Parameters:
  • f_xy (callable or ndarray of shape (Nx, Ny)) – Input field. A callable must accept array-valued x and y and return either a scalar or an array with the supplied mesh shape. Callable inputs are evaluated directly on the final polar grid after centering. Array inputs are interpolated from the Cartesian grid.

  • x (array_like) – One-dimensional, finite, strictly increasing, uniformly spaced Cartesian coordinates. For array input, f_xy.shape must equal (len(x), len(y)).

  • y (array_like) – One-dimensional, finite, strictly increasing, uniformly spaced Cartesian coordinates. For array input, f_xy.shape must equal (len(x), len(y)).

  • Nr (int or None, optional) – Number of radial samples. If None, use min(len(x), len(y)). The radial grid includes both zero and rmax.

  • Ntheta (int, default=512) – Number of uniformly spaced angular samples on [0, 2*pi).

  • rmax (float or None, optional) – Maximum analyzed polar radius. If None, use the largest disk centered on origin that is fully contained in the Cartesian rectangle. The resulting value is available as safe_rmax.

  • m_abs_max (int or None, optional) – Optional bound on admissible |m| during adaptive selection. It may not exceed m_nyquist. This does not change the complete spectrum stored in m_all and rho_all.

  • recon_err_tol (float, default=1.0) – Target relative polar-area-weighted L2 reconstruction error, in percent. The selector uses a Parseval power identity; target_reached reports whether the admissible spectrum can meet the target.

  • radial_power_tail_fraction (float, default=1e-6) – Maximum fraction of a retained mode’s integrated radial power allowed outside radial_power_support_radius[m]. Must satisfy 0 <= radial_power_tail_fraction < 1.

  • radial_relative_amplitude_threshold (float, default=1e-3) – Fraction of max_r |rho_m(r)| used to define radial_amplitude_support_radius[m]. The outermost threshold crossing is used so significant outer radial lobes are retained. Must satisfy 0 <= value < 1.

  • origin ({"centroid"} or array_like of length 2, default="centroid") – Expansion origin. "centroid" uses the Cartesian L2 power centroid of |f|^2. A finite length-two input is interpreted as the explicit Cartesian coordinate (x0, y0).

  • normalize (bool, default=False) – If True, divide the Cartesian field by the square root of its Cartesian L2 power before polar analysis.

  • interp_method ({"linear", "cubic"}, default="cubic") – Cartesian-to-polar interpolation used only for sampled array input.

origin#

Expansion origin (x0, y0) actually used.

Type:

tuple of float

centroid#

Numerically evaluated Cartesian L2 power centroid, regardless of whether an explicit origin was requested.

Type:

tuple of float

r#

Radial sample coordinates.

Type:

ndarray of shape (Nr,)

theta#

Angular sample coordinates in radians.

Type:

ndarray of shape (Ntheta,)

safe_rmax#

Radius of the largest centered disk fully contained in the Cartesian rectangle.

Type:

float

m_nyquist#

Largest representable absolute angular-frequency class implied by Ntheta.

Type:

int

nyquist_mode#

For even Ntheta, the single NumPy FFT Nyquist index -Ntheta/2; otherwise None.

Type:

int or None

f_polar#

Input field evaluated or interpolated on the polar grid.

Type:

ndarray of shape (Nr, Ntheta)

f_recon#

Adaptive retained-mode reconstruction.

Type:

ndarray of shape (Nr, Ntheta)

cartesian_power#

Cartesian physical L2 power after optional normalization.

Type:

float

polar_power#

Physical L2 power in the analyzed polar domain.

Type:

float

domain_consistency#

Ratio polar_power / cartesian_power. It quantifies captured field weight, not pointwise interpolation accuracy.

Type:

float

is_real_input#

Whether the analyzed input is real-valued to PETAL2D’s numerical tolerance.

Type:

bool

m_all#

Complete discrete FFT harmonic indices in NumPy FFT order.

Type:

ndarray of shape (Ntheta,)

rho_all#

Complete radial harmonic coefficients.

Type:

ndarray of shape (Nr, Ntheta)

powers_all#

Complete radial mode powers P_m.

Type:

ndarray of shape (Ntheta,)

power_fracs_all#

powers_all normalized by their sum.

Type:

ndarray of shape (Ntheta,)

m_sorted#

Adaptively retained harmonics, ordered by selected power units.

Type:

list of int

rho#

Retained radial harmonic coefficients keyed by m.

Type:

dict[int, ndarray]

power_fracs#

Retained mode power fractions keyed by m.

Type:

dict[int, float]

selected_pairs#

Adaptive selection units actually retained. Real fields use conjugate (+m, -m) pairs except for m=0 and the even-grid Nyquist singleton. Complex fields use singleton channels.

Type:

list of tuple[int, …]

selected_pair_powers#

Power of each retained adaptive selection unit.

Type:

ndarray

selected_pair_power_fracs#

Fractional power of each retained adaptive selection unit.

Type:

ndarray

retained_power_fraction#

Fraction of the complete discrete polar spectral power retained.

Type:

float

recon_error#

Parseval-predicted adaptive reconstruction error in percent.

Type:

float

recon_error_measured#

Directly evaluated polar weighted L2 reconstruction error in percent.

Type:

float

target_reached#

Whether the admissible angular spectrum satisfies recon_err_tol.

Type:

bool

parseval_relative_error#

Relative mismatch between direct polar power and the sum of discrete harmonic powers; useful as a numerical consistency diagnostic.

Type:

float

radial_power_support_radius#

Per-retained-mode smallest radius satisfying the integrated radial power-tail criterion.

Type:

dict[int, float]

radial_amplitude_support_radius#

Per-retained-mode outermost radius satisfying the relative-amplitude criterion.

Type:

dict[int, float]

cutoff_radius#

Per-retained-mode maximum of the power- and amplitude-support radii. This is a diagnostic only and never truncates reconstruction.

Type:

dict[int, float]

Notes

PETAL2D uses the Fourier convention

\[\rho_m(r) = \frac{1}{2\pi}\int_0^{2\pi} f(r,\theta)e^{-im\theta}\,d\theta.\]

Therefore

\[\|f\|_{L^2(D)}^2 = 2\pi\sum_m P_m.\]

The factor 2*pi cancels from all power fractions and reconstruction error ratios. The package documentation contains full proofs of Parseval error certification, real-field conjugacy, rotation covariance, centering, discrete aliasing, and rotational selection rules.

Examples

Analyze a real p_x-like orbital. The automatic selector retains the conjugate pair (+1, -1):

>>> import numpy as np
>>> from petal2d import PolarDecomposition
>>> x = np.linspace(-5.0, 5.0, 101)
>>> y = np.linspace(-5.0, 5.0, 101)
>>> def px(x, y):
...     return x * np.exp(-0.5 * (x*x + y*y))
>>> dec = PolarDecomposition(px, x, y, Ntheta=128)
>>> dec.selected_pairs
[(1, -1)]
>>> dec.reconstruction_error() < 1e-10
True

See also

numpy.fft.fft

Discrete Fourier transform used for the angular spectrum.

scipy.ndimage.map_coordinates

Interpolation used for sampled input.

reconstruct(modes=None)[source]#

Reconstruct the polar field from selected angular harmonics.

Parameters:

modes (None, {"all"}, or iterable of int, optional) – Harmonics to include. None uses the adaptively retained spectrum. "all" uses the complete discrete FFT spectrum. An iterable uses exactly the requested harmonics. Explicit mode lists are never silently conjugate-paired.

Returns:

Field reconstructed on the stored polar grid. For real input, adaptive, complete, or explicitly conjugate-closed requests return a real array. An intentionally asymmetric explicit subset can return a complex array.

Return type:

ndarray of shape (Nr, Ntheta)

Notes

Reconstruction uses the full analyzed radial grid. cutoff_radius is diagnostic and never crops the result.

reconstruction_error(modes=None)[source]#

Return polar-area-weighted relative L2 reconstruction error.

Parameters:

modes (None, {"all"}, or iterable of int, optional) – Uses the same mode-selection semantics as reconstruct().

Returns:

Relative weighted L2 error in percent.

Return type:

float

Notes

The norm uses the polar area measure r dr dtheta. For the adaptive subset this directly measured value should agree with recon_error, which is predicted from omitted spectral power by Parseval’s identity.

spectrum_table(retained_only=True)[source]#

Return spectrum rows as dependency-free Python dictionaries.

Parameters:

retained_only (bool, default=True) – If True, return only adaptively retained harmonics. If False, return every discrete FFT harmonic.

Returns:

Rows sorted by decreasing individual harmonic power. Every row has keys m, power, power_fraction, radial_power_support_radius, radial_amplitude_support_radius, cutoff_radius, and retained. Radius entries are None for non-retained modes.

Return type:

list of dict

Notes

power_fraction refers to an individual harmonic, whereas selected_pair_power_fracs refers to adaptive selection units when real-field conjugate pairing is active. No pandas dependency is used.

print_spectrum(retained_only=True, file=None)[source]#

Print a compact human-readable table of the angular spectrum.

Parameters:
  • retained_only (bool, default=True) – Print only retained modes by default. Set False to print the complete discrete FFT spectrum, including roundoff/interpolation leakage.

  • file (file-like object or None, optional) – Destination passed to print(). None writes to standard output.

Return type:

None

plot_harmonics_with_hist(title='')[source]#

Plot retained radial profiles and ranked angular power fractions.

Parameters:

title (str, optional) – Figure-level title.

Returns:

  • fig (matplotlib.figure.Figure) – Created figure.

  • axes (ndarray of matplotlib.axes.Axes) – Two axes: radial profiles on the left and retained power-ranked harmonics on the right.

Notes

The left panel shows |rho_m(r)| for each retained harmonic. A color-matched dashed line marks cutoff_radius[m]. The cutoff radius is diagnostic only and never truncates reconstruction.

The right panel is categorical rather than a linear m axis. Only retained harmonics are shown, ordered from greatest to least fractional power, so sparse spectra with large angular indices do not create large empty ranges.

plot_original_vs_reconstructions(title='Reconstruction comparison', display_radius=None)[source]#

Compare the analyzed polar field with the adaptive reconstruction.

Parameters:
  • title (str, default="Reconstruction comparison") – Figure-level title.

  • display_radius (float or None, optional) – Half-width of the displayed Cartesian window in the same length units as x and y. If None, use 115% of the largest retained cutoff_radius, capped at the analyzed radial-domain radius. This changes only the viewport.

Returns:

  • fig (matplotlib.figure.Figure) – Created figure.

  • axes (ndarray of matplotlib.axes.Axes) – Two axes containing the analyzed field and adaptive reconstruction.

Notes

Real sign-changing fields are displayed with sign preserved and a symmetric zero-centered color scale. Real nonnegative fields use a sequential map. Complex fields are displayed as magnitudes. Both panels always share the same color normalization.