| related |
|
|---|
Many scans need a prepared list of points before the scan can start moving hardware.
BEC keeps that logic in position_generators: a collection of reusable helpers that generate
positions for common scan patterns. A scan typically uses these helpers in prepare_scan, then
records the resulting positions in scan_info, checks limits, and lets components handle the
repeated execution pattern.
Position generation is often one of the most scan-specific parts of a scan.
For example, a scan may need to produce:
- a straight line through one or more axes
- a rectangular grid, optionally with snaking
- a spiral or Fermat pattern
- a circular shell pattern
- several disconnected regions combined into one trajectory
Keeping those patterns in reusable helpers makes scan classes shorter and easier to read.
Some of the most commonly used helpers are listed below.
| Helper | Role | Typical scan type |
|---|---|---|
line_scan_positions(...) |
Builds linearly spaced points for one or more axes. | line scans |
log_scan_positions(...) |
Builds positions with logarithmically increasing spacing between start and stop. | logarithmic line scans |
nd_grid_positions(...) |
Builds an N-dimensional grid, with optional snaking to reduce unnecessary travel. | grid scans |
multi_region_line_positions(...) |
Builds one 1D trajectory across several separate line regions. | multi-region line scans |
multi_region_grid_positions(...) |
Builds several rectangular sub-grids and concatenates them into one scan path. | multi-region grid scans |
spiral_positions(...) |
Builds an Archimedean spiral clipped to a rectangular region. | spiral scans |
fermat_spiral_pos(...) |
Builds a Fermat spiral inside rectangular scan bounds. | Fermat spiral scans |
round_scan_positions(...) |
Builds concentric circular shells around a center point. | round scans |
get_round_roi_scan_positions(...) |
Builds circular shell points and clips them to a rectangular region of interest. | round ROI scans |
hex_grid_2d(...) |
Builds a 2D hexagonal grid inside the requested scan bounds. | hexagonal scans |
oscillating_positions(...) |
Yields values in a back-and-forth pattern instead of returning a finite point array. | hysteresis or oscillating scans |
rotate_points(...) |
Rotates a 2D point set around a chosen center. | supporting helper |
For helpers that return a finite point list, the result is a NumPy array of shape
(num_points, num_motors). Each row is one scan point, and each column corresponds to one motor or
scanned axis. For single-axis scans, that usually means shape (num_points, 1).
For example, a two-motor grid scan might generate and consume positions like this:
positions = position_generators.nd_grid_positions(
[(-1.0, 1.0, 3), (-2.0, 2.0, 5)],
snaked=True,
)
for point in positions:
motor1_position = point[0]
motor2_position = point[1]The usual pattern is:
- use a position generator in
prepare_scan - optionally shift those points for relative motion
- check limits against the final point list
- store the positions and point count in
scan_info - pass the prepared positions into
components.step_scan(...)or another execution helper
This keeps the scan code split into clear responsibilities:
- position generators decide where the scan should go
actionshandles lifecycle and reportingcomponentshandles the repeated execution pattern
If the scan wants to improve the traversal order after generating the points, it can then call
components.optimize_trajectory(...) before execution begins. This is useful when the point
generator defines which points should be visited, but the scan still wants to reduce unnecessary
travel between those points.
Not every scan needs a position generator.
For example:
acquiredoes not move through a point list at all- monitor-style scans may react to live updates instead of a precomputed trajectory
- some scans generate their next point on the fly rather than preparing the full array up front
So position generators are common, but they are most useful when the scan can describe its path in advance.
After position generators, continue with fast axis and slow axis.
That page explains the axis-order convention used when scans generate points for more than one motor.
!!! info "What to remember"
- Position generators build reusable point lists for common scan geometries.
- They are most often used in prepare_scan before limit checks and execution begin.
- They keep scan classes shorter by separating path generation from execution logic.