diff --git a/README.md b/README.md old mode 100644 new mode 100755 diff --git a/acquisition/SF_jupyter_acquisition.ipynb b/acquisition/SF_jupyter_acquisition.ipynb old mode 100644 new mode 100755 diff --git a/acquisition/base.py b/acquisition/base.py old mode 100644 new mode 100755 diff --git a/acquisition/multiple_daqs.py b/acquisition/multiple_daqs.py old mode 100644 new mode 100755 diff --git a/beamline/Cristallina_mono.py b/beamline/Cristallina_mono.py new file mode 100755 index 0000000..dacee80 --- /dev/null +++ b/beamline/Cristallina_mono.py @@ -0,0 +1,105 @@ +from types import SimpleNamespace +from time import sleep +import numpy as np + +from slic.core.adjustable import Adjustable, PVAdjustable, PVEnumAdjustable +from slic.core.adjustable.pvchangemon import PVChangeMonitor +from slic.core.device import Device +from slic.utils.hastyepics import get_pv as PV +from slic.devices.general.motor import Motor +from slic.devices.cameras import CameraCA + +class CristallinaMono(Device): + """Cristallina mono SAROP31-ODCC110""" + def __init__(self, ID, name="Cristallina double-channel-cut monochromator", **kwargs): + super().__init__(ID, name=name, **kwargs) + + self.TX = Motor(ID + ":MOT_TX1") + self.bragg1 = Motor(ID + ":MOT_RX1") + self.bragg2 = Motor(ID + ":MOT_RX2") + self.energy = Motor(ID + ":MOT_ENY") + self.energy_offset = Motor(ID + ":MOT_OFS") + self.screen = CristallinaMonoScreen("SAROP31-PSCR110", name='SAROP31-PSCR110') + +class CristallinaMonoScreen(Device): + + def __init__(self, ID, name="Profile Monitor", **kwargs): + super().__init__(ID, name=name, **kwargs) + + self.cam = CameraCA(ID) + + self.target = PVEnumAdjustable(ID + ":SCR_SP", name="target") + self.target_pos = Motor(ID + ":MOT_TY1", name="target position") + self.target_in_position = PVEnumAdjustable(ID + ":IN_POS",name="target in valid position") + + def move_mono(self): + return self.target.set_target_value(1) + + def move_pink(self): + return self.target.set_target_value(2) + + def move_out(self): + return self.target.set_target_value(0) + + + + +# class DoubleCrystalMonoEnergy(Adjustable): + +# def __init__(self, ID, name=None): +# self.wait_time = 0.1 + +# pvname_setvalue = "SAROP21-ARAMIS:ENERGY_SP" +# pvname_readback = "SAROP21-ARAMIS:ENERGY" +# pvname_moving = "SAROP21-ODCM098:MOVING" +# pvname_stop = "SAROP21-ODCM098:STOP.PROC" + +# pv_setvalue = PV(pvname_setvalue) +# pv_readback = PV(pvname_readback) +# pv_moving = PV(pvname_moving) +# pv_stop = PV(pvname_stop) + +# units = pv_readback.units +# super().__init__(ID, name=name, units=units) + +# self.pvnames = SimpleNamespace( +# setvalue = pvname_setvalue, +# readback = pvname_readback, +# moving = pvname_moving, +# stop = pvname_stop +# ) + +# self.pvs = SimpleNamespace( +# setvalue = pv_setvalue, +# readback = pv_readback, +# moving = pv_moving, +# stop = pv_stop +# ) + + +# def get_current_value(self): +# return self.pvs.readback.get() + +# def set_current_value(self, value): +# self.pvs.setvalue.put(value) +# sleep(3) + +# def set_target_value(self, value): +# self.set_current_value(value) +# # while abs(self.wait_for_valid_value() - value) > accuracy: +# while self.is_moving(): +# sleep(self.wait_time) + + +# def wait_for_valid_value(self): +# val = np.nan +# while not np.isfinite(val): +# val = self.get_current_value() +# return val + +# def is_moving(self): +# moving = self.pvs.moving.get() +# return bool(moving) + +# def stop(self): +# self.pvs.stop.put(1) \ No newline at end of file diff --git a/beamline/PSSS_motion.py b/beamline/PSSS_motion.py old mode 100644 new mode 100755 diff --git a/beamline/alignment_laser.py b/beamline/alignment_laser.py old mode 100644 new mode 100755 diff --git a/beamline/apertures.py b/beamline/apertures.py old mode 100644 new mode 100755 diff --git a/beamline/attenuator_scans.py b/beamline/attenuator_scans.py old mode 100644 new mode 100755 diff --git a/beamline/bernina_mono.py b/beamline/bernina_mono.py old mode 100644 new mode 100755 diff --git a/beamline/components.py b/beamline/components.py old mode 100644 new mode 100755 index e16abba..1bdd8fc --- a/beamline/components.py +++ b/beamline/components.py @@ -3,10 +3,11 @@ from loguru import logger from slic.devices.xoptics.aramis_attenuator import Attenuator from slic.devices.xoptics.pulsepicker import PulsePicker from slic.devices.xdiagnostics.intensitymonitor import IntensityMonitorPBPS - +from .Cristallina_mono import CristallinaMono from .alignment_laser import AlignmentLaser from slic.devices.xoptics.kb import KBHor, KBVer +from slic.devices.xoptics.offsetmirrors import OffsetMirror from .pp_shutter import PP_Shutter @@ -27,7 +28,7 @@ test_attenuators() # Shutter pp_shutter = PP_Shutter( "SARES30-LTIM01-EVR0:RearUniv0-Ena-SP", name="Cristallina pulse picker shutter" -) # Shutter button when using the pulse picker +) # Shutter button when ufasing the pulse picker pulsepicker = PulsePicker( @@ -59,3 +60,10 @@ pbps149 = IntensityMonitorPBPS( # KB mirrors kbHor = KBHor("SAROP31-OKBH154", description="Cristallina horizontal KB mirror") kbVer = KBVer("SAROP31-OKBV153", description="Cristallina vertical KB mirror") + +# Mono +mono = CristallinaMono("SAROP31-ODCC110") + +# Offset mirrors +m3 = OffsetMirror('SAROP31-ODMV152') +#TODO the other two offset mirros diff --git a/beamline/kb_focusing.py b/beamline/kb_focusing.py old mode 100644 new mode 100755 diff --git a/beamline/photon_energy.py b/beamline/photon_energy.py new file mode 100755 index 0000000..b4308e9 --- /dev/null +++ b/beamline/photon_energy.py @@ -0,0 +1,605 @@ +import time +from time import sleep + +import numpy as np +from epics import PV + +from logzero import logger as log + +from slic.core.adjustable import Adjustable, PVAdjustable, PVEnumAdjustable +from slic.core.scanner.scanbackend import wait_for_all # , stop_all + +import subprocess + +UND_NAME_FMT = "SARUN{:02}-UIND030" +N_UND_CHIC = None + +N_UNDS = list(range(3, 15 + 1)) +# N_UNDS.remove(N_UND_CHIC) # does not exist + +# offsets fro undulators, PSSS and DCCM +# convention: energy_device = energy_master - energy_offset_device +# readback_energy is energy_undulator + offset_undulator +# move the PSSS motor and the DCCM motor according to the energy_master +# TODO: improve this hack + + +### SETTINGS #### + +PSSS_MOVE = True +DCCM_MOVE = True +TRAJECTORY_FEEDBACK_DISABLE_ENABLE = False +POINTING_FEEDFORWARD = False + +energy_offset_undulators = -14 # eV +energy_offset_PSSS = -14 # eV +energy_offset_DCCM = -12 # eV + +DCCM_RX2_energy_offset = 25.8 # eV + +################ + + +def print_configuration(): + print(f"PSSS_MOVE = {PSSS_MOVE}") + print(f"DCCM_MOVE = {DCCM_MOVE}") + print(f"TRAJECTORY_FEEDBACK_DISABLE_ENABLE = {TRAJECTORY_FEEDBACK_DISABLE_ENABLE}") + print(f"POINTING_FEEDFORWARD = {POINTING_FEEDFORWARD}") + print(f"Undulator energy offset = {energy_offset_undulators}") + print(f"PSSS energy offset = {energy_offset_PSSS}") + print(f"DCCM energy offset = {energy_offset_DCCM}") + print(f" DCCM RX2 energy offset = {DCCM_RX2_energy_offset}") + + + +def pointing_slope_X(energy): + #delta_X_predicted = -0.0010097 * (energy - 10000)**2 + 0.002622 * (energy - 10000) - 0.3225 # function derived from calibration energy scan + delta_X_predicted = (energy - 10000) * 10 # function derived from calibration energy scan + #pointing_X_predicted = delta_X_predicted * 0.01829+ 0.009650 + pointing_X_predicted = delta_X_predicted / 113 * 4.5 + return pointing_X_predicted + + +def pointing_slope_Y(energy): + #delta_Y_predicted = ... + delta_Y_predicted = (energy - 10000) * 10 # function derived from calibration energy scan + #pointing_Y_predicted = ... + pointing_Y_predicted = delta_Y_predicted / 113 * 4.5 + return pointing_Y_predictedS + + +def set_PSSS_energy(energy: float): + """When scanning the energy with the undulator we + adjust the spectrometer to follow. + """ + print(f"Adjusting PSSS to {energy}") + PSSS_energy_PV_name = "SARFE10-PSSS059:ENERGY" + PSSS_energy_PV = PV(PSSS_energy_PV_name) + PSSS_energy_PV.put(energy, wait=True) + + ret = subprocess.run(["python", "/sf/photo/src/PSSS_motor/qt/PSSS_motion.py", "-s", "-m5", "SARFE10-PSSS059"]) + + sleep(2) + + if ret.returncode != 0: + log.warning("WARNING: PSSS adjustment failed.") + else: + print("Finished adjusting PSSS.") + + +def set_DCCM_energy(energy: float): + """When scanning the energy with the undulator we + adjust the monochromator to follow. + """ + print(f"Adjusting DCCM to {energy}") + + DCCM_energy_PV_name = "SAROP31-ODCC110:MOT_ENY" + DCCM_energy_PV = PV(DCCM_energy_PV_name) + + DCCM_energy_offset_PV_name = "SAROP31-ODCC110:MOT_OFS" + DCCM_energy_offset_PV = PV(DCCM_energy_offset_PV_name) + + DCCM_energy_offset_PV.put(DCCM_RX2_energy_offset, wait=True) + DCCM_energy_PV.put(energy, wait=False) + DCCM_energy_offset_PV.put(DCCM_RX2_energy_offset, wait=False) + + print(f"Finished adjusting DCCM.") + + sleep(0.2) + + +def enable_trajectory_feedback(): + print(f"Enabling trajectory FB") + TrFB_PV_name = "SFB_POINTING_AR:ONOFF1" + TrFB_PV = PV(TrFB_PV_name) + TrFB_PV.put("ON") + sleep(0.2) + +def disable_trajectory_feedback(): + print(f"Disabling trajectory FB") + TrFB_PV_name = "SFB_POINTING_AR:ONOFF1" + TrFB_PV = PV(TrFB_PV_name) + TrFB_PV.put("OFF") + sleep(0.2) + + +def set_pointing(energy): + print(f"Pointing correction") + PointingSlopeX_PV_name = "SGE-HL-FPAR:X-SLOPE1" + PointingSlopeY_PV_name = "SGE-HL-FPAR:Y-SLOPE1" + PointingSlopeX_PV = PV(PointingSlopeX_PV_name) + PointingSlopeY_PV = PV(PointingSlopeY_PV_name) + + PointingSlopeX = pointing_slope_X(energy) + PointingSlopeY = pointing_slope_Y(energy) + print(f" X-slope = {PointingSlopeX} um") + print(f" Y-slope = {PointingSlopeY} um") + + PointingSlopeX_PV.put(PointingSlopeX) + PointingSlopeY_PV.put(PointingSlopeY) + sleep(0.2) + + + +class PhotonEnergy(Adjustable): + """ + for n_und_ref=None (default), the reference undulator currently used by the machine will be used + """ + + def __init__( + self, n_unds=N_UNDS, n_und_ref=None, scaled=True, ID="ARAMIS_UNDULATORS", name="Aramis Undulators", units="eV" + ): + # # don't allow setting these since there's no chic :) + # chic_fudge_offset = 0 + # adjust_chic = False + + super().__init__(ID, name=name, units=units) + + machine_n_und_ref = get_machine_n_und_ref() + + if n_und_ref is None: + if machine_n_und_ref is None: + raise ValueError( + f"could not read reference undulator currently used by the machine, please specify n_und_ref" + ) + n_und_ref = machine_n_und_ref + + if n_und_ref != machine_n_und_ref: + log.warning( + f"the chosen reference undulator ({n_und_ref}) is not the reference undulator currently used by the machine ({machine_n_und_ref})" + ) + + n_unds = list(n_unds) + + if N_UND_CHIC in n_unds: + log.warning( + f"the CHIC ({N_UND_CHIC}) is in the list of active undulators: {n_unds}, and will be ignored/removed" + ) + n_unds.remove(N_UND_CHIC) + + if n_und_ref not in n_unds: + raise ValueError(f"the reference undulator ({n_und_ref}) is not in the list of active undulators: {n_unds}") + + self.n_unds = n_unds + self.n_und_ref = n_und_ref + + self.und_names = und_names = [UND_NAME_FMT.format(n) for n in n_unds] + self.und_name_cal = und_name_cal = UND_NAME_FMT.format(n_und_ref) + + self.adjs = {name: Undulator(name) for name in und_names} + # self.chic = CHIC(chic_fudge_offset, name, units) + # self.phases = Phases(n_unds) + + # self.adjust_chic = adjust_chic + self.scaled = scaled + + self.convert = ConverterEK() + + a = self.adjs[und_name_cal] + self.scale = ScalerEK(a) + + def set_target_value(self, energy_master, hold=False): + + value = energy_master + energy_offset_undulators + k = self.convert.K(value) + + if np.isnan(k): + print("K is nan for", value) + return + print(f"{k} <- {value}") + + ks_current = [a.get_current_value(readback=False) for a in self.adjs.values()] + + if self.scaled: + header = "scaled: " + ks_target = self.scale.K(value, ks_current) + else: + header = "all equal:" + ks_target = k * np.ones_like(ks_current) + + print(header, ks_target) + print() + + def change(): + + + if not DCCM_MOVE: + print("no DCCM movement enabled") + else: + energy_DCCM = energy_master + energy_offset_DCCM + set_DCCM_energy(energy_DCCM) + + if not TRAJECTORY_FEEDBACK_DISABLE_ENABLE: + print("No trajectory feedback disabling performed.") + else: + disable_trajectory_feedback() + + print(f"Setting undulator energy to {value}") + + # TODO: replace by set_all_target_values_and_wait when print not needed anymore + tasks = [] + for (name, a), k_old, k_new in zip(self.adjs.items(), ks_current, ks_target): + delta = k_old - k_new + print(f"{name}: {k_old}\t->\t{k_new}\t({delta})") + if np.isnan(k_new): + print(f"{name} skipped since target K is nan") + continue + t = a.set_target_value(k_new, hold=False) + tasks.append(t) + wait_for_all(tasks) + + # # make sure new K values have been written TODO: needed? + # sleep(2) # check if this can be shortened back to 0.5 + + # # switching on radial motors ... + # wait_for_all([ + # a.adj_radial_on.set_target_value(1, hold=False) for a in self.adjs.values() + # ]) + + # # press a few times + # for _ in range(3): + # # ... and pushing go to ensure proper movements + # wait_for_all([ + # a.adj_radial_go.set_target_value(1, hold=False) for a in self.adjs.values() + # ]) + # sleep(0.2) + + # # make sure the undulators finished moving TODO: needed? + # sleep(5) + + # if self.adjust_chic: + # print("CHIC adjustment follows") + ## self.chic.set_target_value(value, hold=False).wait() #TODO: test whether an additional sleep is needed + # self.phases.set(value) + # print("CHIC adjustment done") + # else: + # print("CHIC adjustment skipped") + + # make sure the undulators and phases finished moving TODO: needed? + sleep(5) + + if not PSSS_MOVE: + print("no PSSS movement enabled") + else: + energy_PSSS = energy_master + energy_offset_PSSS + print(f"Adjusting PSSS to {energy_PSSS}") + set_PSSS_energy(energy_PSSS) + + if not POINTING_FEEDFORWARD: + print("No pointing correction performed.") + else: + set_pointing(energy_master) + + + if not TRAJECTORY_FEEDBACK_DISABLE_ENABLE: + print("No trajectory feedback enabling performed.") + else: + enable_trajectory_feedback() + + + + return self._as_task(change, hold=hold) + + + def get_current_value(self): + n = self.und_name_cal + a = self.adjs[n] + k = a.get_current_value() + energy = self.convert.E(k) - energy_offset_undulators + + # all_ks = [a.get_current_value() for a in self.adjs.values()] + # checks = np.isclose(all_ks, k, rtol=0, atol=0.001) + # if not all(checks): + # print(f"Warning: Ks are not all close to {k}:") + # for name, k, chk in zip(self.adjs.keys(), all_ks, checks): + # if not chk: + # print(name, k) + + return energy # if we need to fudge the number to match the mono, do it here! + + + def get_current_value_undulators_real(self): + n = self.und_name_cal + a = self.adjs[n] + k = a.get_current_value() + energy = self.convert.E(k) + + # all_ks = [a.get_current_value() for a in self.adjs.values()] + # checks = np.isclose(all_ks, k, rtol=0, atol=0.001) + # if not all(checks): + # print(f"Warning: Ks are not all close to {k}:") + # for name, k, chk in zip(self.adjs.keys(), all_ks, checks): + # if not chk: + # print(name, k) + + return energy # if we need to fudge the number to match the mono, do it here! + + + def get_current_value_DCCM(self): + + DCCM_energy_PV_name = "SAROP31-ODCC110:MOT_ENY" + DCCM_energy_PV = PV(DCCM_energy_PV_name) + + energy = DCCM_energy_PV.get() - energy_offset_DCCM + + return energy + + def get_current_value_DCCM_real(self): + + DCCM_energy_PV_name = "SAROP31-ODCC110:MOT_ENY" + DCCM_energy_PV = PV(DCCM_energy_PV_name) + + energy = DCCM_energy_PV.get() + + return energy + + + def get_current_value_PSSS(self): + + PSSS_energy_PV_name = "SARFE10-PSSS059:ENERGY" + PSSS_energy_PV = PV(PSSS_energy_PV_name) + + energy = PSSS_energy_PV.get() - energy_offset_PSSS + + return energy + + def get_current_value_PSSS_real(self): + + PSSS_energy_PV_name = "SARFE10-PSSS059:ENERGY" + PSSS_energy_PV = PV(PSSS_energy_PV_name) + + energy = PSSS_energy_PV.get() + + return energy + + def print_energies_with_offset_correction(self): + print("Real photon energies with offset correction") + print(f" Undulator = {self.get_current_value()} eV") + print(f" PSSS = {self.get_current_value_PSSS()} eV") + print(f" DCCM = {self.get_current_value_DCCM()} eV") + + def print_energies(self): + print("Real photon energies") + print(f" Undulator = {self.get_current_value_undulators_real()} eV") + print(f" PSSS = {self.get_current_value_PSSS_real()} eV") + print(f" DCCM = {self.get_current_value_DCCM_real()} eV") + + + def is_moving(self): + return any(a.is_moving() for a in self.adjs) + + +class Undulator(PVAdjustable): + def __init__(self, name, accuracy=0.0005): + pvname_setvalue = name + ":K_SET" + pvname_readback = pvname_setvalue # name + ":K_READ" #TODO: there are no readback values? + super().__init__( + pvname_setvalue, + pvname_readback=pvname_readback, + accuracy=accuracy, + active_move=True, + name=name, + internal=True, + ) + self.adj_energy = PVAdjustable(name + ":FELPHOTENE", internal=True) + + # self.adj_radial_on = PVAdjustable(name + ":RADIAL-ON", internal=True) #TODO: do not exist + # self.adj_radial_go = PVAdjustable(name + ":RADIAL-GO", internal=True) #TODO: do not exist + # self.adj_radial_on_proc = PVAdjustable(name + ":RADIAL-ON.PROC", internal=True) + # self.adj_radial_go_proc = PVAdjustable(name + ":RADIAL-GO.PROC", internal=True) + + @property + def energy(self): + return self.adj_energy.get_current_value() * 1000 + + +class ConverterEK: + h = 4.135667696e-15 # eV * s + c = 299792458 # m / s + lambda_u = 15e-3 # m + const = 2 * h * c / lambda_u # eV + + electron_rest_energy = 0.51099895 # MeV + + # was: pvname_electron_energy="SATCL01-MBND100:P-READ" + # was: pvname_electron_energy="SATCB01:ENE-FILT-OP" + def __init__(self, pvname_electron_energy="SARCL02-MBND100:P-READ"): # S30CB13:ENE-FILT-OP + self.pv_electron_energy = PV(pvname_electron_energy) + + def K(self, energy): + f = self.get_factor() + v = f / energy - 1 + return np.sqrt(2 * v) + + def E(self, k_value): + f = self.get_factor() + v = 1 + k_value**2 / 2 + return f / v + + def get_factor(self): + return self.const * self.get_gamma_squared() + + def get_gamma_squared(self): + electron_energy = self.pv_electron_energy.get() + gamma = electron_energy / self.electron_rest_energy + return gamma**2 + + +class ScalerEK: + def __init__(self, und_reference): + self.und = und_reference + + def K(self, energy_target, K_current=None): + if K_current is None: + K_current = self.und.get_current_value() + K_current = np.asarray(K_current) + energy_current = self.und.energy + energy_ratio = energy_current / energy_target + K_target_squared = energy_ratio * (K_current**2 + 2) - 2 + return np.sqrt(K_target_squared) + + +# class CHIC(PVAdjustable): +# +# def __init__(self, fudge_offset, name, units): +# self.fudge_offset = fudge_offset +# name += " CHIC Energy" +# super().__init__("SATUN-CHIC:PHOTON-ENERGY", name=name) +# self.pvs.start = PV("SATUN-CHIC:APPLY-DELAY-OFFSET-PHASE") +# self.units = units +# +# +# def set_target_value(self, value, hold=False): +# fudge_offset = self.fudge_offset +# print("CHIC fudge offset is", fudge_offset) +# value -= fudge_offset +# value /= 1000 +# +# def change(): +# sleep(1) +# print("CHIC setvalue") +# self.pvs.setvalue.put(value, wait=True) +# print("CHIC start") +# self.pvs.start.put(1, wait=True) +# #TODO: test whether an additional sleep is needed +# sleep(1) +# +# return self._as_task(change, hold=hold) +# +# +# def get_current_value(self): +# return super().get_current_value() * 1000 + + +# class TwoColorChicane(PVAdjustable): +# +# def __init__(self, name):#, t0=0): +## self.t0 = t0 +## name += " Two Color Chicane" +# super().__init__("SATUN14-MBND100:I-SET", "SATUN14-MBND100:I-READ", process_time=1, name=name) +# +## def set_target_value(self, value, hold=False): +## super().set_target_value(value) +# +## def get_current_value(self): +## return super().get_current_value() + + +# class Phases: +# +# def __init__(self, n_unds=N_UNDS): +# # 22 does not have a chicane +# n_unds = n_unds.copy() +# if 22 in n_unds: +# n_unds.remove(22) +# +# # 22 does not have a chicane +# n_unds_all = N_UNDS.copy() +# if 22 in n_unds_all: +# n_unds_all.remove(22) +# +# self.cb_auto_good = {i: PV(f"SATUN{i:02}-CHIC:AUTO-PHASING") for i in n_unds} +# self.cb_auto_bad = {i: PV(f"SATUN{i:02}-CHIC:AUTO-PHASING") for i in set(n_unds_all) - set(n_unds)} +# +# self.pv_fixed_energy = PV("SATUN-CHIC:FIX_PHOTON_ENERGY") +# self.pv_energy = PV("SATUN-CHIC:PHOTON-ENERGY") +# +# +# def set(self, energy): +# for cb in self.cb_auto_good.values(): cb.put(int(False)) +# for cb in self.cb_auto_bad.values(): cb.put(int(False)) +# sleep(0.1) +# +# pv_fixed_energy = self.pv_fixed_energy +# +# current_state = pv_fixed_energy.get() +# pv_fixed_energy.put(int(True)) # enforce fixed energy +# +# self.pv_energy.put(energy / 1000) # in keV +# sleep(0.1) +# +# for cb in self.cb_auto_good.values(): cb.put(int(True)) +# sleep(0.1) +# +# for cb in self.cb_auto_good.values(): cb.put(int(False)) +# for cb in self.cb_auto_bad.values(): cb.put(int(False)) +# sleep(0.1) +# +## pv_fixed_energy.put(current_state) # reset to original state + + +# class Phases: + +# def __init__(self, n_unds=N_UNDS): +# # 22 does not have a chicane +# n_unds = n_unds.copy() +# if 22 in n_unds: +# n_unds.remove(22) +# self.pv_energy = PV("SATUN-CHIC:PHOTON-ENERGY") +# self.pv_fixed_energy = PV("SATUN-CHIC:FIX_PHOTON_ENERGY") +# self.adjs_apply = {i: PVAdjustable(f"SATUN{i:02}-CHIC:APPLY-DOP", internal=True) for i in n_unds} + + +# def set(self, energy): +# pv_energy = self.pv_energy +# pv_fixed_energy = self.pv_fixed_energy + +# current_state = pv_fixed_energy.get() +# pv_fixed_energy.put(int(True)) # enforce fixed energy + +# pv_energy.put(energy / 1000) # in keV + +# sleep(0.1) +# self.update() +## sleep(10) + +# pv_fixed_energy.put(current_state) # reset to original state + + +# def update(self): +# wait_for_all([ +# adj_apply.set_target_value(1) for adj_apply in self.adjs_apply.values() +# ]) + + +def get_machine_n_und_ref(): + + for attempt in range(3): + try: + res = PVEnumAdjustable("SARUN:REF-UND").get() + if not res.startswith("SARUN"): + return None + except AttributeError as e: + print("Error getting undulator, retrying") + print(e) + time.sleep(1) + else: + break + + n = len("SARUN") + res = res[n:] + try: + res = int(res) + except ValueError: + return None + return res diff --git a/beamline/pp_shutter.py b/beamline/pp_shutter.py old mode 100644 new mode 100755 diff --git a/beamline/pulse_picker.py b/beamline/pulse_picker.py old mode 100644 new mode 100755 diff --git a/beamline/undulator.py b/beamline/undulator.py old mode 100644 new mode 100755 index 2039a3e..ea62ab1 --- a/beamline/undulator.py +++ b/beamline/undulator.py @@ -23,8 +23,8 @@ N_UNDS = list(range(3, 15 + 1)) # Cristallina without calibration # offset is the difference between PSSS and undulator setpoint # sign convention: Undulator - PSSS -energy_offset = -70 # eV - +energy_offset = 0 # eV +# PSSS = 8355 eV, machine = 8253 eV # move the PSSS motor according to the energy # TODO: improve this hack diff --git a/channels/.pv_channels.py.swp b/channels/.pv_channels.py.swp new file mode 100755 index 0000000..029bfee Binary files /dev/null and b/channels/.pv_channels.py.swp differ diff --git a/channels/bs_channels.py b/channels/bs_channels.py old mode 100644 new mode 100755 index abb9fc1..370a40c --- a/channels/bs_channels.py +++ b/channels/bs_channels.py @@ -1,18 +1,17 @@ -# Channels to save at Cristallina endstation - ########################################################################################################## ########################################################################################################## ########################################################################################################## -# BS channels +### JUNGFRAU DETECTORS from slic.core.acquisition.detcfg import DetectorConfig # TODO: JF settings regarding raw conversion, compression, etc. detectors = [ - "JF16T03V02", -# "JF16T03V01", -# "JF17T16V01", - "JF20T01V01", + "JF16T03V02", # 1.5M from 2025 +# "JF16T03V01", # 1.5M from 2022 +# "JF17T16V01", # 8M + "JF20T01V01", # IO + # "JF05T01V01", # 0.5M stripsel borrowed from Bernina, now registered in esc network ] # ALLOWED_PARAMS = dict( @@ -53,26 +52,35 @@ detectors = DetectorConfig(detectors) detectors_MX = DetectorConfig() -detectors_MX.add("JF17T16V01", adc_to_energy=True, compression=True, crystfel_lists_laser=True, double_pixels_action="mask", factor=11.00, remove_raw_files=True, save_dap_results=True, geometry=True) +detectors_MX.add("JF17T16V01", adc_to_energy=True, compression=True, crystfel_lists_laser=True, double_pixels_action="mask", factor=12.00, remove_raw_files=True, save_dap_results=True, geometry=False) + + + +########################################################################################################## +########################################################################################################## +########################################################################################################## +### BS CHANNELS + + +########################################################################### +#### CAMERAS COLLECTED + camera_channels = [ # "SARES30-CAMS156-PCO1:FPICTURE", # PCO edge camera for the wavefront analysis (from Alvra) -# "SARES30-CAMS156-SMX-OAV:FPICTURE", # SwissMX OAV camera picture -# "SARES30-CAMS156-SMX-OAV.jet_projection", #SWISSMX oav jET PROJECTION + # "SARES30-CAMS156-SMX-OAV:FPICTURE", # SwissMX OAV camera picture + # "SARES30-CAMS156-SMX-OAV.jet_projection", #SWISSMX oav jET PROJECTION # "SARES30-CAMS156-XE:FPICTURE", # X-ray eye ] -#################### -# Machine gas intensity monitor -channels_gas_monitor = [ - "SARFE10-PBPG050:PHOTON-ENERGY-PER-PULSE-AVG", - # "SARFE10-PBPG050:SLOW-X", - # "SARFE10-PBPG050:SLOW-Y", - "SARFE10-PBIG050-EVR0:CALCI", # good for correlations with total beam intensity - "SARFE10-PBPG050:HAMP-INTENSITY-CAL", -] + + + +########################################################################### +#### MACHINE + # RF phases and amplitudes channels_RF = [ @@ -145,36 +153,38 @@ channels_RF = [ "S30CB14-RLLE-DSP:AMPLT-VS", ] -channels_Xeye = ["SARES30-CAMS156-XE:intensity", - "SARES30-CAMS156-XE:x_center_of_mass", - "SARES30-CAMS156-XE:x_fit_amplitude", - "SARES30-CAMS156-XE:x_fit_mean", - "SARES30-CAMS156-XE:x_fit_offset", - "SARES30-CAMS156-XE:x_fit_standard_deviation", - "SARES30-CAMS156-XE:x_fwhm", - "SARES30-CAMS156-XE:x_profile", - "SARES30-CAMS156-XE:x_rms", - "SARES30-CAMS156-XE:y_center_of_mass", - "SARES30-CAMS156-XE:y_fit_amplitude", - "SARES30-CAMS156-XE:y_fit_mean", - "SARES30-CAMS156-XE:y_fit_offset", - "SARES30-CAMS156-XE:y_fit_standard_deviation", - "SARES30-CAMS156-XE:y_fwhm", - "SARES30-CAMS156-XE:y_profile", - "SARES30-CAMS156-XE:y_rms", - # "SARES30-CAMS156-XE:FPICTURE", - ] -###################### -# PBPS053 + + +########################################################################### +#### FRONT-END + + +#################### +# Gas intensity monitor PBPG050 + +channels_gas_monitor = [ + "SARFE10-PBPG050:PHOTON-ENERGY-PER-PULSE-AVG", + # "SARFE10-PBPG050:SLOW-X", + # "SARFE10-PBPG050:SLOW-Y", + "SARFE10-PBIG050-EVR0:CALCI", # good for correlations with total beam intensity + "SARFE10-PBPG050:HAMP-INTENSITY-CAL", +] + + +################### +# Beam position and intensity monitor PBPS053 + channels_PBPS053 = [ "SARFE10-PBPS053:INTENSITY", "SARFE10-PBPS053:XPOS", "SARFE10-PBPS053:YPOS", ] -#################### -# PSSS059 + +################### +# Spectrometer PSSS059 + channels_PSSS059 = [ "SARFE10-PSSS059:FIT-COM", "SARFE10-PSSS059:FIT-FWHM", @@ -190,7 +200,9 @@ channels_PSSS059 = [ "SARFE10-PSSS059:processing_parameters", ] -# Large bandwidth camera +################### +# Spectrometer PSSS059, large bandwidth camera + channels_PSSS059_LB = [ "SARFE10-PSSS059-LB:FIT-COM", "SARFE10-PSSS059-LB:FIT-FWHM", @@ -207,18 +219,13 @@ channels_PSSS059_LB = [ "SARFE10-PSSS059-LB:FIT-BRT", ] -################################### -## Bernina channels -# Beam position monitor PBPS113 -#channels_Bernina = [ -# "SAROP21-PBPS103:INTENSITY", -# "SAROP21-PBPS103:XPOS", -# "SAROP21-PBPS103:YPOS", -# #"SAROP21-PPRM113:FPICTURE", -# "SAROP21-PPRM113:intensity", -# "SAROP21-PPRM113:x_fit_mean", -# "SAROP21-PPRM113:y_fit_mean", -#] + +########################################################################### +#### BERNINA BRANCH until DCM + + +################### +# Bernina beam intensity and position monitor PBPS113 (alias PBPS103) channels_PBPS113_bernina = [ "SAROP21-PBPS103:INTENSITY", @@ -231,6 +238,10 @@ channels_PBPS113_bernina = [ "SAROP21-PBPS103:YPOS", ] + +################### +# Bernina screen PPRM113 + channels_PPRM113_bernina = [ "SAROP21-PPRM113:intensity", "SAROP21-PPRM113:x_center_of_mass", @@ -253,8 +264,15 @@ channels_PPRM113_bernina = [ ] -################################### -# Beam position monitor PBPS113 + + +########################################################################### +#### CRISTALLINA BRANCH + + +################### +## Beam position and intensity monitor PBPS113 + channels_PBPS113 = [ "SAROP31-PBPS113:INTENSITY", "SAROP31-PBPS113:INTENSITY_UJ", @@ -262,11 +280,11 @@ channels_PBPS113 = [ "SAROP31-PBPS113:Lnk9Ch0-PP_VAL_PD1", "SAROP31-PBPS113:Lnk9Ch0-PP_VAL_PD2", "SAROP31-PBPS113:Lnk9Ch0-PP_VAL_PD3", + "SAROP31-PBPS113:Lnk9Ch0-PP_VAL_PD4", "SAROP31-PBPS113:XPOS", "SAROP31-PBPS113:YPOS", ] -# purpose? channels_PBPS113_waveforms = [ "SAROP31-PBPS113:Lnk9Ch0-WF-DATA", "SAROP31-PBPS113:Lnk9Ch1-WF-DATA", @@ -286,8 +304,18 @@ channels_PBPS113_waveforms = [ "SAROP31-PBPS113:Lnk9Ch15-WF-DATA", ] -#################### -# Profile monitor PPRM113 (from _proc process) + +################### +# Diode PDIM113 + +channels_PDIM113 = [ + "SAROP31-PBPS113:Lnk9Ch0-PP_VAL_PD4", +] + + +################### +# Beam profile monitor PPRM113 + channels_PPRM113 = [ "SAROP31-PPRM113:intensity", "SAROP31-PPRM113:x_center_of_mass", @@ -309,13 +337,10 @@ channels_PPRM113 = [ # "SAROP31-PPRM113:FPICTURE", # full pictures for debugging purposes at the moment, from _ib process ] -########################### -# Beam position monitor PBPS149 -# "SARES30-CAMS156-PCO1:FPICTURE", # PCO edge camera for the wavefront analysis (from Alvra) -# "SARES30-CAMS156-SMX-OAV:FPIC -########################### -# Beam position monitor +################### +# Beam position and intensity monitor PBPS149 + channels_PBPS149 = [ "SAROP31-PBPS149:INTENSITY", "SAROP31-PBPS149:INTENSITY_UJ", @@ -327,6 +352,7 @@ channels_PBPS149 = [ "SAROP31-PBPS149:YPOS", ] + ####################### # Profile monitor PPRM150 (from _proc process) channels_PPRM150 = [ @@ -350,14 +376,32 @@ channels_PPRM150 = [ # "SAROP31-PPRM150:FPICTURE", # full pictures for debugging purposes at the moment, from _ib process ] + +#################### +# Diode under screen between the KB's PSCD153 + +channels_PSCD153 = [ + "SAROP31-PBPS149:Lnk9Ch0-PP_VAL_PD4", +] + + + + +########################################################################### +#### GENERAL PURPOSE EXPERIMENT + + ####################### # Cristallina event reciever + channels_EVR = [ "SAR-CVME-TIFALL6:EvtSet", ] + ####################### # Digitizer + channels_digitizer = [ # extra non-beam synchronous channels: #"SARES30-LTIM01-EVR0:DUMMY_PV1_NBS", @@ -366,37 +410,126 @@ channels_digitizer = [ #"SARES30-LTIM01-EVR0:DUMMY_PV4_NBS", # other EVR channels: "SARES30-LSCP1-FNS:CH0:VAL_GET", # Signal-Background + "SARES30-LSCP1-FNS:CH1:VAL_GET", # Signal-Background + "SARES30-LSCP1-FNS:CH2:VAL_GET", # Signal-Background + "SARES30-LSCP1-FNS:CH3:VAL_GET", # Signal-Background + "SARES30-LSCP1-FNS:CH4:VAL_GET", # Signal-Background + "SARES30-LSCP1-FNS:CH5:VAL_GET", # Signal-Background + "SARES30-LSCP1-FNS:CH6:VAL_GET", # Signal-Background + "SARES30-LSCP1-FNS:CH7:VAL_GET", # Signal-Background "SARES30-LSCP1-CRISTA1:CH0:1", # Waveform signal - "SARES30-LSCP1-CRISTA1:CH2:1", # Waveform trigger + "SARES30-LSCP1-CRISTA1:CH1:1", # Waveform signal + "SARES30-LSCP1-CRISTA1:CH2:1", # Waveform signal + "SARES30-LSCP1-CRISTA1:CH3:1", # Waveform signal + "SARES30-LSCP1-CRISTA1:CH4:1", # Waveform signal + "SARES30-LSCP1-CRISTA1:CH5:1", # Waveform signal + "SARES30-LSCP1-CRISTA1:CH6:1", # Waveform signal + "SARES30-LSCP1-CRISTA1:CH7:1", # Waveform signal + # "SARES30-LSCP1-CRISTA1:CH2:1", # Waveform trigger "SARES30-LTIM01-EVR0:CALCI", # Calculated intensity ] + ####################### -# Other BS channels that we sometimes use -channels_other = [ - # "SARFE10-PPRM053:FPICTURE", # TODO: Test if this works here - # "SARFE10-PPRM064:FPICTURE", # TODO: Test if this works here - ] +# X-ray eye + +channels_Xeye = [ + "SARES30-CAMS156-XE:intensity", + "SARES30-CAMS156-XE:x_center_of_mass", + "SARES30-CAMS156-XE:x_fit_amplitude", + "SARES30-CAMS156-XE:x_fit_mean", + "SARES30-CAMS156-XE:x_fit_offset", + "SARES30-CAMS156-XE:x_fit_standard_deviation", + "SARES30-CAMS156-XE:x_fwhm", + "SARES30-CAMS156-XE:x_profile", + "SARES30-CAMS156-XE:x_rms", + "SARES30-CAMS156-XE:y_center_of_mass", + "SARES30-CAMS156-XE:y_fit_amplitude", + "SARES30-CAMS156-XE:y_fit_mean", + "SARES30-CAMS156-XE:y_fit_offset", + "SARES30-CAMS156-XE:y_fit_standard_deviation", + "SARES30-CAMS156-XE:y_fwhm", + "SARES30-CAMS156-XE:y_profile", + "SARES30-CAMS156-XE:y_rms", + # "SARES30-CAMS156-XE:FPICTURE", +] + + + + +########################################################################### +#### CRISTALLINA-Q EXPERIMENT + + +####################### +# Diffractometer 1 bs_channels + +ID_dm1 = "SARES31-GPS" +diffractometer_1_bs = [ + ID_dm1 + ":ROT2THETA-BS", + ID_dm1 + ":ROTTHETA-BS", + ID_dm1 + ":TRX-BS", + ID_dm1 + ":TRY-BS", + ID_dm1 + ":TRZ-BS", + ID_dm1 + ":TD-BS", + ID_dm1 + ":TRXBASE-BS", + ID_dm1 + ":TRYBASE-BS", + ID_dm1 + ":TRYBASE-Avg-BS", + ID_dm1 + "::CALC1", + ID_dm1 + "::CALC2", + ID_dm1 + "::CALC3", + ID_dm1 + "::CALC4", +] + + + + + + +########################################################################### +#### CHANNEL GROUPS + bs_channels = ( - camera_channels - + channels_gas_monitor + channels_gas_monitor # + channels_RF - + channels_Xeye + channels_PBPS053 + channels_PSSS059 + channels_PSSS059_LB + channels_PBPS113 # + channels_PBPS113_waveforms + + channels_PDIM113 # + channels_PPRM113 + channels_PBPS149 # + channels_PBPS149_waveforms # + channels_PPRM150 # only if screen is inserted + + channels_PSCD153 + channels_EVR - # + channels_digitizer - + channels_other + + channels_digitizer + # + channels_Xeye + # + diffractometer_1_bs + # + camera_channels ) + +bs_channels_cristallina_beamline = ( + channels_gas_monitor + # + channels_RF + + channels_PBPS053 + + channels_PSSS059 + + channels_PSSS059_LB + + channels_PBPS113 + # + channels_PBPS113_waveforms + + channels_PDIM113 + # + channels_PPRM113 + + channels_PBPS149 + # + channels_PBPS149_waveforms + # + channels_PPRM150 # only if screen is inserted + + channels_PSCD153 + + channels_EVR +) + + bs_channels_bernina_DCM = ( channels_gas_monitor # + channels_RF diff --git a/channels/pv_channels.py b/channels/pv_channels.py old mode 100644 new mode 100755 index bf91a5a..2493793 --- a/channels/pv_channels.py +++ b/channels/pv_channels.py @@ -1,15 +1,15 @@ ########################################################################################################## ########################################################################################################## ########################################################################################################## -# Epics PVS +### EPICS PVS + + +########################################################################### +#### MACHINE -# Compression, charge settings -####################### -# Machine pvs_machine = [ "SARCL02-MBND100:P-READ", # Predicted bunch energy "SARUN:FELPHOTENE.VAL", # Predicted photon energy from machine settings - "SARFE10-PBPG050:PHOTON-ENERGY-PER-PULSE-AVG.VAL", # Average pulse energy from the gas detector ] # accelerator parameters @@ -83,8 +83,10 @@ pvs_RF = [ "S30CB14-RSYS:GET-VSUM-AMPLT-SCALE", ] + ####################### # Undulator gap + pvs_undulator = [ "SARUN03-UIND030:K_SET.VAL", "SARUN04-UIND030:K_SET.VAL", @@ -99,17 +101,24 @@ pvs_undulator = [ "SARUN13-UIND030:K_SET.VAL", "SARUN14-UIND030:K_SET.VAL", "SARUN15-UIND030:K_SET.VAL", + "SFB_POINTING_AR:SP1", + "SFB_POINTING_AR:SP2", + "SFB_POINTING_AR:ONOFF1", + "SGE-HL-FPAR:X-SLOPE1", + "SGE-HL-FPAR:Y-SLOPE1", + "SGE-HL-FPAR:X-OFFSET1", + "SGE-HL-FPAR:Y-OFFSET1", ] -#################### -# Machine gas intensity monitor -pvs_gas_monitor = [ - "SARFE10-PBPG050:PHOTON-ENERGY-PER-PULSE-US", - "SARFE10-PBPG050:PHOTON-ENERGY-PER-PULSE-DS", -] + + + +########################################################################### +#### FRONT-END ##################### # Slits OAPU044 + pvs_OAPU044 = [ "SARFE10-OAPU044:MOTOR_X", "SARFE10-OAPU044:MOTOR_Y", @@ -117,37 +126,30 @@ pvs_OAPU044 = [ "SARFE10-OAPU044:MOTOR_H", ] + +#################### +# Gas intensity monitor PBPG050 + +pvs_gas_monitor = [ + "SARFE10-PBPG050:PHOTON-ENERGY-PER-PULSE-US", + "SARFE10-PBPG050:PHOTON-ENERGY-PER-PULSE-DS", + "SARFE10-PBPG050:PHOTON-ENERGY-PER-PULSE-AVG", +] + + ################### -# Beam position monitor PBPS053 +# Beam position and intensity monitor PBPS053 + pvs_PBPS053 = [ "SARFE10-PBPS053:MOTOR_X1", # "SARFE10-PBPS053:MOTOR_X2", # Not available, disabled "SARFE10-PBPS053:MOTOR_PROBE", ] -################### -# Spectrometer PSSS059 -pvs_PSSS059 = [ - "SARFE10-PSSS055:MOTOR_X1.RBV", - "SARFE10-PSSS055:MOTOR_Y1.RBV", - "SARFE10-PSSS055:MOTOR_ROT_X1.RBV", - "SARFE10-PSSS055:MOTOR_PROBE.RBV", - "SARFE10-PSSS059:MOTOR_X3.RBV", - "SARFE10-PSSS059:MOTOR_Y3.RBV", - "SARFE10-PSSS059:MOTOR_ROT_X3.RBV", - "SARFE10-PSSS059:MOTOR_Y4.RBV", - "SARFE10-PSSS059:MOTOR_ROT_X4.RBV", - "SARFE10-PSSS059:MOTOR_X5.RBV", - "SARFE10-PSSS059:MOTOR_Y5.RBV", - "SARFE10-PSSS059:MOTOR_Z5.RBV", - "SARFE10-PSSS055:GRATING_SP", - "SARFE10-PSSS059:CRYSTAL_SP", - "SARFE10-PSSS059:SPC_ROI_YMIN", - "SARFE10-PSSS059:SPC_ROI_YMAX", -] #################### # Upstream attenuator OATT053 + pvs_OATT053_old = [ "SARFE10-OATT053:MOTOR_1", "SARFE10-OATT053:MOTOR_1.RBV", @@ -169,6 +171,7 @@ pvs_OATT053_old = [ #################### # New Upstream attenuator OATT053 + pvs_OATT053 = [ "SARFE10-OATT053:photonenergy", # Photon energy for Transmission "SARFE10-OATT053:transmission", # Total Transmission of all stages @@ -183,38 +186,98 @@ pvs_OATT053 = [ "SARFE10-OATT053:MOTOR_6", # motor Motor 6 ] + ################### # Beam profile monitor PPRM053 + pvs_PPRM053 = [ "SARFE10-PPRM053:MOTOR_PROBE.RBV", #"SARFE10-PPRM053:FPICTURE", #"SARFE10-PPRM064:FPICTURE", # TODO move to correct place ] + ################### -# Bernina mono -#pvs_Bernina = [ -# "SAROP21-ARAMIS:ENERGY_SP", -# "SAROP21-ARAMIS:ENERGY", -# "SAROP21-PBPS103:MOTOR_X1.DRBV", -# "SAROP21-PBPS103:MOTOR_Y1.DRBV", -# "SAROP21-PBPS103:MOTOR_X1.RBV", -# "SAROP21-PBPS103:MOTOR_Y1.RBV", -# "SAROP21-PBPS103:MOTOR_PROBE.RBV", -# "SAROP21-PPRM113:MOTOR_PROBE.RBV" -#] +# Diode PDIM053 + +# pvs_PDIM053 = [ +# ] + + +################### +# Spectrometer PSSS059 + +pvs_PSSS059 = [ + "SARFE10-PSSS055:MOTOR_X1.RBV", + "SARFE10-PSSS055:MOTOR_Y1.RBV", + "SARFE10-PSSS055:MOTOR_ROT_X1.RBV", + "SARFE10-PSSS055:MOTOR_PROBE.RBV", + "SARFE10-PSSS059:MOTOR_X3.RBV", + "SARFE10-PSSS059:MOTOR_Y3.RBV", + "SARFE10-PSSS059:MOTOR_ROT_X3.RBV", + "SARFE10-PSSS059:MOTOR_Y4.RBV", + "SARFE10-PSSS059:MOTOR_ROT_X4.RBV", + "SARFE10-PSSS059:MOTOR_X5.RBV", + "SARFE10-PSSS059:MOTOR_Y5.RBV", + "SARFE10-PSSS059:MOTOR_Z5.RBV", + "SARFE10-PSSS055:GRATING_SP", + "SARFE10-PSSS059:CRYSTAL_SP", + "SARFE10-PSSS059:SPC_ROI_YMIN", + "SARFE10-PSSS059:SPC_ROI_YMAX", + "SARFE10-PSSS055:MOTOR_PROBE.RBV", +] + + +################### +# PPRM064 + +# pvs_PPRM064 = [ +# ] + + +################### +# Alvra M1 horizontal offset mirror + +# pvs_OOMH064 = [ +# ] + + +################### +# PPRM066 + +# pvs_PPRM066 = [ +# ] + + + + +########################################################################### +#### BERNINA BRANCH until DCM + + +################### +# Bernina photon energy + pvs_photon_energy_bernina = [ "SAROP21-ARAMIS:ENERGY_SP", "SAROP21-ARAMIS:ENERGY", ] + +################### +# Bernina apertures OAPU092 + pvs_OAPU092_bernina = [ - "SAROP21-OAPU044:MOTOR_X.RBV", - "SAROP21-OAPU044:MOTOR_Y.RBV", - "SAROP21-OAPU044:MOTOR_W.RBV", - "SAROP21-OAPU044:MOTOR_H.RBV", + "SAROP21-OAPU092:MOTOR_X.RBV", + "SAROP21-OAPU092:MOTOR_Y.RBV", + "SAROP21-OAPU092:MOTOR_W.RBV", + "SAROP21-OAPU092:MOTOR_H.RBV", ] + +################### +# Bernina M1 vertical offset mirror + pvs_OOMV092_bernina = [ "SAROP21-OOMV092:W_X.RBV", "SAROP21-OOMV092:W_Y.RBV", @@ -228,11 +291,19 @@ pvs_OOMV092_bernina = [ "SAROP21-OOMV092:TX.RBV", ] + +################### +# Bernina post-M1 screen PPRM094 + pvs_PPRM094_bernina = [ "SAROP21-PPRM113:MOTOR_PROBE", #"SAROP21-PPRM113:FPICTURE", ] + +################### +# Bernina M2 vertical offset mirror + pvs_OOMV096_bernina = [ "SAROP21-OOMV096:W_X.RBV", "SAROP21-OOMV096:W_Y.RBV", @@ -246,10 +317,18 @@ pvs_OOMV096_bernina = [ "SAROP21-OOMV096:TX.RBV", ] + +################### +# Bernina post-M2 screen PSCR097 + pvs_PSCR097_bernina = [ "SAROP21-PSCR097:MOTOR_Y1.RBV", ] + +################### +# Bernina DCM ODCM098 + pvs_ODCM098_bernina = [ "SAROP21-ODCM098:RX12.RBV", # BRAGG "SAROP21-ODCM098:TX12.RBV", # Horizontal @@ -257,19 +336,32 @@ pvs_ODCM098_bernina = [ "SAROP21-ODCM098:RZ1.RBV", # 1st xtal roll "SAROP21-ODCM098:RZ2.RBV", # 2nd xtal roll "SAROP21-ODCM098:RX2.RBV", # 2nd xtal pitch + "SAROP21-ODCM098:ENERGY", # DCM photon energy ] + +################### +# Bernina apertures OAPU102 + pvs_OAPU102_bernina = [ - "SAROP21-OAPU092:MOTOR_X.RBV", - "SAROP21-OAPU092:MOTOR_Y.RBV", - "SAROP21-OAPU092:MOTOR_W.RBV", - "SAROP21-OAPU092:MOTOR_H.RBV", + "SAROP21-OAPU102:MOTOR_X.RBV", + "SAROP21-OAPU102:MOTOR_Y.RBV", + "SAROP21-OAPU102:MOTOR_W.RBV", + "SAROP21-OAPU102:MOTOR_H.RBV", ] + +################### +# Bernina spontaneous radiation monitor PSRD103 + pvs_PSRD103_bernina = [ "SAROP21-PSRD103:MOTOR_PROBE", ] + +################### +# Bernina beam intensity and position monitor PBPS113 (alias PBPS103) + pvs_PBPS113_bernina = [ "SAROP21-PBPS103:MOTOR_X1.DRBV", "SAROP21-PBPS103:MOTOR_Y1.DRBV", @@ -279,6 +371,10 @@ pvs_PBPS113_bernina = [ "SAROP21-PPRM113:MOTOR_PROBE.RBV" ] + +################### +# Bernina pulse-picker OPPI113 + pvs_OPPI113_bernina = [ "SAROP21-OPPI113:MOTOR_X1.RBV", # X1 instead of X "SAROP21-OPPI113:MOTOR_Y1.RBV", # Y1 instead of X @@ -286,13 +382,23 @@ pvs_OPPI113_bernina = [ ] -pvs_PPRM113 = [ +################### +# Bernina screen PPRM113 + +pvs_PPRM113_bernina = [ "SAROP21-PPRM113:MOTOR_PROBE.RBV", ] + + +########################################################################### +#### CRISTALLINA BRANCH + + #################### -# First Cristallina horizontal offset mirror OOMH067 +# Cristallina M1 horizontal offset mirror OOMH067 + pvs_OOMH067 = [ "SAROP31-OOMH067:W_X.RBV", "SAROP31-OOMH067:W_Y.RBV", @@ -308,14 +414,17 @@ pvs_OOMH067 = [ "SAROP31-OOMH067:RY.RBV", ] + #################### # Beam screen between the first two horizontal mirrors PSCR068 pvs_PSCR068 = [ "SAROP31-PSCR068:MOTOR_PROBE.RBV", ] + #################### -# Second Cristallina horizontal offset mirror OOMH084 +# Cristallina M2 horizontal offset mirror OOMH084 + pvs_OOMH084 = [ "SAROP31-OOMH084:W_X.RBV", "SAROP31-OOMH084:W_Y.RBV", @@ -331,14 +440,18 @@ pvs_OOMH084 = [ "SAROP31-OOMH084:RY.RBV", ] + ################### # Beam profile monitor PPRM085 + pvs_PPRM085 = [ "SAROP31-PPRM085:MOTOR_PROBE.RBV", ] + ################### # Slits OAPU107 + pvs_OAPU107 = [ "SAROP31-OAPU107:MOTOR_X.VAL", "SAROP31-OAPU107:MOTOR_X.RBV", @@ -346,8 +459,30 @@ pvs_OAPU107 = [ "SAROP31-OAPU107:MOTOR_Y.RBV", ] + +################### +## Double channel-cut monochromator ODCC110 + +pvs_ODCC110 = [ + "SAROP31-ODCC110:MOT_RX1.RBV", + "SAROP31-ODCC110:MOT_RX2.RBV", + "SAROP31-ODCC110:MOT_ENY.RBV", + "SAROP31-ODCC110:MOT_OFS.RBV", + "SAROP31-ODCC110:MOT_TX1.RBV", +] + + +################### +## Infra-channel-cut-crystals screen PSCR110110 + +pvs_PSCR110 = [ + "SAROP31-PSCR110:MOT_TY1.RBV", +] + + ################### ## Beam position and intensity monitor PBPS113 + pvs_PBPS113 = [ "SAROP31-PBPS113:MOTOR_X1.DRBV", "SAROP31-PBPS113:MOTOR_Y1.DRBV", @@ -356,20 +491,34 @@ pvs_PBPS113 = [ "SAROP31-PBPS113:MOTOR_PROBE.RBV", ] + +################### +# Diode PDIM113 + +pvs_PDIM113 = [ + "SAROP31-PDIM113:MOTOR_PROBE.RBV", +] + + ################### # Beam profile monitor PPRM113 + pvs_PPRM113 = [ "SAROP31-PPRM113:MOTOR_PROBE.RBV", ] + #################### # Alignment laser mirror OLAS147 + pvs_OLAS147 = [ "SAROP31-OLAS147:MOTOR_1.RBV", ] + ################### # Slits OAPU149 + pvs_OAPU149 = [ "SAROP31-OAPU149:MOTOR_X.RBV", "SAROP31-OAPU149:MOTOR_Y.RBV", @@ -377,8 +526,10 @@ pvs_OAPU149 = [ "SAROP31-OAPU149:MOTOR_H.RBV", ] + ################### # Beam position and intensity monitor PBPS149 + pvs_PBPS149 = [ "SAROP31-PBPS149:MOTOR_X1.DRBV", "SAROP31-PBPS149:MOTOR_Y1.DRBV", @@ -387,14 +538,18 @@ pvs_PBPS149 = [ "SAROP31-PBPS149:MOTOR_PROBE.RBV", ] + ################### # Beam profile monitor PPRM150 + pvs_PPRM150 = [ "SAROP31-PPRM150:MOTOR_PROBE.RBV", ] + #################### -# Attenuators OATA150 +# Attenuators OATA150, old + pvs_OATA150_old = [ "SAROP31-OATA150:MOTOR_1.RBV", "SAROP31-OATA150:MOTOR_2.RBV", @@ -409,8 +564,10 @@ pvs_OATA150_old = [ "SAROP31-OATA150:MOT2TRANS.VALD" ] + #################### -# New Attenuators OATA150 +# Attenuators OATA150 + pvs_OATA150 = [ "SAROP31-OATA150:photonenergy", # ai Photon energy for Transmission SAROP31-CPCL-OSAT150 swissfel "SAROP31-OATA150:transmission", # ai Total Transmission of all stages SAROP31-CPCL-OSAT150 swissfel @@ -429,9 +586,9 @@ pvs_OATA150 = [ ] - #################### # Pulse picker OPPI151 + pvs_OPPI151 = [ "SAROP31-OPPI151:MOTOR_X1.RBV", # X1 instead of X "SAROP31-OPPI151:MOTOR_Y1.RBV", # Y1 instead of X @@ -441,6 +598,7 @@ pvs_OPPI151 = [ #################### ## Horizontal offset mirror ODMV152 + pvs_ODMV152 = [ "SAROP31-ODMV152:W_X.RBV", "SAROP31-ODMV152:W_Y.RBV", @@ -454,8 +612,10 @@ pvs_ODMV152 = [ "SAROP31-ODMV152:TX.RBV", ] + ########################### # Vertical KB mirror OKBV153 + pvs_OKBV153 = [ "SAROP31-OKBV153:W_X.RBV", "SAROP31-OKBV153:W_Y.RBV", @@ -471,14 +631,18 @@ pvs_OKBV153 = [ "SAROP31-OKBV153:TX2.RBV", ] + #################### # Screen between the KB's PSCD153 + pvs_PSCD153 = [ "SAROP31-PSCD153:MOTOR_PROBE.RBV" ] + ########################### # Horizontal KB mirror OKBH154 + pvs_OKBH154 = [ "SAROP31-OKBH154:W_X.RBV", "SAROP31-OKBH154:W_Y.RBV", @@ -493,16 +657,38 @@ pvs_OKBH154 = [ "SAROP31-OKBH154:TX2.RBV", ] + +########################### +# Izero-slit unit + +pvs_i0_chamber = [ + "SARES30-MCS20610:MCS1.RBV", + "SARES30-MCS20610:MCS2.RBV", + "SARES30-MCS20610:MCS3.RBV", + "SARES30-MCS20610:MCS4.RBV", + "SARES30-MCS20610:MCS5.RBV" +] + + + + +########################################################################### +#### GENERAL PURPOSE EXPERIMENT + + #################### # Standa motors (mainly used with the X-ray eye) + pvs_standa = [ "SARES30-MOBI1:MOT_1.RBV", "SARES30-MOBI1:MOT_2.RBV", "SARES30-MOBI1:MOT_3.RBV", ] + #################### # Newport 300 mm stage + pvs_newport_300 = [ "SARES30-MOBI1:MOT_5.RBV", ] @@ -510,6 +696,7 @@ pvs_newport_300 = [ ############################### # Smaract stages mini XYZ from SwissMX + pvs_smaract_xyz = [ "SARES30-MCS2750:MOT_1.RBV", "SARES30-MCS2750:MOT_1.VAL", @@ -519,15 +706,19 @@ pvs_smaract_xyz = [ "SARES30-MCS2750:MOT_3.VAL", ] + #################### # Attocube motors + pvs_attocube = [ "SARES30-ATTOCUBE:A0-POS", "SARES30-ATTOCUBE:A1-POS", ] + ############################### # Smaract stages from Juraj + pvs_smaract_juraj = [ "SARES30-XSMA156:X:MOTRBV", "SARES30-XSMA156:Y:MOTRBV", @@ -537,30 +728,9 @@ pvs_smaract_juraj = [ "SARES30-XSMA156:Rz:MOTRBV", ] -pvs_diffractometer_1 = [ - "SARES30-CPCL-ECMC02:ROT2THETA-PosAct", - "SARES30-CPCL-ECMC02:ROTTHETA-PosAct", - "SARES30-CPCL-ECMC02:TRXBASE-PosAct", - "SARES30-CPCL-ECMC02:TRY-PosAct", - "SARES30-CPCL-ECMC02:TRX-PosAct", - "SARES30-CPCL-ECMC02:TRZ-PosAct", - "SARES30-CPCL-ECMC02:TD-PosAct", - "SARES30-CPCL-ECMC02:m1s012-Drv01-TrqAct", # TRYBASE Mot 1 torque - "SARES30-CPCL-ECMC02:m1s013-Drv01-TrqAct", # TRYBASE Mot 2 torque - "SARES30-CPCL-ECMC02:m1s014-Drv01-TrqAct", # TRYBASE Mot 3 torque - "SARES30-CPCL-ECMC02:m1s015-Drv01-TrqAct", # TRYBASE Mot 4 torque - "SARES30-CPCL-ECMC02:m1s027-Drv01-TrqAct", # TD Mot torque - "SARES30-CPCL-ECMC02:m1s011-Drv01-TrqAct", # TRXBASE Mot torque - "SARES30-CPCL-ECMC02:m1s030-Drv01-TrqAct", # TRX Mot torque - "SARES30-CPCL-ECMC02:m1s031-Drv01-TrqAct", # TRY Mot torque - "SARES30-CPCL-ECMC02:m1s029-Drv01-TrqAct", # TRZ Mot torque - "SARES30-CPCL-ECMC02:m1s010-Drv01-TrqAct", # ROT2THETA Mot torque - "SARES30-CPCL-ECMC02:m1s028-Drv01-TrqAct", # ROTTHETA Mot torque -] -pvs_huber_z= [ - "SARES30-MOBI2:MOT_Z.RBV", -] +############################### +# General purpose JJ-slits pvs_JJ_slits = [ "SARES30-MOBI2:MOT_X.RBV", @@ -569,27 +739,123 @@ pvs_JJ_slits = [ "SARES30-MOBI2:MOT_H.RBV", ] + + + +########################################################################### +#### CRISTALLINA-Q EXPERIMENT + + +############################### +# Diffractometer 1 + +ID_dm1 = "SARES31-GPS" +pvs_diffractometer_1 = [ + ID_dm1 + ":ROT2THETA-PosAct", + ID_dm1 + ":ROTTHETA-PosAct", + ID_dm1 + ":TRXBASE-PosAct", + ID_dm1 + ":TRY-PosAct", + ID_dm1 + ":TRX-PosAct", + ID_dm1 + ":TRZ-PosAct", + ID_dm1 + ":TD-PosAct", + ID_dm1 + ":m1s012-Drv01-TrqAct", # TRYBASE Mot 1 torque + ID_dm1 + ":m1s013-Drv01-TrqAct", # TRYBASE Mot 2 torque + ID_dm1 + ":m1s014-Drv01-TrqAct", # TRYBASE Mot 3 torque + ID_dm1 + ":m1s015-Drv01-TrqAct", # TRYBASE Mot 4 torque + ID_dm1 + ":m1s027-Drv01-TrqAct", # TD Mot torque + ID_dm1 + ":m1s011-Drv01-TrqAct", # TRXBASE Mot torque + ID_dm1 + ":m1s030-Drv01-TrqAct", # TRX Mot torque + ID_dm1 + ":m1s031-Drv01-TrqAct", # TRY Mot torque + ID_dm1 + ":m1s029-Drv01-TrqAct", # TRZ Mot torque + ID_dm1 + ":m1s010-Drv01-TrqAct", # ROT2THETA Mot torque + ID_dm1 + ":m1s028-Drv01-TrqAct", # ROTTHETA Mot torque +] + + +############################### +# Diffractometer 2 + +ID_dm2 = "SARES32-GPS" +pvs_diffractometer_2 = [ + ID_dm2 + ":ROTTHETA-PosAct", + ID_dm2 + ":TRXBASE-PosAct", + ID_dm2 + ":TRY-PosAct", + ID_dm2 + ":TRX-PosAct", + ID_dm2 + ":ROT2THETA-PosAct", + ID_dm2 + ":TRZ-PosAct", + ID_dm2 + ":TD-PosAct", + ID_dm2 + ":ROTX-PosAct", + ID_dm2 + ":ROTZ-PosAct", + ID_dm2 + ":m0s012-Drv01-TrqAct", # TRYBASE Mot 1 torque + ID_dm2 + ":m0s012-Drv01-TrqAct", # TRYBASE Mot 2 torque + ID_dm2 + ":m0s014-Drv01-TrqAct", # TRYBASE Mot 3 torque + ID_dm2 + ":m0s015-Drv01-TrqAct", # TRYBASE Mot 4 torque + ID_dm2 + ":m0s027-Drv01-TrqAct", # TD Mot torque + ID_dm2 + ":m0s011-Drv01-TrqAct", # TRXBASE Mot torque + ID_dm2 + ":m0s030-Drv01-TrqAct", # TRX Mot torque + ID_dm2 + ":m0s032-Drv01-TrqAct", # ROTX Mot torque + ID_dm2 + ":m0s033-Drv01-TrqAct", # ROTZ Mot torque + ID_dm2 + ":m0s010-Drv01-TrqAct", # ROT2THETA Mot torque + ID_dm2 + ":m0s028-Drv01-TrqAct", # ROTTHETA Mot torque +] + + +############################### +# Huber vertical stage + +pvs_huber_z= [ + "SARES30-MOBI2:MOT_Z.RBV", +] + + + + +########################################################################### +#### CRISTALLINA-MX EXPERIMENT + + +############################### +# Fast XY-stage + pvs_swissmx = [ "SAR-EXPMX:MOT_FX.RBV", "SAR-EXPMX:MOT_FY.RBV", ] + + + + + +########################################################################### +#### CHANNEL GROUPS + +############################### +# All channels (slic updates the list based on these) + pv_channels = ( pvs_machine # + pvs_RF - # + pvs_undulator - + pvs_gas_monitor + + pvs_undulator + pvs_OAPU044 + + pvs_gas_monitor + pvs_PBPS053 + pvs_OATT053 + pvs_PPRM053 + # + pvs_PDIM053 + pvs_PSSS059 + # + pvs_PPRM064 + # + pvs_OOMH064 + # + pvs_PPRM066 + pvs_OOMH067 + pvs_PSCR068 + pvs_OOMH084 + pvs_PPRM085 + pvs_OAPU107 + + pvs_ODCC110 + + pvs_PSCR110 + pvs_PBPS113 + + pvs_PDIM113 + pvs_PPRM113 + pvs_OLAS147 + pvs_OAPU149 @@ -601,44 +867,81 @@ pv_channels = ( + pvs_OKBV153 + pvs_PSCD153 + pvs_OKBH154 + + pvs_i0_chamber + pvs_standa -# + pvs_newport_300 + # + pvs_newport_300 # + pvs_smaract_xyz + pvs_diffractometer_1 + + pvs_diffractometer_2 + pvs_huber_z + pvs_JJ_slits - # + pvs_swissmx -# + pvs_Bernina + # + pvs_attocube + # + pvs_smaract_juraj + # + pvs_photon_energy_bernina + # + pvs_OAPU092_bernina + # + pvs_OOMV092_bernina + # + pvs_PPRM094_bernina + # + pvs_OOMV096_bernina + # + pvs_PSCR097_bernina + # + pvs_ODCM098_bernina + # + pvs_OAPU102_bernina + # + pvs_PSRD103_bernina + # + pvs_PBPS113_bernina + # + pvs_OPPI113_bernina + # + pvs_PPRM113_bernina ) - -# + pvs_attocube -# + pvs_smaract_juraj - -pv_channels_front_end = ( +pv_channels_cristallina_beamline = ( pvs_machine # + pvs_RF - # + pvs_undulator - + pvs_gas_monitor + + pvs_undulator + pvs_OAPU044 + + pvs_gas_monitor + pvs_PBPS053 + pvs_OATT053 + pvs_PPRM053 + # + pvs_PDIM053 + pvs_PSSS059 + # + pvs_PPRM064 + # + pvs_OOMH064 + # + pvs_PPRM066 + pvs_OOMH067 + pvs_PSCR068 + + pvs_OOMH084 + + pvs_PPRM085 + + pvs_OAPU107 + + pvs_ODCC110 + + pvs_PSCR110 + + pvs_PBPS113 + + pvs_PDIM113 + + pvs_PPRM113 + + pvs_OLAS147 + + pvs_OAPU149 + + pvs_PBPS149 + + pvs_PPRM150 + + pvs_OATA150 + + pvs_OPPI151 + + pvs_ODMV152 + + pvs_OKBV153 + + pvs_PSCD153 + + pvs_OKBH154 + + pvs_i0_chamber ) -pv_channels_bernina = ( +pv_channels_bernina_DCM = ( pvs_machine # + pvs_RF - # + pvs_undulator - + pvs_gas_monitor + + pvs_undulator + pvs_OAPU044 + + pvs_gas_monitor + pvs_PBPS053 + pvs_OATT053 + pvs_PPRM053 + # + pvs_PDIM053 + pvs_PSSS059 + # + pvs_PPRM064 + # + pvs_OOMH064 + # + pvs_PPRM066 + pvs_OOMH067 + pvs_PSCR068 + pvs_photon_energy_bernina @@ -651,5 +954,6 @@ pv_channels_bernina = ( + pvs_OAPU102_bernina + pvs_PSRD103_bernina + pvs_PBPS113_bernina - + pvs_PPRM113 + + pvs_OPPI113_bernina + + pvs_PPRM113_bernina ) diff --git a/cristallina.py b/cristallina.py old mode 100644 new mode 100755 index fad0f47..c8d8585 --- a/cristallina.py +++ b/cristallina.py @@ -78,11 +78,19 @@ from channels.bs_channels import ( from channels.pv_channels import pv_channels from spreadsheet import overview +from channels.bs_channels import bs_channels_bernina_DCM +from channels.pv_channels import pv_channels_bernina_DCM + +from channels.bs_channels import bs_channels_cristallina_beamline +from channels.pv_channels import pv_channels_cristallina_beamline + + # from channels_minimal import detectors_min, channels_min, pvs_min ################# DEVICES ################# dummy = DummyAdjustable(units="au") +dummy2 = DummyAdjustable(ID='DUMMY2', name='Dummy2', units="au") # from devices.knife_edge import KnifeEdge @@ -97,7 +105,8 @@ from beamline.components import ( pulsepicker, alignment_laser, pbps113, - pbps149, + mono, + m3, ) from beamline.components import kbHor, kbVer @@ -107,13 +116,32 @@ from systems.components import cta from gp_exp.components import Newport_large, OWIS + + # Undulators from beamline import undulator undulators = undulator.Undulators() + logger.info(f"Using undulator (Aramis) offset to PSSS energy of {undulator.energy_offset} eV.") + +# Undulators & mono +from beamline import photon_energy + +cr_photon_energy = photon_energy.PhotonEnergy() + +logger.info(f"Photon energy offsets: PSSS {photon_energy.energy_offset_PSSS} eV , DCCM {photon_energy.energy_offset_DCCM} eV, undulator {photon_energy.energy_offset_undulators} eV.") + + + + +# Pulse picker shutter +#pp_shutter = PP_Shutter( +# "SARES30-LTIM01-EVR0:RearUniv0-Ena-SP", name="Cristallina pulse picker shutter" +#) # Shutter buttton when using the pulse picker + ## Slits from slic.devices.xoptics import slits @@ -129,9 +157,11 @@ BerninaDCM = BerninaMono("SAROP21-ODCM098") # Diffractometer from crq_exp.diffractometer import Diffractometer +dm1 = Diffractometer("SARES30-CPCL-ECMC02") +dm2 = Diffractometer("SARES32-GPS") -diffractometer = Diffractometer("diffractometer") - +# Set according to which diffractometer is being used +diffractometer = dm1 # Dilution fridge from crq_exp.dilsc import Dilution @@ -144,7 +174,7 @@ except Exception as e: # MX adajustables -import mx.mx_adjustables +# import mx.mx_adjustables ################# Stand setup ################## @@ -158,8 +188,8 @@ adjs_for_spreadsheet = { #"Time": Time(), "Transmission": attenuator.trans1st, "Upstream Transmission": upstream_attenuator.trans1st, - "Energy_setpoint": undulators, - "Energy_offset": undulator.energy_offset, + "Energy_setpoint": cr_photon_energy, + "Energy_offset undulator": photon_energy.energy_offset_undulators, "TD": diffractometer.td, "TRX": diffractometer.tr_x, "TRY": diffractometer.tr_y, @@ -176,7 +206,7 @@ if dilution is not None: "Magnet_Y": dilution.y, "Magnet_Z": dilution.z, "DilSc_T_chip": dilution.T_chip, - "DilSc_T_pucksensor": dilution.T_pucksensor, + "DilSc_T_pucksensor": dilution.T_reg, } adjs_for_spreadsheet.update(adjs_dilsc) @@ -195,7 +225,6 @@ spreadsheet = Spreadsheet( try: from stand.client import Client - stand_host = "saresc-vcons-02.psi.ch" stand_client = Client(host=stand_host, port=9090) response = stand_client.get() @@ -209,7 +238,8 @@ except Exception as error: ################# DAQ Setup ################# instrument = "cristallina" -experiment_type = "MX" # "MX" or "Q" for the different setups and detector configurations +# experiment_type = "MX" # "MX" or "Q" for the different setups and detector configurations +experiment_type = "Q" # "MX" or "Q" for the different setups and detector configurations from pgroups import pgroup, pgroup_scratch @@ -242,6 +272,9 @@ else: logger.error(f"Experiment type {experiment_type} not supported. Exiting.") sys.exit(1) +# default_channels=bs_channels, +# default_pvs=pv_channels, + daq.update_config_pvs() @@ -262,7 +295,7 @@ DAQS = multiple_daqs.generate_DAQS(instrument, pgroup, bs_channels, pv_channels, # required fraction defines ammount of data recorded to save the step and move on to the next one check_intensity_gas_monitor = PVCondition( "SARFE10-PBPG050:PHOTON-ENERGY-PER-PULSE-US", - vmin=4, + vmin=400, vmax=2000, wait_time=0.5, required_fraction=0.8, @@ -271,5 +304,6 @@ check_intensity_gas_monitor = PVCondition( scan = Scanner(default_acquisitions=[daq], condition=check_intensity_gas_monitor) gui = GUI(scan, show_goto=True, show_spec=True) + logger.info(f"Running at {instrument} with pgroup {pgroup}. Experiment type: {experiment_type}.") logger.info("Loading finished.") diff --git a/crq_exp/diffractometer.py b/crq_exp/diffractometer.py old mode 100644 new mode 100755 index 8ede227..f084036 --- a/crq_exp/diffractometer.py +++ b/crq_exp/diffractometer.py @@ -16,20 +16,44 @@ from slic.devices.general.motor import Motor class Diffractometer(Device): - def __init__(self, ID, motor_naming="MOTOR", **kwargs): + def __init__(self, ID, **kwargs): super().__init__(ID, **kwargs) - self.twotheta = Motor("SARES30-CPCL-ECMC02:ROT2THETA") # , ID=None, name=None, units=None, internal=False): - self.theta = Motor("SARES30-CPCL-ECMC02:ROTTHETA") # , ID=None, name=None, units=None, internal=False): + self.twotheta = Motor(ID + ":ROT2THETA") # , ID=None, name=None, units=None, internal=False): + self.theta = Motor(ID + ":ROTTHETA") # , ID=None, name=None, units=None, internal=False): - self.trx_base = Motor("SARES30-CPCL-ECMC02:TRXBASE") # , ID=None, name=None, units=None, internal=False): - self.try_base = Motor("SARES30-CPCL-ECMC02:TRYBASE") # , ID=None, name=None, units=None, internal=False): + self.trx_base = Motor(ID + ":TRXBASE") # , ID=None, name=None, units=None, internal=False): + self.try_base = Motor(ID + ":TRYBASE") # , ID=None, name=None, units=None, internal=False): - self.tr_x = Motor("SARES30-CPCL-ECMC02:TRX") - self.tr_y = Motor("SARES30-CPCL-ECMC02:TRY") - self.tr_z = Motor("SARES30-CPCL-ECMC02:TRZ") + self.tr_x = Motor(ID + ":TRX") + self.tr_y = Motor(ID + ":TRY") + self.tr_z = Motor(ID + ":TRZ") - self.td = Motor("SARES30-CPCL-ECMC02:TD") + self.td = Motor(ID + ":TD") + + if ID == "SARES32-GPS": + self.name = "DM2: Cristallina pulsed magnet diffractometer" + # This diffractometer also has extra swivel stages + self.rotx = Motor(ID + ":ROTX") + self.rotz = Motor(ID + ":ROTZ") + if ID == "SARES30-CPCL-ECMC02": + self.name = "DM1: Cristallina dilution fridge diffractometer" + +# class Diffractometer(Device): +# def __init__(self, ID, motor_naming="MOTOR", **kwargs): +# super().__init__(ID, **kwargs) + +# self.twotheta = Motor("SARES30-CPCL-ECMC02:ROT2THETA") # , ID=None, name=None, units=None, internal=False): +# self.theta = Motor("SARES30-CPCL-ECMC02:ROTTHETA") # , ID=None, name=None, units=None, internal=False): + +# self.trx_base = Motor("SARES30-CPCL-ECMC02:TRXBASE") # , ID=None, name=None, units=None, internal=False): +# self.try_base = Motor("SARES30-CPCL-ECMC02:TRYBASE") # , ID=None, name=None, units=None, internal=False): + +# self.tr_x = Motor("SARES30-CPCL-ECMC02:TRX") +# self.tr_y = Motor("SARES30-CPCL-ECMC02:TRY") +# self.tr_z = Motor("SARES30-CPCL-ECMC02:TRZ") + +# self.td = Motor("SARES30-CPCL-ECMC02:TD") # Set speed: diff --git a/crq_exp/dilsc.py b/crq_exp/dilsc.py old mode 100644 new mode 100755 index fa323a7..15774e3 --- a/crq_exp/dilsc.py +++ b/crq_exp/dilsc.py @@ -25,11 +25,9 @@ class Dilution(Device): self.x = MagnetCoil("X", self.dilsc, 'x', limit_low=-0.6, limit_high=0.6) self.y = MagnetCoil("Y", self.dilsc, 'y', limit_low=-0.6, limit_high=0.6) self.z = MagnetCoil("Z", self.dilsc, 'z', limit_low=-5.2, limit_high=5.2) - # not in use currently - # self.T_plato = Thermometer('T_plato', self.dilsc, limit_low=0, limit_high=300) + self.T_plato = Thermometer('T_plato', self.dilsc, limit_low=0, limit_high=300) self.T_chip = Thermometer('T_chip', self.dilsc, limit_low=0, limit_high=300) - self.T_pucksensor = Thermometer('T_pucksensor', self.dilsc, limit_low=0, limit_high=300) - + self.T_reg = Thermometer('T_reg', self.dilsc, limit_low=0, limit_high=300) class Thermometer(Adjustable): @@ -37,7 +35,18 @@ class Thermometer(Adjustable): super().__init__(name, limit_low=limit_low, limit_high=limit_high) self.dilsc = dilsc_connection - + + # Heater to regulate channel A on the Galgen LakeShore372. Give value in Ohm. + self.heater_resistance = 82 + # Defines heater ranges and PID values for various control temperature regions. + self.heater_settings = [ + {'range': 'off', 'pid': (800, 1, 0), 'temp_start': -1, 'temp_end': 0.00099}, + {'range': '300uA', 'pid': (800, 1, 0), 'temp_start': 0.001, 'temp_end': 0.037}, + {'range': '1mA', 'pid': (40, 14, 0), 'temp_start': 0.038, 'temp_end': 0.108}, + {'range': '3mA', 'pid': (4, 15, 0), 'temp_start': 0.110, 'temp_end': 0.350}, + {'range': '10mA', 'pid': (2, 12, 0), 'temp_start': 0.352, 'temp_end': 1.100}, + {'range': '30mA', 'pid': (2, 60, 0), 'temp_start': 1.100, 'temp_end': 2.300} + ] def _check_connection(func): def checker(self, *args, **kwargs): @@ -47,6 +56,26 @@ class Thermometer(Adjustable): return func(self, *args, **kwargs) return checker + @_check_connection + def set_heater_range(self, range_value): + """ Sets the heater range for the control channel and sets the PID parameters for the associated control loop. + TODO: Consider redoing so that it only works for the T_reg channel. + TODO: the pid values are now sent with stringio and not saved as parameters in slic, this should be fixed. + - + """ + heater_range_map = { + 'off': 0, '30uA': 1, '100uA': 2, '300uA': 3, '1mA': 4, '3mA': 5, '10mA': 6, '30mA': 7, '100mA': 8} + if range_value not in heater_range_map: + raise ValueError("Invalid range value. Allowed are: ['off','30uA','100uA','300uA','1mA','3mA','10mA','30mA','100mA']") + enum_value = heater_range_map[range_value] + self.dilsc.setParameter(self.name, 'htrrng', enum_value) + + for setting in self.heater_settings: + if setting['range'] == range_value: + self.set_PID_parameters(*setting['pid']) + break + return + @_check_connection def get_current_value(self): cacheitem = self.dilsc.getParameter(f'{self.name}', 'value', trycache=False) @@ -54,7 +83,16 @@ class Thermometer(Adjustable): @_check_connection - def set_target_value(self, value): + def set_target_value(self, value, adjust_heater_range=True): + if adjust_heater_range: + range_to_set = None + for setting in self.heater_settings: + if setting['temp_start'] <= value and setting['temp_end'] >= value: + range_to_set = setting['range'] + if range_to_set != None: + self.set_heater_range(range_to_set) + else: + raise ValueError(f"Heater range for the target value {value} could not be set") self.dilsc.setParameter(f'{self.name}', 'target', value) @_check_connection @@ -73,7 +111,8 @@ class Thermometer(Adjustable): """ Returns the current PID parameters associated with the control loop for this thermometer. """ - response = self.dilsc.getParameter(f'{self.name}','ctrlpars', trycache=False) + # response = self.dilsc.getParameter(f'{self.name}','ctrlpars', trycache=False) + response = self.dilsc.execCommand('lscio', 'communicate', 'PID?') return response @_check_connection @@ -84,8 +123,10 @@ class Thermometer(Adjustable): - The range is limited to less than the Lakeshore range allows, this needs to be fixed in frappy. """ - self.dilsc.setParameter(f'{self.name}', 'ctrlpars', {'p': p, 'i': i, 'd': d}) - + # self.dilsc.setParameter(f'{self.name}', 'ctrlpars', {'p': p, 'i': i, 'd': d}) + command_string = f'PID {0},{p},{i},{d}; PID?' + self.dilsc.execCommand('lscio', 'communicate', command_string) + class MagnetCoil(Adjustable): @@ -123,5 +164,19 @@ class MagnetCoil(Adjustable): def is_moving(self): response = self.dilsc.getParameter(f'mf{self.direction}','status', trycache=False) return response[0][0] > StatusType.PREPARED + + @_check_connection + def set_ramp_speed(self, direction, value): + """ Sets ramp speed for a given direction (x,y or z) and value in T/min. + """ + if value > 0.5: + raise ValueError('Do not exceed 0.5 T/min unless you want a quench party') + if value <= 0: + raise ValueError('Only positive values are allowed') + if self.direction not in ["x", "y", "z"]: + raise ValueError("Direction must be either x, y or z.") + else: + self.dilsc.setParameter(direction, 'ramp', value) + return \ No newline at end of file diff --git a/crq_exp/synchronization.py b/crq_exp/synchronization.py old mode 100644 new mode 100755 diff --git a/devices/knife_edge.py b/devices/knife_edge.py old mode 100644 new mode 100755 diff --git a/environment.txt b/environment.txt old mode 100644 new mode 100755 diff --git a/exp_temp/I0_foil_motor.py b/exp_temp/I0_foil_motor.py new file mode 100755 index 0000000..8e8e2bf --- /dev/null +++ b/exp_temp/I0_foil_motor.py @@ -0,0 +1,5 @@ +from slic.devices.general.motor import Motor +from slic.devices.general.smaract import SmarActAxis + +I0_foils = Motor("SARES30-MCS20610:MCS3") +# I0_foils = SmarActAxis("SARES30-MCS20610:MCS3") diff --git a/exp_temp/fake_attenuator.py b/exp_temp/fake_attenuator.py new file mode 100755 index 0000000..a82f548 --- /dev/null +++ b/exp_temp/fake_attenuator.py @@ -0,0 +1,184 @@ +from time import sleep + +from slic.core.adjustable import Adjustable, PVAdjustable +from slic.core.device import Device, SimpleDevice +from slic.devices.general.motor import Motor +from slic.utils.hastyepics import get_pv as PV + + + +class Attenuator(Device): + + def __init__(self, ID, energy_threshold=1500, name="Attenuator Aramis", process_time=1, **kwargs): + super().__init__(ID, name=name, **kwargs) + + + # self.motor_limits = motor_limits + + # self.motors = motors = LimitedMotors(ID, motor_limits) + self.motor_status_pvs = [PVAdjustable(f"{ID}:MOTOR_{i}.DMOV") for i in [1,2,3,4,5,6]] + + # self.motors = LimitedMotors(ID, motor_limits) + + self.trans1st = Transmission(ID, process_time=process_time) + self.trans3rd = Transmission(ID, process_time=process_time, third_order=True) + self.energy = LimitedEnergy(ID, energy_threshold, process_time) + self.foils = [Foil(ID, i+1) for i in range(6)] + + + def get_current_transmission(self): + return self.trans1st.get_current_value() + + def get_current_transmission_third_harmonic(self): + return self.trans3rd.get_current_value() + + def set_transmission(self, value, energy=None): + self.energy.set_target_value(energy) + self.trans1st.set_target_value(value) + + def set_transmission_third_harmonic(self, value, energy=None): + self.energy.set_target_value(energy) + self.trans3rd.set_target_value(value) + + + def set_foils_combination(self, combination: list, wait=False): + assert len(combination) == 6, "Invalid combination entered" + + for value in combination: + assert value in self.foils[0].values, "wrong value entered" + + + for i, (foil,value) in enumerate(zip(self.foils,combination)): + print(f"setting {repr(foil)} to position {value}") + foil.set_target_value(value) + + if wait: + self.foils[0].wait_for_motion() + + + + + +class Transmission(PVAdjustable): + + def __init__(self, ID, third_order=False, check_motors=True, **kwargs): + #self.motors = motors + self.third_order = third_order + self.motors = [Motor(f"{ID}:MOTOR_{i}") for i in [1,2,3,4,5,6]] + + self.check_motors = check_motors + + prefix = ID + ":" + # Changed for new attenuator IOC + pvname_setvalue = prefix + "UsrRec.TD" + pvname_readback = prefix + ("UsrRec.TC3" if third_order else "UsrRec.TC1") + super().__init__(pvname_setvalue, pvname_readback, **kwargs) + + self.pvnames.third_order_toggle = pvn = prefix + "UsrRec.HRM3" + self.pvs.third_order_toggle = PV(pvn) + + + pv_calculated = prefix + ("UsrRec.TR3" if third_order else "UsrRec.TR1") + pv_current = prefix + ("UsrRec.TC3" if third_order else "UsrRec.TC1") + + self.calculated_trans = PVAdjustable(pv_calculated).get_current_value() + self.current_trans = PVAdjustable(pv_current).get_current_value() + + def set_target_value(self, *args, **kwargs): + self.set_third_order_toggle() + t = super().set_target_value(*args, **kwargs) + self.wait_for_motion() + return t + + def set_third_order_toggle(self): + self.pvs.third_order_toggle.put(self.third_order) + + def wait_for_motion(self): + sleep(1) # to be certain that motion has started + while True: + if not self.is_moving(): + break # motion is completed + sleep(0.25) + + def is_moving(self): + if self.check_motors: + return any(m.is_moving() for m in self.motors) + + else: + return (self.calculated_trans != self.current_trans) + + + +class LimitedEnergy(PVAdjustable): + + def __init__(self, ID, threshold, wait_time): + self.threshold = threshold + self.wait_time = wait_time + + super().__init__(ID + ":UsrRec.ERY") + + prefix = ID + ":" + self.pvnames.fel_energy = pvn = prefix + "ENY_CALC" # uses the new calculated energy from attenuator IOC + self.pvs.fel_energy = PV(pvn) + + + def set_target_value(self, value): + threshold = self.threshold + wait_time = self.wait_time + + while not value: + value = self.pvs.fel_energy.get() + if value is not None: + if value >= threshold: + break + + print(f"Machine photon energy ({value} eV) is below {threshold} eV - waiting for the machine to recover...") + value = None + sleep(wait_time) + + super().set_target_value(value) # setting the energy is now instantanious, motors will still move afterwards. + print(f"Set attenuator energy to {value} eV") + + +class Foil(PVAdjustable): + + def __init__(self, ID, number,check_motors=True): + """ Foil with possible settings of 0,1,2,3 or 4. + """ + + assert 0 <= int(number) <= 6, "Invalid foil selected" + super().__init__(ID + f":UsrRec.FI{number}") + + self.values = [0, 1, 2, 3, 4] + + prefix = ID + ":" + + self.motors = [Motor(f"{ID}:MOTOR_{i}") for i in [1,2,3,4,5,6]] + self.check_motors = check_motors + + pv_calculated = prefix + "UsrRec.TR1" + pv_current = prefix + "UsrRec.TC1" + + self.calculated_trans = PVAdjustable(pv_calculated).get_current_value() + self.current_trans = PVAdjustable(pv_current).get_current_value() + + def set_target_value(self, *args, **kwargs): + t = super().set_target_value(*args, **kwargs).wait() + self.wait_for_motion() + return t + + def wait_for_motion(self): + sleep(1) # to be certain that motion has started + while True: + if not self.is_moving(): + break # motion is completed + sleep(0.25) + + def is_moving(self): + if self.check_motors: + return any(m.is_moving() for m in self.motors) + + else: + return (self.calculated_trans != self.current_trans) + + diff --git a/exp_temp/mono.py b/exp_temp/mono.py old mode 100644 new mode 100755 diff --git a/experiments/DilSC/TwoTheta_scan.ipynb b/experiments/DilSC/TwoTheta_scan.ipynb old mode 100644 new mode 100755 index 734b835..6b56709 --- a/experiments/DilSC/TwoTheta_scan.ipynb +++ b/experiments/DilSC/TwoTheta_scan.ipynb @@ -193,9 +193,9 @@ ], "metadata": { "kernelspec": { - "display_name": "Python [conda env:conda-slic]", + "display_name": "Python 3 (ipykernel)", "language": "python", - "name": "conda-env-conda-slic-py" + "name": "python3" }, "language_info": { "codemirror_mode": { @@ -207,7 +207,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.18" + "version": "3.9.20" } }, "nbformat": 4, diff --git a/experiments/PMS/PMS.py b/experiments/PMS/PMS.py old mode 100644 new mode 100755 index 70ceda1..7330917 --- a/experiments/PMS/PMS.py +++ b/experiments/PMS/PMS.py @@ -32,3 +32,15 @@ class PP_Shutter: def __repr__(self): tn = typename(self) return f'{tn} "{self.name}" is {self.status}' + + + +def get_Cernox_temperature(): + """ Very improvised """ + import csv + + fname = '/gfa/.mounts/sf_cristallina/applications/devices/CryovacTIC/temperatures_009.csv' + with open(fname) as f: + reader = csv.reader(f) + rows = [row for row in reader] + return float(rows[-1][3]) \ No newline at end of file diff --git a/gp_exp/attocube.py b/gp_exp/attocube.py old mode 100644 new mode 100755 diff --git a/gp_exp/attocube_device_def.py b/gp_exp/attocube_device_def.py old mode 100644 new mode 100755 diff --git a/gp_exp/components.py b/gp_exp/components.py old mode 100644 new mode 100755 diff --git a/gp_exp/jj_device_def.py b/gp_exp/jj_device_def.py old mode 100644 new mode 100755 diff --git a/gp_exp/newport.py b/gp_exp/newport.py old mode 100644 new mode 100755 diff --git a/gp_exp/smaract.py b/gp_exp/smaract.py old mode 100644 new mode 100755 diff --git a/gp_exp/smaract_device_def.py b/gp_exp/smaract_device_def.py old mode 100644 new mode 100755 diff --git a/gp_exp/standa.py b/gp_exp/standa.py old mode 100644 new mode 100755 diff --git a/log/cristallina.log b/log/cristallina.log old mode 100644 new mode 100755 diff --git a/measurement_scripts/Diffractometer_PID_optimisation/Diffractometer_PID_optimisation.ipynb b/measurement_scripts/Diffractometer_PID_optimisation/Diffractometer_PID_optimisation.ipynb new file mode 100755 index 0000000..e36e418 --- /dev/null +++ b/measurement_scripts/Diffractometer_PID_optimisation/Diffractometer_PID_optimisation.ipynb @@ -0,0 +1,278 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 33, + "id": "7a1dc6b7-2324-419f-bece-7e01054d3f5a", + "metadata": {}, + "outputs": [], + "source": [ + "import sys\n", + "sys.path.append('/sf/cristallina/applications/slic/cristallina/crq_exp/')\n", + "from diffractometer import Diffractometer\n", + "from slic.core.adjustable import PVAdjustable\n", + "dm1 = Diffractometer(\"SARES30-CPCL-ECMC02\")\n", + "dm2 = Diffractometer(\"SARES32-GPS\")\n", + "from time import sleep\n", + "import h5py\n", + "import matplotlib.pyplot as plt\n", + "\n", + "from slic.core.acquisition import PVAcquisition\n", + "from slic.core.acquisition.pvacquisition import epics_to_h5_polling\n", + "from sfdata import SFDataFile" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "62731901-0b18-41a7-84ae-c4ada693ce48", + "metadata": {}, + "outputs": [], + "source": [ + "def get_pid_adjustables(axis):\n", + " adj_p = PVAdjustable(axis.ID+'-Ctrl-Kp')\n", + " adj_i = PVAdjustable(axis.ID+'-Ctrl-Ki')\n", + " adj_d = PVAdjustable(axis.ID+'-Ctrl-Kd')\n", + " return [adj_p,adj_i,adj_d]\n", + "\n", + "def get_pids(axis):\n", + " pid_adjs = get_pid_adjustables(axis)\n", + " vals = []\n", + " for pid_adj in pid_adjs:\n", + " vals.append(pid_adj.get())\n", + " return vals\n", + "\n", + "def set_pids(axis,pid_list,check_if_done=True):\n", + " pid_adjs = get_pid_adjustables(axis)\n", + " for i,pid_val in enumerate(pid_list):\n", + " pid_adjs[i].set(pid_val)\n", + " if check_if_done:\n", + " while pid_list != get_pids(axis):\n", + " sleep(0.1)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "ecd2fa4b-7481-4cb3-bfde-ab7ccf4b463e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n"
+      ],
+      "text/plain": []
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/plain": [
+       "\u001b[1m[\u001b[0m\u001b[1;36m4.0\u001b[0m, \u001b[1;36m0.0\u001b[0m, \u001b[1;36m0.0\u001b[0m\u001b[1m]\u001b[0m"
+      ]
+     },
+     "execution_count": 3,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "axis = dm1.tr_x\n",
+    "\n",
+    "set_pids(axis,[4,0,0])\n",
+    "get_pids(axis)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 22,
+   "id": "cf984360-3007-412e-bf85-a5c3490756c1",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "epics_to_h5_polling('test.h5',[dm1.tr_x.ID],n_pulses=100,wait_time=0.1)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 30,
+   "id": "e6878c1c-6a68-441c-ba3e-087e78a83608",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "with h5py.File('test.h5','r') as f:\n",
+    "    dat = f[axis.ID][()]"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 34,
+   "id": "35bcaa94-7efc-48c9-bac8-8c3b161af57c",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/html": [
+       "
\n"
+      ],
+      "text/plain": []
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/plain": [
+       "\u001b[1m[\u001b[0m\u001b[1m<\u001b[0m\u001b[1;95mmatplotlib.lines.Line2D\u001b[0m\u001b[39m object at \u001b[0m\u001b[1;36m0x7f969d2f9f40\u001b[0m\u001b[1m>\u001b[0m\u001b[1m]\u001b[0m"
+      ]
+     },
+     "execution_count": 34,
+     "metadata": {},
+     "output_type": "execute_result"
+    },
+    {
+     "data": {
+      "text/html": [
+       "
\n"
+      ],
+      "text/plain": []
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "image/png": "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goICbd++XdKPkSVJPp9PS5cu1fLly7V37159+umneuutt9w5NTU1ioyM1AcffKBZs2bV2sPr9crr9dZ7PgAAoHVocEj5fD75fL47zhsxYoSCwaCOHTumoUOHSpKOHj2qYDDoBs+tevXqJb/fr+zsbA0ePFiSVFlZqQMHDmj16tWSpCFDhigqKkrZ2dmaNGmSJOnSpUs6deqU1qxZE/beO3bsUHl5ubt3bm6uZs2apYMHD6pPnz6Sfvys1U9/E/Avf/mLVq9ercOHD+v+++8P/9AAAECr1OCQCldiYqImTJigOXPm6P3335ckzZ07V8nJyerXr587r3///srIyNCzzz4rj8ej1NRUrVy5Un379lXfvn21cuVKtW/fXlOnTpUkOY6j2bNna/HixerSpYs6d+6sV199VQMHDtTYsWPD3vtmLN109epVd+3NvxWVmJgYMuf48eOKiIjQgAEDGvm0AABAS9RkISVJ27Zt08KFC93fsJs4caLWr18fMufs2bMKBoPu10uWLFF5eblefPFFFRcXa9iwYdq9e7diYmLcOW+//bYiIyM1adIklZeXa8yYMdqyZYvatGnToL0BAADuhsfc/HAQmkRpaakcx1EwGAz7N/8AAEDzCvf9m39rDwAAwBIhBQAAYImQAgAAsERIAQAAWCKkAAAALBFSAAAAlggpAAAAS4QUAACAJUIKAADAEiEFAABgiZACAACwREgBAABYIqQAAAAsEVIAAACWCCkAAABLhBQAAIAlQgoAAMASIQUAAGCJkAIAALBESAEAAFgipAAAACwRUgAAAJYIKQAAAEuEFAAAgCVCCgAAwBIhBQAAYImQAgAAsERIAQAAWCKkAAAALBFSAAAAlggpAAAAS4QUAACAJUIKAADAEiEFAABgiZACAACwREgBAABYIqQAAAAsEVIAAACWCCkAAABLhBQAAIAlQgoAAMASIQUAAGCJkAIAALBESAEAAFgipAAAACwRUgAAAJYIKQAAAEuEFAAAgCVCCgAAwBIhBQAAYImQAgAAsERIAQAAWCKkAAAALBFSAAAAlggpAAAAS4QUAACAJUIKAADAEiEFAABgKbK5L6C1M8ZIkkpLS5v5SgAAQLhuvm/ffB+vCyHVxMrKyiRJ3bp1a+YrAQAADVVWVibHcep83mPulFq4KzU1Nfr2228VExMjj8fTqK9dWlqqbt26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",
+      "text/plain": [
+       "\u001b[1m<\u001b[0m\u001b[1;95mFigure\u001b[0m\u001b[39m size 64\u001b[0m\u001b[1;36m0x480\u001b[0m\u001b[39m with \u001b[0m\u001b[1;36m1\u001b[0m\u001b[39m Axes\u001b[0m\u001b[1m>\u001b[0m"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "name": "stderr",
+     "output_type": "stream",
+     "text": [
+      "CA.Client.Exception...............................................\n",
+      "    Warning: \"Virtual circuit disconnect\"\n",
+      "    Context: \"sf-test-ecat01.psi.ch:37849\"\n",
+      "    Source File: ../cac.cpp line 1237\n",
+      "    Current Time: Wed Jan 29 2025 09:32:18.185970708\n",
+      "..................................................................\n",
+      "CA.Client.Exception...............................................\n",
+      "    Warning: \"Virtual circuit disconnect\"\n",
+      "    Context: \"sf-test-ecat01.psi.ch:33519\"\n",
+      "    Source File: ../cac.cpp line 1237\n",
+      "    Current Time: Wed Jan 29 2025 10:25:21.921579794\n",
+      "..................................................................\n",
+      "CA.Client.Exception...............................................\n",
+      "    Warning: \"Virtual circuit disconnect\"\n",
+      "    Context: \"sf-test-ecat01.psi.ch:5064\"\n",
+      "    Source File: ../cac.cpp line 1237\n",
+      "    Current Time: Thu Jan 30 2025 09:25:15.723415260\n",
+      "..................................................................\n",
+      "CA.Client.Exception...............................................\n",
+      "    Warning: \"Virtual circuit disconnect\"\n",
+      "    Context: \"sf-test-ecat01.psi.ch:5064\"\n",
+      "    Source File: ../cac.cpp line 1237\n",
+      "    Current Time: Thu Jan 30 2025 10:56:01.344575603\n",
+      "..................................................................\n",
+      "CA.Client.Exception...............................................\n",
+      "    Warning: \"Virtual circuit disconnect\"\n",
+      "    Context: \"sf-test-ecat01.psi.ch:5064\"\n",
+      "    Source File: ../cac.cpp line 1237\n",
+      "    Current Time: Thu Jan 30 2025 10:57:54.795940675\n",
+      "..................................................................\n",
+      "CA.Client.Exception...............................................\n",
+      "    Warning: \"Virtual circuit unresponsive\"\n",
+      "    Context: \"sf-test-ecat01.psi.ch:5064\"\n",
+      "    Source File: ../tcpiiu.cpp line 926\n",
+      "    Current Time: Thu Jan 30 2025 11:24:40.816502886\n",
+      "..................................................................\n",
+      "Unexpected problem with CA circuit to server \"sf-test-ecat01.psi.ch:5064\" was \"Connection reset by peer\" - disconnecting\n",
+      "CA.Client.Exception...............................................\n",
+      "    Warning: \"Virtual circuit disconnect\"\n",
+      "    Context: \"sf-test-ecat01.psi.ch:5064\"\n",
+      "    Source File: ../cac.cpp line 1237\n",
+      "    Current Time: Thu Jan 30 2025 11:27:58.954815616\n",
+      "..................................................................\n",
+      "CA.Client.Exception...............................................\n",
+      "    Warning: \"Virtual circuit unresponsive\"\n",
+      "    Context: \"sf-test-ecat01.psi.ch:5064\"\n",
+      "    Source File: ../tcpiiu.cpp line 926\n",
+      "    Current Time: Thu Jan 30 2025 11:36:30.771356010\n",
+      "..................................................................\n",
+      "Unexpected problem with CA circuit to server \"sf-test-ecat01.psi.ch:5064\" was \"Connection reset by peer\" - disconnecting\n",
+      "CA.Client.Exception...............................................\n",
+      "    Warning: \"Virtual circuit disconnect\"\n",
+      "    Context: \"sf-test-ecat01.psi.ch:5064\"\n",
+      "    Source File: ../cac.cpp line 1237\n",
+      "    Current Time: Thu Jan 30 2025 11:39:35.272507679\n",
+      "..................................................................\n",
+      "CA.Client.Exception...............................................\n",
+      "    Warning: \"Virtual circuit disconnect\"\n",
+      "    Context: \"SARES30-CVME-CRISTA1.psi.ch:5064\"\n",
+      "    Source File: ../cac.cpp line 1237\n",
+      "    Current Time: Thu Jan 30 2025 13:57:33.332400491\n",
+      "..................................................................\n",
+      "CA.Client.Exception...............................................\n",
+      "    Warning: \"Virtual circuit disconnect\"\n",
+      "    Context: \"SARES30-CVME-CRISTA1.psi.ch:5064\"\n",
+      "    Source File: ../cac.cpp line 1237\n",
+      "    Current Time: Thu Jan 30 2025 14:00:49.683000572\n",
+      "..................................................................\n",
+      "CA.Client.Exception...............................................\n",
+      "    Warning: \"Virtual circuit unresponsive\"\n",
+      "    Context: \"SARES30-CVME-CRISTA1.psi.ch:5064\"\n",
+      "    Source File: ../tcpiiu.cpp line 926\n",
+      "    Current Time: Wed Feb 05 2025 21:53:51.102610757\n",
+      "..................................................................\n",
+      "Unexpected problem with CA circuit to server \"SARES30-CVME-CRISTA1.psi.ch:5064\" was \"No route to host\" - disconnecting\n",
+      "CA.Client.Exception...............................................\n",
+      "    Warning: \"Virtual circuit disconnect\"\n",
+      "    Context: \"SARES30-CVME-CRISTA1.psi.ch:5064\"\n",
+      "    Source File: ../cac.cpp line 1237\n",
+      "    Current Time: Wed Feb 05 2025 22:09:43.401250908\n",
+      "..................................................................\n"
+     ]
+    }
+   ],
+   "source": [
+    "plt.figure()\n",
+    "plt.plot(dat)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "651d89df-a972-410c-a1e7-7b8b5ba6120b",
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3 (ipykernel)",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.9.20"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/measurement_scripts/Diffractometer_PID_optimisation/test.h5 b/measurement_scripts/Diffractometer_PID_optimisation/test.h5
new file mode 100755
index 0000000..31daae6
Binary files /dev/null and b/measurement_scripts/Diffractometer_PID_optimisation/test.h5 differ
diff --git a/measurement_scripts/DilSc_commisioning_pulses.py b/measurement_scripts/DilSc_commisioning_pulses.py
old mode 100644
new mode 100755
diff --git a/measurement_scripts/DilSc_frappy_client.py b/measurement_scripts/DilSc_frappy_client.py
old mode 100644
new mode 100755
diff --git a/measurement_scripts/DilSc_meander_scripts.py b/measurement_scripts/DilSc_meander_scripts.py
old mode 100644
new mode 100755
diff --git a/measurement_scripts/Untitled.ipynb b/measurement_scripts/Untitled.ipynb
old mode 100644
new mode 100755
index 5904e57..41c30e9
--- a/measurement_scripts/Untitled.ipynb
+++ b/measurement_scripts/Untitled.ipynb
@@ -30,9 +30,7 @@
    "cell_type": "code",
    "execution_count": 7,
    "id": "d108c981",
-   "metadata": {
-    "scrolled": false
-   },
+   "metadata": {},
    "outputs": [
     {
      "data": {
@@ -1166,7 +1164,7 @@
    "name": "python",
    "nbconvert_exporter": "python",
    "pygments_lexer": "ipython3",
-   "version": "3.9.13"
+   "version": "3.9.20"
   }
  },
  "nbformat": 4,
diff --git a/measurement_scripts/__pycache__/DilSc_commisioning_pulses.cpython-39.pyc b/measurement_scripts/__pycache__/DilSc_commisioning_pulses.cpython-39.pyc
old mode 100644
new mode 100755
diff --git a/measurement_scripts/__pycache__/DilSc_meander_scripts.cpython-39.pyc b/measurement_scripts/__pycache__/DilSc_meander_scripts.cpython-39.pyc
old mode 100644
new mode 100755
diff --git a/measurement_scripts/__pycache__/inprints.cpython-39.pyc b/measurement_scripts/__pycache__/inprints.cpython-39.pyc
old mode 100644
new mode 100755
diff --git a/measurement_scripts/hole_drilling.py b/measurement_scripts/hole_drilling.py
old mode 100644
new mode 100755
diff --git a/measurement_scripts/inprints.py b/measurement_scripts/inprints.py
old mode 100644
new mode 100755
diff --git a/measurement_scripts/move_newport.py b/measurement_scripts/move_newport.py
old mode 100644
new mode 100755
diff --git a/mx/knife_edge_prototype.py b/mx/knife_edge_prototype.py
old mode 100644
new mode 100755
diff --git a/mx/mx_adjustables.py b/mx/mx_adjustables.py
old mode 100644
new mode 100755
index b6b2588..9c10a45
--- a/mx/mx_adjustables.py
+++ b/mx/mx_adjustables.py
@@ -1,22 +1,27 @@
 from slic.core.device.simpledevice import SimpleDevice
 from slic.devices.general.motor import Motor
 
+# hve positioners
+hve_mot_v = Motor("SARES30-MOBI1:MOT_1")
+hve_mot_h1 = Motor("SARES30-MOBI1:MOT_2")
+hve_mot_h2 = Motor("SARES30-MOBI1:MOT_3")
+
 # collimator
-mx_coll_x = Motor("SARES30-SMX:MCS1")
-mx_coll_y = Motor("SARES30-SMX:MCS2")
+coll_x = Motor("SARES30-SMX:MCS1")
+coll_y = Motor("SARES30-SMX:MCS2")
 
 # post-tube
-mx_pt_x1 = Motor("SARES30-SMX:MCS4")
-mx_pt_x2 = Motor("SARES30-SMX:MCS5")
-mx_pt_y1 = Motor("SARES30-SMX:MCS6")
-mx_pt_y2 = Motor("SARES30-SMX:MCS7")
-mx_pt_z = Motor("SARES30-SMX:MCS8")
+pt_x1 = Motor("SARES30-SMX:MCS4")
+pt_x2 = Motor("SARES30-SMX:MCS5")
+pt_y1 = Motor("SARES30-SMX:MCS6")
+pt_y2 = Motor("SARES30-SMX:MCS7")
+pt_z = Motor("SARES30-SMX:MCS8")
 
 # post-tube
-mx_detector_z = Motor("SAR-EXPMX:MOT_DET_Z")
+detector_z = Motor("SAR-EXPMX:MOT_DET_Z")
 
 # post-tube
-mx_backlight = Motor("SAR-EXPMX:MOT_BLGT")
+backlight = Motor("SAR-EXPMX:MOT_BLGT")
 
 # fast stage
 mx_fast_x = Motor("SAR-EXPMX:MOT_FX")
diff --git a/mx/mx_experiment.py b/mx/mx_experiment.py
old mode 100644
new mode 100755
index 806cdc8..da74745
--- a/mx/mx_experiment.py
+++ b/mx/mx_experiment.py
@@ -1,19 +1,104 @@
-# Moved from main cristallina.py here temporarily
-mxdaq = SFAcquisition(
-    instrument,
-    pgroup,
-    default_channels=bs_channels,
-    default_pvs=pvs,
-    default_detectors=detectors_MX,
-    rate_multiplicator=1,
-    append_user_tag_to_data_dir=True
+#!/usr/bin/env python
+import sys
+import os
+from loguru import logger
+
+# at the moment this allows us to group the subdirectories as modules easily
+# TODO: a more general way would be to have this cristallina as a installed package
+sys.path.insert(0, os.path.expanduser("/sf/cristallina/applications/slic/cristallina"))
+
+def setup_general_logging():
+    """Setup logging to console and files in both the snapshots and
+    the respective pgroup.
+    """
+
+    logger.remove()
+    logger.add(
+        sys.stderr,
+        format="{time:YYYY-MM-DD at HH:mm:ss} | {level} | {message}",
+        level="INFO",
+    )
+    logger.info("Loading started.")
+
+    # create file handler which logs
+    try:
+        logger.add(
+            "/sf/cristallina/applications/beamline/snapshots/slic_logs/cristallina_mx.log",
+            format="{time:YYYY-MM-DD at HH:mm:ss} | {level} | {message}",
+            level="DEBUG",
+            rotation="1 week",
+        )
+        logger.info("Logging to snapshots.")
+    except PermissionError as e:
+        logger.warning("Cannot write log file to snapshots.")
+        logger.warning(e)
+
+
+def setup_logging_pgroup(pgroup, level="INFO"):
+    try:
+        logger.add(
+            f"/sf/cristallina/data/{pgroup}/scratch/slic.log",
+            format="{time:YYYY-MM-DD at HH:mm:ss} | {level} | {message}",
+            level=level,
+            rotation="1 week",
+        )
+        logger.info(f"Logging to pgroup {pgroup}.")
+    except PermissionError as e:
+        logger.warning(f"Cannot write log file to pgroup {pgroup}.")
+
+
+# We setup the logging before going further so that
+# other modules can write startup messages into the log file.
+setup_general_logging()
+
+from slic.gui import GUI
+from slic.core.adjustable import Adjustable, PVAdjustable, DummyAdjustable
+from slic.core.acquisition import SFAcquisition, PVAcquisition
+from slic.core.condition import PVCondition
+from slic.core.scanner import Scanner
+
+
+from slic.devices.general.motor import Motor
+
+from slic.utils import devices, Marker, as_shortcut, snapshot
+from slic.utils import Channels, Config, Elog, Screenshot, PV
+from slic.core.acquisition.fakeacquisition import FakeAcquisition
+
+
+print( os.getcwd() )
+
+from channels.bs_channels import (
+    detectors_MX,
+    bs_channels,
+    camera_channels,
 )
 
-mxscan = Scanner(default_acquisitions=[mxdaq], condition=check_intensity_gas_monitor)
+from channels.pv_channels import pv_channels
 
-mxgui = GUI(mxscan, show_goto=True, show_spec=False, show_scan=False, show_scan2D=False, show_run=False, show_static=False, show_sfx=True, start_tab="sfx")
 
-############## MX motors ##############
+################# DEVICES #################
+dummy = DummyAdjustable(units="au")
+
+from devices.knife_edge import KnifeEdge
+# from devices.standa import standa
+# from devices.newport import newport
+
+from beamline.components import (
+    upstream_attenuator,
+    attenuator,
+    pp_shutter,
+    pulsepicker,
+    alignment_laser,
+    pbps113,
+    pbps149,
+)
+
+from systems.components import cta
+
+# MX adajustables
+from slic.core.device.simpledevice import SimpleDevice
+from slic.devices.general.motor import Motor
+
 # hve positioners
 hve_mot_v = Motor("SARES30-MOBI1:MOT_1")
 hve_mot_h1 = Motor("SARES30-MOBI1:MOT_2")
@@ -36,18 +121,71 @@ detector_z = Motor("SAR-EXPMX:MOT_DET_Z")
 # post-tube
 backlight = Motor("SAR-EXPMX:MOT_BLGT")
 
+# fast stage
+mx_fast_x = Motor("SAR-EXPMX:MOT_FX")
+mx_fast_y = Motor("SAR-EXPMX:MOT_FY")
+
+
+################# DAQ Setup #################
+instrument = "cristallina"
+#pgroup = "p21734"
+pgroup = "p22215"
+
+# setup pgroup specific logger
+setup_logging_pgroup(pgroup)
+
+# Moved from main cristallina.py here temporarily
+mxdaq = SFAcquisition(
+    instrument,
+    pgroup,
+    default_channels=bs_channels,
+    default_pvs=pv_channels,
+    default_detectors=detectors_MX,
+    rate_multiplicator=1,
+    append_user_tag_to_data_dir=True
+)
+
+# There is a new EPICS buffer, so the archiver is no longer used. This makes sure we are taking PVs from the right place.
+try:
+    mxdaq.update_config_pvs()
+except Exception as e:
+    logger.warning(f"error updating config pvs for mxdaq: {e}")
+
+check_intensity_gas_monitor = PVCondition(
+    "SARFE10-PBPG050:PHOTON-ENERGY-PER-PULSE-US",
+    vmin=4,
+    vmax=2000,
+    wait_time=0.5,
+    required_fraction=0.8,
+)
+
+mxscan = Scanner(default_acquisitions=[mxdaq], condition=check_intensity_gas_monitor)
+
+mxgui = GUI(mxscan, show_goto=True, show_spec=False, show_scan=True, show_scan2D=False, show_run=False, show_static=False, show_sfx=True, start_tab="sfx")
+
+#############################################
+################# DAQ Setup #################
+
 ############## in positions ##############
-coll_in_pos_x, coll_in_pos_y = 9.51, -5.62 
+coll_in_pos_x, coll_in_pos_y = 9.50, 1.38
 backlight_in = -30000
-detector_in_pos = 1
-pt_in_pos_x1, pt_in_pos_x2, pt_in_pos_y1, pt_in_pos_y2, pt_in_pos_z = -3.039, -2.637, 8.904, 13.857, 0
+detector_in_pos = 116
+pt_in_pos_x1 = -4.301
+pt_in_pos_x2 = -4.501
+pt_in_pos_y1 = 10.996
+pt_in_pos_y2 = 10.664
+pt_in_pos_z = 0.5
 
 
-############## out positions ##############
-coll_out_pos_x, coll_out_pos_y = -14.5, 1.32
+############## out positions ############## 
+coll_out_pos_x, coll_out_pos_y = -12, 1.38
 backlight_out = 1000
-detector_out_pos = 180
-pt_out_pos_x1, pt_out_pos_x2, pt_out_pos_y1, pt_out_pos_y2, pt_out_pos_z = 5.960, 6.361, -15.096, -10.143, -8 
+detector_out_pos = 220
+pt_out_pos_x1 = 4.8
+pt_out_pos_x2 = 4.6
+pt_out_pos_y1 = -12.8
+pt_out_pos_y2 = -13.1
+pt_out_pos_z = -8 
 
 @as_shortcut
 def a_data_collection():
@@ -56,52 +194,61 @@ def a_data_collection():
     backlight.set( backlight_out ).wait()
 
     # post-tube in
+    
     pt_x1_in = pt_x1.set( pt_in_pos_x1 ) # this runs in parallel
     pt_x2_in = pt_x2.set( pt_in_pos_x2 ) # this runs in parallel
     pt_y1_in = pt_y1.set( pt_in_pos_y1 ) # this runs in parallel
     pt_y2_in = pt_y2.set( pt_in_pos_y2 ) # this runs in parallel
-    pt_z_in = pt_z.set( pt_in_pos_z ) # this runs in parallel
-    for t in (pt_x1_in, pt_x2_in, pt_y1_in, pt_y2_in, pt_z_in): # this waits for all of them to be done!
+    
+    for t in (pt_x1_in, pt_x2_in, pt_y1_in, pt_y2_in): # this waits for all of them to be done!
+        logger.info(f"waiting for post-tube to move in")
         t.wait()
+    
+    pt_z_in = pt_z.set( pt_in_pos_z ).wait() # this no longer runs in parallel
 
     # collimator in
     cx_in = coll_x.set( coll_in_pos_x ) # this runs in parallel
     cy_in = coll_y.set( coll_in_pos_y ) # this runs in parallel
     for t in (cx_in, cy_in): # this waits for all of them to be done!
+        logger.info(f"waiting for collimator to move in")
         t.wait()
 
     # detector in
+    logger.info(f"waiting for detector to move in")
     detector_z.set( detector_in_pos ).wait() # this runs in parallel
 
 @as_shortcut
 def b_sample_alignment():
 
     # detector out
+    logger.info(f"waiting for detector to move out")
     detector_z.set( detector_out_pos ).wait()
 
     # collimator out
     cx_out = coll_x.set( coll_out_pos_x ) # this runs in parallel
     cy_out = coll_y.set( coll_out_pos_y ) # this runs in parallel
     for t in (cx_out, cy_out): # this waits for all of them to be done!
+        logger.info(f"waiting for collimator to move out")
         t.wait()
 
     # post-tube out
+    pt_z_out = pt_z.set( pt_out_pos_z ).wait() # this runs in parallel
     pt_x1_out = pt_x1.set( pt_out_pos_x1 ) # this runs in parallel
     pt_x2_out = pt_x2.set( pt_out_pos_x2 ) # this runs in parallel
     pt_y1_out = pt_y1.set( pt_out_pos_y1 ) # this runs in parallel
     pt_y2_out = pt_y2.set( pt_out_pos_y2 ) # this runs in parallel
-    pt_z_out = pt_z.set( pt_out_pos_z ) # this runs in parallel
     for t in (pt_x1_out, pt_x2_out, pt_y1_out, pt_y2_out, pt_z_out): # this waits for all of them to be done!
+        logger.info(f"waiting for post_tube to move out")
         t.wait()
 
 @as_shortcut
 def c_backlight_in():
 
     # safety logic for backlight in
-    if coll_x.get() < 0 and pt_y1.get() < 0 and detector_z.get() > 20:
+    if round( coll_x.get(), 2 ) == coll_out_pos_x and round( pt_y1.get(), 2 ) == pt_out_pos_y1 and round( detector_z.get(), 2 ) > detector_in_pos:
          backlight.set( backlight_in ).wait()
     else:
-        print( "devises are in the way" )
+        logger.warning( "some devices are in the way" )
 
 @as_shortcut
 def ca_backlight_out():
@@ -120,7 +267,7 @@ def post_tube_in():
         for t in (pt_x1_in, pt_x2_in, pt_y1_in, pt_y2_in, pt_z_in): # this waits for all of them to be done!
             t.wait()
     else:
-        print( "devises are in the way" )
+        logger.warning( "devices are in the way" )
 
 @as_shortcut
 def post_tube_out():
@@ -135,13 +282,14 @@ def post_tube_out():
         for t in (pt_x1_out, pt_x2_out, pt_y1_out, pt_y2_out, pt_z_out): # this waits for all of them to be done!
             t.wait()
     else:
-        print( "detector needs to move" )
+        logger.warning( "detector needs to move" )
 
 @as_shortcut
 def coll_in():
     cx_in = coll_x.set( coll_in_pos_x ) # this runs in parallel
     cy_in = coll_y.set( coll_in_pos_y ) # this runs in parallel
     for t in (cx_in, cy_in): # this waits for all of them to be done!
+        logger.info(f"waiting for collimator to move in")
         t.wait()
 
 @as_shortcut
@@ -149,6 +297,7 @@ def coll_out():
     cx_out = coll_x.set( coll_out_pos_x ) # this runs in parallel
     cy_out = coll_y.set( coll_out_pos_y ) # this runs in parallel
     for t in (cx_out, cy_out): # this waits for all of them to be done!
+        logger.info(f"waiting for collimator to move out")
         t.wait()
 
 @as_shortcut
@@ -156,12 +305,14 @@ def detector_in():
 
     # safety logic for detector in
     if backlight.get() > 0 and pt_y1.get() > 8:
+        logger.info(f"waiting for detector to move in")
         detector_z.set( detector_in_pos ).wait() # this runs in parallel
     else:
-        print( "devises are in the way" )
+        logger.warning( "devices are in the way" )
 
 @as_shortcut
 def detector_out():
+    logger.info(f"waiting for detector to move out")
     detector_z.set( detector_out_pos ).wait() # this runs in parallel
 
 
diff --git a/pgroups.py b/pgroups.py
old mode 100644
new mode 100755
index 24db1dc..785febf
--- a/pgroups.py
+++ b/pgroups.py
@@ -39,5 +39,11 @@ pgroup_scratch = "p19150"  # Scratch
 # pgroup = "p21977"   # CrQ - Dilsc - LiErF4
 
 # pgroup = "p21981"   # CrMX JFJ commissioning and other related detector bullocks
-pgroup = "p22198"   # CrMX Fromme - 2024-10-25
+# pgroup = "p22198"   # CrMX Fromme - 2024-10-25
 
+# pgroup = "p22214"   # CrQ in-house DilSc 2024-11-15
+# pgroup = "p22199"   # CrQ commissioning PuMa 2024-11-22
+
+pgroup = "p22259"  # Cr-Bl commissioning Jan 2025
+
+# pgroup = "p22226"  # CrMX user Nogly 2025-02-21
diff --git a/scratch/acquisition_setup.py b/scratch/acquisition_setup.py
old mode 100644
new mode 100755
diff --git a/spreadsheet.py b/spreadsheet.py
old mode 100644
new mode 100755
diff --git a/stand/client.py b/stand/client.py
old mode 100644
new mode 100755
diff --git a/stand/time.py b/stand/time.py
old mode 100644
new mode 100755
diff --git a/systems/README.md b/systems/README.md
old mode 100644
new mode 100755
diff --git a/systems/components.py b/systems/components.py
old mode 100644
new mode 100755
diff --git a/templates/cool_motor.py b/templates/cool_motor.py
old mode 100644
new mode 100755