Commit fbad3d7e authored by bliss administrator's avatar bliss administrator
Browse files

texs controller initial commit

parent 11115dfb
Pipeline #31200 failed
import numpy as np
import id26.controllers.texs_calculations as tc
from bliss.controllers.motor import CalcController
"""
Bliss controller for TEXS spectrometer
(ID26)
Author: Mauro Rovezzi (mauro.rovezzi@esrf.eu) and Blanka Detlefs (blanka.detlefs@esrf.fr)
<!-- need initialization -->
########################################################################################
# these should be configuration parameters (to be changed by a user; something like
# dcm.dcm.xtals.xtal_sel = 'Si111'
CRYST_R = 240 # Rowland radius of the CA
CRYST_MAT = 'Si'
CRYST_HKL = (1,1,1)
########################################################################################
# needed to calculate d-spacing (ie. also possible to give directly dspacing like
# dcm_xtals.dspacing (in yml config file)
# these are ID26specific instrument parameters (constants until we rebuild the machine):
dtower_rot=35.
aW = 25;
aWext = 32;
rSext = 10;
aL = 97;
bender_version = 1
bender = (40., 60., 28.);
actuator=(300, 120);
<!-- REAL MOTORS -->
<axis name="theta_B" tags="real TR" />
<axis name="table_y" tags="real TYT" />
<axis name="det_rot" tags="real DR" />
<axis name="det_long" tags="real DY" />
<axis name="det_short" tags="real DZ" />
<axis name="actuator1" tags="real sag1" /> # this one has to be calculated too!
<axis name="actuator2" tags="real sag2" /> # this one has to be calculated too!
<!-- CALCULATED MOTORS -->
<axis name="xen1" tags="xes_en_texs">
# <axis name="xythe1" tags="xy_theta_eh1">
# fine also for bliss
"""
###################################################
#these cannot be GLOBAL VARIABLES !!!!!
###################################################
# do something like Gilles Berruyer in the dcm: bragg2energy
#============= GLOBAL VARIABLES ==================
HC = 1.2398418743309972e-06 # eV * m
ALAT_SI = 5.431065 # Ang at 25C
ALAT_GE = 5.6579060 # Ang at 25C
#======= for the bender_version = 1 (installed in June 2017) ==========
aW = 25
aWext = 32
rSext = 10
aL = 97
bender_version = 1
bender = (40., 60., 28.)
actuator=(300, 120)
dtower_rot=35.
#================== UTILITY FUNCTIONS =================================
def kev2wlen(energy):
""" convert photon energy (E, keV) to wavelength ($\lambda$, \AA$^{-1}$)"""
return((HC / energy) * 1e7)
def wlen2kev(wlen):
""" convert photon wavelength ($\lambda$, \AA$^{-1}$) to energy (E, keV)"""
return (HC / wlen) * 1e7
def theta_b(ene, d):
"""Bragg angle (rad) given energy (keV) and d-spacing (\AA)"""
if not (d == 0):
return np.arcsin((kev2wlen(ene)) / (2 * d))
else:
print("ERROR: d-spacing is 0")
return
def bragg_kev(theta, d):
"""energy (keV) given Bragg angle (deg) and d-spacing (\AA)"""
return wlen2kev(2 * d * np.sin(np.deg2rad(theta)))
def sqrt1over(d2m):
if (d2m == 0):
return 0
else:
return np.sqrt( 1 / d2m )
def d_cubic(a, hkl, **kws):
"""d-spacing for a cubic lattice"""
#print(hkl)
[h, k, l ]= hkl.split(' ')
d2m = (int(h)**2 + int(k)**2 + int(l)**2) / a**2
return sqrt1over(d2m)
def get_dspacing(mat, hkl):
"""get d-spacing for given crystal material and reflection (hkl)"""
if mat == 'Si':
dspacing = d_cubic(ALAT_SI, hkl)
elif mat == 'Ge':
dspacing = d_cubic(ALAT_GE, hkl)
else:
print("ERROR: available materials -> 'Si' 'Ge'")
dspacing = 0
return dspacing
#================== TEXS CALC CONTROLLER ==============================
class ID26TEXS(CalcController):
# probably first a generic TEXS and then ID26TEXS
def __init__(self, *args, **kwargs):
CalcController.__init__(self, *args, **kwargs)
self._cryst_r = 500
self._hkl = '1 1 1'
self._material = 'Si'
self._pos_dict = {}
self._update_rc()
def initialize_axis(self, axis):
CalcController.initialize_axis(self, axis)
def _update_rc(self):
self.rc = tc.RcHoriz(Rm=self._cryst_r, theta0=35, d=get_dspacing(self._material, self._hkl),\
aW=aW, aWext=aWext, rSext=rSext, aL=aL,\
bender_version=bender_version,\
bender=bender, actuator=actuator,
showInfos=True)
@property
def cryst_r(self):
return self._cryst_r
@cryst_r.setter
def cryst_r(self, value):
print("setting crystal_r")
self._cryst_r = value
self._update_rc()
@property
def hkl(self):
return map(int, self._hkl.split())
@hkl.setter
def hkl(self, value):
value_str = ' '.join(map(str, value))
print("setting hkl", value, value_str)
self._hkl = value_str
self._update_rc()
@property
def material(self):
return self._material
@material.setter
def material(self, value):
print("setting material")
self._material = value
self._update_rc()
def calc_from_real(self, positions_dict):
"""returns emission energy given real motors positions (only TR/ theta_Bragg used)
(INFO: method called when a real motor is moved)
"""
print("----> Entering calc_from_real" )
self._pos_dict = positions_dict #used to store ALL motors positions
#theta0 = positions_dict["TR"]+0.019415
theta0 = positions_dict["TR"]
print("theta0: ", theta0)
print("dispacing: ", get_dspacing(self._material, self._hkl))
xes_en_texs = bragg_kev(theta0, get_dspacing(self._material, self._hkl))
_virt_dict = {"xes_en_texs" : xes_en_texs}
self._pos_dict.update(_virt_dict)
print(" all motors positions are: ", positions_dict)
print("..... returning virtual motors: ", _virt_dict)
return _virt_dict
def calc_to_real(self, positions_dict):
"""returns real motors positions dictionary given the master value of TR
(INFO: method called when a real motor is moved)
"""
print("----> Entering calc_to_real")
self._pos_dict = positions_dict #used to store ALL motors positions
xes_en = positions_dict["xes_en_texs"]
theta0 = np.rad2deg(theta_b(xes_en,get_dspacing(self._material, self._hkl)))
print("theta0: ", theta0)
# init of rc should go into the __init__ section maybe
act_mot_pos = []
TYT = []
DY = []
DZ = []
DR = []
if isinstance(theta0, np.ndarray):
th0_list = list(theta0)
elif not isinstance(theta0, list):
th0_list = [theta0]
else:
th0_list = theta0
for th0 in th0_list:
print("working with theta = ", th0)
self.rc.set_theta0(th0)
# now calculate actuator
pb_in = self.rc.get_bender_pos(aN=5)
print("pb_in: ", pb_in)
act_mot_pos.append(self.rc.get_bender_mot(pb_in))
print("act_mot_pos: ", act_mot_pos)
TYT.append(self.rc.p)
print("TYT: ", TYT)
dpos = self.rc.get_det_pos()
print("dpos: ", dpos)
dpos2 = tc.det_pos_rotated(dpos, drot=dtower_rot)
DY.append(dpos2[0])
DZ.append(dpos2[1])
print("dy: ", DY, "dz: ", DZ)
DR.append(2.*th0)
_real_dict = {"TR" : np.array(theta0),
"sag1" : np.array(act_mot_pos),
"sag2" : np.array(act_mot_pos),
"TYT" : np.array(TYT),
"DY" : np.array(DY),
"DZ" : np.array(DZ),
"DR" : np.array(DR) }
self._pos_dict.update(_real_dict)
print("..... returning real motors:")
for k,v in self._pos_dict.items():
print(f"{k} = {v}")
return _real_dict
This diff is collapsed.
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment