Skip to content

MODE-2: DMM physical insert (Mono) / bypass (Pink) is done by setting the three DMM Y motors (details in the description) #261

Description

@decarlof

MODE-2: DMM physical insert (Mono) / bypass (Pink) is done by setting the three DMM Y motors (m26=dmm_usy_ob, m27=dmm_usy_ib, m29=dmm_dsy) to 0 (in) or -10 mm (out) in parallel with the Bragg-arm park values, via the same energy set coordinated move; no special sequencing between in/out and Bragg-arm moves (all motors get put() commands in dict-iteration order wit
h ~0.1 s spacing then a single AllDone wait); the A-shutter is currently operator-managed (close/open calls commented out as a temporary XANES patch — intended permanent behaviour is conditional close for large moves to prevent filter-frame vacuum bumps); no interlock

MODE-2: Inspected the deployed move.py::motors() directly at /home/beams/2BMB/epics/synApps/support/energy/src/energy/move.py (decarlof/energy fork) — this is the canonical "energy change" workflow that handles mode switching too. Concrete answers below.

What moves

When the operator runs energy set --mode <Mono|Pink> --energy <value> and the mode changes (or the energy changes within a mode), the IOC writes 18 PVs in sequence: the 17 keys matching energy_move* and the 1 key matching energy_pos* (currently just fltr1select, the downstream filter paddle) for the target (mode, energy) row of energy2bm.json store_0:

# Key PV Mono value (typical) Pink value (typical)
1 energy_move_m1angl virtual mirror angle 2.615 mrad 2.615 mrad (constant)
2 energy_move_m1avg virtual mirror Y average 0.0 mm 0.0 mm (constant)
3 energy_move_dmm_usx 2bma:m25 111.0 mm 111.0 mm (constant)
4 energy_move_dmm_dsx 2bma:m28 104.0 mm 104.0 mm (constant)
5 energy_move_dmm_usy_ob 2bma:m26 0.0 mm (in) -10.0 mm (out)
6 energy_move_dmm_usy_ib 2bma:m27 0.0 mm (in) -10.0 mm (out)
7 energy_move_dmm_dsy 2bma:m29 0.0 mm (in) -10.0 mm (out)
8 energy_move_dmm_us_arm 2bma:m30 per energy (e.g. 1.131° @ 13.374 keV) 0.740° (parked) across all 4 Pink
9 energy_move_dmm_ds_arm 2bma:m31 per energy 0.751° (parked) across all 4 Pink
10 energy_move_dmm_m2_y 2bma:m32 per energy 17.020045 mm (parked) across all 4 Pink
11 energy_move_table3y 2bma:? downstream table Y per energy per energy
12 energy_move_flag 2bma:m44 per energy (e.g. 23.0 @ 13.374) 0.0 (parked) across all 4 Pink
13 energy_move_b_slit_top 2bma:m9 per energy 10.0 (wide open) across all 4 Pink
14 energy_move_b_slit_bot 2bma:m10 per energy -10.0 (wide open) across all 4 Pink
15 energy_move_m1mox 2bma:m1 (table-X support corner 0) 8.0 mm per Pink energy (8/10/10/29)
16 energy_move_m1_horizontal 2bma:m3 (mirror stripe selector) 1.0 mm (stripe a) per Pink energy (3.039/13/39/49)
17 energy_move_m1m2x 2bma:m4 (table-X support corner 2) 8.0 mm per Pink energy (8/10/10/29)
18 energy_pos_fltr1select downstream filter paddle index per (mode, energy) per (mode, energy)

The DMM Y motors m26 / m27 / m29 are the physical in/out actuators — drive to 0 mm to insert the DMM into the beam (Mono), drive to -10 mm to retract (Pink). The 10 mm retraction is enough to clear the beam envelope. The X motors m25 / m28 stay at their fixed alignment positions in both modes — they don't translate the DMM laterally as a mode-switch action (see ENERGY-
5 / cora#254
).

The DMM Bragg arms m30 / m31 and m2_y (m32) are also touched on every mode change — in Pink they're parked at fixed retracted values (m30 = 0.740°, m31 = 0.751°, m32 = 17.020045 mm) regardless of the requested Pink energy; in Mono they're set per energy from the calibration table (see ENERGY-1 / cora#249).

In what order

The move.py::motors() loop iterates the dict in insertion order (Python 3.7+ guaranteed), which matches the JSON key order shown in the table above. For each key it does:

epics_pvs[key].put(pos)   # asynchronous, no wait
time.sleep(.1)            # 0.1 s spacing between puts

So all 18 put() commands fire over ~1.8 seconds total, then the IOC waits for AllDoneA AND AllDoneB to clear before returning. All motors move in parallel for most of the move time — there's no sequencing between, for example, "DMM Y retracts" (step 5-7) and "Bragg arms park" (step 8-10). They start moving within ~0.5 s of each other and proceed concurrently.

Important: there is no interlock between the DMM Y retraction and the Bragg-arm retraction. In a Mono → Pink switch, the Y motors drop the tank while the Bragg arms simultaneously swing to their parked positions. The substrate geometry clears in both directions throughout the motion (the crystals don't collide during retraction) but there's no software gate that says "wait fo
r DMM Y to be at -10 before letting the Bragg arms move".

A-shutter handling — currently operator-managed (XANES-friendly patch); intended behaviour is conditional close based on energy-change magnitude

The deployed move.py has both the front-end shutter close AND open calls explicitly commented out:

log.warning('closing A-shutter')
# epics_pvs['CloseShutter'].put(1, wait=True)
# close_frontend_shutter(epics_pvs)
log.warning('opening shutter after energy change has been disabled')
# open_frontend_shutter(epics_pvs)

So the A-shutter (front-end shutter S02BM-PSS:FES:) is NOT touched during the energy / mode change in the current deployment. Note that the close-side log line still prints "closing A-shutter" misleadingly; the open-side log says "disabled" explicitly.

This is a temporary patch, not the intended permanent behaviour. Operator clarification:

  • The commented-out close+open was introduced specifically to support XANES (X-ray absorption near-edge spectroscopy) workflows. In XANES the per-scan energy step is very small (~eV-scale around an absorption edge), and the operator wants the A-shutter to stay open continuously to preserve the thermal stability of the upstream optics. Auto-closing the FES for every XANES step
    would introduce a thermal transient larger than the energy change itself.

  • The intended permanent behaviour is conditional close based on the requested energy-change magnitude:

    Energy-change magnitude A-shutter handling
    Small (XANES, eV-scale step within or near a single calibrated energy) Keep open. Current commented-out behaviour is correct.
    Large (between calibrated energies, Mono <-> Pink switch, any move covering > ~few keV) Close the A-shutter for the duration of the move to prevent vacuum bumps from the filter-holder frames crossing the beam during motion.

    The vacuum-bump risk comes from the filter paddle's mechanical frame intercepting the white beam as the assembly moves between calibrated positions — local heating, outgassing, pressure spike. Closing the FES upstream avoids it.

Until conditional logic is restored to move.py, the A-shutter is operator-managed. For large energy changes (including Mono <-> Pink mode switches) the operator should manually close the FES (S02BM-PSS:FES:CloseEPICSC) before invoking energy set and re-open it (OpenEPICSC) afterwards.

What is interlocked

Nothing software-level. The AllDoneA AND AllDoneB wait at the end of the move is the only post-move check; it's a "did all motors get to their targets within tolerance" check, not a safety interlock.

Hardware-level interlocks (PSS / ACIS / BLEPS) are upstream of the energy CLI and don't gate it — they gate the shutters and the source. If the PSS dropped during the mode switch, the FES would close on its own (independent of move.py), but the energy CLI wouldn't know and would continue commanding motor moves.

CORA-side application

The cora question's existing assumption ("DMM Y to about -10 out / 0 in, Bragg arms parked in pink; exact positions and sequence unknown") is structurally right — exact positions and (lack of) sequencing now confirmed. Recommended model:

  • Mode-switch Method on the Monochromator Asset: parameters are the target mode (Mono / Pink) and target energy. Internally invokes the same coordinated move as a per-energy change (no separate mode-switch primitive in the IOC).
  • Per-motor mode/energy targets: model each of the 18 motors with its (mode, energy) → position lookup. Per ENERGY-5 / cora#254 some are mode-constant (DMM X motors), some are mode-state-only (DMM Y motors, two values), some are mode + energy curves (Bragg arms, mirror stripe in Pink).
  • No mode-switch interlock to model on the cora side either — the move is "fire 18 put commands, wait for AllDone". Cora's Method completion predicate is the AllDone state, not a sequencing barrier.
  • Operator-managed A-shutter (today) — but the natural future state is conditional automation. The cora model today should treat the A-shutter as a Plan-level wrapper around the energy set Method: pre-step "close FES" + post-step "open FES" for any large move (between calibrated energies, Mono <-> Pink switch). Small XANES-style intra-energy scans should NOT wrap. When the
    conditional close logic is eventually restored to move.py, the wrap can move into the Method itself; until then it's the operator's (or Plan author's) responsibility.

Doc updates landed in the same commit

  • procedures/item_005.rst — the set_energy_to_preselect stub previously claimed the A-shutter is auto-closed during the move; corrected to flag that the deployed code has both the close and open calls commented out and the A-shutter is operator-managed.

Cross-references

Net

MODE-2 answer: DMM physical insertion is via 3 Y motors (m26/m27/m29) driven to 0 (in / Mono) or -10 mm (out / Pink), in the same coordinated energy set move as the Bragg-arm park (no sequencing between Y-retraction and arm-park — all 18 motors fire put commands in ~1.8 s then single AllDone wait). No software interlock. A-shutter handling is currently o
perator-managed
— the IOC close/open calls are commented out as a temporary XANES-friendly patch (small XANES steps should NOT close the shutter, to preserve optical thermal stability). Intended permanent behaviour is conditional close for large moves (between calibrated energies, Mono <-> Pink switch) to prevent vacuum bumps when the filter-holder frames cross the beam dur
ing motion. For large moves today, the operator should manually close + reopen the FES around energy set.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions