fix(tomo): no initial 0-deg projection for tomo_type 4 (angle file) #354
No files matched your search
@@ -2426,7 +2426,15 @@ class LamNI(
|
||||
# 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)
|
||||
#
|
||||
# Skipped for tomo_type 4 (angle file): the file may override
|
||||
# the scan parameters per angle, so a 0-degree shot with the
|
||||
# normal tomo parameters could end up as e.g. a low-resolution
|
||||
# projection inside a high-resolution tomogram. The angle file
|
||||
# is the sole source of projections there -- put 0 in the file
|
||||
# if one is wanted. Mirrors Flomni.
|
||||
if self.tomo_type != 4:
|
||||
self._tomo_scan_at_angle(0, 1)
|
||||
|
||||
try:
|
||||
with scans.dataset_id_on_hold:
|
||||
|
||||
@@ -187,8 +187,7 @@ class TomoAngleFileMixin:
|
||||
def _init_angle_file_progress(self, file_angles: AngleFileResult, projection_number) -> None:
|
||||
"""Fill the progress fields that are already known from the file, so the
|
||||
GUI shows the real tomo type/total (and sub-tomogram 1) during the
|
||||
preparation steps and the 0-deg reference shot that precede the loop,
|
||||
instead of the blank reset defaults (tomo type 0, projection 0)."""
|
||||
preparation steps (e.g. empty frames) that precede the loop, instead of the blank reset defaults (tomo type 0, projection 0)."""
|
||||
self.progress["tomo_type"] = "Angles from file"
|
||||
self.progress["total_projections"] = (
|
||||
len(file_angles.angles) if file_angles.is_complete else 0
|
||||
@@ -197,9 +196,12 @@ class TomoAngleFileMixin:
|
||||
self.progress["subtomo"] = 1
|
||||
|
||||
def _run_angle_file_loop(self, file_angles: AngleFileResult, projection_number) -> None:
|
||||
"""Acquire the angles of tomo_type 4 in file order (no extra 0-deg
|
||||
damage-estimation shots: sub-tomogram numbers in a user file are just
|
||||
labels, put 0-deg lines in the file instead). Waits for lines to be
|
||||
"""Acquire the angles of tomo_type 4 in file order. The file is the
|
||||
sole source of projections: no initial 0-deg shot at the start of a
|
||||
new tomogram (it would use the normal tomo parameters, not the
|
||||
file's per-angle ones) and no extra 0-deg damage-estimation shots
|
||||
(sub-tomogram numbers in a user file are just labels) -- put 0-deg
|
||||
lines in the file instead. Waits for lines to be
|
||||
appended while the file has no 'END' marker."""
|
||||
error = self._TOMO_PARAMS_ERROR
|
||||
self.progress["tomo_type"] = "Angles from file"
|
||||
|
||||
@@ -2827,7 +2827,16 @@ class Flomni(
|
||||
# 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)
|
||||
#
|
||||
# Skipped for tomo_type 4 (angle file): the file may override
|
||||
# the scan parameters (step, exp_time, frames_per_trigger, ...)
|
||||
# per angle, so a 0-degree shot taken with the normal tomo
|
||||
# parameters could be e.g. a low-resolution projection inside a
|
||||
# high-resolution tomogram, which breaks loading the projections
|
||||
# for further processing. The angle file is the sole source of
|
||||
# projections there -- put 0 in the file if one is wanted.
|
||||
if self.tomo_type != 4:
|
||||
self._tomo_scan_at_angle(0, 1)
|
||||
|
||||
try:
|
||||
with scans.dataset_id_on_hold:
|
||||
|
||||
@@ -355,8 +355,8 @@ def test_angle_file_scan_follows_file_order_and_subtomos(monkeypatch, tmp_path):
|
||||
|
||||
flomni.tomo_scan()
|
||||
|
||||
# (0, 1): unconditional 0-deg reference shot at the start of a new tomogram
|
||||
assert recorded == [(0, 1), (10.0, 1), (170.0, 1), (270.0, 2), (5.5, 3)]
|
||||
# no initial 0-deg shot: for tomo_type 4 the file is the only source of projections
|
||||
assert recorded == [(10.0, 1), (170.0, 1), (270.0, 2), (5.5, 3)]
|
||||
assert flomni.progress["total_projections"] == 4
|
||||
assert flomni.progress["tomo_type"] == "Angles from file"
|
||||
|
||||
@@ -407,8 +407,9 @@ def test_angle_file_missing_raises_before_any_acquisition(monkeypatch, tmp_path)
|
||||
|
||||
|
||||
def test_angle_file_ignores_golden_zero_deg_damage_flag(monkeypatch, tmp_path):
|
||||
"""No extra 0-deg shots at sub-tomogram transitions for type 4 -- only
|
||||
the unconditional reference shot at the start of a new tomogram."""
|
||||
"""No 0-deg shots at all for type 4 -- neither at sub-tomogram
|
||||
transitions nor the initial reference shot at the start of a new
|
||||
tomogram (it would use the normal tomo parameters, not the file's)."""
|
||||
flomni = make_flomni_for_angle_file(monkeypatch, tmp_path, "10 1\n20 2\n30 3\n40 5\nEND\n")
|
||||
flomni.golden_projections_at_0_deg_for_damage_estimation = 1
|
||||
recorded = []
|
||||
@@ -416,7 +417,7 @@ def test_angle_file_ignores_golden_zero_deg_damage_flag(monkeypatch, tmp_path):
|
||||
|
||||
flomni.tomo_scan()
|
||||
|
||||
assert recorded == [(0, 1), (10.0, 1), (20.0, 2), (30.0, 3), (40.0, 5)]
|
||||
assert recorded == [(10.0, 1), (20.0, 2), (30.0, 3), (40.0, 5)]
|
||||
|
||||
|
||||
def test_max_scan_angle_is_362_only_for_type_4():
|
||||
@@ -452,8 +453,7 @@ def test_angle_file_accepts_physical_overtravel_range(monkeypatch, tmp_path):
|
||||
|
||||
flomni.tomo_scan() # exercises the real _tomo_scan_at_angle(), not a stub
|
||||
|
||||
# 0: unconditional 0-deg reference shot at the start of a new tomogram
|
||||
assert recorded == [0, -2.0, -0.1, 361.0, 362.0]
|
||||
assert recorded == [-2.0, -0.1, 361.0, 362.0]
|
||||
|
||||
|
||||
# ── tomo_type 4: live append -- wait at EOF without 'END', poll, pick up ────
|
||||
@@ -475,8 +475,7 @@ def test_angle_file_waits_then_picks_up_appended_lines(monkeypatch, tmp_path):
|
||||
|
||||
flomni.tomo_scan()
|
||||
|
||||
# (0, 1): unconditional 0-deg reference shot at the start of a new tomogram
|
||||
assert recorded == [(0, 1), (5.0, 1), (10.0, 1), (20.0, 1)]
|
||||
assert recorded == [(5.0, 1), (10.0, 1), (20.0, 1)]
|
||||
assert len(sleep_calls) == 1
|
||||
assert flomni.progress["total_projections"] == 3
|
||||
|
||||
@@ -529,12 +528,11 @@ def test_angle_file_heartbeat_refreshed_while_waiting(monkeypatch, tmp_path):
|
||||
|
||||
flomni.tomo_scan()
|
||||
|
||||
# (0, 1) reference shot, then (5.0, 1) -- both before any wait tick, so no
|
||||
# heartbeat has been written yet (the stub above never sets one itself).
|
||||
# (5.0, 1) comes before any wait tick, so no heartbeat has been written
|
||||
# yet (the stub above never sets one itself).
|
||||
assert recorded_heartbeats[0] is None
|
||||
assert recorded_heartbeats[1] is None
|
||||
# (10.0, 1) is only reached after the wait tick refreshed the heartbeat.
|
||||
assert recorded_heartbeats[2] is not None
|
||||
assert recorded_heartbeats[1] is not None
|
||||
|
||||
|
||||
# ── tomo_type 4: per-line acquisition overrides (step=/exptime=/frames=) ───
|
||||
@@ -554,7 +552,6 @@ def test_angle_file_overrides_reach_tomo_scan_at_angle_and_are_sticky(monkeypatc
|
||||
flomni.tomo_scan()
|
||||
|
||||
assert recorded == [
|
||||
(0, 1, {}), # unconditional 0-deg reference shot at tomogram start
|
||||
(10.0, 1, {}),
|
||||
(20.0, 1, {"step": 0.5}),
|
||||
(30.0, 1, {"step": 0.5}), # sticky: step carries forward
|
||||
@@ -727,7 +724,7 @@ def test_resume_does_not_reset_progress(monkeypatch, tmp_path):
|
||||
assert flomni.progress["estimated_remaining_time"] == 42.0
|
||||
|
||||
|
||||
def test_angle_file_progress_shows_type_and_total_before_the_zero_deg_shot(monkeypatch, tmp_path):
|
||||
def test_angle_file_progress_shows_type_and_total_and_skips_zero_deg_shot(monkeypatch, tmp_path):
|
||||
flomni = make_flomni_for_angle_file(monkeypatch, tmp_path, "10\n20\nEND\n")
|
||||
seen = []
|
||||
flomni._tomo_scan_at_angle = lambda angle, subtomo, **kw: seen.append(
|
||||
@@ -736,7 +733,10 @@ def test_angle_file_progress_shows_type_and_total_before_the_zero_deg_shot(monke
|
||||
|
||||
flomni.tomo_scan()
|
||||
|
||||
assert seen[0] == (0, "Angles from file", 2) # the initial 0-deg shot
|
||||
# no initial 0-deg shot for tomo_type 4: the file is the only source of
|
||||
# projections (a 0-deg shot would use the normal tomo parameters)
|
||||
assert [a for a, _, _ in seen] == [10, 20]
|
||||
assert seen[0] == (10, "Angles from file", 2)
|
||||
|
||||
|
||||
def test_flomni_gui_progress_survives_zero_total(monkeypatch):
|
||||
|
||||
@@ -107,7 +107,7 @@ def test_scan_follows_file_order_and_reports_progress(monkeypatch, tmp_path):
|
||||
|
||||
lamni.tomo_scan(force=True)
|
||||
|
||||
assert recorded == [(0, 1), (10.0, 1), (170.0, 1), (270.0, 2), (362.0, 3)]
|
||||
assert recorded == [(10.0, 1), (170.0, 1), (270.0, 2), (362.0, 3)]
|
||||
assert lamni.progress["total_projections"] == 4
|
||||
assert lamni.progress["tomo_type"] == "Angles from file"
|
||||
|
||||
@@ -175,7 +175,6 @@ def test_per_line_overrides_are_sticky_and_passed_to_scan_at_angle(monkeypatch,
|
||||
lamni.tomo_scan(force=True)
|
||||
|
||||
assert calls == [
|
||||
(0, {}),
|
||||
(10.0, {}),
|
||||
(20.0, {"step": 0.5, "exp_time": 2.0, "frames_per_trigger": 3}),
|
||||
(30.0, {"step": 0.7, "exp_time": 2.0, "frames_per_trigger": 3}),
|
||||
@@ -259,7 +258,7 @@ def test_queue_job_line_shows_angle_file_for_type_4():
|
||||
assert "angles=" not in line
|
||||
|
||||
|
||||
def test_progress_shows_type_and_total_before_the_zero_deg_shot(monkeypatch, tmp_path):
|
||||
def test_progress_shows_type_and_total_and_skips_zero_deg_shot(monkeypatch, tmp_path):
|
||||
lamni = make_lamni_for_angle_file(monkeypatch, tmp_path, "10\n20\nEND\n")
|
||||
seen = []
|
||||
lamni._tomo_scan_at_angle = lambda angle, subtomo: seen.append(
|
||||
@@ -268,4 +267,7 @@ def test_progress_shows_type_and_total_before_the_zero_deg_shot(monkeypatch, tmp
|
||||
|
||||
lamni.tomo_scan(force=True)
|
||||
|
||||
assert seen[0] == (0, "Angles from file", 2) # the initial 0-deg shot
|
||||
# no initial 0-deg shot for tomo_type 4: the file is the only source of
|
||||
# projections (a 0-deg shot would use the normal tomo parameters)
|
||||
assert [a for a, _, _ in seen] == [10, 20]
|
||||
assert seen[0] == (10, "Angles from file", 2)
|
||||
Reference in new issue
Block a user