From 7b9f3fa9b758a85c0be2ca05d4a67ad390876acf Mon Sep 17 00:00:00 2001 From: Alexander Steppke Date: Wed, 13 Dec 2023 14:25:59 +0100 Subject: [PATCH] post-p21592 HVE commissioning cleanup --- beamline/undulator.py | 20 ++++- channels/bs_channels.py | 58 +++++++++++-- channels/pv_channels.py | 4 +- cristallina.py | 65 ++++++++++----- devices/diffractometer.py | 22 +++-- exp_temp/channels.py | 52 ++++++++++-- mx/mx_experiment.py | 168 ++++++++++++++++++++++++++++++++++++++ 7 files changed, 346 insertions(+), 43 deletions(-) create mode 100644 mx/mx_experiment.py diff --git a/beamline/undulator.py b/beamline/undulator.py index 19e8905..83f126d 100644 --- a/beamline/undulator.py +++ b/beamline/undulator.py @@ -1,4 +1,6 @@ +import time from time import sleep + import numpy as np from epics import PV @@ -21,7 +23,7 @@ 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 = -62 # eV +energy_offset = -59 # eV # move the PSSS motor according to the energy @@ -390,9 +392,19 @@ class ScalerEK: def get_machine_n_und_ref(): - res = PVEnumAdjustable("SARUN:REF-UND").get() - if not res.startswith("SARUN"): - return None + + 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: diff --git a/channels/bs_channels.py b/channels/bs_channels.py index 832448b..0dc2d47 100644 --- a/channels/bs_channels.py +++ b/channels/bs_channels.py @@ -28,14 +28,20 @@ detectors = [ # ) detectors_with_config = DetectorConfig(detectors) -detectors_with_config["JF16T03V01"]['save_dap_results'] = True -detectors_with_config["JF16T03V01"]['remove_raw_files'] = True +#detectors_with_config["JF16T03V01"]['save_dap_results'] = True +#detectors_with_config["JF16T03V01"]['remove_raw_files'] = True # detectors_with_config["JF16T03V01"]['disabled_modules'] = [0, 1] # bottom module:0, middle module:1, top module:2 +detectors_MX = DetectorConfig() +detectors_MX.add("JF17T16V01", adc_to_energy=True, compression=True, crystfel_lists_laser=True, double_pixels_action="mask", factor=12.08, remove_raw_files=True, save_dap_results=True, geometry=False) + + + 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:FPICTURE", # SwissMX OAV camera picture + "SARES30-CAMS156-SMX-OAV.jet_projection", #SWISSMX oav jET PROJECTION # "SARES30-CAMS156-XE:FPICTURE", # X-ray eye ] @@ -120,7 +126,24 @@ channels_RF = [ "S30CB14-RLLE-DSP:AMPLT-VS", ] -channels_Xeye = ["SARES30-CAMS156-XE:intensity"] +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", + ] ###################### # PBPS053 @@ -143,8 +166,22 @@ channels_PSSS059 = [ "SARFE10-PSSS059:SPECTRUM_Y_SUM", "SARFE10-PSSS059:SPECTRUM_X", "SARFE10-PSSS059:SPECTRUM_Y", - #"SARFE10-PSSS059:FPICTURE", + # "SARFE10-PSSS059:FPICTURE", "SARFE10-PSSS059:processing_parameters", +# Experimental large bandwith camera + "SARFE10-PSSS059-LB:FIT-BRT", +"SARFE10-PSSS059-LB:FIT-COM", +"SARFE10-PSSS059-LB:FIT-FWHM", +"SARFE10-PSSS059-LB:FIT-RES", +"SARFE10-PSSS059-LB:FIT-RMS", +"SARFE10-PSSS059-LB:SPECT-COM", +"SARFE10-PSSS059-LB:SPECT-RES", +"SARFE10-PSSS059-LB:SPECT-RMS", +"SARFE10-PSSS059-LB:SPECTRUM_X", +"SARFE10-PSSS059-LB:SPECTRUM_Y", +"SARFE10-PSSS059-LB:SPECTRUM_Y_SUM", +"SARFE10-PSSS059-LB:processing_parameters", +"SARFE10-PSSS059-LB:FPICTURE", ] ################################### @@ -154,7 +191,7 @@ channels_Bernina = [ "SAROP21-PBPS103:INTENSITY", "SAROP21-PBPS103:XPOS", "SAROP21-PBPS103:YPOS", - "SAROP21-PPRM113:FPICTURE", + #"SAROP21-PPRM113:FPICTURE", "SAROP21-PPRM113:intensity", "SAROP21-PPRM113:x_fit_mean", "SAROP21-PPRM113:y_fit_mean", @@ -275,7 +312,10 @@ channels_digitizer = [ ####################### # Other BS channels that we sometimes use -channels_other = [] +channels_other = [ + # "SARFE10-PPRM053:FPICTURE", # TODO: Test if this works here + # "SARFE10-PPRM064:FPICTURE", # TODO: Test if this works here + ] bs_channels = ( camera_channels @@ -289,10 +329,10 @@ bs_channels = ( # + channels_PPRM113 + channels_PBPS149 # + channels_PBPS149_waveforms - # + channels_PPRM150 + + channels_PPRM150 # only if screen is inserted + channels_EVR # + channels_digitizer - # + channels_other + + channels_other ) bs_channels_OAPU107_scan = ( diff --git a/channels/pv_channels.py b/channels/pv_channels.py index 5dd8aab..008b1d0 100644 --- a/channels/pv_channels.py +++ b/channels/pv_channels.py @@ -8,7 +8,7 @@ # Machine pvs_machine = [ "SARCL02-MBND100:P-READ", # Predicted bunch energy - "SARUN:FELPHOTENE.VAL", # Predicted photon energy from machine settings + "SARUN:FELPHOTENE.VAL", # Predicted photon energy from machine settings "SARFE10-PBPG050:PHOTON-ENERGY-PER-PULSE-AVG.VAL", # Average pulse energy from the gas detector ] @@ -169,6 +169,8 @@ pvs_OATT053 = [ # Beam profile monitor PPRM053 pvs_PPRM053 = [ "SARFE10-PPRM053:MOTOR_PROBE.RBV", + #"SARFE10-PPRM053:FPICTURE", + #"SARFE10-PPRM064:FPICTURE", # TODO move to correct place ] ################### diff --git a/cristallina.py b/cristallina.py index e2c8c39..69aa671 100644 --- a/cristallina.py +++ b/cristallina.py @@ -69,6 +69,7 @@ from slic.devices.timing.events import CTASequencer from channels.bs_channels import ( detectors, detectors_with_config, + detectors_MX, bs_channels, camera_channels, ) @@ -78,23 +79,6 @@ from spreadsheet import overview # from channels_minimal import detectors_min, channels_min, pvs_min from devices.pp_shutter import PP_Shutter - -# TODO: requires the stand client, need small howto how to start and configure or let it run all the time -# from slic.core.acquisition.spreadsheet import 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() - logger.info("Connected to stand server") -except Exception as error: - # catching with a broad net because different connection errors can occur. - logger.warning(f"Cannot connect to stand server on {stand_host}.") - -# spreadsheet = Spreadsheet(adjs, placeholders=PLACEHOLDERS, host="127.0.0.1", port=8080)) - ################# DEVICES ################# dummy = DummyAdjustable(units="au") @@ -109,6 +93,7 @@ OWIS = Motor("SARES30-MOBI1:MOT_6") # small OWIS linear stage attenuator = Attenuator("SAROP31-OATA150", description="Cristallina attenuator OATA150") +upstream_attenuator = Attenuator("SARFE10-OATT053", description="Aramis attenuator OATT053") def test_attenuator(): tfundamental = attenuator.get_transmission() @@ -189,6 +174,47 @@ from devices.diffractometer import Diffractometer diffractometer = Diffractometer("diffractometer") + +################# Stand setup ################## + +# TODO: requires the stand client, need small howto how to start and configure or let it run all the time +from slic.core.acquisition.spreadsheet import Spreadsheet + +# setup spreadsheet for transmission to stand +spreadsheet = Spreadsheet( + { + "Transmission" : attenuator.trans1st, + "Upstream Transmission": upstream_attenuator.trans1st, + "Energy_setpoint" : undulators, + "Energy_offset": undulator.energy_offset, + "TD": diffractometer.td, + "TRX": diffractometer.tr_x, + "TRY": diffractometer.tr_y, + "TRXBASE": diffractometer.trx_base, + "TRYBASE": diffractometer.try_base, + "THETA": diffractometer.theta, + "TWOTHETA": diffractometer.twotheta, + }, + + placeholders=("comment", "sample"), + host="saresc-vcons-02.psi.ch", + port=9090, +) + +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() + logger.info("Connected to stand server") +except Exception as error: + # catching with a broad net because different connection errors can occur. + logger.warning(f"Cannot connect to stand server on {stand_host}. Disabling spreadsheet.") + spreadsheet = None + +# spreadsheet = Spreadsheet(adjs, placeholders=PLACEHOLDERS, host="127.0.0.1", port=8080)) + ################# DAQ Setup ################# instrument = "cristallina" @@ -215,7 +241,8 @@ instrument = "cristallina" # pgroup = "p21516" # Beamline commissioning September 26-27, Noveber 7 2023 # pgroup = "p21563" # Dil-Sc / diffractometer / tilted bunch / LiErF4 (/ TmVO4) -pgroup = "p21569" # Dil-Sc / diffractometer / tilted bunch / LiErF4 (/ TmVO4), November 17- +# pgroup = "p21569" # Dil-Sc / diffractometer / tilted bunch / LiErF4 (/ TmVO4), November 17- +# pgroup = "p21592" # HVE commissioning # setup pgroup specific logger setup_logging_pgroup(pgroup) @@ -244,7 +271,7 @@ DAQS = multiple_daqs.generate_DAQS(instrument, pgroup,bs_channels, pvs, detector # 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=10, + vmin=1, vmax=2000, wait_time=0.5, required_fraction=0.8, diff --git a/devices/diffractometer.py b/devices/diffractometer.py index 957b7c8..be76761 100644 --- a/devices/diffractometer.py +++ b/devices/diffractometer.py @@ -9,7 +9,7 @@ """ -from slic.core.adjustable import Adjustable +from slic.core.adjustable import Adjustable, PrimarySecondary from slic.core.device import Device, SimpleDevice from slic.devices.general.motor import Motor @@ -27,12 +27,24 @@ class Diffractometer(Device): 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") - -diffractometer = Diffractometer("diffractometer") - # Set speed: -# diffractometer.theta._motor.VELO = 0.25 \ No newline at end of file +# diffractometer.theta._motor.VELO = 0.25 + +class ThetasCombined(PrimarySecondary): + def __init__(self, *args, **kwargs): + super().__init__(self, *args, **kwargs) + + def connect_axis(self): + """ + calculate offset to match scale factor + """ + + offset = self.secondary.get_current_value() - self.primary.get_current_value() * self.scale_factor + self.offset = offset + + diff --git a/exp_temp/channels.py b/exp_temp/channels.py index 0420653..0589dba 100644 --- a/exp_temp/channels.py +++ b/exp_temp/channels.py @@ -144,6 +144,7 @@ channels_PBPS053 = [ #################### # PSSS059 channels_PSSS059 = [ + "SARFE10-PSSS059:FIT-BRT", "SARFE10-PSSS059:FIT-COM", "SARFE10-PSSS059:FIT-FWHM", "SARFE10-PSSS059:FIT-RES", @@ -154,10 +155,35 @@ channels_PSSS059 = [ "SARFE10-PSSS059:SPECTRUM_Y_SUM", "SARFE10-PSSS059:SPECTRUM_X", "SARFE10-PSSS059:SPECTRUM_Y", - #"SARFE10-PSSS059:FPICTURE", "SARFE10-PSSS059:processing_parameters", ] +channels_PSSS059_camera = [ + "SARFE10-PSSS059:FPICTURE", +] + +#################### +# PSSS059 +channels_PSSS059_LB = [ + "SARFE10-PSSS059-LB:FIT-BRT", + "SARFE10-PSSS059-LB:FIT-COM", + "SARFE10-PSSS059-LB:FIT-FWHM", + "SARFE10-PSSS059-LB:FIT-RES", + "SARFE10-PSSS059-LB:FIT-RMS", + "SARFE10-PSSS059-LB:SPECT-COM", + "SARFE10-PSSS059-LB:SPECT-RES", + "SARFE10-PSSS059-LB:SPECT-RMS", + "SARFE10-PSSS059-LB:SPECTRUM_X", + "SARFE10-PSSS059-LB:SPECTRUM_Y", + "SARFE10-PSSS059-LB:SPECTRUM_Y_SUM", + "SARFE10-PSSS059-LB:processing_parameters", +] + +channels_PSSS059_LB_camera = [ + "SARFE10-PSSS059-LB:FPICTURE", +] + + ################################### ## Bernina channels # Beam position monitor PBPS113 @@ -330,18 +356,18 @@ channels_PPRM113_Bernina = [ "SAROP21-PPRM113:intensity", # "SAROP21-PPRM113:x_center_of_mass", # "SAROP21-PPRM113:x_fit_amplitude", -# "SAROP21-PPRM113:x_fit_mean", + "SAROP21-PPRM113:x_fit_mean", # "SAROP21-PPRM113:x_fit_offset", # "SAROP21-PPRM113:x_fit_standard_deviation", -# "SAROP21-PPRM113:x_fwhm", + "SAROP21-PPRM113:x_fwhm", # "SAROP21-PPRM113:x_profile", # "SAROP21-PPRM113:x_rms", # "SAROP21-PPRM113:y_center_of_mass", # "SAROP21-PPRM113:y_fit_amplitude", -# "SAROP21-PPRM113:y_fit_mean", + "SAROP21-PPRM113:y_fit_mean", # "SAROP21-PPRM113:y_fit_offset", # "SAROP21-PPRM113:y_fit_standard_deviation", -# "SAROP21-PPRM113:y_fwhm", + "SAROP21-PPRM113:y_fwhm", # "SAROP21-PPRM113:y_profile", # "SAROP21-PPRM113:y_rms", # "SAROP21-PPRM113:FPICTURE", # full pictures for debugging purposes at the moment, from _ib process @@ -436,7 +462,23 @@ bs_channels_DCM_Bernina = ( + channels_PBPS103_Bernina ) +bs_channels_PSSS = ( + channels_gas_monitor + + channels_PBPS053 + + channels_PSSS059 + + channels_PSSS059_camera + + channels_PBPS113 + + channels_PBPS149 +) +bs_channels_PSSS_LB = ( + channels_gas_monitor + + channels_PBPS053 + + channels_PSSS059_LB + + channels_PSSS059_LB_camera + + channels_PBPS113 + + channels_PBPS149 +) ########################################################################################################## diff --git a/mx/mx_experiment.py b/mx/mx_experiment.py new file mode 100644 index 0000000..806cdc8 --- /dev/null +++ b/mx/mx_experiment.py @@ -0,0 +1,168 @@ +# 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 +) + +mxscan = Scanner(default_acquisitions=[mxdaq], condition=check_intensity_gas_monitor) + +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 ############## +# 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 +coll_x = Motor("SARES30-SMX:MCS1") +coll_y = Motor("SARES30-SMX:MCS2") + +# post-tube +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 +detector_z = Motor("SAR-EXPMX:MOT_DET_Z") + +# post-tube +backlight = Motor("SAR-EXPMX:MOT_BLGT") + +############## in positions ############## +coll_in_pos_x, coll_in_pos_y = 9.51, -5.62 +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 + + +############## out positions ############## +coll_out_pos_x, coll_out_pos_y = -14.5, 1.32 +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 + +@as_shortcut +def a_data_collection(): + + # move backlight up + 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! + t.wait() + + # 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! + t.wait() + + # detector in + detector_z.set( detector_in_pos ).wait() # this runs in parallel + +@as_shortcut +def b_sample_alignment(): + + # detector 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! + t.wait() + + # post-tube out + 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! + 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: + backlight.set( backlight_in ).wait() + else: + print( "devises are in the way" ) + +@as_shortcut +def ca_backlight_out(): + backlight.set( backlight_out ).wait() + +@as_shortcut +def post_tube_in(): + + # safety logic for backlight in + if backlight.get() > 0 and detector_z.get() > 20: + 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! + t.wait() + else: + print( "devises are in the way" ) + +@as_shortcut +def post_tube_out(): + + # safety logic for post-tube out + if detector_z.get() > 20: + 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! + t.wait() + else: + print( "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! + t.wait() + +@as_shortcut +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! + t.wait() + +@as_shortcut +def detector_in(): + + # safety logic for detector in + if backlight.get() > 0 and pt_y1.get() > 8: + detector_z.set( detector_in_pos ).wait() # this runs in parallel + else: + print( "devises are in the way" ) + +@as_shortcut +def detector_out(): + detector_z.set( detector_out_pos ).wait() # this runs in parallel + + +