Merge pull request 'renamed sample environment devices to positioned devices, updated yamls and mx macros.' (#8) from x10sa_production_20260202T095359 into main
All checks were successful
CI for pxii_bec / test (push) Successful in 26s

Reviewed-on: #8
This commit was merged in pull request #8.
This commit is contained in:
2026-02-03 14:03:17 +01:00
9 changed files with 1266 additions and 4660 deletions

View File

@@ -34,3 +34,4 @@ to setup the prompts.
"""
# pylint: disable=invalid-name, unused-import, import-error, undefined-variable, unused-variable, unused-argument, no-name-in-module
init_positioned_devices()

File diff suppressed because it is too large Load Diff

View File

@@ -1,543 +0,0 @@
sls_current:
description: SLS current
deviceClass: ophyd.EpicsSignalRO
deviceConfig: {read_pv: 'ARS07-DPCT-0100:CURR', auto_monitor: true}
onFailure: buffer
enabled: True
readoutPriority: monitored
deviceTags:
- SLS
readOnly: true
softwareTrigger: false
gap:
description: 'U19 gap'
deviceClass: ophyd.EpicsMotor
deviceConfig: {prefix: 'X10SA-UIND:GAP'}
onFailure: buffer
enabled: False
readoutPriority: monitored
deviceTags:
- SLS
readOnly: false
softwareTrigger: false
ps1_press:
description: Pumpstand1 pressure
deviceClass: ophyd.EpicsSignalRO
deviceConfig: {read_pv: 'X10SA-FE-PUM1-VPIG-1020:PRESSURE', auto_monitor: true}
onFailure: buffer
enabled: true
readoutPriority: monitored
deviceTags:
- fe
readOnly: true
softwareTrigger: false
#xbpm1_temp:
# description: XBPM1 temp
# deviceClass: ophyd.EpicsSignalRO
# deviceConfig: {read_pv: 'X10SA-FE-XBPM1-ETTC-0010:TEMP', auto_monitor: true}
# onFailure: buffer
# enabled: true
# readoutPriority: monitored
# deviceTags:
# - fe
# readOnly: true
# softwareTrigger: false
s1_xw:
description: 'BSF slit outboard'
deviceClass: ophyd.EpicsMotor
deviceConfig: {prefix: 'X10SA-OP-SLH:TRXW'}
onFailure: buffer
enabled: True
readoutPriority: monitored
deviceTags:
- bsf
readOnly: false
softwareTrigger: false
s1_xr:
description: 'BSF slit inboard'
deviceClass: ophyd.EpicsMotor
deviceConfig: {prefix: 'X10SA-OP-SLH:TRXR'}
onFailure: buffer
enabled: True
readoutPriority: monitored
deviceTags:
- bsf
readOnly: false
softwareTrigger: false
s1_yt:
description: 'BSF slit top'
deviceClass: ophyd.EpicsMotor
deviceConfig: {prefix: 'X10SA-OP-SLV:TRYT'}
onFailure: buffer
enabled: True
readoutPriority: monitored
deviceTags:
- bsf
readOnly: false
softwareTrigger: false
s1_yb:
description: 'BSF slit bottom'
deviceClass: ophyd.EpicsMotor
deviceConfig: {prefix: 'X10SA-OP-SLV:TRYB'}
onFailure: buffer
enabled: True
readoutPriority: monitored
deviceTags:
- bsf
readOnly: false
softwareTrigger: false
s1_xcen:
description: 'BSF X centre'
deviceClass: ophyd.EpicsMotor
deviceConfig: {prefix: 'X10SA-OP-SLH:CENTER'}
onFailure: buffer
enabled: True
readoutPriority: monitored
deviceTags:
- bsf
readOnly: false
softwareTrigger: false
s1_xsize:
description: 'BSF X size'
deviceClass: ophyd.EpicsMotor
deviceConfig: {prefix: 'X10SA-OP-SLH:SIZE'}
onFailure: buffer
enabled: True
readoutPriority: monitored
deviceTags:
- bsf
readOnly: false
softwareTrigger: false
s1_ycen:
description: 'BSF Y centre'
deviceClass: ophyd.EpicsMotor
deviceConfig: {prefix: 'X10SA-OP-SLV:CENTER'}
onFailure: buffer
enabled: True
readoutPriority: monitored
deviceTags:
- bsf
readOnly: false
softwareTrigger: false
opf1_y:
description: 'BSF Filter 1 Y'
deviceClass: ophyd.EpicsMotor
deviceConfig: {prefix: 'X10SA-OP-FI1:TRY'}
onFailure: buffer
enabled: True
readoutPriority: monitored
deviceTags:
- bsf
readOnly: false
softwareTrigger: false
opf2_y:
description: 'BSF Filter 2 Y'
deviceClass: ophyd.EpicsMotor
deviceConfig: {prefix: 'X10SA-OP-FI2:TRY'}
onFailure: buffer
enabled: True
readoutPriority: monitored
deviceTags:
- bsf
readOnly: false
softwareTrigger: false
ssbpm_x:
description: 'SS BPM X'
deviceClass: ophyd.EpicsMotor
deviceConfig: {prefix: 'X10SA-ES-SSBPM1:TRX1'}
onFailure: buffer
enabled: True
readoutPriority: monitored
deviceTags:
- ss
readOnly: false
softwareTrigger: false
ssbpm_y:
description: 'SS BPM Y'
deviceClass: ophyd.EpicsMotor
deviceConfig: {prefix: 'X10SA-ES-SSBPM1:TRY1'}
onFailure: buffer
enabled: True
readoutPriority: monitored
deviceTags:
- ss
readOnly: false
softwareTrigger: false
s2_xw:
description: 'SS slit wall'
deviceClass: ophyd.EpicsMotor
deviceConfig: {prefix: 'X10SA-ES-SSSH1:TRXW'}
onFailure: buffer
enabled: True
readoutPriority: monitored
deviceTags:
- ss
readOnly: false
softwareTrigger: false
s2_xr:
description: 'SS slit ring'
deviceClass: ophyd.EpicsMotor
deviceConfig: {prefix: 'X10SA-ES-SSSH1:TRXR'}
onFailure: buffer
enabled: True
readoutPriority: monitored
deviceTags:
- ss
readOnly: false
softwareTrigger: false
s2_xcen:
description: 'SS slit X centre'
deviceClass: ophyd.EpicsMotor
deviceConfig: {prefix: 'X10SA-ES-SSSH1:CENTER'}
onFailure: buffer
enabled: True
readoutPriority: monitored
deviceTags:
- ss
readOnly: false
softwareTrigger: false
s2_xsize:
description: 'SS slit X size'
deviceClass: ophyd.EpicsMotor
deviceConfig: {prefix: 'X10SA-ES-SSSH1:CENTER'}
onFailure: buffer
enabled: True
readoutPriority: monitored
deviceTags:
- ss
readOnly: false
softwareTrigger: false
s2_yt:
description: 'SS slit top'
deviceClass: ophyd.EpicsMotor
deviceConfig: {prefix: 'X10SA-ES-SSSV1:TRYT'}
onFailure: buffer
enabled: True
readoutPriority: monitored
deviceTags:
- ss
readOnly: false
softwareTrigger: false
s2_yb:
description: 'SS slit bottom'
deviceClass: ophyd.EpicsMotor
deviceConfig: {prefix: 'X10SA-ES-SSSV1:TRYB'}
onFailure: buffer
enabled: True
readoutPriority: monitored
deviceTags:
- ss
readOnly: false
softwareTrigger: false
s2_ycen:
description: 'SS slit Y centre'
deviceClass: ophyd.EpicsMotor
deviceConfig: {prefix: 'X10SA-ES-SSSV1:CENTER'}
onFailure: buffer
enabled: True
readoutPriority: monitored
deviceTags:
- ss
readOnly: false
softwareTrigger: false
s2_ysize:
description: 'SS slit Y size'
deviceClass: ophyd.EpicsMotor
deviceConfig: {prefix: 'X10SA-ES-SSSV1:SIZE'}
onFailure: buffer
enabled: True
readoutPriority: monitored
deviceTags:
- ss
readOnly: false
softwareTrigger: false
xeye_x:
description: 'SS X-ray eye X'
deviceClass: ophyd.EpicsMotor
deviceConfig: {prefix: 'X10SA-ES-SSXI1:TRX1'}
onFailure: buffer
enabled: True
readoutPriority: monitored
deviceTags:
- ss
readOnly: false
softwareTrigger: false
xeye_y:
description: 'SS X-ray eye Y'
deviceClass: ophyd.EpicsMotor
deviceConfig: {prefix: 'X10SA-ES-SSXI1:TRY1'}
onFailure: buffer
enabled: True
readoutPriority: monitored
deviceTags:
- ss
readOnly: false
softwareTrigger: false
vfm_xu:
description: 'VFM Upstream X'
deviceClass: ophyd.EpicsMotor
deviceConfig: {prefix: 'X10SA-ES-VFM:TRXU'}
onFailure: buffer
enabled: False
readoutPriority: monitored
deviceTags:
- vfm
readOnly: false
softwareTrigger: false
vfm_xd:
description: 'VFM Downstream X'
deviceClass: ophyd.EpicsMotor
deviceConfig: {prefix: 'X10SA-ES-VFM:TRXD'}
onFailure: buffer
enabled: False
readoutPriority: monitored
deviceTags:
- vfm
readOnly: false
softwareTrigger: false
vfm_yur:
description: 'VFM Upstream Ring Y'
deviceClass: ophyd.EpicsMotor
deviceConfig: {prefix: 'X10SA-ES-VFM:TRYUR'}
onFailure: buffer
enabled: False
readoutPriority: monitored
deviceTags:
- vfm
readOnly: false
softwareTrigger: false
vfm_yw:
description: 'VFM Wall Y'
deviceClass: ophyd.EpicsMotor
deviceConfig: {prefix: 'X10SA-ES-VFM:TRYW'}
onFailure: buffer
enabled: False
readoutPriority: monitored
deviceTags:
- vfm
readOnly: false
softwareTrigger: false
vfm_ydr:
description: 'VFM Downstream Ring Y'
deviceClass: ophyd.EpicsMotor
deviceConfig: {prefix: 'X10SA-ES-VFM:TRYDR'}
onFailure: buffer
enabled: False
readoutPriority: monitored
deviceTags:
- vfm
readOnly: false
softwareTrigger: false
vfm_bu:
description: 'VFM Upstream Bender'
deviceClass: ophyd.EpicsMotor
deviceConfig: {prefix: 'X10SA-ES-VFM:BNDU'}
onFailure: buffer
enabled: False
readoutPriority: monitored
deviceTags:
- vfm
readOnly: false
softwareTrigger: false
vfm_bd:
description: 'VFM Downstream Bender'
deviceClass: ophyd.EpicsMotor
deviceConfig: {prefix: 'X10SA-ES-VFM:BNDD'}
onFailure: buffer
enabled: False
readoutPriority: monitored
deviceTags:
- vfm
readOnly: false
softwareTrigger: false
vfm_yaw:
description: 'VFM Virtual Yaw'
deviceClass: ophyd.EpicsMotor
deviceConfig: {prefix: 'X10SA-ES-VFM:YAW'}
onFailure: buffer
enabled: False
readoutPriority: monitored
deviceTags:
- vfm
readOnly: false
softwareTrigger: false
vfm_roll:
description: 'VFM Virtual Roll'
deviceClass: ophyd.EpicsMotor
deviceConfig: {prefix: 'X10SA-ES-VFM:ROLL'}
onFailure: buffer
enabled: False
readoutPriority: monitored
deviceTags:
- vfm
readOnly: false
softwareTrigger: false
vfm_pitch:
description: 'VFM Virtual Pitch'
deviceClass: ophyd.EpicsMotor
deviceConfig: {prefix: 'X10SA-ES-VFM:PITCH'}
onFailure: buffer
enabled: False
readoutPriority: monitored
deviceTags:
- vfm
readOnly: false
softwareTrigger: false
vfm_lat:
description: 'VFM Virtual X'
deviceClass: ophyd.EpicsMotor
deviceConfig: {prefix: 'X10SA-ES-VFM:TRX'}
onFailure: buffer
enabled: False
readoutPriority: monitored
deviceTags:
- vfm
readOnly: false
softwareTrigger: false
vfm_vert:
description: 'VFM Virtual Y '
deviceClass: ophyd.EpicsMotor
deviceConfig: {prefix: 'X10SA-ES-VFM:TRY'}
onFailure: buffer
enabled: False
readoutPriority: monitored
deviceTags:
- vfm
readOnly: false
softwareTrigger: false
hfm_xu:
description: 'HFM Upstream X'
deviceClass: ophyd.EpicsMotor
deviceConfig: {prefix: 'X10SA-ES-HFM:TRXU'}
onFailure: buffer
enabled: False
readoutPriority: monitored
deviceTags:
- hfm
readOnly: false
softwareTrigger: false
hfm_xd:
description: 'HFM Downstream X'
deviceClass: ophyd.EpicsMotor
deviceConfig: {prefix: 'X10SA-ES-HFM:TRXD'}
onFailure: buffer
enabled: False
readoutPriority: monitored
deviceTags:
- hfm
readOnly: false
softwareTrigger: false
hfm_yuw:
description: 'HFM Upstream Wall Y'
deviceClass: ophyd.EpicsMotor
deviceConfig: {prefix: 'X10SA-ES-HFM:TRYUW'}
onFailure: buffer
enabled: False
readoutPriority: monitored
deviceTags:
- hfm
readOnly: false
softwareTrigger: false
hfm_yr:
description: 'HFM Ring Y'
deviceClass: ophyd.EpicsMotor
deviceConfig: {prefix: 'X10SA-ES-HFM:TRYR'}
onFailure: buffer
enabled: False
readoutPriority: monitored
deviceTags:
- hfm
readOnly: false
softwareTrigger: false
hfm_ydw:
description: 'HFM Downstream Wall Y'
deviceClass: ophyd.EpicsMotor
deviceConfig: {prefix: 'X10SA-ES-HFM:TRYDW'}
onFailure: buffer
enabled: False
readoutPriority: monitored
deviceTags:
- hfm
readOnly: false
softwareTrigger: false
hfm_bu:
description: 'HFM Upstream Bender'
deviceClass: ophyd.EpicsMotor
deviceConfig: {prefix: 'X10SA-ES-HFM:BNDU'}
onFailure: buffer
enabled: False
readoutPriority: monitored
deviceTags:
- hfm
readOnly: false
softwareTrigger: false
hfm_bd:
description: 'HFM Downstream Bender'
deviceClass: ophyd.EpicsMotor
deviceConfig: {prefix: 'X10SA-ES-HFM:BNDD'}
onFailure: buffer
enabled: False
readoutPriority: monitored
deviceTags:
- hfm
readOnly: false
softwareTrigger: false
hfm_yaw:
description: 'HFM Virtual Yaw'
deviceClass: ophyd.EpicsMotor
deviceConfig: {prefix: 'X10SA-ES-HFM:YAW'}
onFailure: buffer
enabled: False
readoutPriority: monitored
deviceTags:
- hfm
readOnly: false
softwareTrigger: false
hfm_roll:
description: 'HFM Virtual Roll'
deviceClass: ophyd.EpicsMotor
deviceConfig: {prefix: 'X10SA-ES-HFM:ROLL'}
onFailure: buffer
enabled: False
readoutPriority: monitored
deviceTags:
- hfm
readOnly: false
softwareTrigger: false
hfm_pitch:
description: 'HFM Virtual Pitch'
deviceClass: ophyd.EpicsMotor
deviceConfig: {prefix: 'X10SA-ES-HFM:PITCH'}
onFailure: buffer
enabled: False
readoutPriority: monitored
deviceTags:
- hfm
readOnly: false
softwareTrigger: false
hfm_lat:
description: 'HFM Virtual X'
deviceClass: ophyd.EpicsMotor
deviceConfig: {prefix: 'X10SA-ES-HFM:TRX'}
onFailure: buffer
enabled: False
readoutPriority: monitored
deviceTags:
- hfm
readOnly: false
softwareTrigger: false
hfm_vert:
description: 'HFM Virtual Y '
deviceClass: ophyd.EpicsMotor
deviceConfig: {prefix: 'X10SA-ES-HFM:TRY'}
onFailure: buffer
enabled: False
readoutPriority: monitored
deviceTags:
- hfm
readOnly: false
softwareTrigger: false

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,23 +1,14 @@
"""Get data from an h5 file or BEC history and perform fitting."""
import numpy as np
from lmfit.models import (
GaussianModel,
LorentzianModel,
VoigtModel,
ConstantModel,
LinearModel,
)
from lmfit.models import GaussianModel, LorentzianModel, VoigtModel, ConstantModel, LinearModel
from scipy.ndimage import gaussian_filter1d
import h5py
import matplotlib.pyplot as plt
def create_fit_parameters(
deriv: bool = False,
model: str = "Voigt",
baseline: str = "Linear",
smoothing: None = None,
deriv: bool = False, model: str = "Voigt", baseline: str = "Linear", smoothing: None = None
):
"""Store the fit parameters in a dictionary."""
# map input model to lmfit model name
@@ -54,10 +45,7 @@ def get_data_from_h5(signal_name: str = "lu_bpmsum"):
}
def get_data_from_history(
history_index: int,
signal_name: str = "lu_bpmsum",
):
def get_data_from_history(history_index: int, signal_name: str = "lu_bpmsum"):
"""Read data from the BEC history and return the X and Y data as arrays."""
scan = bec.history[history_index]
md = scan.metadata["bec"]
@@ -96,10 +84,7 @@ def process_data(data, fit_params):
else:
fitting_data = y_data
updated_data = {
"y_to_fit": fitting_data,
"signal_name": signal_name,
}
updated_data = {"y_to_fit": fitting_data, "signal_name": signal_name}
data.update(updated_data)
return data
@@ -124,14 +109,10 @@ def fit(data, fit_params):
params["base_slope"].set(value=0)
# Add peak-specific parameters
params.update(
peak_model.guess(processed_data["y_to_fit"], x=processed_data["x_data"])
)
params.update(peak_model.guess(processed_data["y_to_fit"], x=processed_data["x_data"]))
# Perform the fitting
lmfit_result = full_model.fit(
processed_data["y_to_fit"], params, x=processed_data["x_data"]
)
lmfit_result = full_model.fit(processed_data["y_to_fit"], params, x=processed_data["x_data"])
# Find the X that gives the max Y
max_index = np.argmax(processed_data["y_to_fit"])
@@ -183,11 +164,7 @@ def select_bec_window(dock_area_name="Fitting"):
return wf, text_box
def plot_live_data_bec(
motor_name,
signal_name,
window_name="Fitting"
):
def plot_live_data_bec(motor_name, signal_name, window_name="Fitting"):
"""
Plotting live data for motor and signal using BEC.
@@ -212,10 +189,7 @@ def plot_live_data_bec(
wf.plot(x_name=motor_name, y_name=signal_name)
def plot_fitted_data_bec(
data,
fit_result,
):
def plot_fitted_data_bec(data, fit_result):
"""
Plot fitted data and display fitting parameters in the specified window.
@@ -248,7 +222,4 @@ def plot_fitted_data_bec(
wf.x_label = data["motor_name"]
wf.y_label = data["signal_name"]
wf.plot(x=data["x_data"], y=data["y_to_fit"], label="data")
wf.plot(
x=data["x_data"], y=fit_result["lmfit_result"].best_fit, label="Fit to data"
)
wf.plot(x=data["x_data"], y=fit_result["lmfit_result"].best_fit, label="Fit to data")

View File

@@ -3,7 +3,9 @@
2) fits data from a bec history file
"""
from dataclasses import dataclass
import numpy as np
# from pxiii_parameters import FitDefaults, BPMScans, MirrorConfig
# from mx_basics import (
@@ -69,7 +71,7 @@ def move_to_position(motor_device, motor_name: str, position: float, data: dict)
motor_centre = (motor_max + motor_min) / 2
if not motor_min <= position <= motor_max:
umv(motor_device, motor_centre)
scans.umv(motor_device, motor_centre, relative=False)
msg = (
f"Position {position: .2f} is outside the scan range of "
f"{motor_min: .2f}to {motor_max: .2f}. "
@@ -77,10 +79,21 @@ def move_to_position(motor_device, motor_name: str, position: float, data: dict)
)
raise ValueError(msg)
motor_position = round(position, 4)
umv(motor_device, motor_position)
scans.umv(motor_device, motor_position, relative=False)
print(f"\n Moving {motor_name} to position {motor_position: .3f}")
@dataclass(frozen=True)
class FitDefaults:
"""Default values for fitting routines"""
# Constants for default models, baselines, and parameters
MODEL = "Voigt"
BASELINE = "Linear"
SETTLE_TIME = 0.1
RELATIVE_MODE = True
def go_to_peak(
motor_device,
signal_device,
@@ -175,14 +188,10 @@ def go_to_peak(
if gomax:
value = fit_result["x_max"]
print(f"Max position is at {value}")
move_to_position(
data["motor_device"], data["motor_name"], fit_result["x_max"], data
)
move_to_position(data["motor_device"], data["motor_name"], fit_result["x_max"], data)
else:
# Safely move the motor to the peak position
move_to_position(
data["motor_device"], data["motor_name"], fit_result["centre"], data
)
move_to_position(data["motor_device"], data["motor_name"], fit_result["centre"], data)
def fit_history(
@@ -225,9 +234,7 @@ def fit_history(
# Optionally move the motor to the peak position
if move_to_peak:
move_to_position(
data["motor_device"], data["motor_name"], fit_result["centre"], data
)
move_to_position(data["motor_device"], data["motor_name"], fit_result["centre"], data)
def scan_bpm(bpmname):
@@ -261,48 +268,23 @@ def scan_bpm(bpmname):
wf1.x_label = cfg["x_name"]
wf1.y_label = cfg["y_name"]
wf1.plot(
x_name=cfg["x_name"],
y_name=cfg["y_name"],
z_name=cfg["z1_name"],
color_map="plasma",
)
wf1.plot(x_name=cfg["x_name"], y_name=cfg["y_name"], z_name=cfg["z1_name"], color_map="plasma")
wf2.x_label = cfg["x_name"]
wf2.y_label = cfg["y_name"]
wf2.plot(
x_name=cfg["x_name"],
y_name=cfg["y_name"],
z_name=cfg["z2_name"],
color_map="plasma",
)
wf2.plot(x_name=cfg["x_name"], y_name=cfg["y_name"], z_name=cfg["z2_name"], color_map="plasma")
wf3.x_label = cfg["x_name"]
wf3.y_label = cfg["y_name"]
wf3.plot(
x_name=cfg["x_name"],
y_name=cfg["y_name"],
z_name=cfg["z3_name"],
color_map="plasma",
)
wf3.plot(x_name=cfg["x_name"], y_name=cfg["y_name"], z_name=cfg["z3_name"], color_map="plasma")
wf4.x_label = cfg["x_name"]
wf4.y_label = cfg["y_name"]
wf4.plot(
x_name=cfg["x_name"],
y_name=cfg["y_name"],
z_name=cfg["z4_name"],
color_map="plasma",
)
wf4.plot(x_name=cfg["x_name"], y_name=cfg["y_name"], z_name=cfg["z4_name"], color_map="plasma")
wf5.x_label = cfg["x_name"]
wf5.y_label = cfg["y_name"]
wf5.plot(
x_name=cfg["x_name"],
y_name=cfg["y_name"],
z_name=cfg["z5_name"],
color_map="plasma",
)
wf5.plot(x_name=cfg["x_name"], y_name=cfg["y_name"], z_name=cfg["z5_name"], color_map="plasma")
# Run the scan
x_mot = cfg["x_device"]
y_mot = cfg["y_device"]
@@ -325,17 +307,12 @@ def optimise_kb(mirror):
wf1 = dock_area.new("Heatmap").new(bec.gui.available_widgets.Heatmap)
wfscan = dock_area.new("ScanControl").new(bec.gui.available_widgets.ScanControl)
cfg = getattr(MirrorConfig, mirror)
wf1.x_label = cfg["bu_name"]
wf1.y_label = cfg["bd_name"]
wf1.plot(
x_name=cfg["bu_name"],
y_name=cfg["bd_name"],
z_name=cfg["z_name"],
color_map="plasma",
)
wf1.plot(x_name=cfg["bu_name"], y_name=cfg["bd_name"], z_name=cfg["z_name"], color_map="plasma")
# Run the scan
x_mot = cfg["x_device"]

View File

@@ -8,6 +8,7 @@ Plotting optional
"""
import numpy as np
# from pxii_gap import set_gap
# from mx_methods import go_to_peak
# from pxii_parameters import EnergyDefaults, Calibration
@@ -25,9 +26,7 @@ def get_current_energy():
Returns the energy in eV from the current bragg angle.
"""
current_bragg_angle = dev.dcm_bragg.user_readback.get()
current_energy = convert_from_bragg(current_bragg_angle, print_result=False)[
"energy_ev"
]
current_energy = convert_from_bragg(current_bragg_angle, print_result=False)["energy_ev"]
return current_energy
@@ -81,7 +80,12 @@ def set_mirror_stripe(energy_ev):
def mono_pitch_scan(plot=True):
"""Scan the monochromator pitch and move to the peak."""
# Move to the calculated pitch value for the current energy
energy = get_current_energy()
pos = get_dcm_motors_positions(energy)
print(f"Setting DCM Pitch to default value of {pos['dcm_pitch']}")
scans.umv(EnergyDefaults.mono_pitch, pos["dcm_pitch"], relative=False)
# Go to peak using default parameters from EnergyDefaults
if plot:
print("Scanning monochromator pitch and moving to peak, with plotting.")
go_to_peak(
@@ -163,12 +167,7 @@ def get_dcm_motors_positions(energy_ev):
perp = calc_perp_position(energy_ev, print_result=False)
bragg_angle = convert_from_energy(energy_ev, print_result=False)["bragg_angle_mrad"]
dcm_motor_values.update(
{
"bragg_angle": bragg_angle,
"perp": perp,
"dcm_pitch": pitch,
"dcm_roll": roll,
}
{"bragg_angle": bragg_angle, "perp": perp, "dcm_pitch": pitch, "dcm_roll": roll}
)
return dcm_motor_values
@@ -192,10 +191,17 @@ def move_dcm_motors(energy_ev):
# umv(EnergyDefaults.mono_pitch, dcm_pos["dcm_pitch"])
# umv(EnergyDefaults.mono_roll, dcm_pos["dcm_roll"])
# print("\n***DCM Roll movement is currently disabled ***\n")
umv(EnergyDefaults.energy, dcm_pos["bragg_angle"],
EnergyDefaults.mono_perp, dcm_pos["perp"],
EnergyDefaults.mono_pitch, dcm_pos["dcm_pitch"],
EnergyDefaults.mono_roll, dcm_pos["dcm_roll"])
scans.umv(
EnergyDefaults.energy,
dcm_pos["bragg_angle"],
EnergyDefaults.mono_perp,
dcm_pos["perp"],
EnergyDefaults.mono_pitch,
dcm_pos["dcm_pitch"],
EnergyDefaults.mono_roll,
dcm_pos["dcm_roll"],
relative=False,
)
def bl_energy(energy_ev, move_gap=False, mono_scan=True, plot=True):
@@ -222,9 +228,7 @@ def bl_energy(energy_ev, move_gap=False, mono_scan=True, plot=True):
energy_diff = calculate_energy_difference(current_energy, energy_ev)
if energy_diff <= EnergyDefaults.min_energy_change:
print(
f"Energy change of {energy_diff:.2f} eV is too small, not changing energy."
)
print(f"Energy change of {energy_diff:.2f} eV is too small, not changing energy.")
return
# Step 1: Move the gap if needed.

View File

@@ -1,18 +1,11 @@
"""File to store beamline parameters and defaults"""
from dataclasses import dataclass
from typing import Callable
import numpy as np
import yaml
@dataclass(frozen=True)
class FitDefaults:
"""Default values for fitting routines"""
# Constants for default models, baselines, and parameters
MODEL = "Voigt"
BASELINE = "Linear"
SETTLE_TIME = 0.1
RELATIVE_MODE = True
@dataclass(frozen=True)
@@ -23,11 +16,7 @@ class EnergyDefaults:
min_energy_ev = 4800
max_energy_ev = 30002
beam_offset = 6
signals = {
"sig1": dev.lu_bpmsum,
"sig2": dev.bsc_bpmsum,
"sig3": dev.bcu_bpmsum,
}
signals = {"sig1": dev.lu_bpmsum, "sig2": dev.bsc_bpmsum, "sig3": dev.bcu_bpmsum}
energy = dev.dcm_bragg
mono_pitch = dev.dcm_pitch
mono_perp = dev.dcm_perp
@@ -48,6 +37,8 @@ class Calibration:
@dataclass(frozen=True)
class Gap:
"""Fit parameters to calculate gap from harmonics"""
harmonics = {
"H3": np.array([9.15e-04, 4.49e-01]),
"H5": np.array([5.19e-04, 7.149e-01]),
@@ -77,7 +68,8 @@ class Gap:
energy_ev (float): The energy value (eV).
Returns:
Optional[str]: The harmonic name (e.g., 'H3', 'H7') if the range matches, None otherwise.
Optional[str]: The harmonic name (e.g., 'H3', 'H7')
if the range matches, None otherwise.
"""
for harmonic, (low, high) in Gap.harmonic_ranges.items():
if low < energy_ev <= high:
@@ -92,7 +84,8 @@ class Gap:
energy_ev (float): The energy value (eV).
Returns:
Optional[np.array]: The corresponding array of harmonic values if the range matches, None otherwise.
Optional[np.array]: The corresponding array of harmonic values
if the range matches, None otherwise.
"""
harmonic = self.get_harmonic_by_energy(energy_ev)
return self.harmonics.get(harmonic) if harmonic else None
@@ -116,14 +109,7 @@ class Harmonics:
12: np.array([3.20520798e-05, 2.39253145e-03, 8.09198503e-02, 2.22897377e00]),
13: np.array([0.00278744, 0.07979874, 2.05143916]),
}
energy_ranges = {
3: (0, 7),
5: (7, 10),
7: (10, 13),
9: (13, 16),
11: (16, 19),
13: (19, 22),
}
energy_ranges = {3: (0, 7), 5: (7, 10), 7: (10, 13), 9: (13, 16), 11: (16, 19), 13: (19, 22)}
high_energy = [(15, (23, 25)), (17, (25, 29)), (19, (29, float("inf")))]
@@ -204,9 +190,14 @@ class MirrorConfig:
"y_device": dev.vfm_bd,
}
from typing import Callable
@dataclass
class Target:
class PositionedDevice:
"""Class for devices with defined in and out positions"""
device_name: str
type: str
name: str
inpos: float
outpos: float
tol: float
@@ -215,42 +206,133 @@ class Target:
@property
def actual(self):
"""Returns current motor position"""
return self.reader()
def checkpos(self):
def checkin(self):
"""Returns True if motor in in the 'in' position"""
return abs(self.actual - self.inpos) <= self.tol
def mvin(self):
umv(self.mot, self.inpos)
"""Moves motor to the 'in' position"""
scans.umv(self.mot, self.inpos, relative=False)
def mvout(self):
umv(self.mot, self.outpos)
@dataclass
class GroupTarget:
def __init__(self, **targets: Target):
self.targets = targets
def checkpos(self):
return all(t.checkpos() for t in self.targets.values())
def report(self):
return {name: t.checkpos() for name, t in self.targets.items()}
"""Moves motor to the 'out' position"""
scans.umv(self.mot, self.outpos, relative=False)
def status(self):
""" Check if device is in or out or moving"""
positions = ("in", "out", "moving", "undefined")
target_in = self.inpos
target_out = self.outpos
actual = self.actual
delta_in = actual - target_in
delta_out = actual - target_out
# Check if motor is moving
if "Signal" in self.type:
moving = 0
elif "Motor" in self.type:
d = getattr(dev, self.device_name)
moving = d.motor_is_moving.get()
if moving:
pos = positions[2]
return {"position": pos.upper(),
"name": self.name,
"moving": moving}
if abs(delta_in) > self.tol and abs(delta_out) > self.tol:
pos = positions[3]
return {"position": pos.upper(),
"name": self.name,
"actual": actual,
"moving": moving}
elif abs(delta_in) <= self.tol:
target = self.inpos
pos = positions[0]
delta = delta_in
elif abs(delta_out) <= self.tol:
target = self.outpos
pos = positions[1]
delta = delta_out
return {
"name": self.name,
"position": pos.upper(),
"target": target,
"actual": actual,
"delta": delta,
"tol": self.tol,
"moving": moving,
}
def report(self):
""" Print status of motor """
s = self.status()
if s['position'] == "UNDEFINED":
return (f"{s['name']:15s}: "
f"{s['position']} "
f"position {s['actual']:.3f}")
elif s['position'] == "MOVING":
return (f"{s['name']:15s}: "
f"{s['position']} ")
else:
return (
f"{s['name']:15s}: "
f"[{s['position']}] "
f"actual = {s['actual']:.3f} "
f"target = {s['target']:.3f} "
f"delta = {s['delta']:.3f}"
)
@dataclass(frozen=True)
class SE:
"""Define settings for scintillator, collimator, i1"""
scin = Target(38.6, 20.0, 0.1, dev.scin_y, lambda: dev.scin_y.read()['scin_y']['value'])
i1 = Target(44.0, 20.0, 0.2, dev.scin_y, lambda: dev.scin_y.read()['scin_y']['value'])
colly = Target(41.5, 20.0, 0.05, dev.coll_y, lambda: dev.coll_y.read()['coll_y']['value'])
bsy = Target(0.1, -0.9, 0.05, dev.bs_y, lambda: dev.bs_y.read()['bs_y']['value'])
bsx = Target(2.45, 2.45, 0.05, dev.bs_x, lambda: dev.bs_x.read()['bs_x']['value'])
# coll = GroupTarget(
# x = Target(0.0517, 0.0517, 0.02, dev.coll_x, lambda: dev.coll_x.read()['coll_x']['value']),
# y = Target(41.5, 20.0, 0.05, dev.coll_y, lambda: dev.coll_y.read()['coll_y']['value']),
# )
# bs = GroupTarget(
# x = Target(2.65, 2.65, 0.05, dev.bs_x, lambda: dev.bs_x.read()['bs_x']['value']),
# y = Target(0.1, 0.1, 0.05, dev.bs_y, lambda: dev.bs_y.read()['bs_y']['value'])
# )
class PD:
"""Class for positioned device positions"""
def build_pd(yaml_file):
"""Takes the in and out values from the yaml file
and adds them to the PD class
"""
with open(yaml_file, encoding="utf-8") as f:
data = yaml.safe_load(f)
for device_name, cfg in data.items():
# Skip devices without userParameter
user = cfg.get("userParameter")
if not user:
continue
# Set tolerance
if "tol" not in user:
user["tol"] = 0.01
try:
dev_obj = getattr(dev, device_name)
except:
raise KeyError(f"Device {device_name} not found in device list")
desc = cfg.get("description")
type = cfg.get("deviceClass")
target = PositionedDevice(
device_name=device_name,
type = type,
name=desc,
inpos=user["in"],
outpos=user["out"],
tol=user["tol"],
mot=dev_obj,
reader=lambda d=dev_obj, n=device_name: d.read()[n]["value"],
)
setattr(PD, device_name, target)
def init_positioned_devices():
"""Initialises the positioned devices"""
file = (
"/sls/x10sa/config/bec/production/pxii_bec/pxii_bec/device_configs/pxii-autogenerated.yaml"
)
build_pd(file)
print("Defined positions for devices have been updated from pxii-autogenerated.yaml")