Scan Plans#

All scan plans are available in the session namespace after from id4_common.startup import *. They wrap or extend standard Bluesky plans with POLAR-specific defaults (counters, monitor selection, DM integration).


Common Scan Flags#

Most scan plans (lup, ascan, th2th, qxscan, grid_scan) share three optional boolean flags that enable advanced measurement modes.

The dichro and lockin flags require running pr_setup first to configure the scan (see Phase Retarder setup).

dichro — circular dichroism#

Switches the X-ray helicity at every point using the phase retarder (PR2) in a user defined sequence (usually +, −, −, +). The readings are accumulated by the dichro data stream into XAS and XMCD signals.

RE(lup(energy, -0.05, 0.05, 50, 1.0, dichro=True))
RE(ascan(field, -3, 3, 60, 2.0, dichro=True))

lockin — lock-in detection#

Oscillates the phase retarder continuously and reads the lock-in amplifier signal instead of the scaler. Use for high-sensitivity dichroism measurements.

RE(lup(energy, -0.05, 0.05, 50, 1.0, lockin=True))

fixq — fixed reciprocal-space position#

Keeps the diffractometer at a fixed HKL position throughout the scan by adjusting the real-space angles after each motor move. Useful for energy scans where you want to track a specific Bragg reflection as the energy changes. The HKL correction is applied after all other motors have moved.

RE(qxscan(7.514, 1.0, fixq=True))
RE(lup(energy, -0.05, 0.05, 50, 1.0, fixq=True))

Positioner Motions#

Motions are an exception in that it can be done using both the RE wrapper and through the mov and movr python magics.

Note that a positioner can be motor, temperature, magnetic field, energy, etc.

# Absolute move
mov positioner position
mov positioner1 pos1 positioner2 pos2 # simultaneous multi-motor move

# Relative move
movr positioner delta

These are the same as:

# Absolute move
RE(mv(motor, position))
RE(mv(motor1, pos1, motor2, pos2))

# Relative move
RE(mvr(motor, delta))

The move options above will wait until the motion is done. If you want to start a motion and gain back command line control use abs_set:

RE(abs_set(motor, position))

1-D Scans#

lup — relative scan#

Scan motor from start to stop relative to its current position, collecting npts points.

RE(lup(motor, start, stop, npts, time))
RE(lup(motor, start, stop, npts, time, md={"sample": "Fe3O4"}))

ascan — absolute scan#

Scan motor from start to stop in absolute coordinates.

RE(ascan(motor, start, stop, npts, time))

th2th — relative theta/2theta scan#

Coupled scan of mu and gamma (theta and 2theta) relative to their current positions. tth_start/tth_end set the 2theta range; theta moves at half that rate.

RE(th2th(tth_start, tth_end, npts, time_per_point))

Optional flags:

RE(th2th(tth_start, tth_end, npts, time_per_point, dichro=True))
RE(th2th(tth_start, tth_end, npts, time_per_point, lockin=True))
RE(th2th(tth_start, tth_end, npts, time_per_point, fixq=True))

2-D / N-D Scans#

grid_scan — absolute grid#

RE(grid_scan(
    motor1, start1, stop1, npts1,
    motor2, start2, stop2, npts2,
    time,
))

rel_grid_scan — relative grid#

Same as grid_scan but offsets are relative to current motor positions.


Simple Count#

RE(count(num, time))              # count num times with time per point
RE(count(num, time, delay=1.0))   # add delay (seconds) between readings

XAFS Energy Scan (qxscan)#

qxscan is an XAFS-style energy scan covering pre-edge, edge, and post-edge regions. The post-edge step size is defined in k-space (Å⁻¹) and converted to energy internally, so the energy step increases with energy above the edge.

All energies in qxscan_params are relative to the absorption edge.

Step 1 — Configure qxscan_params#

Call the device to enter the interactive setup wizard:

qxscan_params()

This prompts for pre-edge regions (energy start, step, time factor), the edge region (energy start/end, step, time factor), and post-edge regions (k end, k step, time factor). It then computes and stores the full energy list.

Parameters can also be saved and reloaded:

qxscan_params.save_params_json("my_scan.json")
qxscan_params.load_params_json("my_scan.json")
qxscan_params.load_from_scan(scan_id)   # restore from a previous run

Print the current setup:

print(qxscan_params)

Step 2 — Run the scan#

RE(qxscan(edge_energy, time))
  • edge_energy: absorption edge energy in eV (absolute)

  • time: counting time factor applied to all detectors

Optional flags:

RE(qxscan(edge_energy, time, dichro=True))   # switch polarization at each point
RE(qxscan(edge_energy, time, lockin=True))   # lock-in detection mode
RE(qxscan(edge_energy, time, fixq=True))     # fix diffractometer hkl during scan

APS Data Management Plans#

Plans in id4_common.plans.dm_plans submit workflow jobs to the APS DM system after data collection:

# Submit a workflow to DM after a scan completes
RE(dm_workflow_plan(
    workflow_name="4id-xmcd",
    scan_plan=ascan(motor, 0, 1, 10),
))

See the API reference for full parameter documentation of dm_plans.


RunEngine#

The RunEngine is available as RE after startup:

RE(lup(m1, -1, 1, 21))                       # run a plan
RE(lup(m1, -1, 1, 21), md={"note": "test"})  # add metadata
RE.abort()                                     # abort current plan
RE.stop()                                      # graceful stop
RE.pause()                                     # pause (resume with RE.resume())
RE.resume()