Skip to content

feat: origin from the Bragg peaks (measure_origin, fit_origin) with a symmetry centre guess - #75

Merged
Paullo9 merged 5 commits into
mainfrom
claude/project-thread-we7qwt
Nov 5, 2026
Merged

Paullo9 merged 5 commits into
mainfrom
claude/project-thread-we7qwt

Conversation

@Paullo9

@Paullo9 Paullo9 commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor

Requested by Paul · project thread

What and why

For mac4DSTEM/mac4DSTEM#138: on py4DSTEM's Si/SiGe tutorial cube the centre-of-mass origin is pulled 6–10 px off the direct beam by strong disks, so the Bragg vector map smears and strain refuses. This adds the origin from the detected Bragg peaks.

  • BraggOrigin.measure / BraggOrigin.fit: py4DSTEM's BraggVectors.measure_origin and fit_origin, pinned on planted peaks (tools/bragg-origin/pins.py, fixture braggorigin.json). Chosen peaks exact; raw map at assert_allclose Float64 (rtol 1e-7, atol 1e-12); fitted surfaces at np.allclose defaults like the existing fit_origin rows (upstream's curve_fit stops about 1e-6 px from the least-squares surface). Deviations are named in the doc comments and docs/parity.md (upstream's guess is always (0, 0) because it passes the map object to gaussian_filter).
  • BraggOrigin.rawHistogram: histogram(mode="raw"), public.
  • OriginFit.fit(mask:): fit_origin's boolean mask; the robust loop scores masked positions as upstream does. Default nil, so existing calls are unchanged.
  • BraggOrigin.symmetryCenterGuess (mac4DSTEM variant, not in py4DSTEM): the maximum of the peak-count map about which that map is most point-symmetric (Friedel), over every maximum above 5%. Pinned to a NumPy/SciPy transcription in the same script.

Additive only: no existing function changes its output.

Before / after on Si/SiGe, full scan (py4DSTEM 0.14.19, 50 × 200 scan, 128 × 128 detector)

downsample_Si_SiGe_exp.h5 (md5 c363d362…). Peaks from find_Bragg_disks (synthetic sigmoid template, r = 3.74 px, 20.8 peaks per position). Origin fitted as a plane. "Top-40 share": share of the origin-corrected Bragg vector map in its 40 brightest pixels (sharp map = high). "Indexed": share of all peaks within 1 px of a maximum of that map.

Origin Fitted mean (row, col) px Plane span (row, col) px Measured std (row, col) px Fit RMS px Top-40 share Indexed
Centre of mass (default, unchanged) 57.99, 69.86 13.04, 16.93 8.78, 6.35 9.44 0.10 0.04
Bragg peaks, py4DSTEM default guess 57.76, 69.98 13.18, 17.09 9.10, 6.56 9.84 0.09 0.04
Bragg peaks, symmetry guess (64, 64), distance score 63.78, 63.80 0.35, 0.14 0.15, 0.16 0.20 0.88 0.79

The CoM scatter (8.8 / 6.4 px) matches what #138 reported for py4DSTEM's get_origin. The direct beam is at (64, 64); a first-order disk at (49, 69) is brighter, so py4DSTEM's guess and intensity score pick it.

Checklist

  • swift test -c release passes (CI)
  • A test fails without the change and passes with it (BraggOriginTests)
  • Pinned numbers are unchanged (new fixture only)
  • Docs (docs/parity.md) updated

🤖 Generated with Claude Code

https://claude.ai/code/session_01VkKZEUZ2RdEgWryyPgjZzR

Paullo9 and others added 2 commits November 5, 2026 19:42
Started for mac4DSTEM#138 and parked on 2026-11-05 at Paul's request (for
later, not before the App Store launch). Untested draft: BraggOrigin.swift,
its fixture and the pin script. Not for merge as is.

Done: draft port parked on this branch. Next: finish, parity test, review.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…n) and a symmetry centre guess

BraggOrigin.measure and BraggOrigin.fit port py4DSTEM's BraggVectors.measure_origin and fit_origin, pinned on planted
peaks (tools/bragg-origin/pins.py). OriginFit.fit takes fit_origin's boolean mask. BraggOrigin.symmetryCenterGuess is
a mac4DSTEM variant: the centre about which the map of peak counts is most point-symmetric, so a distance score finds
the direct beam where a reflection is brighter than it (py4DSTEM's Si/SiGe tutorial cube).

Refs mac4DSTEM/mac4DSTEM#138

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VkKZEUZ2RdEgWryyPgjZzR
@Paullo9 Paullo9 self-assigned this Nov 5, 2026
A lattice whose disks are all detected equally often has equal counts at every disk, so a top-12 cut could leave
its centre out.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VkKZEUZ2RdEgWryyPgjZzR
…r fit_origin rows

py4DSTEM fits by curve_fit, which stops about 1e-6 px from the least-squares surface on these pins.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VkKZEUZ2RdEgWryyPgjZzR
@Paullo9
Paullo9 merged commit 4542585 into main Nov 5, 2026
6 checks passed
@Paullo9
Paullo9 deleted the claude/project-thread-we7qwt branch November 5, 2026 19:35
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