move ESF_RIXS.py from git@git.psi.ch:epics_ioc_modules/ESF_RIXS.git
This commit is contained in:
1033
RIXSconfig/ESF_RIXS.py
Executable file
1033
RIXSconfig/ESF_RIXS.py
Executable file
File diff suppressed because it is too large
Load Diff
61
RIXSconfig/ESF_RIXSconfig.py
Normal file
61
RIXSconfig/ESF_RIXSconfig.py
Normal file
@@ -0,0 +1,61 @@
|
||||
import numpy as np
|
||||
|
||||
class Config:
|
||||
#_lutProbes={
|
||||
# '<key>':{
|
||||
# 'R':<radius of the grating mirror>
|
||||
# 'a0':<lines/mm>,'a1':<lines/mm^2>,'a2':<lines/mm^3>,'a3':<lines/mm^4> # polynome for line density of the mirror
|
||||
# 'eqs'{ #<equation solving parameters>
|
||||
# 'k': <deflection order (default=1)>,
|
||||
# 'lut': [# lookup table for starting guess values at a given energy for equation solver
|
||||
# ( <energy (eV)>, <r1(mm)>, <r2(mm)>, <alpha(rad)>),
|
||||
# ( ... ),
|
||||
# ],
|
||||
# },
|
||||
# },
|
||||
# '<key>':{
|
||||
# ...
|
||||
# }
|
||||
#}
|
||||
|
||||
_lutProbes={
|
||||
'g80_1':{ # grating-key { radius of the grating mirror, polynome for line density of the mirror
|
||||
'R':65144.054, 'a0':1160.759,'a1':0.3409,'a2':-3.74E-5,'a3':1.98E-7,
|
||||
'e':(300,1600), #optional energy range min max
|
||||
'esp':{'k': 1, # equation solving parameters { deflection order
|
||||
'lut': [ # lookup table for starting guess values at a given energy for equation solver
|
||||
(300, 1193, 4127, np.deg2rad(88)), # energy(eV), r1(mm), r2(mm), alpha(rad),
|
||||
],},},
|
||||
'g80_2':{
|
||||
'R':65144.054, 'a0':1160.759,'a1':0.3409,'a2':-3.74E-5,'a3':1.98E-7,
|
||||
'esp':{'k': 2,
|
||||
'lut': [
|
||||
(350, 1045, 4557, np.deg2rad(88)),
|
||||
(500, 1398, 3763, np.deg2rad(88)),
|
||||
],},},
|
||||
'g80_3':{
|
||||
'R':65144.054, 'a0':1160.759,'a1':0.3409,'a2':-3.74E-5,'a3':1.98E-7,
|
||||
'esp':{'k': 3,
|
||||
'lut': [
|
||||
(400, 988, 4778, np.deg2rad(88)),
|
||||
(800, 1156, 4218, np.deg2rad(88)),
|
||||
],},},
|
||||
'gtst_1':{
|
||||
'R':65144.054, 'a0':1360.759,'a1':0.3409,'a2':-3.74E-5,'a3':1.98E-7,
|
||||
'e':(1500,1900), #optional energy range min max
|
||||
'esp':{'k': 1,
|
||||
'lut': [
|
||||
(300, 1133, 4468, np.deg2rad(88)),
|
||||
],},},
|
||||
}
|
||||
|
||||
_spectrometer={
|
||||
'vis':{'ofs':(500, 500),},
|
||||
'probes':{ # upstream 2x6
|
||||
'80meV' :{'probe':'g80_1','txy':(0., 0.),}, # txy='sample motor positions for probe'
|
||||
'80meV_k=2' :{'probe':'g80_2','txy':(0., 1.),}, # ofs= offsets for various motors
|
||||
'80meV_k=3' :{'probe':'g80_3','txy':(0., 2.),},
|
||||
'gratingTest1':{'probe':'gtst_1','txy':(0., 3.),},
|
||||
},
|
||||
}
|
||||
|
||||
169
RIXSconfig/ESF_RIXSgeometry.py
Normal file
169
RIXSconfig/ESF_RIXSgeometry.py
Normal file
@@ -0,0 +1,169 @@
|
||||
#!/usr/bin/env python
|
||||
# *-----------------------------------------------------------------------*
|
||||
# | |
|
||||
# | Copyright (c) 2022 by Paul Scherrer Institute (http://www.psi.ch) |
|
||||
# | |
|
||||
# | Author Thierry Zamofing (thierry.zamofing@psi.ch) |
|
||||
# *-----------------------------------------------------------------------*
|
||||
'''
|
||||
coordinate systems, optical center, xray axis, pixel sizes etc.
|
||||
|
||||
ppm= pixel per mm
|
||||
update_pix2pos
|
||||
|
||||
modes:
|
||||
0x01: update_pix2pos
|
||||
0x02: update_optical_center
|
||||
'''
|
||||
import logging
|
||||
_log=logging.getLogger(__name__)
|
||||
|
||||
import numpy as np
|
||||
from scipy.optimize import fsolve
|
||||
from ESF_RIXSconfig import Config
|
||||
|
||||
|
||||
|
||||
class VLSgrating: #Gratings with Variable Line Density
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def setup(self,key):
|
||||
self._param=Config._lutProbes[key]
|
||||
|
||||
# ---------- eugenio calculation functions ----------
|
||||
def solve_focus_equ(self, eV):
|
||||
esp=self._param['esp']
|
||||
p0=esp['lut'][0][1:] # fsolve starting values for [r1, r2, alpha]
|
||||
esp['En']=eV # Update the dictionary with the energy at which I want to calculate the positions
|
||||
esp['Lam_nm']=1239.842/eV # Convert energy to wavelength
|
||||
pn=fsolve(self.equations, p0, maxfev=100000000)
|
||||
#es['r1'], es['r2'], es['alpha']=pn
|
||||
return pn
|
||||
|
||||
def equations(self, pn): # system of 3 equations for 3 parameters
|
||||
return (self.Eq1(*pn), self.Eq2(*pn), self.Eq4(*pn))
|
||||
|
||||
def Eq1(self, r1, r2, alpha):
|
||||
p=self._param;esp=p['esp']
|
||||
a1, R, a0, k, Lam_nm=p['a1'], p['R'], p['a0'], esp['k'], esp['Lam_nm']
|
||||
beta=self.beta(r1, r2, alpha)
|
||||
return np.cos(alpha)**2/r1+np.cos(beta)**2/r2-\
|
||||
(np.cos(alpha)+np.cos(beta))/R-a1*k*Lam_nm/1e6
|
||||
|
||||
def Eq2(self, r1, r2, alpha):
|
||||
p=self._param;esp=p['esp']
|
||||
a0, a1, a2, R, k, Lam_nm=p['a0'], p['a1'], p['a2'], p['R'], esp['k'], esp['Lam_nm']
|
||||
beta=self.beta(r1, r2, alpha)
|
||||
return np.sin(alpha)/r1*(np.cos(alpha)**2/r1-np.cos(alpha)/R)-\
|
||||
np.sin(beta)/r2*(
|
||||
np.cos(beta)**2/r2-np.cos(beta)/R)+2*a2*k*Lam_nm/1e6/3
|
||||
|
||||
def Eq4(self, r1, r2, alpha):
|
||||
p=self._param;esp=p['esp']
|
||||
a3, R, k, Lam_nm, a0=p['a3'], p['R'], esp['k'], esp['Lam_nm'], p['a0']
|
||||
beta=self.beta(r1, r2, alpha)
|
||||
return 4*np.sin(alpha)**2/r1**2*(np.cos(alpha)**2/r1-np.cos(alpha)/R)\
|
||||
-1/r1*(np.cos(alpha)**2/r1-np.cos(alpha)/R)**2+1/R**2*(1/r1-np.cos(alpha)/R)\
|
||||
+4*np.sin(beta)**2/r2**2*(
|
||||
np.cos(beta)**2/r2-np.cos(beta)/R)\
|
||||
-1/r2*(np.cos(beta)**2/r2-np.cos(beta)/R)**2\
|
||||
+1/R**2*(1/r2-np.cos(beta)/R)-2*a3*k*Lam_nm/1e6
|
||||
|
||||
def beta(self, r1, r2, alpha): # calculate beta as a function of alpha, energy, and diffraction order (k)
|
||||
p=self._param;esp=p['esp']
|
||||
a0, k, Lam_nm=p['a0'], esp['k'], esp['Lam_nm']
|
||||
return np.arcsin(np.sin(alpha)-a0*k*Lam_nm/1e6)
|
||||
|
||||
def gamma(self, r1, r2, alpha): # compute detector lean angle in respect of the r2 arm
|
||||
p=self._param;esp=p['esp']
|
||||
R, a0, a1=p['R'], p['a0'], p['a1']
|
||||
beta=self.beta(r1, r2, alpha)
|
||||
return np.arctan(np.cos(beta)/(2*np.sin(beta)-r2*(np.tan(beta)/R+a1/a0)))
|
||||
|
||||
|
||||
def energy2geometry(self, energy,probe):
|
||||
# mode raytrace or interpolate lut
|
||||
# prec precision requitements (precision iteration)
|
||||
# returns R1,R2,aa,bb,cc
|
||||
pn=self.solve_focus_equ(energy)
|
||||
r1, r2, alpha=pn
|
||||
beta=self.beta(*pn)
|
||||
gamma=self.gamma(*pn)
|
||||
geo={'r1':r1,'r2':r2,'aa':np.rad2deg(alpha), 'bb':np.rad2deg(beta), 'cc':np.rad2deg(gamma)}
|
||||
return geo
|
||||
|
||||
if __name__=="__main__":
|
||||
import argparse
|
||||
|
||||
logging.basicConfig(level=logging.DEBUG, format='%(levelname)s:%(module)s:%(lineno)d:%(funcName)s:%(message)s ')
|
||||
|
||||
#(h, t)=os.path.split(sys.argv[0]);cmd='\n '+(t if len(h)>20 else sys.argv[0])+' '
|
||||
#exampleCmd=('', '-m0xf -v0')
|
||||
epilog=__doc__ #+'\nExamples:'+''.join(map(lambda s:cmd+s, exampleCmd))+'\n'
|
||||
parser=argparse.ArgumentParser(epilog=epilog, formatter_class=argparse.RawDescriptionHelpFormatter)
|
||||
parser.add_argument("-m", "--mode", type=lambda x: int(x,0), help="mode (see bitmasks) default=0x%(default)x", default=0xff)
|
||||
|
||||
args=parser.parse_args()
|
||||
_log.info('Arguments:{}'.format(args.__dict__))
|
||||
|
||||
np.set_printoptions(suppress=True)
|
||||
np.set_printoptions(linewidth=196)
|
||||
|
||||
if args.mode&0x01:
|
||||
|
||||
|
||||
vlsg=VLSgrating()
|
||||
print(f'{"grating":>7s} | {"energy(eV)":>10s} | {"r1(mm)":>8s} | {"r2(mm)":>8s} | {"aa(°)":>5s} | {"bb(°)":>5s} | {"cc(°)":>5s}')
|
||||
for gr,eV in (
|
||||
('g80_1',500),
|
||||
('g80_2',500),
|
||||
('g80_3',500),
|
||||
('gtst_1',500),
|
||||
):
|
||||
vlsg.setup(gr)
|
||||
geo=vlsg.energy2geometry(eV)
|
||||
print('{gr:>7s} | {eV:10.4g} | {r1:8.2f} | {r2:8.2f} | {aa:5.2f} | {bb:5.2f} | {cc:5.2f}'.format(gr=gr,eV=eV,**geo))
|
||||
#print(g, vlsg.energy2geometry(e))
|
||||
|
||||
vlsg.setup('g80_1')
|
||||
Es=np.arange(250, 1550, 50)
|
||||
print('')
|
||||
print(f'{"energy(eV)":>10s} | {"r1(mm)":>8s} | {"r2(mm)":>8s} | {"aa(°)":>5s} | {"bb(°)":>5s} | {"cc(°)":>5s}')
|
||||
for eV in Es:
|
||||
geo=vlsg.energy2geometry(eV)
|
||||
print('{eV:10.4g} | {r1:8.2f} | {r2:8.2f} | {aa:5.2f} | {bb:5.2f} | {cc:5.2f}'.format(eV=eV,**geo))
|
||||
#pn=vlsg.solve_focus_equ(eV)
|
||||
#r1, r2, alpha=pn
|
||||
#beta=vlsg.beta(*pn)
|
||||
#gamma=vlsg.gamma(*pn)
|
||||
#print(f'{eV:10.4g} | {r1:8.2f} | {r2:8.2f} | {np.rad2deg(alpha):5.2f} | {np.rad2deg(beta):5.2f} | {np.rad2deg(gamma):5.2f}')
|
||||
|
||||
|
||||
#vlsg.setup('80meV')
|
||||
#[[ 250. 1137.32196015 4269.702359 88.0898184 83.55888668 36.27529716]
|
||||
# [ 300. 1193.65841126 4127.21279026 88.10217759 84.07300885 31.7396222 ]
|
||||
# [ 350. 1247.36978781 4010.83457789 88.10391338 84.46686259 28.55391769]
|
||||
# [ 400. 1299.07861098 3914.08632128 88.0986373 84.77926418 26.23765458]
|
||||
# [ 450. 1349.21078537 3832.65392519 88.08846973 85.03343956 24.51153466]
|
||||
# [ 500. 1398.0721341 3763.52172859 88.0747408 85.24424965 23.20495827]
|
||||
# [ 550. 1445.89064959 3704.49942068 88.05832818 85.42170416 22.20901965]
|
||||
# [ 600. 1492.84122971 3653.94623698 88.03983478 85.57282519 21.45157952]
|
||||
# [ 650. 1539.06120728 3610.60137571 88.01968853 85.70270455 20.88341654]
|
||||
# [ 700. 1584.66050296 3573.4750186 87.9982015 85.8151371 20.47018758]
|
||||
# [ 750. 1629.72855701 3541.77572852 87.9756065 85.91301734 20.18757719]
|
||||
# [ 800. 1674.33907781 3514.86055917 87.95208076 85.99859726 20.01827307]
|
||||
# [ 850. 1718.5535488 3492.19987783 87.92776146 86.07365932 19.95002982]
|
||||
# [ 900. 1762.42379391 3473.35198496 87.90275638 86.13963571 19.97440536]
|
||||
# [ 950. 1805.9938836 3457.94443997 87.87715139 86.19769259 20.08592919]
|
||||
# [1000. 1849.30170875 3445.66007701 87.85101563 86.24879078 20.28155755]
|
||||
# [1050. 1892.37950222 3436.22635374 87.82440571 86.29373053 20.5603321 ]
|
||||
# [1100. 1935.25594973 3429.40718162 87.79736786 86.33318446 20.92317777]
|
||||
# [1150. 1977.95569753 3424.99649949 87.76994068 86.36772314 21.37282654]
|
||||
# [1200. 2020.5004814 3422.81321829 87.74215652 86.39783427 21.91383066]
|
||||
# [1250. 2062.90951189 3422.6972001 87.71404265 86.42393782 22.55267071]
|
||||
# [1300. 2105.19981239 3424.50599743 87.6856223 86.44639783 23.29795403]
|
||||
# [1350. 2147.38653997 3428.11221941 87.6569154 86.46553179 24.16071358]
|
||||
# [1400. 2189.48325951 3433.40137902 87.62793911 86.4816182 25.15482162]
|
||||
# [1450. 2231.50212122 3440.27009676 87.59870838 86.49490264 26.29753817]
|
||||
# [1500. 2273.45406713 3448.62463224 87.56923627 86.50560273 27.61022401]]
|
||||
138
RIXSconfig/ESF_RIXSgirder.py
Normal file
138
RIXSconfig/ESF_RIXSgirder.py
Normal file
@@ -0,0 +1,138 @@
|
||||
|
||||
from PyQt5.QtWidgets import QApplication, QWidget
|
||||
from PyQt5.QtGui import QPainter, QColor
|
||||
import PyQt5.QtGui as QtGui
|
||||
import PyQt5.QtCore as QtCore
|
||||
import numpy as np
|
||||
|
||||
class WndGirderRIXS(QWidget):
|
||||
_lutColor={0:(255,0,0),1:(0,255,0),2:(255,255,0)}
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._param={'ctr':(200, 300), # location of big chamber
|
||||
'rCmb':80, # radius of chamber
|
||||
'rTrg':10, # radius of target
|
||||
'szSeal': (20,50),
|
||||
'szArm': (80,350),
|
||||
'aaSeal':70, # RIXS sliding angle
|
||||
'aaArm':70, # RIXS arm angle
|
||||
'airPads':0, # air pads (off=0 on=1 undef=2)
|
||||
'defComp':0, # deformation compensation (off=0 on=1 undef=2)
|
||||
}
|
||||
|
||||
self.initUI()
|
||||
|
||||
def initUI(self):
|
||||
self.setGeometry(300, 300, 850, 800)
|
||||
self.setWindowTitle('RIXS girder')
|
||||
#label = QLabel('Python', self)
|
||||
#label.move(50,50)
|
||||
#b1 = QPushButton("Button1",self)
|
||||
#b1.move(400,150)
|
||||
self._wdGrpDraw=w=QtGui.QGroupBox("Drawing",self)
|
||||
w.move(10,10)
|
||||
l=QtGui.QVBoxLayout(w)
|
||||
|
||||
sld={}
|
||||
for key,rng,tk in (('aaSeal',(0,180),5),('aaArm',(0,180),5),('airPads',(0,2),1),('defComp',(0,2),1),):
|
||||
sl=QtGui.QSlider(QtCore.Qt.Horizontal,objectName=key)
|
||||
sl.setFixedWidth(200);sl.setMinimum(rng[0]);sl.setMaximum(rng[1])
|
||||
sl.setValue(self._param[key])
|
||||
sl.setTickPosition(QtGui.QSlider.TicksBelow);sl.setTickInterval(tk)
|
||||
l.addWidget(sl)
|
||||
sl.valueChanged.connect(lambda val,key=key: self.sldChanged(key,val))
|
||||
sld[key]=sl
|
||||
|
||||
self.show()
|
||||
|
||||
def sldChanged(self,key,val,*args,**kwargs):
|
||||
p=self._param
|
||||
#print(key,val)
|
||||
if key=='aaArm':
|
||||
p['aaSeal']=v=p['aaSeal']-p['aaArm']+val
|
||||
wSl=self._wdGrpDraw.findChild(QtGui.QSlider, 'aaSeal')
|
||||
wSl.blockSignals(True)
|
||||
wSl.setValue(int(v))
|
||||
wSl.blockSignals(False)
|
||||
sl=QtGui.QSlider(QtCore.Qt.Horizontal,objectName=key)
|
||||
|
||||
p[key]=val
|
||||
self.update()
|
||||
|
||||
def paintEvent(self, e):
|
||||
p=self._param
|
||||
ctr=p['ctr']
|
||||
aaSeal=p['aaSeal']
|
||||
aaArm=p['aaArm']
|
||||
rCmb=p['rCmb']
|
||||
rTrg=p['rTrg']
|
||||
xa,ya=p['szArm']
|
||||
xs,ys=p['szSeal']
|
||||
ap=p['airPads']
|
||||
dc=p['defComp']
|
||||
|
||||
|
||||
qp = QPainter()
|
||||
qp.begin(self)
|
||||
qp.translate(ctr[0],ctr[1])
|
||||
tf0=qp.transform()
|
||||
qp.setPen(QtGui.QPen(QtCore.Qt.black, 2, QtCore.Qt.SolidLine))
|
||||
qp.setBrush(QColor(155, 155, 155, 128))
|
||||
br1=qp.brush()
|
||||
|
||||
|
||||
qp.drawEllipse(-rCmb, -rCmb, 2*rCmb, 2*rCmb) # big chamber
|
||||
qp.drawEllipse(-rTrg, -rTrg, 2*rTrg, 2*rTrg) # target
|
||||
qp.drawLine(0,-rCmb-50,0,-rTrg) #beam
|
||||
qp.rotate(-aaSeal)
|
||||
qp.translate(0,rCmb)
|
||||
tf1=qp.transform()
|
||||
xs2=xs//2
|
||||
xa2=xa//2
|
||||
ya2=ya//2
|
||||
d=int(abs(aaSeal-aaArm)*20)
|
||||
r=min(155+d,255)
|
||||
gb=max(155-d,0)
|
||||
qp.setBrush(QColor(r, gb, gb, 255))
|
||||
qp.drawRect(-xs2, 0, xs, ys) # seal bellow
|
||||
|
||||
qp.setBrush(br1)
|
||||
|
||||
qp.setTransform(tf0)
|
||||
qp.rotate(-aaArm)
|
||||
qp.translate(0,rCmb+ys)
|
||||
tf2=qp.transform()
|
||||
qp.drawRect(-xa2, 0, xa, ya) # girder
|
||||
#qp.drawEllipse(-ya2, 100+150, 20, 20) #pusher left
|
||||
#qp.drawEllipse( ya2-r, 100+150, 20, 20) #pusher right
|
||||
|
||||
#air pad
|
||||
r,g,b=WndGirderRIXS._lutColor[ap]
|
||||
rd=13
|
||||
r3=int(rd*np.sqrt(3))
|
||||
qp.setBrush(QColor(r, g, b, 255))
|
||||
qp.drawEllipse(-xa2-rd, ya-4*rd, 2*rd, 2*rd) #left
|
||||
qp.drawEllipse(-xa2-rd+r3,ya-3*rd, 2*rd, 2*rd) #left
|
||||
qp.drawEllipse(-xa2-rd+r3,ya-5*rd, 2*rd, 2*rd) #left
|
||||
|
||||
qp.drawEllipse( xa2-rd, ya-4*rd, 2*rd, 2*rd) #right
|
||||
qp.drawEllipse( xa2-rd-r3,ya-3*rd, 2*rd, 2*rd) #right
|
||||
qp.drawEllipse( xa2-rd-r3,ya-5*rd, 2*rd, 2*rd) #right
|
||||
|
||||
qp.drawEllipse( -rd, 3*rd, 2*rd, 2*rd) #grating
|
||||
qp.drawEllipse( 0, 3*rd-r3, 2*rd, 2*rd) #grating
|
||||
qp.drawEllipse( -2*rd, 3*rd-r3, 2*rd, 2*rd) #grating
|
||||
|
||||
qp.setBrush(br1)
|
||||
|
||||
#deformation compensation
|
||||
r,g,b=WndGirderRIXS._lutColor[dc]
|
||||
rd=14
|
||||
qp.setBrush(QColor(r, g, b, 255))
|
||||
qp.drawEllipse(-xa2, ya2, 2*rd, 2*rd) #air pad left
|
||||
qp.drawEllipse( xa2-2*rd, ya2, 2*rd, 2*rd) #air pad right
|
||||
#qp.drawEllipse( -rd, rd, 2*rd, 2*rd) #air pad grating
|
||||
qp.setBrush(br1)
|
||||
qp.end()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user