Skip to content

Inventory

The 2-BM equipment inventory: the CORA Asset model view (the device tree, settings, vendor Models, drawings, and signal wiring) plus the computed axes. Live condition is served by the app, not this page.

Assets

One row per registered Asset under the 2-BM root (tier = Unit, bound to its Site Facility via facility_code). Hutches 2-BM-A (optics) / 2-BM-B (experiment) are Enclosures, not Assets. Condition is served live by the app.

Asset Family Model Parent Key settings Condition
StationShutter Shutter (none cited) 2-BM - live
SampleTable Table (none) 2-BM axis_layout=translation_xyz live
RotaryDriveChassis Housing aerotech_tm3a 2-BM serial 160591-A-1-1, order 730578 live
RotaryDrive MotionController aerotech_ensemble_ml RotaryDriveChassis serial 730792/1, axis_count=1, Aerotech_Native live
Rotary RotaryStage aerotech_abrs250mp LaminographyPitch -360..360 deg, max_speed=720 deg/s (operational), aperture=35 mm, encoder_resolution=0.000676 deg live
SampleStageDrive MotionController oms_vme58 2-BM axis_count=91, OMS_VME live
FrontEndDrive MotionController oms_vme58 2-BM axis_count=91, OMS_VME live
FrontEndWindow Window (none) 2-BM OFHC-housed Be window assembly; identity-only (beam-effect deferred) live
Window_W4_20 Window (none) FrontEndWindow Be 0.25 mm, z 28718, aperture 120 x 15 mm live
Window_W4_60 Window (none) FrontEndWindow Be 0.13 mm, z 30804, aperture 25 x 120 mm live
Window_DS Window (none) FrontEndWindow Be 0.25 mm, z 32417, aperture 8.8 x 145 mm (unlabelled, downstream-most) live
Mirror Mirror (none) MirrorTable driven by FrontEndDrive; 4 coating stripes (a/b/c/d), held in Mono, swept per Pink energy (MIRROR-1) live
Mirror_StripeReachX PseudoAxis (none) Mirror m1_horizontal (2bma:m3); Mono held at stripe a, Pink swept per energy live
MirrorTable Table (none) 2-BM axis_layout=virtual_pose, virtual_record=2bma:table1 live
Monochromator Monochromator (none) 2-BM dmm_insertion=inserted (MODE-2); driven by FrontEndDrive live
Monochromator_BraggArmUpstream PseudoAxis (none) Monochromator energy->upstream Bragg arm (deg) live
Monochromator_BraggArmDownstream PseudoAxis (none) Monochromator energy->downstream Bragg arm (deg) live
Monochromator_M2Y PseudoAxis (none) Monochromator energy->M2 vertical offset (mm) live
ConditioningSlit Slit (none) 2-BM white-beam slits; driven by FrontEndDrive live
Filter Filter (none) 2-BM foil changer; driven by FrontEndDrive live
Filter_FoilSelector_Upstream PseudoAxis (none) Filter m17 slot index -> position (Nearest); operational selector live
Filter_FoilSelector_Downstream PseudoAxis (none) Filter m18 slot index -> position (Nearest); bindings retained Faulted (m18 failed 2026-06-19)
DiagnosticFlag Screen (none) 2-BM 2bma:m44 at z=32500; raised in Mono, parked in Pink live
DiagnosticFlag_Y PseudoAxis (none) DiagnosticFlag energy->flag height (Mono); parked out in Pink live
SampleSlit Slit (none) 2-BM B-station slits; driven by FrontEndDrive live
SampleSlit_VerticalTop PseudoAxis (none) SampleSlit energy->top blade beam position (mm) live
SampleSlit_VerticalBottom PseudoAxis (none) SampleSlit energy->bottom blade beam position (mm) live
SampleSlit_VerticalCenter PseudoAxis (none) SampleSlit derived MidRange(top, bot); beam-walk centre live
SampleSlit_VerticalAperture PseudoAxis (none) SampleSlit derived Difference(top, bot); constant 20 mm gap live
SampleTop_X LinearStage kohzu_cyat070 Rotary -10..10 mm, max_speed=1 mm/s, encoder_resolution=0.0005 mm live
SampleTop_Z LinearStage kohzu_cyat070 SampleTop_X same Model/controller as SampleTop_X live
HexapodDrive MotionController aerotech_automation1_ixr3 2-BM serial 486125-01, axis_count=6, Aerotech_Native live
Hexapod Hexapod aerotech_hex300 SampleTable serial 486060-01; full HEX300 envelope (see Settings) live
Hexapod_X PseudoAxis (none) Hexapod DoF; translation X live
Hexapod_Y PseudoAxis (none) Hexapod DoF; translation Y live
Hexapod_Z PseudoAxis (none) Hexapod DoF; translation Z; modelled, not wired in current EPICS modelled (unwired)
Hexapod_Roll PseudoAxis (none) Hexapod DoF; rotation A about X live
Hexapod_Pitch PseudoAxis (none) Hexapod DoF; rotation B about Y live
Hexapod_Yaw PseudoAxis (none) Hexapod DoF; rotation C about Z; modelled, not wired in current EPICS modelled (unwired)
LaminographyPitch TiltStage kohzu_sa16a Hexapod Kohzu SA16A 2bmb:m49; tomo/lamino = tilt setpoint live
PropagationDistanceDrive MotionController aerotech_ensemble_hle 2-BM serial 228849-02, axis_count=1, Aerotech_Native live
Timing TimingController (none) 2-BM softGlueZynq trigger box; protocol=EPICS; no controller_id live
ApertureFineDrive MotionController piezosystem_jena_nv200d 2-BM two Jena NV200D units; fine-positions the Aperture coded-mask; FPGA-stepped via Timing live
DetectorTable Table (none) 2-BM axis_layout=virtual_pose, virtual_record=2bmb:table3 live
DetectorTable_X PseudoAxis (none) DetectorTable IOC virtual axis; 2bmb:table3.X live
DetectorTable_Y PseudoAxis (none) DetectorTable IOC virtual axis; 2bmb:table3.Y live
DetectorTable_Z PseudoAxis (none) DetectorTable IOC virtual axis; 2bmb:table3.Z live
DetectorTable_Roll PseudoAxis (none) DetectorTable IOC virtual axis; raw AZ; 2bmb:table3.AZ live
DetectorTable_Pitch PseudoAxis (none) DetectorTable IOC virtual axis; raw AX; 2bmb:table3.AX live
DetectorTable_Yaw PseudoAxis (none) DetectorTable IOC virtual axis; raw AY; 2bmb:table3.AY live
Housing Housing (none) PropagationDistance Microscope chassis; installed into a Mount live
Turret LinearStage (microscope catalog) Housing -60.3784..59.2300 mm (DET-11), encoder_resolution=0.0016 mm live
Objective_10x Objective (microscope catalog) Housing mag 10.0, NA 0.28, WD 33.5 mm live
Objective_2x Objective (microscope catalog) Housing mag 2.0, NA 0.055, WD 34 mm live
Objective_1.1x Objective (microscope catalog) Housing mag 1.1, NA 0.03, WD 50 mm live
Objective_Selector PseudoAxis (none) Housing writes MCTOptics LensSelect; (lens x camera) turret lookup, Camera 0 column as 1D provenance rule (DET-11) live
PropagationDistance LinearStage aerotech_pro225sl DetectorTable sample-to-detector rail; driven by PropagationDistanceDrive live
Camera Camera (microscope catalog) Housing 5 MP FLIR Oryx; 2448x2048, 3.45 um, 12 bit, 162 Hz, CMOS GlobalShutter live
Camera_HighRes Camera flir_oryx_31mp Housing 31 MP FLIR Oryx; pixel 3.45 um; other settings pending live (settings pending)
Camera_Selector LinearStage schunk_lptm_30 Housing Schunk LPTM 30; Pos.0=20, Pos.1=15; stage settings pending live (settings pending)
Scintillator Scintillator (microscope catalog) Housing thickness 100 um, decay 0.07 us live

Microscope-bound Models (turret motor, Mitutoyo MPLAPO kit, FLIR Oryx, Crytur LuAG) live on the Microscope deployment page. The kohzu_sa16a binding for LaminographyPitch is on the Sample tower page.

Settings

Per-asset settings the source spells out in prose. Open-item tags (DRIVE-1, DRIVE-2) kept inline.

Asset Settings
SampleTable axis_layout=translation_xyz; direct motors 2bmb:m24 Y, 2bmb:m20 Z, 2bmb:m21 X-up, 2bmb:m22 X-down
DetectorTable axis_layout=virtual_pose; virtual_record=2bmb:table3; geometry=SRI: 3 Y-supports, 2 X-supports, 1 Z-support
MirrorTable axis_layout=virtual_pose; virtual_record=2bma:table1; geometry=SRI support table; X axes M0X/M2X driven by energy-change IOC. Stale: upstream removed the 2bma:table1 record on 2026-06-15, so this virtual-pose layout no longer matches the beamline; re-modelling onto the raw motors is open as MODE-3
Monochromator dmm_insertion=inserted (closed enum inserted | retracted, MODE-2); DMM Y motors 2bma:m26 / m27 / m29 to 0 in (Mono) / -10 mm out (Pink); driven by FrontEndDrive
RotaryDrive serial_number=730792/1; firmware_version=unknown-pending-confirmation (DRIVE-2); axis_count=1; protocol=Aerotech_Native; installed in RotaryDriveChassis
RotaryDriveChassis altids: serial 160591-A-1-1 (SerialNumber), order 730578 (Other); drawing 630D2079 REV-H; inventory-only, no command surface
PropagationDistanceDrive serial_number=228849-02; firmware_version=unknown-pending-confirmation (DRIVE-2); axis_count=1; protocol=Aerotech_Native; IOC handle 2bmbAERO (EPICS_PV altid); addressed 2bmbAERO:m1
SampleStageDrive serial_number=unknown-pending-confirmation (DRIVE-1); firmware_version=unknown-pending-confirmation (DRIVE-2); axis_count=91; protocol=OMS_VME; crate ioc2bmb, no IP (VME-bus)
FrontEndDrive serial_number=unknown-pending-confirmation (DRIVE-1); firmware_version=unknown-pending-confirmation (DRIVE-2); axis_count=91; protocol=OMS_VME; crate ioc2bma, no IP (VME-bus)
HexapodDrive serial_number=486125-01; firmware_version=unknown-pending-confirmation (DRIVE-2); axis_count=6; protocol=Aerotech_Native; Automation1-iXR3 in separate rack
Timing firmware_version=2.0 (softGlueZynq gateware/bitstream); serial_number=unknown (IOC-admin only, not beamline-retrievable); output_channel_count=unknown (IOC-admin only, not beamline-retrievable); protocol=EPICS; 2bmbMZ1:SG:
Rotary min_position=-360 deg; max_position=360 deg; max_speed=720 deg/s (operational soft limit); max_speed_datasheet=3000 deg/s (500 rpm); encoder_resolution=0.000676 deg; homing_offset=0 deg; aperture=35 mm; accuracy_rotation=2 arcsec; repeatability_rotation=1 arcsec; load_capacity_axial=66 kg; load_capacity_radial=36 kg; load_capacity_tilt=28 Nm; stage_mass=15.6 kg; altid serial 146853-A-1-1-X; part ABRS-250MP-M-AS; datasheet on file (#164)
SampleTop_X min_position=-10 mm; max_position=10 mm; max_speed=1 mm/s; encoder_resolution=0.0005 mm; channel 2bmb:m18
SampleTop_Z same Model kohzu_cyat070 + controller as SampleTop_X; channel 2bmb:m17
Hexapod travel_x=55 mm, travel_y=60 mm, travel_z=25 mm, travel_a=15 deg, travel_b=15 deg, travel_c=30 deg; max_speed_translation=25 mm/s, max_speed_rotation=15 deg/s; resolution_translation=20 nm, resolution_rotation=0.2 urad; accuracy_translation=1 um, accuracy_rotation=10 urad; load_capacity_vertical=45 kg, load_capacity_horizontal=21 kg; stage_mass=12 kg; altid serial 486060-01; part HEX300-230HL-E1-PL4-TAS; datasheet on file (Hex300-Data-Sheet-D20250203). The speed and accuracy pairs are dominant-axis envelope figures, not per-DoF measurements
Scintillator thickness=100 um; decay_time=0.07 us
Camera sensor_width=2448 pixel; sensor_height=2048 pixel; pixel_size=3.45 um; bit_depth=12 bit; max_framerate_hz=162 Hz; sensor_kind=CMOS; readout_mode=GlobalShutter; altids model Oryx ORX-10G-51S5M, serial 19173710, firmware 1710.0.0.0, EPICS 2bmSP1:
Camera_HighRes model Oryx ORX-10G-310S9M; serial 22150530; firmware 1904.0.72.0; EPICS 2bmSP2:; pixel_size=3.45 um; remaining Camera-schema settings pending
Camera_Selector Schunk LPTM 30 (2bmb:m5); Pos.0=20, Pos.1=15; min/max/max_speed/encoder_resolution pending
Turret min_position=-60.3784 mm; max_position=59.2300 mm; encoder_resolution=0.0016 mm; Nanotec ST4118M1404-B, Heidenhain ERO 1420 encoder; MCTOptics resolves the (lens x camera) position (DET-11), Camera 0 column: 1.1x=-59.8184 mm, 2x=-0.5734 mm, 10x=58.8707 mm
Objective_10x magnification=10.0; numerical_aperture=0.28; working_distance=33.5 mm
Objective_2x magnification=2.0; numerical_aperture=0.055; working_distance=34 mm
Objective_1.1x magnification=1.1; numerical_aperture=0.03; working_distance=50 mm

Vendor catalog

Models bound to non-microscope 2-BM Assets. Model ids are derived from (manufacturer, part number), so one vendor product converges on one id. Microscope-housing Models are on the Microscope deployment page.

Generated from the catalog

This table is generated from catalog/catalog.yaml. Edit the catalog, not this table.

Model Manufacturer Part number Used by
aerotech_hex300 Aerotech HEX300-230HL-E1-PL4-TAS Hexapod
aerotech_abrs250mp Aerotech ABRS-250MP-M-AS Rotary
aerotech_ensemble_ml Aerotech ENSEMBLEML 10-40-IO-MXH RotaryDrive
aerotech_automation1_ixr3 Aerotech Automation1-iXR3-VL1-VB4-VB4-SB0CT222222-P1P1P1P1P1P1-CO-LC1MT1PSO6-SI0-TAS HexapodDrive
aerotech_ensemble_hle Aerotech EnsembleHLe10-40-A-IO-MXH PropagationDistanceDrive
aerotech_pro225sl Aerotech PRO225SL-1000 PropagationDistance
aerotech_tm3a Aerotech TM3-A-20B VDC-20B VDC / NO SPLIT / PS24-1 / C1ML-06 / C2ML-09 / US-115VAC RotaryDriveChassis
oms_vme58 Oregon Micro Systems VME58 SampleStageDrive, FrontEndDrive
kohzu_cyat070 Kohzu CYAT-070 SampleTop_X, SampleTop_Z
piezosystem_jena_nv200d Piezosystem Jena NV200D/NET ApertureFineDrive

Controller back-references: RotaryDrive->Rotary.controller_id; HexapodDrive->Hexapod.controller_id; PropagationDistanceDrive->PropagationDistance.controller_id (IOC 2bmbAERO); SampleStageDrive->SampleTop_X (2bmb:m18) / SampleTop_Z (2bmb:m17) + 89 further motors on ioc2bmb; FrontEndDrive->Mirror, Monochromator, ConditioningSlit, SampleSlit, Filter on ioc2bma. The Objective_Selector (2bmb:m1) and Camera_Selector (2bmb:m5) steppers run through the SampleStageDrive OMS crate, not distinct controller Assets. The six Hexapod_* DoF facets bind no Model (the physical Hexapod carries aerotech_hex300).

Engineering drawings

One canonical (system, number, revision) triple per Asset, except Rotary, which cites both its per-unit vendor drawing and the ABRS family datasheet. Optique Peter MAN-11863 (rev 0521-0465-A) is the shared housing manual covering every Microscope-bound constituent.

Asset Drawing System
Hexapod Hex300-Data-Sheet rev D20250203 EDMS
Rotary 630C2125 rev (-) EDMS
Rotary ABRS series datasheet (2021-01) Aerotech
RotaryDriveChassis 630D2079 rev H EDMS
PropagationDistance MAN-11863 rev 0521-0465-A EDMS
Turret MAN-11863 rev 0521-0465-A EDMS
Objective_10x MAN-11863 rev 0521-0465-A EDMS
Objective_2x MAN-11863 rev 0521-0465-A EDMS
Objective_1.1x MAN-11863 rev 0521-0465-A EDMS
Scintillator MAN-11863 rev 0521-0465-A EDMS
StationShutter 41050401-410003 rev (-) (P6-50 shutter element; assembly 41050401-500000) ICMS
Mask 4102020101-240000 (M3-24 front-end exit mask; RSS 02-BM-A-F-01) ICMS
Window_W4_20 4105090804-200000 (front-end Be window W4-20) ICMS
Window_W4_60 4105090804-600000 (front-end Be window W4-60) ICMS
Window_DS 4102020106-400000 (front-end Be window, unlabelled, downstream-most) ICMS

Not yet cited: Kohzu CYAT-070 datasheet (SampleTop_*), a FLIR Oryx datasheet (Camera).

Signal wiring

Trigger and step signals are modelled as typed ports plus wires resolved at Plan-bind time. Executable model: apps/api/tests/integration/scenarios/test_2bm_trigger_wiring.py.

Fine-positioning piezo controllers

  • ApertureFineDrive = two Piezosystem Jena NV200D/NET single-channel controllers (staff item_028), one per axis (X 10.54.113.126, Y 10.54.113.125), EPICS IOC JenaNV200D on host arcturus. They fine-position the Aperture coded-mask via a nanoSXY 120 CAP XY flexure stage (part T-223-06D, 120 um nominal / 100 um closed-loop per axis, 12.5 mm clear aperture); the axes step under FPGA trigger for compressive-sensing dithered sampling (PIEZO-1/2/4).
  • The Jena NV100D (formerly the provisional OpticsFineDrive, IOC JenaNV100D) is physically present but not in operational use at 2-BM: it lacks the external trigger mode tomoscan fly-scan needs, so no Run drives it. Recorded as provenance, not modelled as an active controller.
  • The driven X/Y LinearStage axis Assets and the Aperture identity registration are deferred to a follow-up slice. The FPGA out2/out3 -> X/Y cable map is confirmed (PIEZO-5, see below).

NV200D trigger wiring

Asset Port Direction signal_type
Timing out2, out3 OUTPUT step_trigger_ttl
ApertureFineDrive step_x_in, step_y_in INPUT step_trigger_ttl
  • Wires: Timing.out2 -> ApertureFineDrive.step_x_in, Timing.out3 -> ApertureFineDrive.step_y_in; up to 1024 positions/axis.
  • Per-axis chain, confirmed end to end by a physical patch-panel trace on 2026-07-28 (PIEZO-5, staff item_020):
Axis FPGA out softGlue signal Gate-delay PV NV200D TRG IN
X out2 JenaX 2bmbMZ1:SG:GateDly-2_DLY 10.54.113.126
Y out3 JenaY 2bmbMZ1:SG:GateDly-3_DLY 10.54.113.125
  • CORA previously recorded the two gate-delay PVs with their axes reversed and flagged the result as a cable-map contradiction. There was no contradiction: the axis comments in staff item_028 were themselves wrong, and the same trace corrected them (2bm-docs a3aa6be0). The cable map was right throughout, so the wires above are unchanged; only the delay-PV association moved. An operator screen still carrying the old annotation would point at the wrong axis's delay, which is why the binding is recorded per axis rather than as a PV list.
  • Ports sit on the controller box today; they migrate onto per-axis Assets when registered.

Camera trigger wiring

Asset Port Direction signal_type
Timing out1 OUTPUT frame_trigger_ttl
Camera trigger_in INPUT frame_trigger_ttl
  • One wire: Timing.out1 -> Camera.trigger_in. frame_trigger_ttl (start exposure) is distinct from the piezo step_trigger_ttl (advance a motion step).
  • Signal path (TIME-2): PSO -> MUX2-1 -> GateDly1 -> outTrig -> FPGA out1 -> FLIR Oryx Line2. The port is named for the pin, matching out2/out3; outTrig is the softGlue signal carried on it, and Line2 is the camera-side input for both installed Oryx bodies.
  • Pulse shaping on this leg is the GateDly1 block, whose PV fields are dot-separated record fields (2bmbMZ1:SG:GateDly1.DLY, .Width) where the two piezo legs use flat underscore names (GateDly-2_DLY, _WIDTH). The three blocks are peers in function but not in PV form, so each is recorded verbatim rather than derived from a template. They are not registered as Assets: nothing yet needs to address a gate-delay block by identity.
  • softGlue Width/DLY count 10 MHz clock cycles (100 ns/count, so Width=100 = 10 us pulse); per-scan values are Method/Plan config.

Computed axes

The PseudoAxis Assets whose position is computed from the motors underneath, divided by who owns the math: firmware SolverReference (hexapod), an edge IOC with no rule (detector table), or a Calibration-backed LookupTable (energy axes and foil selector). Executable models cited per subsection.

Hexapod DoF model

One physical Device (vendor-sealed Aerotech HEX300; inverse kinematics in firmware solver 2bmHXP). Six DoF surfaced as PseudoAxis sub-modules, each carrying a SolverReference partition rule; per-DoF envelope stays on the Hexapod settings, not duplicated onto facets.

DoF Asset Kind Axis Vendor label EPICS channel
Hexapod_X translation along X n/a 2bmHXP:m1
Hexapod_Y translation along Y n/a 2bmHXP:m2
Hexapod_Z translation along Z n/a none (no operator handle)
Hexapod_Roll rotation about X A (travel_a) 2bmHXP:m5
Hexapod_Pitch rotation about Y B (travel_b) 2bmHXP:m4
Hexapod_Yaw rotation about Z C (travel_c) none (no operator handle)
  • Z and Yaw exist physically but are not exposed as operator channels in 2-BM's current EPICS (no m3/m6); CORA still models all six (deployment-configuration limit, not a device one).
  • Constituent-port wiring: each DoF reads feedback from Hexapod via Plan.wires (not a partition-rule field). Hexapod exposes x/y/z_feedback_out (position_feedback_linear_mm) and roll/pitch/yaw_feedback_out (position_feedback_rotation_deg); each facet has one constituent_in INPUT plus one <axis>_out setpoint OUTPUT. Six wires, one per DoF (Hexapod.<axis>_feedback_out -> Hexapod_<Axis>.constituent_in). validate_pseudoaxis_fanout exempts SolverReference from the arity check; decomposition is owned by the firmware solver.
  • Dial-to-user coordinate convention, user = dial home + OFFSET, confirmed 2026-07-28 (HXP-8). Y is the axis where the two differ: it homes at dial 350 with OFFSET = -350, so a homed Y reads user 0. The other axes carry their own pair. The source states the Y numbers without a unit; they are recorded as given rather than assigned one. This is calibration state, not an advisory: it replaces an earlier Caution that described a manual post-reboot dial correction which is no longer performed. What re-homes the axes is HXP-9.

Detector table axes

Six virtual axes on the 2bmb:table3 record, modelled as PseudoAxis sub-Assets of DetectorTable. No partition rule and no constituent wiring: the table_full IOC computes the pose from six support motors (M0X / M0Y / M1Y / M2X / M2Y / M2Z) in SRI geometry. Addressing is a direct ControlPort write to the table3.* PV (spine/edge seam). Model: apps/api/tests/integration/scenarios/test_2bm_optical_tables_setup.py.

Axis Asset Kind table3 field Raw label
DetectorTable_X translation .X n/a
DetectorTable_Y translation .Y n/a
DetectorTable_Z translation .Z n/a
DetectorTable_Pitch rotation .AX AX
DetectorTable_Yaw rotation .AY AY
DetectorTable_Roll rotation .AZ AZ

Angular mapping (AX=pitch, AY=yaw, AZ=roll) is staff-confirmed (STAGE-9).

Energy-tracking optic axes

Setting energy is a discrete coordinated move. The staff energy-change IOC stores per-energy positions (store_0 saved table) and drives ~15 motors. Each per-axis relationship is modelled as a continuous curve: a PseudoAxis carrying a LookupTable partition rule converting energy (unit_in=keV) to axis position, pinning a Calibration revision by id (energy_position_curve quantity). Each axis carries a Mono curve (beam_mode=mono, the active rule) and a sibling Pink curve (beam_mode=pink, parked constant). invertible is per-axis: the Bragg arms + M2Y are monotonic in energy (invertible=True); the slit blades (constant 20 mm aperture, non-monotonic centre walk) and the flag Y (flat at the top of its range) are invertible=False with readback_aggregator_kind=Identity. Coordinating operation = the energy_setting Procedure, which accepts a free keV value between saved points. Models: test_2bm_energy_curves_setup.py (curves) + test_2bm_energy_setting.py (operation).

Configured Mono energies (the curve x-points, real): 13.374, 13.574, 18.0, 20.0, 25.0, 25.584 keV; Pink: 30, 40, 50, 60 keV (axes parked). Pink mode bypasses the monochromator. Beam-mode switching itself is on the Procedures page, not a virtual axis.

Axis Asset Motors / handle Curve unit_out
Monochromator_BraggArmUpstream dmm_us_arm energy -> upstream Bragg arm deg
Monochromator_BraggArmDownstream dmm_ds_arm energy -> downstream Bragg arm deg
Monochromator_M2Y dmm_m2_y energy -> M2 vertical offset compensator mm
SampleSlit_VerticalTop b_slit_top energy -> top blade beam-walk mm
SampleSlit_VerticalBottom b_slit_bot energy -> bottom blade beam-walk mm
DiagnosticFlag_Y energy_move_flag (2bma:m44) energy -> flag height (Mono), parked out in Pink mm
Mirror_StripeReachX m1_horizontal (2bma:m3) held at stripe a in Mono (m3 1.0), swept per Pink energy (3.039/13/39/49) mm
  • Slit aperture is held constant at 20 mm; only the centre tracks the beam walk (non-monotonically). The centre and aperture are modelled as derived axes: SampleSlit_VerticalCenter (Aggregation MidRange) and SampleSlit_VerticalAperture (Aggregation Difference) over the two blades. Aggregation is one-way (computed from constituents), so they are read-only views. The rules declare the relationship; binding the two specific blades via constituent port wiring (the hexapod-pose pattern) is deferred with the rest of the per-facet conduct wiring.
  • DMM insert/bypass (MODE-2): the DMM is physically inserted in Mono and retracted in Pink. This two-state, mode-keyed position is NOT a per-energy curve; it is the dmm_insertion setting on the Monochromator (closed enum inserted | retracted, the Table.axis_layout pattern), with the three DMM Y motors (2bma:m26 / m27 / m29, driven together to 0 in / -10 mm out) documentary in beamline.yaml. The coordinated move that drives it is the deferred beam_mode_change (MODE-3 / MIRROR-1).
  • Mirror coating stripe (MIRROR-1 / MODE-3): the stripe selector m1_horizontal (2bma:m3) is held at stripe a in Mono (constant m3 1.0) and swept per Pink energy (3.039 / 13 / 39 / 49 mm at 30 / 40 / 50 / 60 keV), modelled as the Mirror_StripeReachX facet (constant Mono curve + swept Pink sibling, invertible=False). The named-stripe-to-position map (a Pt / b,c,d W-Si) is in the Mirror stripes list in beamline.yaml. The coordinated mirror-table X move (m1mox / m1m2x = 2bma:m1 / m4) that stripe d needs at 60 keV stays deferred to its MirrorTable virtual_pose axes (2bm-docs#171); the mirror angle is constant 2.615 mrad across all modes/energies.
  • Not energy axes: crystal2_z (M2 Z, 2bma:m8) is a setup translation the IOC does not drive, so it carries no curve.
  • DMM substrate stripe is a one-time setup, not a per-energy selector (ENERGY-6, settled): the substrate carries two W-B4C/Si multilayer stripes (periods 24 and 13.8 angstrom, 4 mm apart), recorded as the Monochromator stripes list with active_stripe=24.0 in beamline.yaml. The 24 angstrom stripe is the in-beam one (inferred from Bragg-arm geometry); the 13.8 angstrom stripe is never calibrated into store_0. The lateral X motors 2bma:m25 / 2bma:m28 are constant alignment positions in both modes (ENERGY-5), not stripe selectors, so CORA models no DMM stripe-selector axis. Switching stripes would be a 4 mm lateral move plus a full recalibration, outside any current move.
  • Curves carry the REAL saved store_0 positions (ENERGY-1/2, FLAG-1): full 6-energy Mono curves plus parked Pink curves. Runtime eval_lookup_table is wired; out-of-range refuses (extrapolation_kind=Error, staff-confirmed ENERGY-4: the inter-mode band 25.584-30 keV is not bridgeable by interpolation).
  • Recalibration updates these curves in place: the energy_characterization Procedure (channel-cut rocking curve, item_022) re-saves the corrected positions as a new revision of the affected energy_position_curve, MeasuredSource-cited, preserving the prior revision as history. There is no separate energy offset (ENERGY-8).

Filter foil selection

Discrete "pick one of N" move. Two PseudoAxis facets under Filter (Filter_FoilSelector_Upstream for the operational 2bma:m17, Filter_FoilSelector_Downstream for the 2bma:m18), each carrying a LookupTable rule with interpolation_kind=Nearest backed by its own index_position_table Calibration. extrapolation_kind=Error (cannot select an absent foil); invertible=False with readback_aggregator_kind=Identity. The downstream m18 paddle failed 2026-06-19 (parked 107.19 mm), modelled condition Faulted with bindings retained and lifecycle still Active; m17 is the sole operational selector. Runtime proven end-to-end in apps/api/tests/integration/test_pseudoaxis_roundtrip.py; model: test_2bm_filter_foil_setup.py.

Upstream paddle (2bma:m17), the operational selector, slot positions (REAL, from the 2filter_setup.adl admin screen):

Slot index Material Position
0 1 mm C 2
1 150 um Al 25
2 600 um Al 52
3 1 mm Al 79
4 None 106

Downstream paddle (2bma:m18), Faulted since 2026-06-19, bindings retained, slot positions (REAL, staff-published):

Slot index Material Position
0 600 um Al 0
1 150 um Al 26
2 300 um C 53
3 50 um C 80
4 None 106

The slot index is the selector's own index, 0 to 4, and the position is where that slot sits on the paddle in mm. Command the index, never the position. The two columns are not interchangeable and the runtime cannot catch the confusion for you: extrapolation_kind=Error refuses only an index outside 0 to 4, so a mistyped 53 is rejected, but a position that happens to fall inside that range is not. Commanding 2 on the upstream paddle selects slot 2 (600 um Al), not the slot sitting at position 2 mm (1 mm C). The downstream unit is staff-confirmed (caget 2bma:m18.EGU, FOIL-1); the upstream positions come from the 2filter_setup.adl admin screen in the same mm unit. Foil ATTENUATION (Attenuable) and the energy-dependent mirror coating stripe (2bma:m3) are deliberately out of scope here (the stripe is modelled with the beam-mode work).