###################################################################################################\ # Diffcalc utilities ################################################################################################### ###################################################################################################\ # Installaling ################################################################################################### #1- Download from: https://github.com/DiamondLightSource/diffcalc/archive/master.zip #2- Extract the contents to {script}/Lib/diffcalc #3- Download http://central.maven.org/maven2/gov/nist/math/jama/1.0.3/jama-1.0.3.jar # to the extensions folder. ###################################################################################################\ # Library loading and Hardware setup ################################################################################################### #1- Create a MotorGroup with the diffractometer motors # e.g. 'sixc', containing mu, delta, gam, eta, chi, phi motors (gam = nu) # or 'fivec', containing delta, gam, eta, chi, phi motors # or 'fourc', containing delta, eta, chi, phi motors #2- Create positioner to read/set the energy in kEv, e.g. named 'en' #3- Execute: run("diffutils") #4- Execute: setup_diff(sixc, en) from __future__ import absolute_import import traceback import Jama.Matrix diffcalc_path = os.path.abspath(expand_path("{script}/Lib/diffcalc")) if not diffcalc_path in sys.path: sys.path.append(diffcalc_path) import diffcalc from diffcalc import settings from diffcalc.hkl.you.geometry import YouGeometry,SixCircle, FiveCircle, FourCircle, YouPosition from diffcalc.hardware import HardwareAdapter from diffcalc.ub.persistence import UbCalculationNonPersister from diffcalc.gdasupport.minigda.scannable import ScannableBase, ScannableGroup #from diffcalc.gdasupport.minigda import command from diffcalc.hardware import HardwareAdapter import ch.psi.pshell.device.PositionerConfig as PositionerConfig import ch.psi.pshell.device.RegisterConfig as RegisterConfig import ch.psi.pshell.device.Register as Register _difcalc_names = {} # # Disable error handling designed for interactive use #diffcalc.util.DEBUG = True ################################################################################################### # Device mapping to difcalc ################################################################################################### class PositionerScannable(ScannableBase): def __init__(self, positioner, name = None): self.positioner = positioner self.name = positioner.name if name is None else name self.inputNames = [self.name] self.outputFormat = ['% 6.4f'] self.level = 3 def isBusy(self): return self.positioner.state == State.Busy def waitWhileBusy(self): self.positioner.waitReady(-1) def asynchronousMoveTo(self, new_position): #print "Moving " , self.name, " to: ", new_position self.positioner.moveAsync(float(new_position), -1) def getPosition(self): return self.positioner.getPosition() def _get_diffcalc_axis_names(): nu_name=diffcalc.hkl.you.constraints.NUNAME return ("mu", "delta", nu_name, "eta", "chi", "phi") class PositionerScannableGroup(ScannableGroup): def __init__(self, name, motors, diffcalc_axis_names=None): self.name = name global _difcalc_names _difcalc_names = {} positioners = [] if diffcalc_axis_names is None: if len(motors) == 6: diffcalc_axis_names = _get_diffcalc_axis_names() elif len(motors) == 5: diffcalc_axis_names = ("delta", "gam", "eta", "chi", " phi") elif len(motors) == 4: diffcalc_axis_names = ("delta", "eta", "chi", " phi") for i in range(len(motors)): _difcalc_names[motors[i]] = diffcalc_axis_names[i] exec('self.' + diffcalc_axis_names[i] + ' = PositionerScannable(' + motors[i].name + ', "' +diffcalc_axis_names[i] + '")') exec('positioners.append(self.' + diffcalc_axis_names[i] + ')' ) #for m in motors: # exec('self.' + m.name + ' = PositionerScannable(' + m.name + ', "' + m.name + '")') # exec('positioners.append(self.' + m.name + ')' ) ScannableGroup.__init__(self, self.name, positioners) class MotorGroupScannable(PositionerScannableGroup): def __init__(self, motor_group, diffcalc_axis_names=None): self.motor_group = motor_group PositionerScannableGroup.__init__(self, motor_group.name, motor_group.motors, diffcalc_axis_names) class ScannableAdapter(HardwareAdapter): def __init__(self, diffractometer, energy, energy_multiplier_to_kev=1): self.diffractometer = diffractometer self.energy = energy self.energy_multiplier_to_kev = energy_multiplier_to_kev input_names = diffractometer.getInputNames() HardwareAdapter.__init__(self, input_names) #Returns the current physical POSITIONS def get_position(self): """ pos = getDiffractometerPosition() -- returns the current physical diffractometer position as a list in degrees """ return self.diffractometer.getPosition() #returns energy in kEv def get_energy(self): """energy = get_energy() -- returns energy in kEv (NOT eV!) """ multiplier = self.energy_multiplier_to_kev energy = self.energy.getPosition() * multiplier if energy is None: raise DiffcalcException("Energy has not been set") return energy @property def name(self): return self.diffractometer.getName() class MotorGroupAdapter(ScannableAdapter): def __init__(self, diffractometer, energy, energy_multiplier_to_kev=1, diffcalc_axis_names=None): self.diffractometer = MotorGroupScannable(diffractometer, diffcalc_axis_names) self.energy = PositionerScannable(energy) self.energy.level = 3 ScannableAdapter.__init__(self, self.diffractometer, self.energy, energy_multiplier_to_kev) class Wavelength(RegisterBase): def doRead(self): try: return get_wavelength().getPosition() except: return None def doWrite(self, val): get_wavelength().asynchronousMoveTo(val) ################################################################################################### # HKL Pseudo-devices ################################################################################################### class HklPositoner (PositionerBase): def __init__(self, name, index, hkl_group): PositionerBase.__init__(self, name, PositionerConfig()) self.setParent(hkl_group) self.index = index def isReady(self): return PositionerBase.isReady(self) and self.getParent().isReady() def doRead(self): return self.getParent()._setpoint[self.index] def doWrite(self, value): #print "Setting " , self.getName(), "to: ", value pos = [None, None, None] pos[self.index] = value self.getParent().write(pos) def doReadReadback(self): if java.lang.Thread.currentThread() != self.getParent()._updating_thread: self.getParent().update() return self.getParent()._readback[self.index] class HklGroup(RegisterBase, Register.RegisterArray): def __init__(self, name): RegisterBase.__init__(self, name, RegisterConfig()) self.hkl=get_hkl() self.h, self.k, self.l = HklPositoner("h", 0, self), HklPositoner("k", 1, self), HklPositoner("l", 2, self) add_device(self.h, True) add_device(self.k, True) add_device(self.l, True) self._setpoint = self.doRead() self._updating = False def getSize(self): return 3 def doRead(self): try: self._readback = self.hkl.getPosition() self._updating_thread = java.lang.Thread.currentThread() self.h.update() self.k.update() self.l.update() except: #traceback.print_exc() self._readback = (None, None, None) finally: self._updating_thread = None return self._readback def doWrite(self, pos): self._setpoint = pos #print "Moving to: " + str(pos) self.hkl.asynchronousMoveTo(pos) def sim(self, pos): return self.hkl.simulateMoveTo(pos) ################################################################################################### # System setup ################################################################################################### you = None dc, ub, hardware, hkl = None, None, None, None _motor_group = None def setup_diff(diffractometer, energy, diffcalc_axis_names = None, geometry=None): """ diffractometer: Diffraction motor group energy: Positioner having energy in kev geometry: YouGeometry extension. If none, uses default diffcalc_axis_names: if None use defaults: - mu, delta, gam, eta, chi, phi (six circle) - delta, gam, eta, chi, phi (ficve circle) - delta, eta, chi, phi (four circle) """ global you, dc, ub, hardware, hkl, _motor_group _motor_group = diffractometer you = None if geometry is not None: settings.geometry = geometry elif diffcalc_axis_names is not None: class CustomGeometry(YouGeometry): def __init__(self): self.all_axis_names = _get_diffcalc_axis_names() self.my_axis_names = diffcalc_axis_names fixed_constraints = {} for axis in self.all_axis_names: if not axis in self.my_axis_names: fixed_constraints[axis] = 0 YouGeometry.__init__(self, diffractometer.name, fixed_constraints) def physical_angles_to_internal_position(self, physical_angle_tuple): pos=[] index = 0 for axis in self.all_axis_names: pos.append(physical_angle_tuple[index] if (axis in self.my_axis_names) else 0) index = index+1 return YouPosition(*pos) def internal_position_to_physical_angles(self, internal_position): pos = internal_position.totuple() ret = [] for i in range (len(self.all_axis_names)): if self.all_axis_names[i] in self.my_axis_names: ret.append(pos[i]) return tuple(ret) settings.geometry = CustomGeometry() elif len(diffractometer.motors) == 6: settings.geometry = SixCircle() elif len(diffractometer.motors) == 5: settings.geometry = FiveCircle() elif len(diffractometer.motors) == 4: settings.geometry = FourCircle() else: raise Exception("Invalid motor group") settings.hardware = MotorGroupAdapter(diffractometer, energy, diffcalc_axis_names = diffcalc_axis_names) settings.ubcalc_persister = UbCalculationNonPersister() settings.axes_scannable_group = settings.hardware.diffractometer settings.energy_scannable = settings.hardware.energy settings.ubcalc_strategy = diffcalc.hkl.you.calc.YouUbCalcStrategy() settings.angles_to_hkl_function = diffcalc.hkl.you.calc.youAnglesToHkl from diffcalc.gdasupport import you reload(you) # These must be imported AFTER the settings have been configured from diffcalc.dc import dcyou as dc from diffcalc.ub import ub from diffcalc import hardware from diffcalc.hkl.you import hkl add_device(HklGroup("hkl_group"), True) add_device(Wavelength("wavelength", 6), True) hkl_group.polling = 250 wavelength.polling = 250 def setup_axis(motor, min=None, max=None, cut=None): name = _difcalc_names[motor] if min is not None: hardware.setmin(name, min) if max is not None: hardware.setmax(name, max) if cut is not None: hardware.setcut(name, cut) def print_axis_setup(): print "Diffcalc names:" for m in _difcalc_names.keys(): print " \t" + m.name + " = " + _difcalc_names[m] print "------------------------------------------------------" hardware.hardware() ################################################################################################### # Acceess functions ################################################################################################### def get_diff(): return settings.hardware.diffractometer def get_en(): return settings.hardware.energy def get_motor_group(): return _motor_group def get_wavelength(): return you.wl def get_hkl(): return you.hkl def hkl_to_angles(h, k, l, energy=None): return dc.hkl_to_angles(h, k, l, energy) def angles_to_hkl(positions, energy=None): return dc.angles_to_hkl(positions, energy) def hkl_read(): return hkl_group.read() def hkl_write(h, k, l): hkl_group.write([h,k,l]) def hkl_simulate(h, k, l): return hkl_group.sim([h,k,l]) def con(*args): hkl.con(*args) def uncon(name): hkl.uncon(name) def print_con(): hkl.con() ################################################################################################### # HKL Combined Scan ################################################################################################### def hklscan(vector, readables,latency = 0.0, passes = 1, **pars): """ HKL Scan: Args: vector(list of lists): HKL values to be scanned readables(list of Readable): Sensors to be sampled on each step. latency(float, optional): settling time for each step before readout, defaults to 0.0. passes(int, optional): number of passes pars(keyworded variable length arguments, optional): scan optional named arguments: - title(str, optional): plotting window name. - hidden(bool, optional): if true generates no effects on user interface. - before_read (function, optional): callback on each step, before sampling. Arguments: positions, scan - after_read (function, optional): callback on each step, after sampling. Arguments: record, scan. - before_pass (function, optional): callback before each scan pass execution. Arguments: pass_num, scan. - after_pass (function, optional): callback after each scan pass execution. Arguments: pass_num, scan. - Aditional arguments defined by set_exec_pars. Returns: ScanResult object. """ readables=to_list(string_to_obj(readables)) pars["initial_move"] = False scan = ManualScan([h,k,l], readables ,vector[0], vector[-1], [len(vector)-1] * 3, dimensions = 1) if not "domain_axis" in pars.keys(): pars["domain_axis"] = "Index" processScanPars(scan, pars) scan.start() try: for pos in vector: #print "Writing ", pos hkl_group.write(pos) time.sleep(0.1) #Make sure is busy get_motor_group().update() get_motor_group().waitReady(-1) time.sleep(latency) hkl_group.update() scan.append ([h.take(), k.take(), l.take()], [h.getPosition(), k.getPosition(), l.getPosition()], [readable.read() for readable in readables ]) finally: scan.end() return scan.result def test_diffcalc(): print "Start test" en.move(20.0) delta.config.maxSpeed = 50.0 delta.speed = 50.0 delta.move(1.0) #Setup setup_diff(sixc, en) setup_axis('gam', 0, 179) setup_axis('delta', 0, 179) setup_axis('delta', min=0) setup_axis('phi', cut=-180.0) print_axis_setup() #Orientation help(ub.ub) ub.listub() # Create a new ub calculation and set lattice parameters ub.newub('test') ub.setlat('cubic', 1, 1, 1, 90, 90, 90) # Add 1st reflection (demonstrating the hardware adapter) settings.hardware.wavelength = 1 ub.c2th([1, 0, 0]) # energy from hardware settings.hardware.position = 0, 60, 0, 30, 0, 0 ub.addref([1, 0, 0])# energy and position from hardware # Add 2nd reflection (this time without the harware adapter) ub.c2th([0, 1, 0], 12.39842) ub.addref([0, 1, 0], [0, 60, 0, 30, 0, 90], 12.39842) # check the state ub.ub() ub.checkub() #Constraints help(hkl.con) hkl.con('qaz', 90) hkl.con('a_eq_b') hkl.con('mu', 0) hkl.con() #Motion print angles_to_hkl((0., 60., 0., 30., 0., 0.)) print hkl_to_angles(1, 0, 0) sixc.write([0, 60, 0, 30, 90, 0]) print "sixc=" , sixc.position wavelength.write(1.0) print "wavelength = ", wavelength.read() ub.lastub() ub.setu ([[1, 0, 0], [0, 1, 0], [0, 0, 1]]) ub.showref() ub.swapref(1,2) #print you.hkl #pos(get_hkl()) hkl_group.read() #you.hkl.simulateMoveTo([0,1,1]) #sim(get_hkl(), [0,1,1]) hkl_group.sim([0.0,1.0,1.0]) #pos(get_hkl(), [0,1,1]) hkl_group.write([0.0,1.0,1.0]) #Scans lscan(l, [sin], 1.0, 1.5, 0.1) ascan([k,l], [sin], [1.0, 1.0], [1.2, 1.3], [0.1, 0.1], zigzag=True, parallel_positioning = False) vector = [[1.0,1.0,1.0], [1.0,1.0,1.1], [1.0,1.0,1.2], [1.0,1.0,1.4]] hklscan(vector, [sin, arr], 0.9) # #run("diffutils") ###################################################################################################\ #Setup ################################################################################################### #alpha, delta, gamma, omegaV setup_diff(fourcv, energy, ("mu", "delta", "gam", "eta")) print_axis_setup() setup_axis(mu, mu.getMinValue(), mu.getMaxValue()) setup_axis(delta, delta.getMinValue(), 90) #delta.getMaxValue()) setup_axis(gamma, 0, gamma.getMaxValue()) setup_axis(eta, eta.getMinValue(), eta.getMaxValue()) wavelength.write(1.0) ###################################################################################################\ #Orientation ################################################################################################### help(ub.ub) ub.listub() #alpha delta gamma omegaV # Create a new ub calculation and set lattice parameters ub.newub('test') """ ub.setlat('cubic', 5.114, 5.8361, 11.058, 90, 90, 90) en = 8 #ub.c2th([0, 0, 4], en) #ub.addref([0, 0, 4]) #From current position and ekergy ub.addref([0, 0, 4], [16.2785, 0.0, 32.5568, 0.0], en) #ub.c2th([2, 0, 12], en) ub.addref([2, 0, 12], [71.8285, 37.3082, 138.7440, 0.0], en) #ub.c2th([1, -4, 10], en) ub.addref([1, -4, 10], [27.7185, 17.6409 , 128.4220, 0.0], en) """ ub.setlat('cubic', 1.0, 1.0, 1.0, 90, 90, 90) en = 12.4 #ub.c2th([0, 0, 4], en) #ub.addref([0, 0, 4]) #From current position and ekergy ub.addref([0, 0, 1], [30.0, 0.0, 60.0, 0.0], en) #ub.c2th([2, 0, 12], en) ub.addref([1, 0, 1], [20.0, 45.5564,90.000, 44.4437], en) #ub.c2th([1, -4, 10], en) ub.addref([0, 1, 1], [20.0, 45.5564,90.000, 134.4437], en) ub.ub() #ub.setub([[1.22862,0.00000,0.00000], [-0.00000,1.07663,0.00000], [-0.00000,-0.00000,0.56820]]) # check the state ub.checkub() ###################################################################################################\ #Constraints ################################################################################################### help(hkl.con) hkl.con('a_eq_b') #hkl.con('eta:0') #hkl.con( 'eta', 0) #OmegaV constant #hkl.con( 'mu', 20) #Alpha constant ###################################################################################################\ #Motion ################################################################################################### #print angles_to_hkl((16.278, 0.0000, 32.5568, 0.0)) #print angles_to_hkl((44.3400, 0.0000, 123.7322 , 0.0)) #print hkl_to_angles(2, -2, 10)