Skip to content

feat: azimuthal average of a centred diffraction pattern - #39

Merged
Paullo9 merged 4 commits into
mainfrom
issue-9-azimuthal-average
Oct 31, 2026
Merged

Paullo9 merged 4 commits into
mainfrom
issue-9-azimuthal-average

Conversation

@Paullo9

@Paullo9 Paullo9 commented Oct 31, 2026

Copy link
Copy Markdown
Contributor

Requested by Paul · project thread

Part of #9

The azimuthal average of a centred diffraction pattern, abTEM 1.0.10 DiffractionPatterns.azimuthal_average:

  • RealField.azimuthalAverage(beam:maxAngle:radialSampling:weighting:width:) on a pattern centred as Waves.diffractionPattern and PixelatedDetector return it (abTEM fftshift=True). Rings at 0, step, 2 step, ... below the maximum angle; .step (|alpha - ring| < width) or .gaussian weighting (new enum AzimuthalWeighting); a ring with no weight is 0; returns ReciprocalSpaceLineProfiles with sampling step / (lambda 1e3) in 1/A.
  • Radial sampling and width are in units of the finer angular sampling, the maximum angle in mrad; nil is abTEM default (the larger |lowest angle| of the two axes).
  • The coordinates follow abTEM: a float32 linspace between the pattern limits, a float32 radius, a float64 ring test, so a pixel on a ring edge lands where abTEM puts it. Sums are float64 (abTEM sums a float32 pattern in float32: about 1e-7 relative on float32 data).
  • Refused where abTEM is silent or returns NaN: a radial sampling, width or maximum angle that is not finite and positive, a million rings or more.

Additive API, so feat:.

Numbers: Tests/SimulatoDetectorTests/AzimuthalAverageTests.swift holds abTEM 1.0.10 output, printed by the new tools/pins/azimuthal/pins.py, for planted 9 x 8 and 8 x 6 patterns (non-square sampling, odd and even axes, both weightings, custom sampling, width and maximum angle, an empty ring) at rtol 1e-7, the assert_allclose defaults (abTEM states no tolerance).

Not in this PR: the docs/parity.md row (azimuthal_average from missing to partial: single pattern, not over a pixelated scan; rtol 1e-7) is left out because two open PRs already touch that file; it follows once they merge. The average over a pixelated scan is not offered (the scan measurement carries no sampling).

Test evidence: the new suite (5 tests) fails to compile on main (the API is new) and passes here; the gate swift test -c release --skip SimulatoKitParityTests is green locally. Reviewed by a separate reviewer pass before the commit: no must-fix; it re-ran the pins and compared a line-by-line copy of the algorithm against abTEM on 300 random patterns (worst 1.5e-15 relative); doc nits applied.

Remaining in #9: LineScan end=None and probe-matched defaults, integrate_disc(interpolate:) (needs Images.interpolate), line profiles for segmented and pixelated line scans, tilt distributions.

🤖 Generated with Claude Code

RealField.azimuthalAverage(beam:maxAngle:radialSampling:weighting:width:) ports abTEM 1.0.10 DiffractionPatterns.azimuthal_average for one centred pattern: rings at 0, step, 2 step, ... below the maximum angle, step or Gaussian weighting, a ReciprocalSpaceLineProfiles out. The angles follow abTEM (float32 linspace coordinates, float32 radius, float64 ring test) so a pixel on a ring edge lands where abTEM puts it. Refuses a radial sampling, width or maximum angle that is not finite and positive, and a million rings or more.

Pinned against abTEM 1.0.10 by tools/pins/azimuthal/pins.py at rtol 1e-7 (assert_allclose defaults; abTEM states no tolerance). Reviewed before the commit: no must-fix; doc nits applied.

Done: the azimuthal average of one pattern. Next: LineScan end=None and probe-matched defaults (#9).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
docs/open-items.md: the detector item keeps this branch wording (the single-pattern azimuthal average is in; the segmented DPC measurement is in main as PolarMeasurements.differentials, so it is not listed); the Potential API item takes the CrystalPotential wording from main.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Paullo9 added a commit that referenced this pull request Oct 31, 2026
…he mixed-radix FFT (#44)

parity.md: Images.interpolate gets its own row, partial (method fft, both normalizations, pinned at rtol 1e-7 by
tools/pins/image-interpolate); DiffractionPatterns.interpolate stays missing. integrate_disc names interpolate:. The Grid
row says lengths off vDSP that factor into 2, 3, 5 and 7 run the mixed-radix FFT (#42). Totals recounted from the table:
129 rows, 38 present, 40 partial, 51 missing; the study split was stale (now 12, 7, 8).

open-items.md: the FFT item names what is left after #42 (a prime factor above 7, 506 squared); the #9 item drops
integrate_disc(interpolate:) and adds the spline method.

tools/README.md: the image-interpolate generator row.

The azimuthal_average (#39) and LineScan defaults (#40) rows wait for those PRs to merge.

Done: rows for #41 and #42. Next: rows for #39 and #40 when they merge.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…zimuthal-average branch

Conflict in docs/open-items.md resolved keeping both: main lists the interpolation and DPC state, this branch says the azimuthal average of a single pattern is in (RealField.azimuthalAverage) and over a pixelated scan it is not, so "the azimuthal average" leaves the open list.

Done: the azimuthal-average branch is on current main, fast gate green.
Next: CI on the PR.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Paullo9
Paullo9 merged commit 82ec3d0 into main Oct 31, 2026
8 checks passed
@Paullo9
Paullo9 deleted the issue-9-azimuthal-average branch October 31, 2026 22:27
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.

1 participant