Commit Graph
947 Commits
Author SHA1 Message Date
7319e132bc refactor(LamNI): rename rotation-center calibration entry point, drop sample_type arg
rotation_center_calibration_start(sample_type="isolated") didn't match the
xrayeye_* naming convention used by the other X-ray eye entry points, and
the sample_type string enum was easy to forget (both the parameter name
and its accepted values). Split into two entry points instead:

  lamni.xrayeye_rotation_center_calibration_isolated(keep_shutter_open=False)
  lamni.xrayeye_rotation_center_calibration_extended(keep_shutter_open=False)

Both delegate to a shared _xrayeye_rotation_center_calibration() private
helper (single copy of the KeyboardInterrupt cleanup). Updated the user
docs to match; XrayEyeAlign.find_rotation_center()'s own sample_type
parameter is unchanged since it's internal shared implementation, not
what was being objected to.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-24 13:02:01 +02:00
7014760175 fix(LamNI): remove redundant rotate, simplify sweep, add center precondition
Three refinements found while testing find_rotation_center() against the
simulated LamNI:

- Drop the trailing tomo_rotate(0) at the end of find_rotation_center():
  both the isolated path (explicit rotate-back before applying the
  correction) and the extended path (verification sweep ends at 0) already
  leave lsamrot at 0 by then, so it was a pure duplicate move.

- Simplify _live_sweep() to rotate directly to each waypoint instead of
  stepping through artificial step_deg-sized sub-moves with settle pauses
  in between -- a single tomo_rotate() already produces continuous physical
  rotation, so the stepping added nothing but jerkier motion and extra
  queue overhead.

- Add _ensure_at_configured_center(), called at the start of
  find_rotation_center() before lfzp_in(). lamni_move_to_scan_center's
  interferometer-drift safety check (LamNIFermatScan.py) assumes lsamx/
  lsamy start near their configured center and silently skips the entire
  corrective move if that drift exceeds 150um -- exactly the situation
  find_rotation_center() is meant to handle, since its purpose is
  recalibrating a potentially-stale center. Confirmed this was a real gap:
  a run where the stage hadn't started at its configured center produced a
  correct absolute target (verified via the interferometer reading) but a
  confusingly small apparent motor delta.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-24 13:02:01 +02:00
26e7fddfc6 fix(LamNI): ensure OSA is out before requesting the FZP-centre click
Both align() and find_rotation_center() moved the FZP in and immediately
asked the operator to click its centre, but only confirmed OSA was out
afterwards (via loptics_out(), which runs after that click). If OSA was
left in from a previous scan setup, it would still be in the beam path
during the FZP reference click. Add an explicit losa_out() call right
before lfzp_in() in both procedures; it's a cheap, skip-if-already-out
no-op otherwise.

Found while testing find_rotation_center() against the simulated LamNI.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-24 13:02:01 +02:00
bd2e87ebdd feat(LamNI): add automated rotation-center calibration via X-ray eye
lsamx_center/lsamy_center have to be re-measured by hand for nearly every
new sample (the rotation stage is tilted on top of lsamx/lsamy, so the
effective axis position shifts with sample thickness/mounting). Add
XrayEyeAlign.find_rotation_center()/lamni.rotation_center_calibration_start()
to automate this using the existing X-ray eye GUI and lamni_move_to_scan_center
interferometer-feedback move:

- "isolated" sample: click particle centre at 0 and 180 deg, use the
  midpoint (angle-tilt-independent) as the rotation axis position.
- "extended" sample: live 0->180->0 sweep for visual identification, single
  click, then a verification sweep and an accept/iterate prompt.

Both write the result to lsamx/lsamy's "center" userParameter after
confirmation. Heavily verbose logging throughout since this is fundamentally
an interactive, hardware-in-the-loop procedure that can't be fully exercised
by automated tests.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-24 13:02:01 +02:00
wakonig_k 1a12212609 fix: unify device tag usage
CI for csaxs_bec / test (pull_request) Successful in 2m23s
Read the Docs Deploy Trigger / trigger-rtd-webhook (push) Successful in 2s
CI for csaxs_bec / test (push) Successful in 6m43s
2026-07-23 16:18:55 +02:00
x01dcandClaude Sonnet 5 851df3619b fix(webpage): hide Temperatures section when setup has no temperature data
CI for csaxs_bec / test (pull_request) Successful in 28m21s
Read the Docs Deploy Trigger / trigger-rtd-webhook (push) Successful in 2s
CI for csaxs_bec / test (push) Successful in 7m3s
renderInstrument() checked setup.temperatures for truthiness, but an
empty JS object is truthy, so LamNI's deliberately-empty temperatures
dict (it has no monitoring device) still rendered an empty
"Temperatures" heading/table. flomni only worked by coincidence since
its _TEMP_MAP always has entries. Adds a hasTemps check
(Object.keys(...).length>0), mirroring the existing hasParams idiom
one line above for the same card.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-22 16:01:50 +02:00
x01dcandClaude Sonnet 5 7717efd19a feat(webpage): mirror status-page uploads to a second URL
CI for csaxs_bec / test (push) Successful in 3m42s
HttpUploader now accepts a single URL or a list, fanning every upload
(dir/changed/single-file/cleanup) out to each configured server with an
independent per-URL mtime cache, so a newly added mirror backfills on
its own and a failing one doesn't block the others. Session-auth
endpoints (session_query/set_password) still derive from the primary
URL only.

flomni.py/lamni.py now mirror to https://omny-test.psi.ch/upload.php
alongside production, marked TEMP for easy removal once that server
is evaluated. Adds OMNY_shared/AI_docs/WEBPAGE_UPLOAD_MECHANISM.md
consolidating the upload mechanism (the prior doc predates this file
and still called LamNI a stub).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-22 15:49:33 +02:00
x01dc 564f8dcf4e remove date parameter
CI for csaxs_bec / test (push) Successful in 2m48s
2026-07-22 14:53:49 +02:00
x01dc b65a643fb3 remove date parameter 2026-07-22 14:53:26 +02:00
x01dcandClaude Sonnet 5 353718d196 feat(webpage): add force_start_webpage() to manually override account-match check
CI for csaxs_bec / test (pull_request) Successful in 2m37s
Read the Docs Deploy Trigger / trigger-rtd-webhook (push) Successful in 2s
CI for csaxs_bec / test (push) Successful in 2m25s
Lets testing/debugging sessions running under a mismatched BEC account
still exercise the real upload path on demand -- e.g. to confirm uploads
work from the allowed subnet, or to confirm the remote server's IP
whitelist rejects uploads from outside it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-22 10:08:20 +02:00
x01dcandClaude Sonnet 5 25422d07a4 fix(webpage): remove duplicate EPS uploader, allow .svg uploads
CI for csaxs_bec / test (pull_request) Successful in 1m37s
Read the Docs Deploy Trigger / trigger-rtd-webhook (push) Successful in 2s
CI for csaxs_bec / test (push) Successful in 1m37s
EpsStatusGenerator was given its own upload_url when embedded in
WebpageGeneratorBase, spinning up a second HttpUploader that redundantly
re-scanned and re-POSTed the same shared output directory the base
generator's own uploader already covers every cycle. It now always embeds
with upload_url=None. Also adds .svg to HttpUploader's suffix whitelist so
eps_synoptic.svg is actually picked up (matches a corresponding fix already
applied server-side in upload.php/auth_gate.php).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 20:37:49 +02:00
x01dcandClaude Sonnet 5 6ba277abee feat(webpage): integrate EPS/machine-status pages into lamni/flomni webpage
CI for csaxs_bec / test (push) Successful in 4m51s
Lands the standalone EPS status prototype (OMNY_shared/eps/) as tracked
files and wires it into WebpageGeneratorBase so it starts alongside the
experiment status page, sharing one output dir/port. Demo-vs-real data is
now auto-detected from the host's subnet (129.129.122.x = on the X12SA
beamline) instead of requiring an explicit --demo flag, independent of the
existing account-mismatch local-only fallback. Cross-links the two page
families via a new EPS nav button in status.html's header (the EPS pages
already linked back). Also moves HttpUploader/LocalHttpServer into a
shared web_common.py, picking up an SSRF fix (allow_redirects=False) that
the inline copy lacked.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 19:40:04 +02:00
x01dcandClaude Sonnet 5 10ef93557b fix/stop lamni tests from leaking a fake bec global across the suite
CI for csaxs_bec / test (pull_request) Successful in 3m9s
Read the Docs Deploy Trigger / trigger-rtd-webhook (push) Successful in 2s
CI for csaxs_bec / test (push) Successful in 3m13s
test_lamni_account_change_reset.py and test_lamni_tomo_angles.py set
builtins.__dict__["bec"] directly with no cleanup, so a leaked fake
account ("e22222") from an earlier test file was still in place when
test_x_ray_eye_align.py later constructed a real LamNI(), tripping
_maybe_reset_params_on_account_change()'s interactive yesno() prompt
and failing under pytest's captured stdin. Use monkeypatch.setitem so
pytest reverts the global after each test, matching the pattern already
used in test_lamni_tomo_alignment_scan.py.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 16:57:06 +02:00
x01dcandClaude Sonnet 5 9198638f9b docs/add file-loaded at_each_angle hook example (exposure + step size)
CI for csaxs_bec / test (push) Successful in 2m23s
CI for csaxs_bec / test (pull_request) Failing after 2m3s
Adds a worked example to both lamni.md and flomni.md showing a hook
loaded from a file that temporarily bumps tomo_countingtime and
tomo_shellstep every 5th projection within sub-tomogram 2 (tomo_type 1),
restoring both in a finally block -- demonstrating that a hook can
change any scan parameter, not just move devices like the existing
polarizer example. Notes that progress["subtomo"]/["subtomo_projection"]
work the same way for tomo_types 2/3, just with an open-ended
sub-tomogram count there instead of type 1's fixed 8.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 16:38:00 +02:00
x01dcandClaude Sonnet 5 1362a63a85 fix/disable lamni's broken upload_last_pon.sh call
Confirmed this script no longer works and needs a rewrite. Commented
out the subprocess.run() call in write_pdf_report(), mirroring Flomni
which already has the identical call disabled the same way. omny.py's
own copy is left untouched (out of scope for this migration).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 16:24:33 +02:00
x01dcandClaude Sonnet 5 a64d7cff93 refactor/move add_sample_database() into shared TomoQueueMixin
Flomni.add_sample_database() and LamNI.add_sample_database() were
byte-for-byte identical -- both just forward to
self.tomo_id_manager.register(), a TomoIDManager from
omny/omny_general_tools.py that each setup already constructs
independently in its own __init__. Moved the method into
TomoQueueMixin (OMNY_shared), the base class both already inherit,
same "moved code, not changed logic" precedent as the tomo-queue
backend and webpage generator. omny.py's own separate, older
add_sample_database() (different URL, no e-account fallback) is
untouched.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 16:21:12 +02:00
x01dcandClaude Sonnet 5 f524021cce feat/show estimated finish time and active hook in lamni progress ring
Flomni's ring-progress center label already showed "Est. finish" and
the active at-each-angle hook name; LamNI's equivalent
(_lamnigui_update_progress()) was missing both. Ported the same
formatting/display logic over so the two setups' progress displays
match.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 16:21:05 +02:00
x01dcandClaude Sonnet 5 5be3d0719b feat/include at-each-angle hook in lamni's start-of-scan PDF/scilog
write_pdf_report() didn't mention the active at_each_angle_hook at all,
unlike Flomni's equivalent which already appends the hook's description
and source to both the PDF and its scilog entry. Ported that pattern
over using the same _describe_active_hook()/_active_hook_source() shared
helpers lamni already uses elsewhere (end-of-scan summary,
tomo_parameters() printout) -- no new logic needed, just the missing
call site.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 16:03:20 +02:00
x01dcandClaude Sonnet 5 636dd0ec88 fix/stop LamNI per-sub-tomogram scilog entries
LamNI's tomo_scan() wrote a separate scilog entry ("Starting subtomo:
N...") every time the sub-tomogram number changed -- 8 extra entries
per tomo_type-1 scan, and one per sub-tomogram traversed for types 2/3
-- leftover behavior carried over from omny.py during the flomni->lamni
port. Flomni itself never does this: it only writes at scan start (PDF
report) and scan end (timing summary). Removed the three
_write_subtomo_to_scilog() call sites and the now-unused method so
LamNI's scilog behavior matches Flomni's.

Also fixed a stale test (test_tomo_queue_reacquire_rejects_when_another_job_is_incomplete)
that encoded the old, buggy tomo_queue_reacquire() invariant (rejecting
on ANY other job being incomplete/running) rather than the fixed one
from the prior commit (only an EARLIER job is a real conflict) --
split it into two tests covering both directions.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 15:59:02 +02:00
x01dcandClaude Sonnet 5 887c732865 fix/tomo queue GUI projection count for tomo_type 3
_format_projections() computed type 3's count with type 1's fixed
"N*8 sub-tomograms" formula, ignoring golden_max_number_of_projections
-- the value that actually bounds a type-3 run (lamni.py/flomni.py's
type-3 scan loop has no sub-tomogram cap, it stops only once
ii > golden_max_number_of_projections, same as type 2). This produced
an ~8x-inflated "Projections" column (e.g. 240 instead of 30) for any
type-3 job. Merged type 3 into type 2's branch since both share the
same uncapped/golden_max_number_of_projections model.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 15:41:25 +02:00
x01dcandClaude Sonnet 5 a001bc20bd fix/tomo_queue_reacquire wrongly blocked on a later incomplete job
The single-resumable-job invariant check scanned all other jobs, but
jobs after job_index get force-reset to pending anyway -- so a later
incomplete/running job was never a real conflict, only an earlier one
is (since earlier jobs are left untouched).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 15:12:40 +02:00
x01dcandClaude Sonnet 5 d963cd6dfd feat/auto-refresh stale at_each_angle hooks on redefinition
register_at_each_angle_hook() stores a function object by reference, so
editing a hook's def and forgetting to re-register left the old version
running silently -- easy to miss mid-beamtime. Detect it via
func.__globals__ (a redefined top-level def, or a reloaded module,
mutates the same namespace dict in place) and auto-adopt the new
definition with a printed note instead of failing or staying stale.

Centralizes the shared lookup/error logic (previously duplicated in
lamni.py and flomni.py) into TomoQueueMixin._resolve_at_each_angle_hook().

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 15:08:11 +02:00
x01dc f25eea9488 newline removed 2026-07-21 14:23:35 +02:00
x01dcandClaude Sonnet 5 df19ce917a docs/update session summary through e3c979a
Add items 8-12 (timing log, account-change reset, optics
skip-if-in-position, OSA collision warning, tomo_alignment_scan) plus
the BEC_ptycho_align naming fix, updated test count (458), and
corresponding manual-check lines.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 13:25:19 +02:00
x01dcandClaude Sonnet 5 e3c979a375 fix/correct matlab tool name to BEC_ptycho_align in tomo_alignment_scan
The tool is called BEC_ptycho_align now, not SPEC_ptycho_align.m as
the previous docs/comments said -- fixes the references introduced in
the previous commit (12b2538) in lamni.py's docstrings/print message
and the user docs.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 13:24:37 +02:00
x01dcandClaude Sonnet 5 12b2538333 feat/add lamni.tomo_alignment_scan() -- dedicated fine-alignment scan
Replaces the awkward documented workaround (configure a full tomo_type
1 setup with 96 projections, then launch just sub_tomo_scan(1, 0)) with
a dedicated command, ported from flomni's tomo_alignment_scan(): adjust
tomo_parameters() (FOV/step/counting time), then call
lamni.tomo_alignment_scan() directly -- no tomo_type/sub-tomogram
bookkeeping involved, matching flomni's clean two-step workflow.

Runs 12 points evenly spaced across the full 360 degrees (lamni has no
180-degree symmetry the way flomni does, so unlike flomni's 5-point/
180-degree scan, this covers the full circle -- point count matches
what the old workaround's docs defaulted to, endpoint=False since
360==0 degrees). Aborts if x-ray-eye alignment hasn't been done yet
(tomo_fit_xray_eye unset), mirroring flomni's equivalent guard.
write_alignment_scan_numbers() writes the same 4-line scan-number/
angle/offset log flomni's version does, to
~/data/raw/logs/ptychotomoalign_scannum.txt for SPEC_ptycho_align.m,
also printed at the console (flomni's own console-print equivalent is
dead/commented-out code; lamni's actually prints).

Scope note: flomni's version conditionally skips its eye-out/optics-in
transition when already in measurement condition with feedback
running, to avoid an unneeded interferometer reset -- lamni has no
equivalent helpers for that check, so this calls leye_out()
unconditionally instead. Left as a possible follow-up, not in scope
here.

Item 6 of csaxs_bec/bec_ipython_client/plugins/LamNI/AI_docs/
FLOMNI_LAMNI_FEATURE_GAPS_2026-07.md. Documented in
docs/user/ptychography/lamni.md's "Fine alignment" section.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 13:22:01 +02:00
x01dcandClaude Sonnet 5 db18929742 feat/add OSA collision-clearance warning to lamni lfzp_info()
Ports flomni's ffzp_info() OSA collision-clearance section (compares
live losaz against nominal "in" position and a collision-boundary
constant, warns if closer to collision than nominal or reports
clearance at IN) -- same formula shape and sign convention as flomni
(motion directions confirmed identical between the two setups).

lamni's collision-boundary constant has never been measured, so it's
read from loptz's device config (userParameter.collision_offset) via
a new _get_user_param_optional() helper -- unlike the existing
_get_user_param_safe(), it returns None instead of raising when the
parameter is undefined (as it currently is for every lamni device
config, including the simulated one), since absence here means
"not yet commissioned" rather than a config error. lfzp_info() prints
a clear warning and skips the numeric estimate entirely until someone
adds the real measured value to the config -- deliberately not a
CLI-settable/global-var parameter, since this is deployment-level
hardware calibration.

Item 7 of csaxs_bec/bec_ipython_client/plugins/LamNI/AI_docs/
FLOMNI_LAMNI_FEATURE_GAPS_2026-07.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 12:58:24 +02:00
x01dcandClaude Sonnet 5 7863b8fd37 feat/skip lfzp_in/losa_in/losa_out moves when already in position
Ports flomni's ffzp_in()/fosa_in() "skip the move (and, for the FZP,
the expensive feedback-disable + reset-enable cycle) if already there"
optimization to lamni's lfzp_in()/losa_in(). Also adds the same
skip-if-already-there check to losa_out(), which has no flomni
equivalent to mirror (fosa_out() always moves unconditionally there)
-- added as a lamni-side enhancement per discussion.

Checked every existing caller of lfzp_in()/loptics_in() before making
this change, since a skip changes behavior for callers that used to
get an unconditional reset. Found one real risk:
x_ray_eye_align.py's "Step 0: FZP centre" (the start of a fresh
alignment run) had its manual feedback disable/enable calls already
commented out, meaning it relied entirely on lfzp_in()'s old
unconditional reset to establish a correct interferometer zero.
flomni's equivalent call site doesn't have this problem because
flomni's alignment flow does its own explicit
feedback_enable_with_reset() independently beforehand -- lamni's
doesn't. Fixed by passing force_feedback_reset=True at that one call
site so it keeps its current always-reset behavior; no other lamni
caller needed this.

Item 4 of csaxs_bec/bec_ipython_client/plugins/LamNI/AI_docs/
FLOMNI_LAMNI_FEATURE_GAPS_2026-07.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 12:40:45 +02:00
x01dcandClaude Sonnet 5 b4c262d41b feat/offer tomo-param reset on lamni experiment-account change
Ports flomni's _maybe_reset_params_on_account_change()/
_set_default_tomo_params() to lamni: at LamNI.__init__, if the active
BEC account differs from the one defaults were last applied for
(persisted via defaults_applied_for_account), interactively offer to
reset tomo params to defaults -- preventing a new experiment silently
inheriting the previous one's tuned FOV/stitch/etc. Same account is a
silent no-op, so restarting a client mid-experiment never disturbs
tuned parameters.

_set_default_tomo_params() is a direct port adapted to lamni's own
param names (tomo_circfov/lamni_stitch_x/y/lamni_piezo_range_x/y
instead of fovx/fovy/stitch_x/y, no tomo_angle_range/
single_point_random_shift_max -- lamni has neither concept). Per-
sample alignment state is deliberately not touched, matching flomni.

Item 2 of csaxs_bec/bec_ipython_client/plugins/LamNI/AI_docs/
FLOMNI_LAMNI_FEATURE_GAPS_2026-07.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 11:37:37 +02:00
x01dcandClaude Sonnet 5 e59a42f978 feat/port timing-statistics log + scilog_last_ptycho_scans() to lamni
Ports flomni's per-projection/tomogram JSONL timing log
(~/data/raw/logs/timing_statistics/*.jsonl, feeding a future scan-time
prediction model) and the scilog_last_ptycho_scans(n) user command to
lamni. Direct port adapted only for lamni's own param names (no
fovx/fovy/stitch_x/stitch_y/single_point_instead_of_fermat_scan --
uses tomo_circfov/lamni_stitch_x/y/lamni_piezo_range_x/y instead, no
single-point concept at all).

_log_projection_timing() is only called on a clean (non-error_caught)
completion of _tomo_scan_at_angle()'s retry loop, so an
AlarmBase/TimeoutError attempt (no reliable duration measurement)
doesn't pollute the log.

Item 1 of csaxs_bec/bec_ipython_client/plugins/LamNI/AI_docs/
FLOMNI_LAMNI_FEATURE_GAPS_2026-07.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 10:50:08 +02:00
x01dcandClaude Sonnet 5 4f938fc543 docs/document remaining flomni->lamni feature gaps for triage
Follow-up sweep after this session's ports (timing overview, scan
interruption handling, tooltip filtering, constant shift, hook
display, zero-deg reference). Found and verified 9 items: a missing
per-projection/tomogram timing log + scilog_last_ptycho_scans()
command, no reset-tomo-params-on-account-change offer, no
collect_empty_frames() flat-field acquisition, no skip-if-already-in
optimization on lfzp_in(), no hard-stop button wired into lamni's
GUI, tomo_alignment_scan() with no clear lamni equivalent (lamni has
a different lookup-table correction scheme instead -- needs
discussion, not necessarily a gap), no OSA-collision-clearance
warning in lfzp_info(), a large confirmed-hardware-specific block
(sample-transfer/gripper/laser-tracker methods, not portable), and a
webpage-display opportunity that's symmetric on both setups rather
than a one-sided gap. No code changes -- findings only, for review.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 04:33:29 +02:00
x01dcandClaude Sonnet 5 8733b6cf91 docs/update session summary with zero-deg reference feature
Extend commit range to c3877ec, add item 7 (zero_deg_reference_at_each_subtomo
for lamni tomo_type 1), update expected test count to 423, and add a
manual-check line for the new feature.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 04:19:01 +02:00
x01dcandClaude Sonnet 5 c3877ecdef feat/add zero-deg radiation-damage reference shot for lamni tomo_type 1
Ports flomni's zero_deg_reference_at_each_subtomo to lamni's "8 equally
spaced sub-tomograms" mode: an extra dedicated projection at exactly 0
degrees before every sub-tomogram where the rotation naturally passes
back through 0, plus one final shot once the tomogram completes, for
tracking radiation damage over a long acquisition. lamni's tomo_type
2/3 already had the equivalent (golden_projections_at_0_deg_for_damage_estimation,
byte-for-byte identical to flomni's) -- only tomo_type 1 was missing.

_subtomo_starts_near_zero() uses subtomo_number % 2 (every odd
sub-tomogram) rather than flomni's 360-mode subtomo_number % 4 == 1 --
confirmed against lamni's actual rotation behavior rather than derived
from the position-array math, which doesn't cleanly split odd/even.

The GUI (tomo_params.py) needed no new UI code: _build_type1_section()
already gated this control on a per-setup has_zero_deg_reference flag,
so enabling it for lamni was just flipping that flag plus adding the
param name/default, mirroring flomni's entries exactly.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 04:18:23 +02:00
x01dcandClaude Sonnet 5 5ee00c997a docs/add session summary + test checklist for this branch's fixes
Brief AI_docs note covering commits 6c3d603..8a7a350: what changed and
what to test (automated + manual/simulated-session) to confirm the
branch is in good shape before relying on it at the beamline.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 03:58:33 +02:00
x01dcandClaude Sonnet 5 8a7a350280 feat/warn early about Fermat scans below the minimum point count
FlomniFermatScan/LamNIFermatScan already refuse to run with fewer than
20 positions (_check_min_positions(), raises ScanAbortion), but only
once the scan actually starts -- too late for an unattended tomo-queue
run, where the job just aborts mid-run and pauses the queue. Surface
the same prediction earlier instead, as a non-blocking warning:

- Extracted the position-generating methods on both scan classes
  (get_flomni_fermat_spiral_pos, and lamni's chain --
  _lamni_compute_scan_center, _lamni_compute_stitch_center,
  _compute_total_shift, _lamni_check_pos_in_fov_range_and_circ_fov,
  get_lamni_fermat_spiral_pos) into pure @staticmethods, so the exact
  same algorithm the scan server runs can also be called from
  client-side code without a live scan session. Behavior-preserving --
  verified against the existing exact-position/instruction assertion
  tests for both classes. Lifted the hardcoded "20" into a _MIN_POSITIONS
  class attribute on each, so client code references the same threshold.
- lamni.py/flomni.py: new _expected_fermat_position_count() calls the
  real scan-class algorithm with the live tomo parameters and prints a
  warning line in tomo_parameters() when below the minimum.
- tomo_params.py: a new live-updating "Estimated Fermat scan points"
  field (mirroring the existing achievable-step preview's styling),
  wired to the fov/step/stitch/piezo-range fields, flagged orange below
  the minimum -- never blocks Submit/Add-to-queue.

lamni's circular-FOV crop is angle/stitch-dependent, so the estimate is
representative (center tile, angle 0 for the CLI; the live stitch tile
for the GUI, which has those fields right there) rather than an exact
per-projection guarantee -- documented as such in both places.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 03:52:44 +02:00
x01dcandClaude Sonnet 5 4ada8e85ce docs/document tomo_queue_reacquire and tomo_queue_resume
Add both to the tomo-queue command reference in flomni.md and
lamni.md: tomo_queue_resume() as an alias for tomo_queue_execute(),
and tomo_queue_reacquire(job_index, projection_number) for reopening
a job (including an already-"done" one) at an earlier projection and
cascading later jobs to pending, mirroring the code added in the
previous commit.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 03:20:55 +02:00
x01dcandClaude Sonnet 5 abc2a3d041 feat/tomo queue reacquire from an earlier projection
Add tomo_queue_reacquire(job_index, projection_number) to the shared
TomoQueueMixin: reopens any queue job (even an already-"done" one) at
an earlier projection than its own recorded progress, and resets every
job queued after it -- tomo and command jobs alike -- to "pending", so
a following tomo_queue_resume() (a new alias for tomo_queue_execute(),
added for naming symmetry with tomo_scan_resume()) re-runs everything
from there forward in order. Reuses the existing tomo_scan_resume()
machinery by writing the requested resume point into the shared
progress global var, rather than adding new parameters to
tomo_scan()/tomo_queue_execute(). Guards against clobbering another
job's progress if one is already "incomplete"/"running" elsewhere in
the queue.

A flat projection number is used as the resume-point unit for all
three tomo_types, including type 1 (8 equally-spaced sub-tomograms),
via a new per-setup _resolve_type1_projection() that maps it to
(subtomo_start, start_angle). flomni's version reuses its existing
_subtomo_angle_plan(); lamni's inline angle math in sub_tomo_scan() was
extracted into an analogous _subtomo_angle_plan() static method first
(behavior-preserving -- verified against the existing angle-math test
suite) so both setups share the same pattern.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-20 22:00:22 +02:00
x01dcandClaude Sonnet 5 80f6972a31 feat/show at_each_angle_hook in lamni tomo_parameters()
flomni's tomo_parameters() CLI printout already showed the active
at_each_angle_hook; lamni's didn't, making it easy to have a custom
per-projection hook active without noticing from the parameter dump.
Added the identical guarded print, reusing the shared
TomoQueueMixin._describe_active_hook() helper both classes already
inherit. Also switched lamni's tomo_scan() end-of-scan summary to use
that same helper instead of the raw attribute, matching flomni's
equivalent block and correctly flagging a stale/unregistered hook name.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-20 21:37:51 +02:00
x01dcandClaude Sonnet 5 382e325093 feat/print constant shift alongside per-projection corrections
Correction 1, correction 2, and the x-ray-eye correction were already
printed for every projection, but the manual/constant shift
(manual_shift_x/manual_shift_y) applied in the same offset math wasn't
-- added on both setups, right after the existing correction calls in
lamni's tomo_scan_projection() and flomni's tomo_scan_projection() /
tomo_acquire_at_angle().

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-20 21:27:42 +02:00
x01dcandClaude Sonnet 5 cdfc02335d fix/tomo queue tooltip hides fields irrelevant to a job's tomo_type
Hovering a queue entry showed every snapshotted param unconditionally,
including golden-ratio settings on a type-1 (non-golden) job -- and
vice versa, type-1-only fields (angle range, zero-deg reference) on a
golden-ratio job. Reuses _update_type_visibility()'s existing
per-tomo_type section/field gating (already used for the live edit
form) to filter _job_tooltip()'s params list the same way, for both
flomni and lamni jobs.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-20 21:19:10 +02:00
x01dcandClaude Sonnet 5 6c3d60306d feat/lamni tomogram timing overview + repeat-on-interruption
Port two flomni tomo_scan() behaviors lamni was missing: an end-of-scan
timing overview (total time, time excluding gaps, time lost to gaps,
sample/hook info) printed and written to scilog, and automatic repeat
of an entire projection when the beamline interlock trips mid-scan.

The latter needed two pieces together: enabling
bec.builtin_actors.scan_interlock at tomo_scan() start (previously
never enabled for lamni), and wrapping _tomo_scan_at_angle in
@scan_repeat so the resulting ScanRestart (or any other transient
exception) redoes the whole projection instead of aborting the scan.
A new LamNIError marks the one non-retryable case (unregistered
at_each_angle_hook), replacing a plain ValueError there.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-20 21:13:21 +02:00
x01dc 55f6f944f7 fix/improve doc
CI for csaxs_bec / test (push) Successful in 1m34s
2026-07-20 14:37:00 +02:00
x01dc c0fdba0e6c feat/smaract_show_all
CI for csaxs_bec / test (push) Successful in 1m35s
2026-07-20 14:35:09 +02:00
x01dc 6f07c636ad remove data10 references 2026-07-20 14:31:15 +02:00
x01dc f7a1bf4a98 update/docs 2026-07-20 14:22:53 +02:00
x01dc cdc4ae02b5 fix/delays after newpar. lamni needs more time
CI for csaxs_bec / test (push) Successful in 1m35s
2026-07-20 14:18:26 +02:00
x01dc 3c46555df4 fix/increase delay after NEWPAR in FES
CI for csaxs_bec / test (push) Successful in 1m38s
2026-07-20 14:04:20 +02:00
x01dc 64170812a9 config adjustments 2026-07-20 14:04:00 +02:00
x01dcandholler 2863cd51c4 fix(bec_widgets): require sample owner in sample-storage widget
Read the Docs Deploy Trigger / trigger-rtd-webhook (push) Successful in 2s
CI for csaxs_bec / test (push) Successful in 2m3s
The owner prompt was labeled and treated as optional, but an occupied
slot must always have an owner recorded. Both the rename and new-sample
dialogs now validate the owner the same way the name field already is:
cancelling or leaving it empty aborts the write instead of silently
falling back to a blank/previous owner.
2026-07-16 16:28:12 +02:00
x01dcandholler d6c0c85a60 fix(bec_widgets): use cached device reads for sample-storage & slit polling
Both widgets' polling loops were issuing live EPICS round-trips every
2s even though the underlying signals are auto_monitor=True and BEC
already keeps a fresh monitored value locally. Switching to
read(cached=True) removes that redundant hardware traffic; for the
slit widget this also makes the old round-robin "selected slit every
tick, others in rotation" strategy unnecessary, so it now polls all
six slits every tick.
2026-07-16 16:28:12 +02:00