fix(lamni): scan-number file lines 3/4 are xeye x and y in um
CI for csaxs_bec / test (push) Successful in 3m2s

BEC_ptycho_align reads line 3 as scan_xpos and line 4 as scan_ypos (um).
lamni wrote the x correction twice, in mm (line 3 rounded to 10 um).
Write x and y X-ray-eye corrections in um instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ALDnH16dC6iyHjhe5Ey4w9
This commit is contained in:
x12saandClaude Opus 5.5 committed 2026-10-15 17:10:48 +02:00
1 parent 9c7b6dfd3b
commit f680f9dd18
2 files changed
+30 -12

No files matched your search

@@ -1504,11 +1504,14 @@ class LamNI(
one, stepping by the number of stitch tiles per projection. Without
``scans`` the whole list is predicted from ``first_scan``.
Mirrors Flomni.write_alignment_scan_numbers() (same 4-line format),
adapted to lamni's own alignment-scan angle set (13 points, 0 to 360
degrees inclusive, see alignment_scan_angles()) and its own x-ray-eye-fit
offset source (lamni_compute_additional_correction_xeye_mu() instead
of get_alignment_offset()).
File format, as read by BEC_ptycho_align (MATLAB):
line 1 scans first scan number per angle
line 2 theta angle [deg]
line 3 scan_xpos X-ray-eye x correction at that angle [um]
line 4 scan_ypos X-ray-eye y correction at that angle [um]
Lines 3/4 are only used by its sinusoid branch (lamni_fit = false);
the LaMNI ellipse fit uses just the clicks, scans and theta.
lamni_compute_additional_correction_xeye_mu() returns mm, hence *1000.
"""
angles = self.alignment_scan_angles()
scans_per_angle = (2 * self.lamni_stitch_x + 1) * (2 * self.lamni_stitch_y + 1)
@@ -1516,20 +1519,19 @@ class LamNI(
while len(scans) < len(angles):
scans.append(scans[-1] + scans_per_angle)
x_vals = []
x_um, y_um = [], []
for angle in angles:
x, _y = self.lamni_compute_additional_correction_xeye_mu(angle, verbose=False)
x_vals.append(x)
zeros = [0] * len(angles)
x, y = self.lamni_compute_additional_correction_xeye_mu(angle, verbose=False)
x_um.append(x * 1000)
y_um.append(y * 1000)
file = os.path.expanduser("~/data/raw/logs/ptychotomoalign_scannum.txt")
os.makedirs(os.path.dirname(file), exist_ok=True)
with open(file, "w") as f:
f.write(" ".join(map(str, scans)) + "\n")
f.write(" ".join(map(str, angles)) + "\n")
f.write(" ".join(f"{x:.2f}" for x in x_vals) + "\n")
f.write(" ".join(map(str, x_vals)) + "\n")
f.write(" ".join(f"{x:.4f}" for x in x_um) + "\n")
f.write(" ".join(f"{y:.4f}" for y in y_um) + "\n")
def tomo_alignment_scan(self, force: bool = False) -> None:
"""Perform a laminogram alignment scan: a quick ptychography scan at
@@ -248,3 +248,19 @@ def test_tomo_alignment_scan_with_stitching_records_first_scan_per_angle(tmp_pat
log_file = tmp_path / "data/raw/logs/ptychotomoalign_scannum.txt"
scans = [int(s) for s in log_file.read_text().splitlines()[0].split()]
assert scans == list(range(100, 217, 9))
def test_write_alignment_scan_numbers_lines_3_4_are_x_and_y_in_um(tmp_path, monkeypatch):
"""BEC_ptycho_align reads line 3 as scan_xpos and line 4 as scan_ypos, in
microns; the X-ray eye fit is stored in microns ([[Ax,Bx,Cx],[Ay,By,Cy]])."""
lamni = make_lamni(monkeypatch, xray_eye_fit=[[0.0, 0.0, 12.5], [0.0, 0.0, -3.25]])
monkeypatch.setattr(
"os.path.expanduser",
lambda p: p.replace("~", str(tmp_path)) if p.startswith("~") else p,
)
lamni.write_alignment_scan_numbers(100)
lines = (tmp_path / "data/raw/logs/ptychotomoalign_scannum.txt").read_text().splitlines()
assert [float(v) for v in lines[2].split()] == pytest.approx([12.5] * 13)
assert [float(v) for v in lines[3].split()] == pytest.approx([-3.25] * 13)