diff --git a/docs/scan_v4_migration_plan.md b/docs/scan_v4_migration_plan.md new file mode 100644 index 0000000..7d53945 --- /dev/null +++ b/docs/scan_v4_migration_plan.md @@ -0,0 +1,125 @@ +# SuperXAS scan v4 migration plan + +## Scope and source mapping + +This migration keeps the SuperXAS scan module boundaries intact. + +| Debye v4 source | SuperXAS target | +| --- | --- | +| `debye_bec/debye_bec/scans/xas_simple_scan.py` | `superxas_bec/superxas_bec/scans/mono_bragg_scans.py` | +| `debye_bec/debye_bec/scans/nidaq_continuous_scan.py` | `superxas_bec/superxas_bec/scans/nidaq_cont_scan.py` | + +The migration should not merge these scans into one file. `mono_bragg_scans.py` +remains the SuperXAS home for `xas_simple_scan` and `xas_advanced_scan`. + +## Step 2: migrate `mono_bragg_scans.py` + +Refactor `XASSimpleScan` and `XASAdvancedScan` from the old +`AsyncFlyScanBase` generator/stub API to the v4 `ScanBase` API used in Debye: + +- inherit from `ScanBase` +- set `scan_type = ScanType.HARDWARE_TRIGGERED` +- use `@scan_hook` lifecycle methods: + - `prepare_scan` + - `open_scan` + - `stage` + - `pre_scan` + - `scan_core` + - `at_each_point` + - `post_scan` + - `unstage` + - `close_scan` + - `on_exception` +- use `self.update_scan_info(...)` for scan metadata and additional scan + parameters +- set NIDAQ readout priority through + `self.actions.set_device_readout_priority([self.daq], priority="async")` +- read monitored devices through `self.actions.read_monitored_devices()` +- kick off and complete the mono bragg motor through `self.actions.kickoff(...)` + and `self.actions.complete(..., wait=False)` + +### SuperXAS-specific gonio pre-positioning + +The old SuperXAS scan stages `mo1_gonio` before the mono bragg oscillation by: + +1. converting start and stop energies to bragg angles +2. averaging both angles +3. moving `mo1_gonio` to that midpoint + +This behavior should be preserved in the v4 scan, but migrated away from +`yield from self.stubs.send_rpc_and_wait(...)`. + +Use the v4 device container and actions API instead: + +```python +self.mo1_bragg = self.dev["mo1_bragg"] +self.mo1_gonio = self.dev["mo1_gonio"] + +pos_start = self.mo1_bragg.convert_angle_energy(mode="EnergyToAngle", inp=self.start) +pos_end = self.mo1_bragg.convert_angle_energy(mode="EnergyToAngle", inp=self.stop) +gonio_position = (pos_start + pos_end) / 2 +self.actions.set(self.mo1_gonio, gonio_position, wait=False).wait(timeout=30) +``` + +This pre-positioning should happen before `self.actions.stage_all_devices()`. + +The default devices should be resolved from the v4 device container when they +are not passed explicitly: + +- `motor`: `self.dev["mo1_bragg"]` +- `daq`: `self.dev["nidaq"]` +- `gonio`: `self.dev["mo1_gonio"]` + +`XASAdvancedScan` should continue to extend the simple scan and only add spline +metadata (`p_kink`, `e_kink`) through `update_scan_info`. + +## Step 3: migrate `nidaq_cont_scan.py` + +Refactor `NIDAQContinuousScan` from `AsyncFlyScanBase` to v4 `ScanBase`, +following Debye's `NidaqContinuousScan`: + +- keep the scan name `nidaq_continuous_scan` +- resolve the default DAQ as `self.dev["nidaq"]` +- persist `scan_duration` and `compression` with `update_scan_info` +- set DAQ async readout priority +- add device progress scan report instructions for the DAQ +- run baseline readout asynchronously in `prepare_scan` +- kick off DAQ with `wait=False`, wait up to 5 seconds for kickoff, then + `complete(..., wait=False)` +- poll monitored devices until the DAQ completion status is done +- close the scan only after the baseline status has completed +- call `self.actions.check_for_unchecked_statuses()` during close + +## Step 4: migrate scan tests + +Add SuperXAS tests equivalent to the Debye v4 scan tests: + +- `tests/tests_scans/test_mono_bragg_scans_v4.py` +- `tests/tests_scans/test_nidaq_continuous_scan_v4.py` +- `tests/tests_scans/conftest.py`, if SuperXAS needs local v4 fixtures and + mock devices + +The scan tests should verify: + +- default v4 hook behavior +- scan metadata and readout priority +- mono bragg kickoff/completion loop +- NIDAQ kickoff/completion loop +- `post_scan` completion behavior +- advanced scan spline metadata +- SuperXAS gonio pre-positioning via `self.actions.set(...).wait(timeout=30)` + +## Later device migration analysis + +After the scan migration and tests are agreed and committed, analyze the +SuperXAS devices against the Debye equivalents: + +- `superxas_bec/superxas_bec/devices/nidaq` +- `superxas_bec/superxas_bec/devices/mo1_bragg` + +Special attention should be paid to Debye's migration helper +`devices/utils/utils.py::fetch_scan_info`, because it bridges old and new scan +metadata during the migration phase. The device migration plan should describe +where SuperXAS still reads old `scan_info.msg` or scan parameter shapes and how +to move those reads to the v4-compatible helper. +