Initial slic setup at cristallina

This commit is contained in:
2022-09-06 14:32:45 +02:00
parent c86c64e96e
commit 00fe2b6844
10 changed files with 1699 additions and 0 deletions

293
PSSS_motion.py Normal file
View File

@ -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()

259
channels.py Normal file
View File

@ -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

260
channels_copy.py Normal file
View File

@ -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

15
cool_motor.py Normal file
View File

@ -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 ;)

101
cristallina.py Normal file
View File

@ -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.")

207
environment.txt Normal file
View File

@ -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

7
knife_edge.py Normal file
View File

@ -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)

14
smaract.py Normal file
View File

@ -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',
)

130
spreadsheet.py Normal file
View File

@ -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)

413
undulator.py Normal file
View File

@ -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