Skip to content

Commit 1de51a0

Browse files
committed
feat: add scan documentation
1 parent 29cb64a commit 1de51a0

10 files changed

Lines changed: 1602 additions & 2 deletions
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
---
2+
related:
3+
- title: Position Generators
4+
url: learn/scans/position-generators.md
5+
- title: ScanArgument
6+
url: learn/scans/scanargument.md
7+
- title: Learn by Example
8+
url: learn/scans/learn-by-example.md
9+
---
10+
11+
# Fast Axis and Slow Axis
12+
13+
When a scan moves more than one axis, the order of those axes matters.
14+
15+
BEC follows one consistent convention:
16+
17+
- the outermost axis is the slow axis
18+
- the innermost axis is the fast axis
19+
20+
That means the fast axis changes most often, while the slow axis changes only after the inner sweep
21+
has finished.
22+
23+
## What That Means In Practice
24+
25+
A useful way to read the convention is:
26+
27+
For every point in `samx` from `-5` to `5`, move `samy` from `-10` to `10`.
28+
29+
In that example:
30+
31+
- `samx` is the slow axis
32+
- `samy` is the fast axis
33+
34+
So the scan stays on one `samx` value while it sweeps through the full `samy` range, then advances
35+
to the next `samx` value and repeats.
36+
37+
!!! example
38+
A 2D grid scan like this:
39+
40+
```py
41+
scans.grid_scan(dev.samx, -5, 5, 3, dev.samy, -10, 10, 5, snaked=False, relative=False)
42+
```
43+
44+
would have `samx` as the slow axis and `samy` as the fast axis, so the scan would:
45+
46+
1. keep `samx` fixed at `-5` while it sweeps `samy` from `-10` to `10`
47+
2. advance `samx` to the next value (in this case `0`) while it again sweeps `samy` from `-10` to `10`
48+
3. advance `samx` to the next value (in this case `5`) while it again sweeps `samy` from `-10` to `10`
49+
4. finish the scan after the last `samx` value has been reached and its inner sweep has completed
50+
51+
If the requirement is to have `samy` as the slow axis and `samx` as the fast axis, one would just swap the order of the motor arguments:
52+
53+
```py
54+
scans.grid_scan(dev.samy, -10, 10, 5, dev.samx, -5, 5, 3, snaked=False, relative=False)
55+
```
56+
57+
## Why This Matters
58+
59+
This convention affects how you read and define multi-axis scans:
60+
61+
- the order of axes in a grid or nested scan is meaningful
62+
- the generated point order follows that nesting
63+
- snaking typically changes the traversal direction of the fast axis while keeping the same slow-axis structure
64+
65+
Keeping that convention stable makes scan definitions easier to reason about and makes generated
66+
point lists more predictable.
67+
68+
## A Simple Example
69+
70+
At the user level, a grid scan might look like this:
71+
72+
```py
73+
scans.grid_scan(dev.samx, -5, 5, 3, dev.samy, -10, 10, 5, snaked=False)
74+
```
75+
76+
The same ordering appears in the generated positions:
77+
78+
```py
79+
positions = position_generators.nd_grid_positions(
80+
[(-5.0, 5.0, 3), (-10.0, 10.0, 5)],
81+
snaked=False,
82+
)
83+
84+
for point in positions:
85+
samx_position = point[0]
86+
samy_position = point[1]
87+
```
88+
89+
Here the first axis is the outer loop and the second axis is the inner loop:
90+
91+
- axis 1: slow axis
92+
- axis 2: fast axis
93+
94+
So the point order follows this pattern:
95+
96+
1. keep the first axis fixed
97+
2. sweep the second axis through all of its values
98+
3. advance the first axis
99+
4. repeat
100+
101+
## How To Read Existing Scan Code
102+
103+
When you see code such as:
104+
105+
```py
106+
positions = position_generators.nd_grid_positions(
107+
[(start_motor1, stop_motor1, steps_motor1), (start_motor2, stop_motor2, steps_motor2)],
108+
snaked=True,
109+
)
110+
```
111+
112+
read it as:
113+
114+
- the first tuple defines the slow axis
115+
- the second tuple defines the fast axis
116+
117+
That same idea also applies more generally to nested point generation: outer definitions correspond
118+
to slower-changing axes, and inner definitions correspond to faster-changing axes.
119+
120+
## Next Step
121+
122+
After axis-order conventions, continue with [ScanArgument](scanargument.md).
123+
124+
That page covers the rich input metadata used in scan signatures.
125+
126+
## What To Remember
127+
128+
!!! info "What to remember"
129+
- In BEC, the outermost axis is the slow axis and the innermost axis is the fast axis.
130+
- The fast axis changes most often within the generated point list.
131+
- This convention makes multi-axis scan definitions and point ordering easier to read.

docs/learn/scans/introduction.md

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
---
2+
related:
3+
- title: System architecture overview
4+
url: learn/system-architecture/overview/index.md
5+
- title: File writing
6+
url: learn/file-writer/introduction.md
7+
- title: Access BEC history
8+
url: how-to/scans/access-bec-history.md
9+
---
10+
11+
# Scans in BEC
12+
13+
!!! Info "Overview"
14+
Scans are the core of BEC's functionality. They are the tools that move your devices, trigger readouts, and produce the data you analyze. In BEC, all scans follow a shared structure and report themselves in a consistent way, even when their motion logic differs.
15+
16+
BEC scans follow one shared model. Whether you run a simple acquisition, a line scan, a grid scan,
17+
or a continuous scan, BEC handles them in the same overall way.
18+
19+
20+
## The Main Idea
21+
22+
The most important idea in BEC scan execution is simple:
23+
24+
- all scans follow the same overall structure
25+
- all scans are reported through the same backend model
26+
- all scans are executed on the server
27+
- all scans produce data that can be accessed in the same general way afterward
28+
- the client learns the available scans from the scan server, including their current signatures and metadata
29+
30+
That is true even when the middle of the scan is very different.
31+
32+
For example, a line scan, a grid scan, and a continuous scan may move differently, but they still fit into one common scan framework.
33+
34+
## What Happens During A Scan
35+
36+
!!! Note "Dataflow during a scan"
37+
A general overview of the dataflow in BEC can be found in the [system architecture overview](../../learn/system-architecture/overview/data-flow.md){ data-preview }.
38+
39+
At a high level, a scan in BEC follows this path:
40+
41+
1. The scan server publishes the available scan classes together with their serialized signatures, grouped inputs, and GUI metadata.
42+
2. The client exposes those scans dynamically, so commands such as `scans.line_scan(...)` use the current server-side definition.
43+
3. When a scan is called, the client validates the user inputs, resolves device-name strings to device objects where needed, and bundles repeated positional inputs for scans such as `line_scan` or `umv` before sending the request to the server.
44+
4. On the server, the request is queued and receives runtime identifiers.
45+
5. Once it is the scan's turn to run, the scan server queue hands over the request to a scan worker.
46+
6. The scan worker runs the scan class's lifecycle hooks and sends device instructions through Redis.
47+
7. Devices publish readouts and status updates.
48+
8. The scan bundler groups those readouts into logical scan points.
49+
9. Clients, history, and the file writer consume the resulting scan data.
50+
51+
From the user side, the important part is consistency: every scan goes through the same backend
52+
steps, reports its progress in the same general way, and produces scan data that can be accessed
53+
through the same tools afterward.
54+
55+
## The Shared Scan Shape
56+
57+
As mentioned above, all BEC scans follow the same overall shape. They share the same lifecycle steps, and they use the same helpers to report themselves and produce data. This allows users to be always prompted with a familiar structure, even when the motion logic of the scan differs. The shared shape also makes it easier to learn new scans, since you can focus on the differences in motion logic rather than having to learn a new overall structure for each scan. The lifecycle steps are:
58+
59+
<!-- The table is in html as the columns would otherwise wrap. There seems to be no internal way to control column width in markdown -->
60+
<table>
61+
<colgroup>
62+
<col style="width: 24%;">
63+
<col style="width: 76%;">
64+
</colgroup>
65+
<thead>
66+
<tr>
67+
<th>Hook</th>
68+
<th>Role</th>
69+
</tr>
70+
</thead>
71+
<tbody>
72+
<tr>
73+
<td style="white-space: nowrap;"><code>prepare_scan</code></td>
74+
<td>Prepare the scan for the upcoming acquisition.</td>
75+
</tr>
76+
<tr>
77+
<td style="white-space: nowrap;"><code>open_scan</code></td>
78+
<td>Open the scan and emit a new scan status message with all relevant metadata.</td>
79+
</tr>
80+
<tr>
81+
<td style="white-space: nowrap;"><code>stage</code></td>
82+
<td>Stage the devices for the upcoming acquisition.</td>
83+
</tr>
84+
<tr>
85+
<td style="white-space: nowrap;"><code>pre_scan</code></td>
86+
<td>Run any pre-scan logic, such as preparing time-critical devices.</td>
87+
</tr>
88+
<tr>
89+
<td style="white-space: nowrap;"><code>scan_core</code></td>
90+
<td>Run the core logic of the scan; trigger readouts if needed and optionally call <code>at_each_point()</code> for per-point logic.</td>
91+
</tr>
92+
<tr>
93+
<td style="white-space: nowrap;"><code>post_scan</code></td>
94+
<td>Run any post-scan logic, such as moving devices back to their original position.</td>
95+
</tr>
96+
<tr>
97+
<td style="white-space: nowrap;"><code>unstage</code></td>
98+
<td>Unstage the devices.</td>
99+
</tr>
100+
<tr>
101+
<td style="white-space: nowrap;"><code>close_scan</code></td>
102+
<td>Close the scan and emit a final scan status message with all relevant metadata.</td>
103+
</tr>
104+
<tr>
105+
<td style="white-space: nowrap;"><code>on_exception</code></td>
106+
<td>If an exception is raised during any earlier step, run cleanup logic here.</td>
107+
</tr>
108+
</tbody>
109+
</table>
110+
111+
BEC runs these lifecycle steps in the same order every time.
112+
113+
This shared shape is why BEC scans feel related rather than like separate one-off tools.
114+
115+
## Where To Go Next
116+
117+
If you want the next layer of detail:
118+
119+
- read [Learn by example](../learn/scans/learn-by-example.md){ data-preview } to go through the `acquire` scan example in detail, and see how the shared scan shape applies to a specific scan type.
120+
121+
## What to Remember
122+
123+
!!! info "What to remember"
124+
- In BEC, all scans follow the same overall shape.
125+
- Different scan types use the same backend framework, even when their motion logic differs.
126+
- Every scan reports itself in a common structured way while it runs.
127+
- Every scan has to implement the same lifecycle steps, even when some of those steps are empty.
128+
- The lifecycle steps are called in the same order for every scan.

0 commit comments

Comments
 (0)