### # Copyright 2008-2011 Diamond Light Source Ltd. # This file is part of Diffcalc. # # Diffcalc is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Diffcalc is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Diffcalc. If not, see . ### from math import pi from diffcalc.util import DiffcalcException, differ TORAD = pi / 180 TODEG = 180 / pi class HklCalculatorBase(object): def __init__(self, ubcalc, geometry, hardware, raiseExceptionsIfAnglesDoNotMapBackToHkl=False): self._ubcalc = ubcalc # to get the UBMatrix, tau and sigma self._geometry = geometry # to access information about the # diffractometer geometry and mode_selector self._hardware = hardware # Used for tracking parameters only self.raiseExceptionsIfAnglesDoNotMapBackToHkl = \ raiseExceptionsIfAnglesDoNotMapBackToHkl def anglesToHkl(self, pos, wavelength): """ Return hkl tuple and dictionary of all virtual angles in degrees from Position in degrees and wavelength in Angstroms. """ h, k, l = self._anglesToHkl(pos.inRadians(), wavelength) paramDict = self.anglesToVirtualAngles(pos, wavelength) return ((h, k, l), paramDict) def anglesToVirtualAngles(self, pos, wavelength): """ Return dictionary of all virtual angles in degrees from Position object in degrees and wavelength in Angstroms. """ anglesDict = self._anglesToVirtualAngles(pos.inRadians(), wavelength) for name in anglesDict: anglesDict[name] = anglesDict[name] * TODEG return anglesDict def hklToAngles(self, h, k, l, wavelength): """ Return verified Position and all virtual angles in degrees from h, k & l and wavelength in Angstroms. The calculated Position is verified by checking that it maps back using anglesToHkl() to the requested hkl value. Those virtual angles fixed or generated while calculating the position are verified by by checking that they map back using anglesToVirtualAngles to the virtual angles for the given position. Throws a DiffcalcException if either check fails and raiseExceptionsIfAnglesDoNotMapBackToHkl is True, otherwise displays a warning. """ # Update tracked parameters. During this calculation parameter values # will be read directly from self._parameters instead of via # self.getParameter which would trigger another potentially time-costly # position update. self.parameter_manager.update_tracked() pos, virtualAngles = self._hklToAngles(h, k, l, wavelength) # in rad # to degrees: pos.changeToDegrees() for key, val in virtualAngles.items(): if val is not None: virtualAngles[key] = val * TODEG self._verify_pos_map_to_hkl(h, k, l, wavelength, pos) virtualAnglesReadback = self._verify_virtual_angles(h, k, l, wavelength, pos, virtualAngles) return pos, virtualAnglesReadback def _verify_pos_map_to_hkl(self, h, k, l, wavelength, pos): hkl, _ = self.anglesToHkl(pos, wavelength) e = 0.001 if ((abs(hkl[0] - h) > e) or (abs(hkl[1] - k) > e) or (abs(hkl[2] - l) > e)): s = "ERROR: The angles calculated for hkl=(%f,%f,%f) were %s.\n" % (h, k, l, str(pos)) s += "Converting these angles back to hkl resulted in hkl="\ "(%f,%f,%f)" % (hkl[0], hkl[1], hkl[2]) if self.raiseExceptionsIfAnglesDoNotMapBackToHkl: raise DiffcalcException(s) else: print s def _verify_virtual_angles(self, h, k, l, wavelength, pos, virtualAngles): # Check that the virtual angles calculated/fixed during the hklToAngles # those read back from pos using anglesToVirtualAngles virtualAnglesReadback = self.anglesToVirtualAngles(pos, wavelength) for key, val in virtualAngles.items(): if val != None: # Some values calculated in some mode_selector r = virtualAnglesReadback[key] if ((differ(val, r, .00001) and differ(val, r + 360, .00001) and differ(val, r - 360, .00001))): s = "ERROR: The angles calculated for hkl=(%f,%f,%f) with"\ " mode=%s were %s.\n" % (h, k, l, self.repr_mode(), str(pos)) s += "During verification the virtual angle %s resulting "\ "from (or set for) this calculation of %f" % (key, val) s += "did not match that calculated by "\ "anglesToVirtualAngles of %f" % virtualAnglesReadback[key] if self.raiseExceptionsIfAnglesDoNotMapBackToHkl: raise DiffcalcException(s) else: print s return virtualAnglesReadback def repr_mode(self): pass ### Collect all math access to context here def _getUBMatrix(self): return self._ubcalc.UB def _getMode(self): return self.mode_selector.getMode() def _getSigma(self): return self._ubcalc.sigma def _getTau(self): return self._ubcalc.tau def _getParameter(self, name): # Does not use context.getParameter as this will trigger a costly # parameter collection pm = self.parameter_manager return pm.getParameterWithoutUpdatingTrackedParemeters(name) def _getGammaParameterName(self): return self._gammaParameterName