feat(flomni,lamni): unconditional 0-deg reference projection at scan start
CI for csaxs_bec / test (push) Successful in 2m15s

Take one real 0-degree projection at the very start of every new tomogram
(subtomo_number=1, included in reconstruction like any grid projection),
independent of zero_deg_reference_at_each_subtomo /
golden_projections_at_0_deg_for_damage_estimation -- gives an immediate
radiation-damage baseline even when those flags are off. Placed right after
Flomni's existing collect_empty_frames() step; LamNI gets the same 0-deg
shot but no flat-field step, since it has no way to retract the sample out
of the beam. Omny intentionally left as a follow-up (not implemented here).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0132KBoxsovfcMNRGJhS1Pbw
This commit is contained in:
x01dcandClaude Sonnet 5 committed 2026-09-19 10:06:49 +02:00
1 parent 22a882fafa
commit 0a12d4892f
3 files changed
+35 -4

No files matched your search

@@ -1836,6 +1836,18 @@ class LamNI(TomoQueueMixin, LamNIAlignmentMixin, LamNIOpticsMixin, LamniGuiTools
self.progress["estimated_finish_time"] = None
self.progress["accumulated_idle_time"] = 0.0
self.progress["heartbeat"] = None
# Unconditional single 0-degree reference projection at the very
# start of a new tomogram, independent of
# zero_deg_reference_at_each_subtomo /
# golden_projections_at_0_deg_for_damage_estimation -- gives an
# immediate radiation-damage baseline even when those flags are
# off. Real data (uses _tomo_scan_at_angle like any grid
# projection), included in reconstruction like any other
# projection. Logged under subtomo_number=1. Mirrors Flomni's
# equivalent addition -- no flat-field/empty-frame step here
# (unlike Flomni's collect_empty_frames()), since LamNI has no
# way to retract the sample out of the beam.
self._tomo_scan_at_angle(0, 1)
try:
with scans.dataset_id_on_hold:
@@ -2731,6 +2731,16 @@ class Flomni(
self.progress["accumulated_idle_time"] = 0.0
self.progress["heartbeat"] = None
self.collect_empty_frames()
# Unconditional single 0-degree reference projection at the very
# start of a new tomogram, independent of
# zero_deg_reference_at_each_subtomo /
# golden_projections_at_0_deg_for_damage_estimation -- gives an
# immediate radiation-damage baseline even when those flags are
# off. Real data (uses _tomo_scan_at_angle like any grid
# projection), included in reconstruction like any other
# projection -- not a flat-field/reference-only frame like
# collect_empty_frames() above. Logged under subtomo_number=1.
self._tomo_scan_at_angle(0, 1)
try:
with scans.dataset_id_on_hold:
@@ -164,6 +164,10 @@ def make_lamni_for_tomo_scan(
obj.special_angles = []
obj.write_pdf_report = lambda: None
obj.sub_tomo_scan = lambda subtomo_number, start_angle=None: None
# Default no-op for the unconditional 0-deg reference projection tomo_scan()
# now fires at the start of every new scan (see lamni.py) -- tests that
# care about it override this after calling make_lamni_for_tomo_scan().
obj._tomo_scan_at_angle = lambda angle, subtomo_number: None
obj._print_progress = lambda: None
obj._format_duration = lambda seconds: "0s"
obj.lamnigui_show_progress = lambda: None
@@ -301,7 +305,9 @@ def test_subtomo_starts_near_zero(subtomo_number, expected):
def test_zero_deg_reference_disabled_by_default(monkeypatch):
"""zero_deg_reference_at_each_subtomo defaults to False -- a fresh
tomo_scan() must not fire any extra 0-deg shots."""
tomo_scan() must not fire any *flag-gated* extra 0-deg shots, but the
unconditional single 0-deg reference projection at scan start (see
lamni.py) always fires regardless of the flag."""
lamni = make_lamni_for_tomo_scan(monkeypatch, 45.0, active_account="")
lamni.add_sample_database = lambda *a, **k: 0
recorded = []
@@ -312,13 +318,16 @@ def test_zero_deg_reference_disabled_by_default(monkeypatch):
lamni.tomo_scan()
assert recorded == []
assert recorded == [(0, 1)]
def test_zero_deg_reference_fires_for_odd_subtomos_and_final_shot(monkeypatch):
"""With the flag on, a fresh tomo_scan() must fire an extra angle-0 shot
before each odd sub-tomogram (1, 3, 5, 7) plus one final shot after
sub-tomogram 8 completes -- mirrors Flomni.tomo_scan()'s equivalent."""
sub-tomogram 8 completes -- mirrors Flomni.tomo_scan()'s equivalent. The
leading (0, 1) is the unconditional scan-start reference projection
(fires regardless of the flag), followed by the flag's own (0, 1) for
sub-tomogram 1 itself -- the two are independent and both fire here."""
lamni = make_lamni_for_tomo_scan(monkeypatch, 45.0, active_account="")
lamni.add_sample_database = lambda *a, **k: 0
lamni.zero_deg_reference_at_each_subtomo = True
@@ -330,7 +339,7 @@ def test_zero_deg_reference_fires_for_odd_subtomos_and_final_shot(monkeypatch):
lamni.tomo_scan()
assert recorded == [(0, 1), (0, 3), (0, 5), (0, 7), (0, 8)]
assert recorded == [(0, 1), (0, 1), (0, 3), (0, 5), (0, 7), (0, 8)]
def test_zero_deg_reference_skipped_when_resuming_mid_subtomo(monkeypatch):