This commit is contained in:
mohacsi_i 2022-11-15 19:09:21 +01:00
parent 5f251db891
commit cb1549cf2d

View File

@ -9,8 +9,16 @@ IMPORTANT: Virtual monochromator axes should be implemented already in EPICS!!!
import numpy as np
from math import isclose
from ophyd import (EpicsSignal, EpicsSignalRO, EpicsMotor, PseudoPositioner,
PseudoSingle, Device, Component, Kind)
from ophyd import (
EpicsSignal,
EpicsSignalRO,
EpicsMotor,
PseudoPositioner,
PseudoSingle,
Device,
Component,
Kind,
)
from ophyd.pseudopos import pseudo_position_argument, real_position_argument
from ophyd.sim import SynAxis, Syn2DGauss
@ -19,10 +27,10 @@ LN_CORR = 2e-4
def a2e(angle, hkl=[1, 1, 1], lnc=False, bent=False, deg=False):
"""Convert between angle and energy for Si monchromators
ATTENTION: 'angle' must be in radians, not degrees!
ATTENTION: 'angle' must be in radians, not degrees!
"""
lncorr = LN_CORR if lnc else 0.0
angle = angle*np.pi/180 if deg else angle
angle = angle * np.pi / 180 if deg else angle
# Lattice constant along direction
d0 = 5.43102 * (1.0 - lncorr) / np.linalg.norm(hkl)
@ -31,20 +39,18 @@ def a2e(angle, hkl=[1, 1, 1], lnc=False, bent=False, deg=False):
def e2w(energy):
"""Convert between energy and wavelength
"""
"""Convert between energy and wavelength"""
return 0.1 * 12398.42 / energy
def w2e(wwl):
"""Convert between wavelength and energy
"""
"""Convert between wavelength and energy"""
return 12398.42 * 0.1 / wwl
def e2a(energy, hkl=[1, 1, 1], lnc=False, bent=False):
"""Convert between energy and angle for Si monchromators
ATTENTION: 'angle' must be in radians, not degrees!
ATTENTION: 'angle' must be in radians, not degrees!
"""
lncorr = LN_CORR if lnc else 0.0
@ -68,20 +74,21 @@ class MonoMotor(PseudoPositioner):
Small wrapper to combine a real angular axis with the corresponding energy.
ATTENTION: 'angle' is in degrees, at least for PXIII
"""
# Real axis (in degrees)
angle = Component(EpicsMotor, "", name='angle')
# Virtual axis
energy = Component(PseudoSingle, name='energy')
_real = ['angle']
# Real axis (in degrees)
angle = Component(EpicsMotor, "", name="angle")
# Virtual axis
energy = Component(PseudoSingle, name="energy")
_real = ["angle"]
@pseudo_position_argument
def forward(self, pseudo_pos):
return self.RealPosition(angle=180.0*e2a(pseudo_pos.energy)/3.141592)
return self.RealPosition(angle=180.0 * e2a(pseudo_pos.energy) / 3.141592)
@real_position_argument
def inverse(self, real_pos):
return self.PseudoPosition(energy=a2e(3.141592*real_pos.angle/180.0))
return self.PseudoPosition(energy=a2e(3.141592 * real_pos.angle / 180.0))
class MonoDccm(PseudoPositioner):
@ -94,13 +101,13 @@ class MonoDccm(PseudoPositioner):
"""
# Real axis (in degrees)
th1 = Component(EpicsMotor, "ROX1", name='theta1')
th2 = Component(EpicsMotor, "ROX2", name='theta2')
th1 = Component(EpicsMotor, "ROX1", name="theta1")
th2 = Component(EpicsMotor, "ROX2", name="theta2")
# Virtual axes
en1 = Component(PseudoSingle, name='en1')
en2 = Component(PseudoSingle, name='en2')
energy = Component(PseudoSingle, name='energy', kind=Kind.hinted)
en1 = Component(PseudoSingle, name="en1")
en2 = Component(PseudoSingle, name="en2")
energy = Component(PseudoSingle, name="energy", kind=Kind.hinted)
# Other parameters
# feedback = Component(EpicsSignal, "MONOBEAM", name="feedback")
@ -110,23 +117,27 @@ class MonoDccm(PseudoPositioner):
@pseudo_position_argument
def forward(self, pseudo_pos):
"""WARNING: We have an overdefined system! Not sure if common crystal movement is reliable without retuning"""
if abs(pseudo_pos.energy-self.energy.position) > 0.0001 and abs(pseudo_pos.en1-self.en1.position) < 0.0001 and abs(pseudo_pos.en2-self.en2.position) < 0.0001:
if (
abs(pseudo_pos.energy - self.energy.position) > 0.0001
and abs(pseudo_pos.en1 - self.en1.position) < 0.0001
and abs(pseudo_pos.en2 - self.en2.position) < 0.0001
):
# Probably the common energy was changed
return self.RealPosition(
th1=-180.0 * e2a(pseudo_pos.energy) / 3.141592,
th2=180.0 * e2a(pseudo_pos.energy) / 3.141592
)
th2=180.0 * e2a(pseudo_pos.energy) / 3.141592,
)
else:
# Probably the individual axes was changes
return self.RealPosition(
th1=-180.0 * e2a(pseudo_pos.en1 / 3.141592,
th2=180.0 * e2a(pseudo_pos.en2) / 3.141592
)
th1=-180.0 * e2a(pseudo_pos.en1) / 3.141592,
th2=180.0 * e2a(pseudo_pos.en2) / 3.141592,
)
@real_position_argument
def inverse(self, real_pos):
return self.PseudoPosition(
en1=-a2e(3.141592 * real_pos.th1 / 180.0),
en2=a2e(3.141592 * real_pos.th2 / 180.0),
energy=-a2e(3.141592 * real_pos.th1 / 180.0)
)
energy=-a2e(3.141592 * real_pos.th1 / 180.0),
)