From 00fe2b684429a97f99d5216915d4a132e256d256 Mon Sep 17 00:00:00 2001 From: Alexander Steppke Date: Tue, 6 Sep 2022 14:32:45 +0200 Subject: [PATCH] Initial slic setup at cristallina --- PSSS_motion.py | 293 +++++++++++++++++++++++++++++++++ channels.py | 259 +++++++++++++++++++++++++++++ channels_copy.py | 260 +++++++++++++++++++++++++++++ cool_motor.py | 15 ++ cristallina.py | 101 ++++++++++++ environment.txt | 207 ++++++++++++++++++++++++ knife_edge.py | 7 + smaract.py | 14 ++ spreadsheet.py | 130 +++++++++++++++ undulator.py | 413 +++++++++++++++++++++++++++++++++++++++++++++++ 10 files changed, 1699 insertions(+) create mode 100644 PSSS_motion.py create mode 100644 channels.py create mode 100644 channels_copy.py create mode 100644 cool_motor.py create mode 100644 cristallina.py create mode 100644 environment.txt create mode 100644 knife_edge.py create mode 100644 smaract.py create mode 100644 spreadsheet.py create mode 100644 undulator.py diff --git a/PSSS_motion.py b/PSSS_motion.py new file mode 100644 index 0000000..4e8760f --- /dev/null +++ b/PSSS_motion.py @@ -0,0 +1,293 @@ +#!/usr/bin/env python +#*-----------------------------------------------------------------------* +#| | +#| Copyright (c) 2014 by Paul Scherrer Institute (http://www.psi.ch) | +#| | +#| Author Thierry Zamofing (thierry.zamofing@psi.ch) | +#*-----------------------------------------------------------------------* +''' +motion procedures for PSSS: + +mode: + 0: homing + 1: calc energy and SPECTRUM_X out of camera arm angle + move CamPosX out of CristBendRot +''' + +from __future__ import print_function +from errno import ENETRESET +import logging, sys, os, json +import CaChannel,time +import numpy as np + + +class PSSS: + + def __init__(self, args): + # import pdb; pdb.set_trace() + # sys.exit() + # {'mode': 0, 'stdout': True, 'var': []} + # {'mode': 5, 'stdout': True, 'var': ['SARFE10-PSSS059']} + + self.args=args + prefix=self.args.var[0] + prefix=prefix[0:prefix.find('-')+1] + self.prefix=prefix + #print('Prefix='+prefix) + self.pv=dict() + + def getPv(self,name): + try: + pv=self.pv[name] + except KeyError: + prefix=self.prefix + pv = CaChannel.CaChannel(prefix+name) ;pv.searchw() + self.pv[name]=pv + return pv + + def moveLimit(self,m,val): + self.moveAbs(m,val) + + def waitMotionDone(self,m): + s=m+'.DMOV' + pv=self.getPv(s) + sys.stdout.write('wait motion '+s+' ');sys.stdout.flush(); + while pv.getw()==0: + sys.stdout.write('.');sys.stdout.flush(); + time.sleep(.1) + print('done'); + + def setPos(self,m,ofs): + pvSET=self.getPv(m+'.SET') + pvVAL=self.getPv(m+'.VAL') + pvSET.putw(1)#Set + print('set ofset to %f'%ofs) + pvVAL.putw(ofs) #set value (without move) + time.sleep(.1) # wait for safety + pvSET.putw(0)#Use + time.sleep(.1) # wait for safety + + def moveAbs(self,m,val): + pvVAL=self.getPv(m+'.VAL') + pvVAL.putw(val) #set position and move + + def homing(self): + pvNameLst=('PSSS055:MOTOR_ROT_X1', + 'PSSS055:MOTOR_X1', + 'PSSS055:MOTOR_Y1', + 'PSSS055:MOTOR_PROBE', + 'PSSS059:MOTOR_X5', + 'PSSS059:MOTOR_Y5', + 'PSSS059:MOTOR_Z5', + 'PSSS059:MOTOR_X3', + 'PSSS059:MOTOR_Y3') + + pvHOME = self.getPv('PSSS059:HOMING.VAL') + if pvHOME.getw(1) == 1: + print('homing still in progress. abort new procedure') + #return + pvHOME.putw(1)#homing in progress + + try: + pvHomr=self.getPv(pvNameLst[0]+'.HOMR') + pvHomr.putw(1)#homing MOTOR_ROT_X1 + self.waitMotionDone(pvNameLst[0]) + + homing=( + (1,200,10,0), # PSSS055:MOTOR_X1 Home on +limit switch to +10mm + (2,10,4.475,2.22), # PSSS055:MOTOR_Y1 Home on +limit switch to +4.475mm + (3,50,0,-9), # PSSS055:MOTOR_PROBE Home on +limit switch to +0mm + (4,80,35,0), # PSSS059:MOTOR_X5 Home on +limit switch to +35mm + (5,30,10,0), # PSSS059:MOTOR_Y5 Home on +limit switch to +10mm +# (6,20,8.9,0), # PSSS059:MOTOR_Z5 Home on +limit switch to +8.9mm Set HLM to 10, LLM to -2mm + (7,30,1,0), # PSSS059:MOTOR_X3 Home on +limit switch to +10mm + (8,30,1,-1.4), # PSSS059:MOTOR_Y3 Home on +limit switch to +10mm + ) + for idx,mvLim,setPos,mvPos in homing: + pvName=pvNameLst[idx] + print('homing %s %f %f %f'%(pvName,mvLim,setPos,mvPos)) + self.moveLimit(pvName,mvLim) + + for idx,mvLim,setPos,mvPos in homing: + pvName=pvNameLst[idx] + self.waitMotionDone(pvName) + self.setPos(pvName,setPos) + + time.sleep(2);print ("sleep 2 sec.") + for idx,mvLim,setPos,mvPos in homing: + pvName=pvNameLst[idx] + self.moveAbs(pvName,mvPos) + + for idx,mvLim,setPos,mvPos in homing: + pvName=pvNameLst[idx] + self.waitMotionDone(pvName) + + + except AssertionError as e: #BaseException as e: + print(e) + pvHOME.putw(3)#homing failed + else: + pvHOME.putw(2)#homing done + + + def set_energy_motor(self,energy2motor,scan=False, rotWait=False): + crystalType=self.getPv('PSSS059:CRYSTAL_SP').getw() + #crystalType=2 + #print(crystalType) + #print(type(crystalType)) + if crystalType==0: return + # 0 None + # 1 Si(111)R155 + # 2 Si(220)R75 + # 3 Si(220)R145 + # 4 Si(220)R200 + # 5 Si(333)R155 + + #load lut + fnXtlLst=(None,"Si111R155","Si220R75","Si220R145","Si220R200","Si333R155") + #print(__file__) + base=os.path.dirname(__file__) + fn=os.path.join(base,'lut'+fnXtlLst[crystalType]+'.txt') + lut= np.genfromtxt(fn, delimiter='\t',names=True) + #lut + #lut.dtype + #lut[1]['Energy'] + #lut['Energy'] + #lut=np.genfromtxt(fn, delimiter='\t',skip_header=1) + if energy2motor: + energy =self.getPv('PSSS059:ENERGY').getw() + #energy =6000 + gratingType=self.getPv('PSSS055:GRATING_SP').getw() + #gratingType=3 + camPosX='CamPosX' + if gratingType in(1,2):camPosX+='_100nm' + elif gratingType==3:camPosX+='_150nm' + elif gratingType==4:camPosX+='_200nm' + + camArmRot =np.interp(energy,lut['Energy'],lut['CamArmRot']) + cristBendRot=np.interp(energy,lut['Energy'],lut['CristBendRot']) + camPosX =np.interp(energy,lut['Energy'],lut[camPosX]) + evPerPix =np.interp(energy,lut['Energy'],lut['EvPerPix']) + else: + camArmRot=self.getPv('PSSS059:MOTOR_ROT_X4').getw() + idx=~np.isnan(lut['CamArmRot']) + lutCamArmRot=lut['CamArmRot'][idx] + energy =np.interp(camArmRot,lutCamArmRot[::-1],lut['Energy'][idx][::-1]) + evPerPix =np.interp(camArmRot,lutCamArmRot[::-1],lut['EvPerPix'][idx][::-1]) + + #camera: 2560 x 2160 + n=2560 + i=np.arange(n)-n/2 + spctr_x=energy+i*evPerPix + pv=self.getPv('PSSS059:SPECTRUM_X') + pv.putw(spctr_x) + + pv=self.getPv('PSSS059:SPECTRUM_Y') + mu=2560./2 + sigma=100. + x=np.arange(2560.) + spctr_y= 1000.*np.exp(-( (x-mu)**2 / ( 2.0 * sigma**2 ) ) ) + pv.putw(spctr_y) + + if energy2motor: + print('energy2motor: camArmRot: %g cristBendRot: %g camPosX:%g evPerPix:%g'%(camArmRot,cristBendRot,camPosX,evPerPix)) + pv=self.getPv('PSSS059:MOTOR_ROT_X4') + pv.putw(camArmRot) + pv=self.getPv('PSSS059:MOTOR_ROT_X3') + pv.putw(cristBendRot) + if rotWait == True: + self.waitMotionDone('PSSS059:MOTOR_ROT_X3') + if scan == False: # if True the camera X position will not be changed + pv=self.getPv('PSSS059:MOTOR_X5') + pv.putw(camPosX) + else: + print('motor2energy: energy: %g evPerPix:%g'%(energy,evPerPix)) + pv=self.getPv('PSSS059:ENERGY') + pv.putw(energy) + + def grating2motor(self): + energy =self.getPv('PSSS059:ENERGY').getw() + gratingType=self.getPv('PSSS055:GRATING_SP').getw() + if gratingType==0: + print('no grating') + girderX=0. + else: + base=os.path.dirname(__file__) + fn=fn=os.path.join(base,'lutGirderXTrans.txt') + lut= np.genfromtxt(fn, delimiter='\t',names=True) + + d={1:'100nm',2:'100nm',3:'150nm',4:'200nm'} + print(gratingType) + grtStr=d[gratingType] + + girderX =np.interp(energy,lut['Energy'],lut[grtStr]) + + print("girderX:%g"%(girderX)) + pv=self.getPv('PSSS059:MOTOR_X2') + pv.putw(girderX) + + +if __name__=='__main__': + from optparse import OptionParser, IndentedHelpFormatter + class MyFormatter(IndentedHelpFormatter): + 'helper class for formating the OptionParser' + + def __init__(self): + IndentedHelpFormatter.__init__(self) + + def format_epilog(self, epilog): + if epilog: + return epilog + else: + return "" + + def parse_args(): + 'main command line interpreter function' + (h, t)=os.path.split(sys.argv[0]);cmd='\n '+(t if len(h)>3 else sys.argv[0])+' ' + exampleCmd=('MYPREFIX', + ) + epilog=__doc__+''' +Examples:'''+''.join(map(lambda s:cmd+s, exampleCmd))+'\n ' + + fmt=MyFormatter() + parser=OptionParser(epilog=epilog, formatter=fmt) + parser.add_option('-m', '--mode', type="int", help='move instead of homing', default=0) + parser.add_option('-s', '--stdout', action="store_true", help='log to stdout instead of file') + + (args, other)=parser.parse_args() + #print(args,other) + args.var=other + #args.var=('SARFE10-',) + + fn='/afs/psi.ch/intranet/Controls/scratch/'+os.path.basename(__file__)+'_'+os.environ.get('USER')+'.log' + if not args.stdout: + print('output redirected to file:\n'+fn) + stdoutBak=sys.stdout + sys.stdout = open(fn, 'a+') + print('*'*30+'\n'+time.ctime()+': run on host:'+os.environ.get('HOSTNAME')) + print('Args:'+str(args)+' '+str(args.var)) + sys.stdout.flush() + + psss=PSSS(args) + if args.mode==0: + psss.homing() + elif args.mode==1: + psss.set_energy_motor(energy2motor=True) + elif args.mode==2: + psss.set_energy_motor(energy2motor=False) + elif args.mode==3: + psss.grating2motor() + elif args.mode==4: + psss.set_energy_motor(energy2motor=True, scan=True) + elif args.mode==5: + psss.set_energy_motor(energy2motor=True, scan=True,rotWait=True) + print('PSSS_motion done.') + + return + + + #os.environ['EPICS_CA_ADDR_LIST']='localhost' + #os.environ['EPICS_CA_ADDR_LIST']='172.26.0.255 172.26.2.255 172.26.8.255 172.26.16.255 172.26.24.255 172.26.32.255 172.26.40.255 172.26.110.255 172.26.111.255 172.26.120.255 129.129.242.255 129.129.243.255' + + parse_args() + diff --git a/channels.py b/channels.py new file mode 100644 index 0000000..c0d6821 --- /dev/null +++ b/channels.py @@ -0,0 +1,259 @@ +# Channels at Cristallina endstation + + +########################################################################################################## +# BS channels + +# TODO: JF settings regarding raw conversion, compression, etc. +detectors = [ + # "JF16T03V01", +] + +#################### +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", +] + +###################### +### PCO edge camera for the wavefront analysis +channels_PCO = [ + "SARES30-CAMS156-PCO1:FPICTURE", +] + +###################### +### SwissMX OAV camera picture +channels_OAV = [] # "SARES30-CAMS156-SMX-OAV:FPICTURE", + +###################### +### PBPS053 +channels_PBPS053 = [ + "SARFE10-PBPS053:INTENSITY", + "SARFE10-PBPS053:XPOS", + "SARFE10-PBPS053:YPOS", +] + +################################### +### Beam position monitor PBPS113 +channels_PBPS113 = [ + "SAROP31-PBPS113:INTENSITY", + "SAROP31-PBPS113:INTENSITY_UJ", + "SAROP31-PBPS113:Lnk9Ch0-PP_VAL_PD0", + "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", +] + + +#################### +## PSS059 +channels_pss059 = [ + # "SARFE10-PSSS059:FPICTURE", # full pictures only when really needed + "SARFE10-PSSS059:SPECTRUM_X", + "SARFE10-PSSS059:SPECTRUM_Y", + "SARFE10-PSSS059:SPECTRUM_CENTER", + "SARFE10-PSSS059:SPECTRUM_COM", + "SARFE10-PSSS059:SPECTRUM_FWHM", + "SARFE10-PSSS059:SPECTRUM_STD", + "SARFE10-PSSS059:FIT_ERR", + "SARFE10-PSSS059:processing_parameters", + # SARFE10-PSSS059:SPECTRUM_AVG_CENTER + # SARFE10-PSSS059:SPECTRUM_AVG_FWHM + # SARFE10-PSSS059:SPECTRUM_AVG_Y +] + + +####################### +# from _proc process +channels_PPRM113 = [ + "SAROP31-PPRM113:intensity", + "SAROP31-PPRM113:x_center_of_mass", + "SAROP31-PPRM113:x_fit_amplitude", + "SAROP31-PPRM113:x_fit_mean", + "SAROP31-PPRM113:x_fit_offset", + "SAROP31-PPRM113:x_fit_standard_deviation", + "SAROP31-PPRM113:x_fwhm", + "SAROP31-PPRM113:x_profile", + "SAROP31-PPRM113:x_rms", + "SAROP31-PPRM113:y_center_of_mass", + "SAROP31-PPRM113:y_fit_amplitude", + "SAROP31-PPRM113:y_fit_mean", + "SAROP31-PPRM113:y_fit_offset", + "SAROP31-PPRM113:y_fit_standard_deviation", + "SAROP31-PPRM113:y_fwhm", + "SAROP31-PPRM113:y_profile", + "SAROP31-PPRM113:y_rms", + # "SAROP31-PPRM113:FPICTURE", # full pictures for debugging purposes at the moment, from _ib process +] + +####################### +# from _proc process +channels_PPRM150 = [ + "SAROP31-PPRM150:intensity", + "SAROP31-PPRM150:x_center_of_mass", + "SAROP31-PPRM150:x_fit_amplitude", + "SAROP31-PPRM150:x_fit_mean", + "SAROP31-PPRM150:x_fit_offset", + "SAROP31-PPRM150:x_fit_standard_deviation", + "SAROP31-PPRM150:x_fwhm", + "SAROP31-PPRM150:x_profile", + "SAROP31-PPRM150:x_rms", + "SAROP31-PPRM150:y_center_of_mass", + "SAROP31-PPRM150:y_fit_amplitude", + "SAROP31-PPRM150:y_fit_mean", + "SAROP31-PPRM150:y_fit_offset", + "SAROP31-PPRM150:y_fit_standard_deviation", + "SAROP31-PPRM150:y_fwhm", + "SAROP31-PPRM150:y_profile", + "SAROP31-PPRM150:y_rms", + # "SAROP31-PPRM150:FPICTURE", # full pictures for debugging purposes at the moment, from _ib process +] + +########################### +# Beam position monitor +channel_PBPS149 = [ + "SAROP31-PBPS149:INTENSITY", + "SAROP31-PBPS149:INTENSITY_UJ", + "SAROP31-PBPS149:Lnk9Ch0-PP_VAL_PD0", + "SAROP31-PBPS149:Lnk9Ch0-PP_VAL_PD1", + "SAROP31-PBPS149:Lnk9Ch0-PP_VAL_PD2", + "SAROP31-PBPS149:Lnk9Ch0-PP_VAL_PD3", + "SAROP31-PBPS149:Lnk9Ch0-PP_VAL_PD4", + "SAROP31-PBPS149:XPOS", + "SAROP31-PBPS149:YPOS", +] + + +further_channels = [ + #################### + ## Digitizer + # Integration limits + "SARES30-LTIM01-EVR0:DUMMY_PV1_NBS", + "SARES30-LTIM01-EVR0:DUMMY_PV2_NBS", + "SARES30-LTIM01-EVR0:DUMMY_PV3_NBS", + "SARES30-LTIM01-EVR0:DUMMY_PV4_NBS", + # Signal-Background + "SARES30-LSCP1-FNS:CH0:VAL_GET", + # Waveform signal + "SARES30-LSCP1-CRISTA1:CH0:1", + # Waveform trigger + "SARES30-LSCP1-CRISTA1:CH2:1", + # Calculated intensity + "SARES30-LTIM01-EVR0:CALCI", +] + + +channels = ( + channels_gas_monitor + + channels_PCO + + channels_OAV + + channels_PBPS053 + + channels_pss059 + + channels_PPRM113 + + channels_PPRM150 + + channel_PBPS149 + + channel_PBPS149 + + further_channels +) + +########################################################################################################## + +pv_channels = [ + #################### + ## OAPU044 + "SARFE10-OAPU044:MOTOR_X", + "SARFE10-OAPU044:MOTOR_Y", + "SARFE10-OAPU044:MOTOR_W", + "SARFE10-OAPU044:MOTOR_H", + #################### + ## OATT053 + "SARFE10-OATT053:MOTOR_1", + "SARFE10-OATT053:MOTOR_1.RBV", + "SARFE10-OATT053:MOTOR_2", + "SARFE10-OATT053:MOTOR_2.RBV", + "SARFE10-OATT053:MOTOR_3", + "SARFE10-OATT053:MOTOR_3.RBV", + "SARFE10-OATT053:MOTOR_4", + "SARFE10-OATT053:MOTOR_4.RBV", + "SARFE10-OATT053:MOTOR_5", + "SARFE10-OATT053:MOTOR_5.RBV", + "SARFE10-OATT053:MOTOR_6", + "SARFE10-OATT053:MOTOR_6.RBV", + "SARFE10-OATT053:ENERGY", + "SARFE10-OATT053:TRANS_SP", + "SARFE10-OATT053:TRANS_RB", + #################### + ## OATA150 + "SAROP31-OATA150:MOTOR_1", + "SAROP31-OATA150:MOTOR_2", + "SAROP31-OATA150:MOTOR_3", + "SAROP31-OATA150:MOTOR_4", + "SAROP31-OATA150:MOTOR_5", + "SAROP31-OATA150:MOTOR_6", + #################### + ## PSSS + "SARFE10-PSSS059:MOTOR_Y3.VAL", + "SARFE10-PSSS059:MOTOR_ROT_X3.VAL", + "SARFE10-PSSS059:MOTOR_X5.VAL", + "SARFE10-PSSS059:MOTOR_X3.VAL", + ########################### + # KB mirrors + "SAROP31-OKBV153:W_X.RBV", + "SAROP31-OKBV153:W_Y.RBV", + "SAROP31-OKBV153:W_RX.RBV", + "SAROP31-OKBV153:W_RY.RBV", + "SAROP31-OKBV153:W_RZ.RBV", + "SAROP31-OKBV153:BU.RBV", + "SAROP31-OKBV153:BD.RBV", + "SAROP31-OKBV153:TY1.RBV", + "SAROP31-OKBV153:TY2.RBV", + "SAROP31-OKBV153:TY3.RBV", + "SAROP31-OKBV153:TX1.RBV", + "SAROP31-OKBV153:TX2.RBV", + "SAROP31-OKBH154:W_X.RBV", + "SAROP31-OKBH154:W_Y.RBV", + "SAROP31-OKBH154:W_RX.RBV", + "SAROP31-OKBH154:W_RY.RBV", + "SAROP31-OKBH154:W_RZ.RBV", + "SAROP31-OKBH154:BU.RBV", + "SAROP31-OKBH154:BD.RBV", + "SAROP31-OKBH154:TY1.RBV", + "SAROP31-OKBH154:TY2.RBV", + "SAROP31-OKBH154:TY3.RBV", + "SAROP31-OKBH154:TX2.RBV", + #################### + ## FEL Photon Energy + "SARUN:FELPHOTENE", + ################### + ## FEL Photon Pulse Energy + "SARFE10-PBPG050:PHOTON-ENERGY-PER-PULSE-AVG", +] + +pvs_slits = [ + # TODO: PVS slits can't be read by the DAQ module currently. Therefore disabled it. + # "SARFE10-OAPU044:MOTOR_X.VAL", + # "SARFE10-OAPU044:MOTOR_X.RBV" +] + +pvs_apertures = [ + "SAROP31-OAPU149:MOTOR_X.VAL", # the x pos of the aperture + "SAROP31-OAPU149:MOTOR_X.RBV", # the x pos of the aperture +] + +############################### +smaract_channels = [ + "SARES30-XSMA156:X:MOTRBV", + "SARES30-XSMA156:Y:MOTRBV", + "SARES30-XSMA156:Z:MOTRBV", + "SARES30-XSMA156:Ry:MOTRBV", + "SARES30-XSMA156:Rx:MOTRBV", + "SARES30-XSMA156:Rz:MOTRBV", +] + +pvs = pvs_slits + pv_channels + smaract_channels diff --git a/channels_copy.py b/channels_copy.py new file mode 100644 index 0000000..f108f73 --- /dev/null +++ b/channels_copy.py @@ -0,0 +1,260 @@ +# Channels at Cristallina endstation + + +########################################################################################################## +# BS channels + +# TODO: JF settings regarding raw conversion, compression, etc. +detectors = [ +# "JF16T03V01", +] + +#################### +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", +] + +###################### +### PCO edge camera for the wavefront analysis +channels_PCO=["SARES30-CAMS156-PCO1:FPICTURE", +] + +###################### +### SwissMX OAV camera picture +channels_OAV=[#"SARES30-CAMS156-SMX-OAV:FPICTURE", +] + +###################### +### PBPS053 +channels_PBPS053 = [ + "SARFE10-PBPS053:INTENSITY", + "SARFE10-PBPS053:XPOS", + "SARFE10-PBPS053:YPOS", +] + +################################### +### Beam position monitor PBPS113 +channels_PBPS113 = [ +"SAROP31-PBPS113:INTENSITY", +"SAROP31-PBPS113:INTENSITY_UJ", +"SAROP31-PBPS113:Lnk9Ch0-PP_VAL_PD0", +"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", +] + + +#################### +## PSS059 +channels_pss059 = [ +#"SARFE10-PSSS059:FPICTURE", # full pictures only when really needed +"SARFE10-PSSS059:SPECTRUM_X", +"SARFE10-PSSS059:SPECTRUM_Y", +"SARFE10-PSSS059:SPECTRUM_CENTER", +"SARFE10-PSSS059:SPECTRUM_COM", +"SARFE10-PSSS059:SPECTRUM_FWHM", +"SARFE10-PSSS059:SPECTRUM_STD", +"SARFE10-PSSS059:FIT_ERR", +"SARFE10-PSSS059:processing_parameters", +#SARFE10-PSSS059:SPECTRUM_AVG_CENTER +#SARFE10-PSSS059:SPECTRUM_AVG_FWHM +#SARFE10-PSSS059:SPECTRUM_AVG_Y +] + + +####################### +# from _proc process +channels_PPRM113 = [ +"SAROP31-PPRM113:intensity", +"SAROP31-PPRM113:x_center_of_mass", +"SAROP31-PPRM113:x_fit_amplitude", +"SAROP31-PPRM113:x_fit_mean", +"SAROP31-PPRM113:x_fit_offset", +"SAROP31-PPRM113:x_fit_standard_deviation", +"SAROP31-PPRM113:x_fwhm", +"SAROP31-PPRM113:x_profile", +"SAROP31-PPRM113:x_rms", +"SAROP31-PPRM113:y_center_of_mass", +"SAROP31-PPRM113:y_fit_amplitude", +"SAROP31-PPRM113:y_fit_mean", +"SAROP31-PPRM113:y_fit_offset", +"SAROP31-PPRM113:y_fit_standard_deviation", +"SAROP31-PPRM113:y_fwhm", +"SAROP31-PPRM113:y_profile", +"SAROP31-PPRM113:y_rms", +#"SAROP31-PPRM113:FPICTURE", # full pictures for debugging purposes at the moment, from _ib process +] + +####################### +# from _proc process +channels_PPRM150 = [ +"SAROP31-PPRM150:intensity", +"SAROP31-PPRM150:x_center_of_mass", +"SAROP31-PPRM150:x_fit_amplitude", +"SAROP31-PPRM150:x_fit_mean", +"SAROP31-PPRM150:x_fit_offset", +"SAROP31-PPRM150:x_fit_standard_deviation", +"SAROP31-PPRM150:x_fwhm", +"SAROP31-PPRM150:x_profile", +"SAROP31-PPRM150:x_rms", +"SAROP31-PPRM150:y_center_of_mass", +"SAROP31-PPRM150:y_fit_amplitude", +"SAROP31-PPRM150:y_fit_mean", +"SAROP31-PPRM150:y_fit_offset", +"SAROP31-PPRM150:y_fit_standard_deviation", +"SAROP31-PPRM150:y_fwhm", +"SAROP31-PPRM150:y_profile", +"SAROP31-PPRM150:y_rms", +#"SAROP31-PPRM150:FPICTURE", # full pictures for debugging purposes at the moment, from _ib process +] + +########################### +# Beam position monitor +channel_PBPS149 =[ +"SAROP31-PBPS149:INTENSITY", +"SAROP31-PBPS149:INTENSITY_UJ", +"SAROP31-PBPS149:Lnk9Ch0-PP_VAL_PD0", +"SAROP31-PBPS149:Lnk9Ch0-PP_VAL_PD1", +"SAROP31-PBPS149:Lnk9Ch0-PP_VAL_PD2", +"SAROP31-PBPS149:Lnk9Ch0-PP_VAL_PD3", +"SAROP31-PBPS149:Lnk9Ch0-PP_VAL_PD4", +"SAROP31-PBPS149:XPOS", +"SAROP31-PBPS149:YPOS", +] + + + +further_channels = [ +#################### +## Digitizer + +#Integration limits +"SARES30-LTIM01-EVR0:DUMMY_PV1_NBS", +"SARES30-LTIM01-EVR0:DUMMY_PV2_NBS", +"SARES30-LTIM01-EVR0:DUMMY_PV3_NBS", +"SARES30-LTIM01-EVR0:DUMMY_PV4_NBS", + +# Signal-Background +"SARES30-LSCP1-FNS:CH0:VAL_GET", + +#Waveform signal +"SARES30-LSCP1-CRISTA1:CH0:1", + +#Waveform trigger +"SARES30-LSCP1-CRISTA1:CH2:1", + +#Calculated intensity +"SARES30-LTIM01-EVR0:CALCI", +] + + +channels = channels_gas_monitor + channels_PCO + channels_OAV + channels_PBPS053 + channels_pss059 + channels_PPRM113 + channels_PPRM150 + channel_PBPS149 + channel_PBPS149 + further_channels + +########################################################################################################## + +pv_channels = [ +#################### +## OAPU044 +"SARFE10-OAPU044:MOTOR_X", +"SARFE10-OAPU044:MOTOR_Y", +"SARFE10-OAPU044:MOTOR_W", +"SARFE10-OAPU044:MOTOR_H", + +#################### +## OATT053 +"SARFE10-OATT053:MOTOR_1", +"SARFE10-OATT053:MOTOR_1.RBV", +"SARFE10-OATT053:MOTOR_2", +"SARFE10-OATT053:MOTOR_2.RBV", +"SARFE10-OATT053:MOTOR_3", +"SARFE10-OATT053:MOTOR_3.RBV", +"SARFE10-OATT053:MOTOR_4", +"SARFE10-OATT053:MOTOR_4.RBV", +"SARFE10-OATT053:MOTOR_5", +"SARFE10-OATT053:MOTOR_5.RBV", +"SARFE10-OATT053:MOTOR_6", +"SARFE10-OATT053:MOTOR_6.RBV", +"SARFE10-OATT053:ENERGY", +"SARFE10-OATT053:TRANS_SP", +"SARFE10-OATT053:TRANS_RB", + +#################### +## OATA150 +"SAROP31-OATA150:MOTOR_1", +"SAROP31-OATA150:MOTOR_2", +"SAROP31-OATA150:MOTOR_3", +"SAROP31-OATA150:MOTOR_4", +"SAROP31-OATA150:MOTOR_5", +"SAROP31-OATA150:MOTOR_6", + +#################### +## PSSS +'SARFE10-PSSS059:MOTOR_Y3.VAL', +'SARFE10-PSSS059:MOTOR_ROT_X3.VAL', +'SARFE10-PSSS059:MOTOR_X5.VAL', +'SARFE10-PSSS059:MOTOR_X3.VAL', + +########################### +# KB mirrors +"SAROP31-OKBV153:W_X.RBV", +"SAROP31-OKBV153:W_Y.RBV", +"SAROP31-OKBV153:W_RX.RBV", +"SAROP31-OKBV153:W_RY.RBV", +"SAROP31-OKBV153:W_RZ.RBV", +"SAROP31-OKBV153:BU.RBV", +"SAROP31-OKBV153:BD.RBV", +"SAROP31-OKBV153:TY1.RBV", +"SAROP31-OKBV153:TY2.RBV", +"SAROP31-OKBV153:TY3.RBV", +"SAROP31-OKBV153:TX1.RBV", +"SAROP31-OKBV153:TX2.RBV", +"SAROP31-OKBH154:W_X.RBV", +"SAROP31-OKBH154:W_Y.RBV", +"SAROP31-OKBH154:W_RX.RBV", +"SAROP31-OKBH154:W_RY.RBV", +"SAROP31-OKBH154:W_RZ.RBV", +"SAROP31-OKBH154:BU.RBV", +"SAROP31-OKBH154:BD.RBV", +"SAROP31-OKBH154:TY1.RBV", +"SAROP31-OKBH154:TY2.RBV", +"SAROP31-OKBH154:TY3.RBV", +"SAROP31-OKBH154:TX2.RBV", + +#################### +## FEL Photon Energy +'SARUN:FELPHOTENE', + +################### +## FEL Photon Pulse Energy +'SARFE10-PBPG050:PHOTON-ENERGY-PER-PULSE-AVG', +] + +pvs_slits = [ +# TODO: PVS slits can't be read by the DAQ module currently. Therefore disabled it. +# "SARFE10-OAPU044:MOTOR_X.VAL", +# "SARFE10-OAPU044:MOTOR_X.RBV" +] + +pvs_apertures = [ + "SAROP31-OAPU149:MOTOR_X.VAL", # the x pos of the aperture + "SAROP31-OAPU149:MOTOR_X.RBV", # the x pos of the aperture +] + +############################### +smaract_channels = [ + "SARES30-XSMA156:X:MOTRBV", + "SARES30-XSMA156:Y:MOTRBV", + "SARES30-XSMA156:Z:MOTRBV", + "SARES30-XSMA156:Ry:MOTRBV", + "SARES30-XSMA156:Rx:MOTRBV", + "SARES30-XSMA156:Rz:MOTRBV", +] + +pvs = pvs_slits + pv_channels + smaract_channels diff --git a/cool_motor.py b/cool_motor.py new file mode 100644 index 0000000..5f4c3cf --- /dev/null +++ b/cool_motor.py @@ -0,0 +1,15 @@ +from slic.core.adjustable import Adjustable + + +class MyNewCoolThing(Adjustable): + + pos = 0 + + def get_current_value(self): + return self.pos + + def set_target_value(self, value): + self.pos = value + + def is_moving(self): + return False # OK OK, this is probably cheating ;) diff --git a/cristallina.py b/cristallina.py new file mode 100644 index 0000000..f469333 --- /dev/null +++ b/cristallina.py @@ -0,0 +1,101 @@ +#!/usr/bin/env python + +import logging + +logger = logging.getLogger("slic") +logger.setLevel(logging.INFO) +logger.info("Loading started.") + +from time import sleep +from datetime import datetime +import numpy as np + +# from tqdm import trange +from epics import PV + +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.simpledevice import SimpleDevice +from slic.devices.general.motor import Motor +from slic.utils import devices, Marker, as_shortcut +from slic.utils import Channels, Config, Elog, Screenshot, PV +from slic.core.acquisition.fakeacquisition import FakeAcquisition + +# from devices.attenuator import Attenuator + +from channels import detectors, channels, pvs +from spreadsheet import overview + +# DEVICES +## example devices and adjustables +from cool_motor import MyNewCoolThing + +cool_motor = MyNewCoolThing("cool_motor") + +dummy = DummyAdjustable(units="au") + +## Attenuator +from slic.devices.xoptics.attenuator_aramis import AttenuatorAramis +from knife_edge import KnifeEdge + +attenuator_ID = "SAROP31-OATA150" +attenuator = AttenuatorAramis( + attenuator_ID, description="Attenuators with absolute encoders" +) + + +def test_attenuator(): + tfundamental, tHG = attenuator.get_transmission(verbose=False) + try: + assert tfundamental > 0 + except TypeError: + print("No transmission value reported from {attenuator.ID}") + + +test_attenuator() + + +## Undulator +import undulator + +undulators = undulator.Undulators() + + +## Slits +from slic.devices.xoptics import slits + +# _old for Alvra codepath, recommended here +slits_ADC = slits.SlitPosWidth_old("SAROP31-OAPU149", name="Apertures - ADC") + +## Smaract stage +from smaract import smaract + + +########################################### +instrument = "cristallina" + +# pgroup = "p19739" # commissioning March 2022 -- July 2022 +pgroup = "p20443" # commissioning Wavefront Sensor August 2022 + +daq = SFAcquisition( + instrument, + pgroup, + default_channels=channels, + default_pvs=pvs, + default_detectors=detectors, + rate_multiplicator=1, +) + +# daq = FakeAcquisition(instrument, pgroup) +# daqPV = PVAcquisition(instrument, pgroup, default_channels=channels_ks) # workaround for KS not going to DB + +# scan = Scanner(scan_info_dir=f"/sf/{instrument}/data/{pgroup}/res/scan_info", default_acquisitions=[daq], condition=None) + +scan = Scanner(default_acquisitions=[daq]) +gui = GUI(scan, show_goto=True, show_spec=True) + +logger.info(f"Running at {instrument} with pgroup {pgroup}.") +logger.info("Loading finished.") diff --git a/environment.txt b/environment.txt new file mode 100644 index 0000000..35642cd --- /dev/null +++ b/environment.txt @@ -0,0 +1,207 @@ +# packages in environment at /sf/cristallina/applications/conda/envs/slic: +# +# Name Version Build Channel +_libgcc_mutex 0.1 main +_openmp_mutex 5.1 1_gnu +alsa-lib 1.2.3 h516909a_0 conda-forge +appdirs 1.4.4 pyhd3eb1b0_0 +argon2-cffi 21.3.0 pyhd3eb1b0_0 +argon2-cffi-bindings 21.2.0 py39h7f8727e_0 +asttokens 2.0.5 pyhd3eb1b0_0 +atk-1.0 2.36.0 h28cd5cc_0 +attrs 21.4.0 pyhd3eb1b0_0 +backcall 0.2.0 pyhd3eb1b0_0 +bcrypt 3.2.0 py39he8ac12f_0 +bitshuffle 0.3.6.2 py39h6bb024c_0 paulscherrerinstitute +black 19.10b0 py_0 +blas 1.0 mkl +bottleneck 1.3.5 py39h7deecbd_0 +brotli 1.0.9 he6710b0_2 +brotlipy 0.7.0 py39h27cfd23_1003 +bsread 1.5.6 py_0 paulscherrerinstitute +ca-certificates 2022.4.26 h06a4308_0 +cachannel 3.1.4 py39hb93dfd8_0 paulscherrerinstitute +cairo 1.16.0 hf32fb01_1 +cam_server_client 5.2.1 py_0 paulscherrerinstitute +certifi 2022.6.15 py39h06a4308_0 +cffi 1.15.0 py39hd667e15_1 +charset-normalizer 2.0.4 pyhd3eb1b0_0 +click 8.0.4 py39h06a4308_0 +colorama 0.4.5 py39h06a4308_0 +commonmark 0.9.1 pyhd3eb1b0_0 +cryptography 37.0.1 py39h9ce1e76_0 +cycler 0.11.0 pyhd3eb1b0_0 +data_api 0.8.6 py_0 paulscherrerinstitute +dataclasses 0.8 pyh6d0b6a4_7 +dbus 1.13.18 hb2f20db_0 +debugpy 1.5.1 py39h295c915_0 +decorator 5.1.1 pyhd3eb1b0_0 +elog 1.3.11 py_0 paulscherrerinstitute +entrypoints 0.4 py39h06a4308_0 +epics-base 3.14.12.8 hf484d3e_3 paulscherrerinstitute +executing 0.8.3 pyhd3eb1b0_0 +expat 2.4.4 h295c915_0 +font-ttf-dejavu-sans-mono 2.37 hd3eb1b0_0 +font-ttf-inconsolata 2.001 hcb22688_0 +font-ttf-source-code-pro 2.030 hd3eb1b0_0 +font-ttf-ubuntu 0.83 h8b1ccd4_0 +fontconfig 2.13.1 h6c09931_0 +fonts-anaconda 1 h8fa9717_0 +fonts-conda-ecosystem 1 hd3eb1b0_0 +fonttools 4.25.0 pyhd3eb1b0_0 +freetype 2.11.0 h70c0345_0 +fribidi 1.0.10 h7b6447c_0 +future 0.18.2 py39h06a4308_1 +fuzzywuzzy 0.18.0 pyhd8ed1ab_0 conda-forge +gdk-pixbuf 2.42.6 h04a7f16_0 conda-forge +gettext 0.21.0 hf68c758_0 +giflib 5.2.1 h7b6447c_0 +glib 2.68.2 h36276a3_0 +gobject-introspection 1.68.0 py39h2109141_1 +graphite2 1.3.14 h295c915_1 +gst-plugins-base 1.18.4 hf529b03_2 conda-forge +gstreamer 1.18.4 h76c114f_2 conda-forge +gtk2 2.24.33 h539f30e_1 conda-forge +h5py 3.6.0 py39ha0f2276_0 +harfbuzz 2.8.1 h83ec7ef_0 conda-forge +hdf5 1.10.6 hb1b8bf9_0 +icu 68.1 h2531618_0 +idna 3.3 pyhd3eb1b0_0 +intel-openmp 2021.4.0 h06a4308_3561 +ipykernel 6.9.1 py39h06a4308_0 +ipython 8.3.0 py39h06a4308_0 +jbig 2.1 hdba287a_0 +jedi 0.18.1 py39h06a4308_1 +jpeg 9e h7f8727e_0 +jungfrau_utils 3.7.2 py_0 paulscherrerinstitute +jupyter_client 7.2.2 py39h06a4308_0 +jupyter_core 4.10.0 py39h06a4308_0 +kiwisolver 1.4.2 py39h295c915_0 +krb5 1.19.2 hac12032_0 +lcms2 2.12 h3be6417_0 +ld_impl_linux-64 2.38 h1181459_1 +lerc 2.2.1 h2531618_0 +libclang 11.1.0 default_ha53f305_1 conda-forge +libdeflate 1.7 h27cfd23_5 +libedit 3.1.20210910 h7f8727e_0 +libevent 2.1.10 hcdb4288_3 conda-forge +libffi 3.3 he6710b0_2 +libgcc-ng 11.2.0 h1234567_1 +libgfortran-ng 7.5.0 ha8ba4b0_17 +libgfortran4 7.5.0 ha8ba4b0_17 +libglib 2.68.2 h3e27bee_0 conda-forge +libglu 9.0.0 hf484d3e_1 +libgomp 11.2.0 h1234567_1 +libiconv 1.16 h7f8727e_2 +libllvm11 11.1.0 h3826bc1_1 +libogg 1.3.5 h27cfd23_1 +libopenblas 0.3.18 hf726d26_0 +libopus 1.3.1 h7b6447c_0 +libpng 1.6.37 hbc83047_0 +libpq 13.3 hd57d9b9_0 conda-forge +libsodium 1.0.18 h7b6447c_0 +libstdcxx-ng 11.2.0 h1234567_1 +libtiff 4.3.0 hf544144_1 conda-forge +libuuid 1.0.3 h7f8727e_2 +libvorbis 1.3.7 h7b6447c_0 +libwebp 1.2.2 h55f646e_0 +libwebp-base 1.2.2 h7f8727e_0 +libxcb 1.15 h7f8727e_0 +libxkbcommon 1.0.3 he3ba5ed_0 conda-forge +libxml2 2.9.12 h72842e0_0 conda-forge +libxslt 1.1.33 h15afd5d_2 conda-forge +llvmlite 0.38.0 py39h4ff587b_0 +logzero 1.7.0 py39hf3d152e_1 conda-forge +lxml 4.6.3 py39h107f48f_0 conda-forge +lz4-c 1.9.3 h295c915_1 +matplotlib 3.5.1 py39h06a4308_1 +matplotlib-base 3.5.1 py39ha18d171_1 +matplotlib-inline 0.1.2 pyhd3eb1b0_2 +mflow 0.2.0 pyh39e3cac_2 paulscherrerinstitute +mkl 2021.4.0 h06a4308_640 +mkl-service 2.4.0 py39h7f8727e_0 +mkl_fft 1.3.1 py39hd3c417c_0 +mkl_random 1.2.2 py39h51133e4_0 +munkres 1.1.4 py_0 +mypy_extensions 0.4.3 py39h06a4308_1 +mysql-common 8.0.25 ha770c72_2 conda-forge +mysql-libs 8.0.25 hfa10184_2 conda-forge +ncurses 6.3 h5eee18b_3 +nest-asyncio 1.5.5 py39h06a4308_0 +ninja 1.10.2 h06a4308_5 +ninja-base 1.10.2 hd09550d_5 +nspr 4.33 h295c915_0 +nss 3.74 h0370c37_0 +numba 0.55.1 py39h51133e4_0 +numexpr 2.8.3 py39h807cd23_0 +numpy 1.21.5 py39h6c91a56_3 +numpy-base 1.21.5 py39ha15fc14_3 +openssl 1.1.1p h5eee18b_0 +packaging 21.3 pyhd3eb1b0_0 +pandas 1.4.2 py39h295c915_0 +pango 1.48.5 hb8ff022_0 conda-forge +parso 0.8.3 pyhd3eb1b0_0 +passlib 1.7.4 pyhd3eb1b0_0 +pathlib2 2.3.6 py39h06a4308_2 +pathspec 0.7.0 py_0 +pcre 8.45 h295c915_0 +pexpect 4.8.0 pyhd3eb1b0_3 +pickleshare 0.7.5 pyhd3eb1b0_1003 +pillow 9.0.1 py39h22f2fdc_0 +pip 21.2.4 py39h06a4308_0 +pixman 0.40.0 h7f8727e_1 +prompt-toolkit 3.0.20 pyhd3eb1b0_0 +pthread-stubs 0.3 h0ce48e5_1 +ptyprocess 0.7.0 pyhd3eb1b0_2 +pure_eval 0.2.2 pyhd3eb1b0_0 +pycparser 2.21 pyhd3eb1b0_0 +pyepics 3.4.3 py_0 paulscherrerinstitute +pygments 2.11.2 pyhd3eb1b0_0 +pyopenssl 22.0.0 pyhd3eb1b0_0 +pyparsing 3.0.4 pyhd3eb1b0_0 +pypubsub 4.0.3 py_0 conda-forge +pyqt 5.12.3 py39hf3d152e_7 conda-forge +pyqt-impl 5.12.3 py39h0fcd23e_7 conda-forge +pyqt5-sip 4.19.18 py39he80948d_7 conda-forge +pyqtchart 5.12 py39h0fcd23e_7 conda-forge +pyqtwebengine 5.12.1 py39h0fcd23e_7 conda-forge +pysocks 1.7.1 py39h06a4308_0 +python 3.9.12 h12debd9_1 +python-dateutil 2.8.2 pyhd3eb1b0_0 +python-levenshtein 0.12.2 py39h27cfd23_0 +python_abi 3.9 2_cp39 conda-forge +pytz 2022.1 py39h06a4308_0 +pyzmq 22.3.0 py39h295c915_2 +qt 5.12.9 hda022c4_4 conda-forge +readline 8.1.2 h7f8727e_1 +regex 2022.3.15 py39h7f8727e_0 +requests 2.28.0 py39h06a4308_0 +rich 10.16.2 pyhd3eb1b0_0 +scipy 1.7.3 py39hc147768_0 +setuptools 61.2.0 py39h06a4308_0 +sfdata 0.2.8 py_0 paulscherrerinstitute +six 1.16.0 pyhd3eb1b0_1 +slic 0.0.3 pypi_0 pypi +sqlite 3.38.5 hc218d9a_0 +stack_data 0.2.0 pyhd3eb1b0_0 +tbb 2021.5.0 hd09550d_0 +termcolor 1.1.0 py39h06a4308_1 +tk 8.6.12 h1ccaba5_0 +toml 0.10.2 pyhd3eb1b0_0 +tornado 6.1 py39h27cfd23_0 +tqdm 4.64.0 py39h06a4308_0 +traitlets 5.1.1 pyhd3eb1b0_0 +typed-ast 1.4.3 py39h7f8727e_1 +typing-extensions 4.1.1 hd3eb1b0_0 +typing_extensions 4.1.1 pyh06a4308_0 +tzdata 2022a hda174b7_0 +urllib3 1.26.9 py39h06a4308_0 +wcwidth 0.2.5 pyhd3eb1b0_0 +wheel 0.37.1 pyhd3eb1b0_0 +wxpython 4.1.1 py39h09f47c6_1 conda-forge +xarray 0.20.1 pyhd3eb1b0_1 +xz 5.2.5 h7f8727e_1 +yaspin 2.1.0 pyhd8ed1ab_0 conda-forge +zeromq 4.3.4 h2531618_0 +zlib 1.2.12 h7f8727e_2 +zstd 1.5.2 ha4553b6_0 diff --git a/knife_edge.py b/knife_edge.py new file mode 100644 index 0000000..1be0f04 --- /dev/null +++ b/knife_edge.py @@ -0,0 +1,7 @@ +from slic.devices.simpledevice import SimpleDevice +from slic.devices.general.motor import Motor + +mot_x = Motor("SAR-EXPMX:MOT_FX") +mot_y = Motor("SAR-EXPMX:MOT_FY") + +KnifeEdge = SimpleDevice("2D Knife edge stage", x=mot_x, y=mot_y) diff --git a/smaract.py b/smaract.py new file mode 100644 index 0000000..a1b3be4 --- /dev/null +++ b/smaract.py @@ -0,0 +1,14 @@ +from slic.devices.general.smaract import SmarActStage + +# this currently uses a modified SmarActStage module +# otherwise the wait times are not working correctly. + +smaract = SmarActStage("SARES30-XSMA156", + X='SARES30-XSMA156:X', + Y='SARES30-XSMA156:Y', + Z='SARES30-XSMA156:Z', + Ry='SARES30-XSMA156:Ry', + Rx='SARES30-XSMA156:Rx', + Rz='SARES30-XSMA156:Rz', + ) + diff --git a/spreadsheet.py b/spreadsheet.py new file mode 100644 index 0000000..bc87e02 --- /dev/null +++ b/spreadsheet.py @@ -0,0 +1,130 @@ +from slic.core.adjustable import PVAdjustable, PVEnumAdjustable +from slic.devices.simpledevice import SimpleDevice +from slic.utils import as_shortcut + + +class PVStringAdjustable(PVAdjustable): + def get_current_value(self): + return self.pvs.readback.get(as_string=True).strip() + + + +n_unds = [ + 6, 7, 8, 9, 10, 11, 12, 13, # 14 is the CHIC + 15, 16, 17, 18, 19, 20, 21, 22 +] + +undulator_info = {} +for i in n_unds: + undulator_info[f"energy{i}"] = PVAdjustable(f"SATUN{i:02}-UIND030:FELPHOTENE", internal=True) + undulator_info[f"polarisation{i}"] = PVEnumAdjustable(f"SATUN{i:02}-UIND030:POL-SET", internal=True) + + + +overview = SimpleDevice("Maloja Overview", +# standa = standa, +# exp_delay = exp_delay, +# laser_delay = laser_delay, +# LXT = lxt, + + FELrepRate = PVAdjustable("SWISSFEL-STATUS:Bunch-2-Appl-Freq-RB", internal=True), + PaddleChamber1x = PVAdjustable("SATES21-XSMA166:MOT1:MOTRBV", internal=True), + PaddleChamber1y = PVAdjustable("SATES21-XSMA166:MOT2:MOTRBV", internal=True), + PaddleChamber1z = PVAdjustable("SATES21-XSMA166:MOT3:MOTRBV", internal=True), + SmartActTTx = PVAdjustable("SATES22-XSMA168:MOT10:MOTRBV", internal=True), + SmartActTTy = PVAdjustable("SATES22-XSMA168:MOT11:MOTRBV", internal=True), + SmartActTTz = PVAdjustable("SATES22-XSMA168:MOT12:MOTRBV", internal=True), + ToFV1m = PVAdjustable("SATES21-XSHV166:V-1-S-CH0", internal=True), + ToFV1p = PVAdjustable("SATES21-XSHV166:V-0-S-CH0", internal=True), + ToFV2m = PVAdjustable("SATES21-XSHV166:V-1-S-CH1", internal=True), + ToFV2p = PVAdjustable("SATES21-XSHV166:V-0-S-CH1", internal=True), + ToFV3m = PVAdjustable("SATES21-XSHV166:V-1-S-CH2", internal=True), + ToFV3p = PVAdjustable("SATES21-XSHV166:V-0-S-CH2", internal=True), +# energy1 = PVAdjustable("SATUN06-UIND030:FELPHOTENE", internal=True), +# energy2 = PVAdjustable("SATUN15-UIND030:FELPHOTENE", internal=True), + manip2needleESx = PVAdjustable("SATES20-MANIP2:MOTOR_1.VAL", internal=True), + manip2needleESy = PVAdjustable("SATES20-MANIP2:MOTOR_2.VAL", internal=True), + manip2needleESz = PVAdjustable("SATES20-MANIP2:MOTOR_3.VAL", internal=True), +# pol1 = PVEnumAdjustable("SATUN06-UIND030:POL-SET", internal=True), +# pol2 = PVEnumAdjustable("SATUN15-UIND030:POL-SET", internal=True), + pressChamb2 = PVAdjustable("SATES21-VM-VT2020:PRESSURE", internal=True), + pressChamb3 = PVAdjustable("SATES21-VM-VT3010:PRESSURE", internal=True), + pressChamb3GasCell = PVAdjustable("SATES21-VM-VT3030:PRESSURE", internal=True), + pulse_energy = PVAdjustable("SATFE10-PEPG046:PHOTON-ENERGY-PER-PULSE-AVG", internal=True), + timeStamp = PVAdjustable("SF-CPCL-TIM:TIME", internal=True), + + chicane_current_rb = PVAdjustable("SATUN14-MBND100:I-READ", internal=True), + chicane_current_sv = PVAdjustable("SATUN14-MBND100:I-SET", internal=True), + + att64 = PVStringAdjustable("SATFE10-OATT064:MOT2TRANS.VALD", internal=True), + att65 = PVStringAdjustable("SATFE10-OATT065:MOT2TRANS.VALD", internal=True), + + **undulator_info +) + + + +spreadsheet_line = [ + "timeStamp", "File name", + "Gas cell / TOF", + "standa","Sample", + "pressChamb3", + "pressChamb3GasCell", "Static/scan", "Scan parameter", "ScanStep", "shots", "Comments", "Two colors (Y/N)", "energy1", + "polarisation10", "energy2", + "polarisation19", + "pulse_energy", + "chicane_current_rb", + "FELrepRate", + "att64", + "att65", "Grating", "order", "Slit", "Detector position X", "Detector position Y", "Detector position (angle)","Ek", "Ep", "Slit", "Mode", + "pressChamb2", "Gas", + "manip2needleESx", + "manip2needleESy", + "manip2needleESz", + "ToFV1p", + "ToFV2p", + "ToFV3p", + "ToFV1m", + "ToFV2m", + "ToFV3m", + "PaddleChamber1x", + "PaddleChamber1y", + "PaddleChamber1z", + "energy6", + "energy7", + "energy8", + "energy9", + "energy10", + "energy11", + "energy12", + "energy13", + "energy14", + "energy15", + "energy16", + "energy17", + "energy18", + "energy19", + "energy20", + "energy21", + "energy22", +] + + + +@as_shortcut +def print_overview(): + print(overview) + +@as_shortcut +def print_line_for_spreadsheet(): + ov = overview.__dict__ + def get(i): + if i in ov: + return str(ov[i].get()) + return "" + res = [get(i) for i in spreadsheet_line] + res = ",".join(res) + print(res) + + + diff --git a/undulator.py b/undulator.py new file mode 100644 index 0000000..340413e --- /dev/null +++ b/undulator.py @@ -0,0 +1,413 @@ +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 + +# from Alvra mono calibration +# energy_offset = 20.37839 + +# Cristallina without calibration +energy_offset = 0.0 + +# move the PSSS motor according to the energy +# TODO: improve this hack +PSSS_MOVE = False + +def set_PSSS_energy(energy:float): + """ When scanning the energy with the undulator we + adjust the spectrometer to follow. + """ + print("Adjusting PSSS") + 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.") + +class Undulators(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, value, hold=False): + value = value + energy_offset + 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(): + # 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: + set_PSSS_energy(value) + + 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 + +# 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 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(): + res = PVEnumAdjustable("SARUN:REF-UND").get() + if not res.startswith("SARUN"): + return None + n = len("SARUN") + res = res[n:] + try: + res = int(res) + except ValueError: + return None + return res + + + + +