Skip to content

Pair-angle distribution function (PADF) and 2D-PADF - #309

Open
cophus wants to merge 50 commits into
electronmicroscopy:devfrom
cophus:padf
Open

cophus wants to merge 50 commits into
electronmicroscopy:devfrom
cophus:padf

Conversation

@cophus

@cophus cophus commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

Pair-angle distribution function (PADF)

PADF from orientation-averaged diffraction, following pypadf (Martin et al.). Primarily Emanuel Tsegaye's work, with Karen Ehrhardt's fixes to the Legendre fit and output.

What's here

  • diffraction/padf.py: PairAngleDistributionFunction (per-pattern angular correlation, median subtraction, Legendre fit with |sin θ| weights, spherical Bessel transform, PADF Θ(r, r′, θ) with an optional taper of the Legendre orders to suppress ringing in θ), plot_g2_g3, simple_plot (side-by-side and fixed-r maps), plot_angular_correlation
    • bandlimit_volume: band-limits a model g3 to the measured q range and l_max
    • padf_from_clusters: model PADF from all interatomic vectors of atomic clusters (2-, 3- and 4-body terms, as the diffraction PADF measures)
  • diffraction/padf2d.py: PairAngleDistributionFunction2D, an in-plane variant (circular harmonics and Hankel transforms) for 2D samples that only rotate in plane, which also retains odd harmonics when Friedel symmetry is broken, plus padf2d_from_clusters
  • diffraction/bispectrum.py: angular bispectrum for Friedel-symmetry breaking
  • diffraction/gr_truth.py: three-body g3 histograms of 3D and 2D atomic models (adapted from Arthur McCray's PDFs_3batch)
  • diffraction/lattices.py, multislice.py: test-image and pattern simulation (2D lattices, random clusters of 3D structures with Weickenmeier-Kohl scattering factors)
  • polar_transform: accepts 2D patterns and 3D stacks as well as 4D-STEM
  • PairDistributionFunction: the default background is now a constant plus one power law, B(k) = c + a (1 + k²/s²)^(−p), with positive parameters so the single scattering f(k) = B − c is positive and monotonic, fit with k / I(k) weighting (the error in F(k)). bg_model="power" with two terms, "known" (N⟨f⟩² + c from tabulated scattering factors) and the previous "gaussian" are options. The low-r density correction now acts over the transform window rather than the background fit range.
  • core.visualization.show_1d (identical to the copy on diffractive_imaging)

Validation (notebooks, not in the repo)

  • bcc Fe / fcc Al, 1600 random orientations: the nearest-neighbor PADF peaks are at 70.4/109.6° and 60.3/90.5/119.7°, and the diagonal and fixed-r maps match the model PADF at the first two shells. A single [001] pattern gives the 90° angles of the projected column lattice instead.
  • Amorphous Si (1000-atom WWW continuous random network from atomode): the PADF of 6400 simulated patterns peaks at 109.6° on the nearest-neighbor shell, and the model PADF from all interatomic vectors peaks at 107.6°. A diffraction PADF contains two-, three- and four-body terms, so for amorphous samples the model reference must include all vector pairs (padf_from_clusters), not only triplets. With the known-scattering-factor background (fit at k > 1 1/Å) over the full k range and the low-r correction below 2 Å, g(r) is zero below the first shell and the first peak matches the model (2.30 Å, height 4.0 vs 4.3).
  • Amorphous Si, 2D-PADF of the projected patterns: the traces at the first and second neighbor distances correlate at 0.98 and 0.96 with the 2D-PADF calculated from the projected atomic positions.
  • 2D lattices (triangular, honeycomb, square, decagonal): the 2D-PADF of a single FFT matches the 2D-PADF calculated from the atomic positions, with peaks at the expected bond angles. In an h-BN multislice series, with q limited to where f(q) > 10⁻³, the odd-harmonic fraction at the bond length increases from 0.003 for one slice to 0.99 for 64 slices, while the centrosymmetric honeycomb stays at 0.000.

🤖 Generated with Claude Code

ectsegaye and others added 19 commits August 22, 2026 12:59
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Take dev's calibration, ellipticity, polar (PDF) and widget removal; keep
2D/3D input support in polar_transform.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ange and l_max

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ine plots

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- padf_from_clusters / padf2d_from_clusters: model PADFs from all
  interatomic vectors (2-, 3-, 4-body terms), band-limited like the data
- lattices.random_clusters / cluster_patterns: kinematic patterns of random
  clusters with the Weickenmeier-Kohl scattering factor
- PairAngleDistributionFunction: l_max and r-grid kwargs; simple_plot in
  physical units with an optional volume to plot
- PairAngleDistributionFunction2D.plot_diagonal: optional volume to plot
- torch in place of scipy except spherical_jn / jv

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…methods for batched correlation

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- subtract_angular_median, used by PairAngleDistributionFunction
- l_taper / n_taper (cos^2 over the harmonic orders) in the data
  reconstructions and in padf_from_clusters / padf2d_from_clusters, to
  suppress ringing from truncating at l_max / n_max
- simple_plot: several volumes side by side, fixed-r slices, r marks,
  color limits from the displayed r range; plot_angular_correlation
- plot_diagonal: several volumes side by side
- random_clusters / cluster_patterns: random_orientation=False keeps the
  cell axes (zone-axis patterns)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Render atoms as sampled Gaussians and divide the Gaussian out; patterns
match a direct structure-factor sum to 6e-5. blur_px applies a real-space
Gaussian blur for partial coherence (a Fourier-space blur truncated at
Nyquist rang along the axes next to the central beam).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rounds

- bg_model='power': c + sum_i a_i (1 + k^2/s_i^2)^(-p_i), 1 or 2 terms, all
  parameters positive so f(k) = B - c is positive and monotonic
- bg_model='known': N <f>^2 + c from tabulated scattering factors (the
  standard ePDF normalization), N, c >= 0 by weighted least squares
- bg_weight='k' or 'k/I' (error in F(k)) besides the py4DSTEM weighting
- windowed_model_pdf: model g(r) through the same k window, for comparison

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- default background: one power law with k / I(k) weighting
- background weights per curve, so batched and single fits agree
- low-r density correction acts over the window, not the bg fit range
- remove windowed_model_pdf
- tests: each background model recovers a background of its own form

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fixes:
- random_clusters works for any cell shape (fractional minimum image,
  radius capped at half the smallest face spacing); hcp clusters were
  missing atoms
- cluster_patterns raises when the cluster does not fit the render grid
  and returns a calibrated Dataset4dstem
- padf_from_clusters / padf2d_from_clusters accept r grids not starting at 0
- PairAngleDistributionFunction: accepts a list of polar datasets (averaged
  angular correlation), fq as a function of q, exposes q, skips the origin
  search when polar data is given, constructor r grid includes r_max, Na = 1
- k/I background weights floor I(k) at 1e-3 of its median

Cleanup:
- one batched Levenberg-Marquardt helper for the power and Gaussian
  backgrounds; 'default' weighting renamed 'py4dstem'; stale docs fixed
- torch Legendre polynomials and Hankel transforms in place of scipy/numpy
- plot_diagonal: full r range by default, (r r')^p weighting as in
  simple_plot, median subtracted over angle for display
- gr_truth_2d folded into gr_truth (identical for 3D cells)
- dead code and stale comments removed

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cophus

cophus commented Oct 26, 2026

Copy link
Copy Markdown
Contributor Author

…ee steps

- simple_plot / plot_diagonal: per-panel r_fixed, theta_marks and r_marks,
  so several structures fit in one row; angle axes end at 180 (or 360) deg
  with 30 (or 60) degree ticks
- plot_angular_correlation and plot_g2_g3 use the same angle ticks
- show_1d: xlim and xticks options

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cophus

cophus commented Oct 29, 2026

Copy link
Copy Markdown
Contributor Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants