def makeReader(self): """Return a reader instance based on self """ # check validity of input self._check() # # Set up image names in right format # fullPath = lambda fn: os.path.join(self.imageDir, fn) numEmpty = lambda fn: self.imageNameD[fn][0] imgInfo = [(fullPath(f), numEmpty(f)) for f in self.imageNames] ref_reader = self.RC(imgInfo) # # Check for omega info # nfile = len(imgInfo) dinfo = [self.imageNameD[f] for f in self.imageNames] omin = dinfo[0][1] if omin is not None: odel = dinfo[nfile - 1][3] print "omega min and delta: ", omin, odel omargs = (valunits.valWUnit('omin', 'angle', float(omin), 'degrees'), valunits.valWUnit('odel', 'angle', float(odel), 'degrees')) else: omargs = () pass print 'omargs: ', omargs # # Dark file # subDark = not (self.darkMode == ReaderInput.DARK_MODE_NONE) if (self.darkMode == ReaderInput.DARK_MODE_FILE): drkFile = os.path.join(self.darkDir, self.darkName) elif (self.darkMode == ReaderInput.DARK_MODE_ARRAY): drkFileName = os.path.join(self.darkDir, self.darkName) drkFile = ref_reader.frame( buffer=numpy.fromfile(drkFileName, dtype=ref_reader.dtypeRead ) ) else: drkFile = None pass # # Flip options # doFlip = not (self.flipMode == ReaderInput.FLIP_NONE) flipArg = ReaderInput.FLIP_DICT[self.flipMode] # # Make the reader # print 'reader: \n', imgInfo, subDark, drkFile, doFlip, flipArg r = self.RC(imgInfo, *omargs, subtractDark = subDark, dark = drkFile, doFlip = doFlip, flipArg = flipArg) return r
def processWavelength(arg): """ Convert an energy value to a wavelength. If argument has units of length or energy, will convert to globally specified unit type for wavelength (dUnit). If argument is a scalar, assumed input units are keV. """ if hasattr(arg, "getVal"): if arg.isLength(): retval = arg.getVal(dUnit) elif arg.isEnergy(): try: speed = C.c planck = C.h except: raise NotImplementedError, "scipy does not have constants" # speed = ... # planck = ... e = arg.getVal("J") retval = valunits.valWUnit("wavelength", "length", planck * speed / e, "m").getVal(dUnit) else: raise RuntimeError, "do not know what to do with " + str(arg) else: keV2J = 1.0e3 * C.e e = keV2J * arg retval = valunits.valWUnit("wavelength", "length", C.h * C.c / e, "m").getVal(dUnit) return retval
def processWavelength(arg): """ Convert an energy value to a wavelength. If argument has units of length or energy, will convert to globally specified unit type for wavelength (dUnit). If argument is a scalar, assumed input units are keV. """ if hasattr(arg, 'getVal'): if arg.isLength(): retval = arg.getVal(dUnit) elif arg.isEnergy(): try: speed = C.c planck = C.h except: raise NotImplementedError, 'scipy does not have constants' # speed = ... # planck = ... e = arg.getVal('J') retval = valunits.valWUnit('wavelength', 'length', planck*speed/e, 'm').getVal(dUnit) else: raise RuntimeError, 'do not know what to do with '+str(arg) else: keV2J = 1.e3*C.e e = keV2J * arg retval = valunits.valWUnit('wavelength', 'length', C.h*C.c/e, 'm').getVal(dUnit) return retval
def materials(self): """Return a dict of materials.""" if not hasattr(self, '_materials'): self._materials = material.load_materials_hdf5( f=self.definitions, dmin=valWUnit("dmin", "length", self.dmin, "angstrom")) return self._materials
def OnDefaultWavelength(self, e): """Make the current wavelength the default for the session""" kev = float(self.waveKEV_txt.GetValue()) Material.DFLT_KEV = valWUnit('wavelength', 'energy', kev, 'keV') print 'new default for kev', Material.DFLT_KEV return
def __init__(self, material_file=None, material_keys=None, dmin=_nm(0.05), wavelength={ 'alpha1': [_nm(0.15406), 1.0], 'alpha2': [_nm(0.154443), 0.52] }): self.phase_dict = {} self.num_phases = 0 """ set wavelength. check if wavelength is supplied in A, if it is convert to nm since the rest of the code assumes those units """ wavelength_nm = {} for k, v in wavelength.items(): wavelength_nm[k] = [ valWUnit('lp', 'length', v[0].getVal('nm'), 'nm'), v[1] ] self.wavelength = wavelength_nm self.dmin = dmin if (material_file is not None): if (material_keys is not None): if (type(material_keys) is not list): self.add(material_file, material_keys) else: self.add_many(material_file, material_keys)
def save(self, filename): """ self.dataStore self.planeData self.iHKLList self.etaEdges self.omeEdges self.etas self.omegas """ args = np.array(self.planeData.getParams())[:4] args[2] = valWUnit('wavelength', 'length', args[2], 'angstrom') hkls = self.planeData.hkls save_dict = { 'dataStore': self.dataStore, 'etas': self.etas, 'etaEdges': self.etaEdges, 'iHKLList': self.iHKLList, 'omegas': self.omegas, 'omeEdges': self.omeEdges, 'planeData_args': args, 'planeData_hkls': hkls } np.savez_compressed(filename, **save_dict) return
def refine_grains(self, minCompl, nSubIter=3, doFit=False, etaTol=valunits.valWUnit('etaTol', 'angle', 1.0, 'degrees'), omeTol=valunits.valWUnit('etaTol', 'angle', 1.0, 'degrees'), fineDspTol=5.0e-3, fineEtaTol=valunits.valWUnit('etaTol', 'angle', 0.5, 'degrees'), fineOmeTol=valunits.valWUnit('etaTol', 'angle', 0.5, 'degrees')): """ refine a grain list """ # refine grains formally using a multi-pass refinement nGrains = self.rMats.shape[0] grainList = [] for iG in range(nGrains): #indexer.progress_bar(float(iG) / nGrains) grain = G.Grain(self.spots_for_indexing, rMat=self.rMats[iG, :, :], etaTol=etaTol, omeTol=omeTol, claimingSpots=False) if grain.completeness > minCompl: for i in range(nSubIter): grain.fit() s1, s2, s3 = grain.findMatches(etaTol=etaTol, omeTol=omeTol, strainMag=fineDspTol, updateSelf=True, claimingSpots=False, doFit=doFit, testClaims=True) if grain.completeness > minCompl: grainList.append(grain) pass pass pass self.grainList = grainList self._fitRMats = numpy.array( [self.grainList[i].rMat for i in range(len(grainList))]) return
def refine_grains(self, minCompl, nSubIter=3, doFit=False, etaTol=valunits.valWUnit('etaTol', 'angle', 1.0, 'degrees'), omeTol=valunits.valWUnit('etaTol', 'angle', 1.0, 'degrees'), fineDspTol=5.0e-3, fineEtaTol=valunits.valWUnit('etaTol', 'angle', 0.5, 'degrees'), fineOmeTol=valunits.valWUnit('etaTol', 'angle', 0.5, 'degrees')): """ refine a grain list """ # refine grains formally using a multi-pass refinement nGrains = self.rMats.shape[0] grainList = [] for iG in range(nGrains): indexer.progress_bar(float(iG) / nGrains) grain = G.Grain(self.spots_for_indexing, rMat=self.rMats[iG, :, :], etaTol=etaTol, omeTol=omeTol, claimingSpots=False) if grain.completeness > minCompl: for i in range(nSubIter): grain.fit() s1, s2, s3 = grain.findMatches(etaTol=etaTol, omeTol=omeTol, strainMag=fineDspTol, updateSelf=True, claimingSpots=False, doFit=doFit, testClaims=True) if grain.completeness > minCompl: grainList.append(grain) pass pass pass self.grainList = grainList self._fitRMats = numpy.array([self.grainList[i].rMat for i in range(len(grainList))]) return
def add(self, material_file, material_key): self[material_key] = {} self.num_phases += 1 for l in self.wavelength: lam = self.wavelength[l][0].value * 1e-9 E = constants.cPlanck * constants.cLight / constants.cCharge / lam E *= 1e-3 kev = valWUnit('beamenergy', 'energy', E, 'keV') self[material_key][l] = Material_Rietveld(material_file, material_key, dmin=self.dmin, kev=kev) for k in self: for l in self.wavelength: self[k][l].pf = 1.0 / self.num_phases
def _set_beamEnergy(self, keV): """ Set method for beamEnergy * note that units are assumed to be keV for float arguments. Also can take a valWUnit instance """ if(isinstance(keV, valWUnit)): self._beamEnergy = keV else: self._beamEnergy = valWUnit('kev', 'energy', keV, 'keV') ''' acceleration potential is set in volts therefore the factor of 1e3 @TODO make voltage valWUnit instance so this that we dont have to track this manually inn the future ''' self.unitcell.voltage = self.beamEnergy.value*1e3 self.planeData.wavelength = keV self.update_structure_factor() return
def save_eta_ome_maps(eta_ome, filename): """ eta_ome.dataStore eta_ome.planeData eta_ome.iHKLList eta_ome.etaEdges eta_ome.omeEdges eta_ome.etas eta_ome.omegas """ args = np.array(eta_ome.planeData.getParams(), dtype=object)[:4] args[2] = valWUnit('wavelength', 'length', args[2], 'angstrom') hkls = eta_ome.planeData.hkls save_dict = { 'dataStore': eta_ome.dataStore, 'etas': eta_ome.etas, 'etaEdges': eta_ome.etaEdges, 'iHKLList': eta_ome.iHKLList, 'omegas': eta_ome.omegas, 'omeEdges': eta_ome.omeEdges, 'planeData_args': args, 'planeData_hkls': hkls } np.savez_compressed(filename, **save_dict)
class Material(object): """Simple class for holding lattice parameters, accessible by name. The class references materials by name and contains lattice and space group data. """ DFLT_NAME = 'material' DFLT_SGNUM = 230 DFLT_LPARMS = [ angstroms(1.0), angstroms(1.0), angstroms(1.0), degrees(90.0), degrees(90.0), degrees(90.0) ] DFLT_SSMAX = 50 DFLT_KEV = valWUnit('wavelength', 'energy', 80.725e0, 'keV') DFLT_STR = 0.002 DFLT_TTH = 0.002 # def __init__(self, name=DFLT_NAME, cfgP=None): """Constructor for Material name -- (str) name of material cfgP -- (instance) configuration file parser with -- the material name as a section """ # self.name = name self.description = '' # if cfgP: # Get values from configuration self._readCfg(cfgP) pass else: # Use default values self._hklMax = Material.DFLT_SSMAX self._lparms = Material.DFLT_LPARMS self.sgnum = Material.DFLT_SGNUM # self.description = '' pass # return def __str__(self): """String representation""" s = 'Material: %s\n' % self.name if self.description: s += ' description: %s\n' % self.description pass s += ' plane Data: %s' % str(self.planeData) return s def _readCfg(self, p): """Read values from config parser""" # Lattice parameters lpStrings = (('a-in-angstroms', angstroms), ('b-in-angstroms', angstroms), ('c-in-angstroms', angstroms), ('alpha-in-degrees', degrees), ('beta-in-degrees', degrees), ('gamma-in-degrees', degrees)) sgnum = p.getint(self.name, 'space-group') tmpSG = SG(sgnum) lparams = [] for ind in tmpSG.reqParams: param, unit = lpStrings[ind] lparams.append(unit(p.getfloat(self.name, param))) pass # Initialize self._hklMax = p.getint(self.name, 'hkls-ssmax') self._lparms = self._toSixLP(sgnum, lparams) self.sgnum = sgnum # self.description = p.get(self.name, 'description') return def _newPdata(self): """Create a new plane data instance""" hkls = numpy.array(self.spaceGroup.getHKLs(self.hklMax)).T lprm = [self._lparms[i] for i in self.spaceGroup.reqParams] laue = self.spaceGroup.laueGroup self._pData = PData(hkls, lprm, laue, Material.DFLT_KEV, Material.DFLT_STR, tThWidth=Material.DFLT_TTH) # # Set default exclusions # dflt_excl = numpy.array( [1 for i in range(len(self._pData.exclusions))], dtype=bool) dflt_excl[:5] = False self._pData.exclusions = dflt_excl return def _toSixLP(self, sgn, lp): """Generate all six lattice parameters, making sure units are attached.""" tmpSG = SG(sgn) lp6 = list(tmpSG.sixLatticeParams(lp)) for i in range(6): # make sure angles have attached units if not hasattr(lp6[i], 'getVal'): if i in range(3): lp6[i] = angstroms(lp6[i]) else: lp6[i] = degrees(lp6[i]) pass pass pass return lp6 # # ============================== API # # ========== Properties # # property: spaceGroup @property def spaceGroup(self): """(read only) Space group""" return self._spaceGroup # property: sgnum def _get_sgnum(self): """Get method for sgnum""" return self._sgnum def _set_sgnum(self, v): """Set method for sgnum""" self._sgnum = v self._spaceGroup = SG(self._sgnum) self._newPdata() return sgnum = property(_get_sgnum, _set_sgnum, None, "Space group number") # property: hklMax def _get_hklMax(self): """Get method for hklMax""" return self._hklMax def _set_hklMax(self, v): """Set method for hklMax""" self._hklMax = v self._newPdata() # update planeData return hklMax = property(_get_hklMax, _set_hklMax, None, "Max sum of squares for HKLs") # property: planeData @property def planeData(self): """(read only) Return the planeData attribute (lattice parameters)""" return self._pData # property: latticeParameters def _get_latticeParameters(self): """Get method for latticeParameters""" return self._lparms def _set_latticeParameters(self, v): """Set method for latticeParameters""" self._lparms = self._toSixLP(self.sgnum, v) #self._newPdata() self.planeData.lparms = v return lpdoc = r"""Lattice parameters On output, all six paramters are returned. On input, either all six or a minimal set is accepted. The values have units attached, i.e. they are valWunit instances. """ latticeParameters = property(_get_latticeParameters, _set_latticeParameters, None, lpdoc) # property: "name" def _get_name(self): """Set method for name""" return self._name def _set_name(self, v): """Set method for name""" self._name = v return name = property(_get_name, _set_name, None, "Name of material") # # ========== Methods # # pass # end class
Use the Material class directly for new materials. Known materials are defined by name in materialDict. """ from ConfigParser import SafeConfigParser as Parser import numpy from hexrd.xrd.crystallography import PlaneData as PData from hexrd.xrd.spacegroup import SpaceGroup as SG from hexrd.valunits import valWUnit # __all__ = ['Material', 'loadMaterialList'] # # ================================================== Module Data # angstroms = lambda x: valWUnit('lp', 'length', x, 'angstrom') degrees = lambda x: valWUnit('lp', 'angle', x, 'degrees') # # ---------------------------------------------------CLASS: Material # class Material(object): """Simple class for holding lattice parameters, accessible by name. The class references materials by name and contains lattice and space group data. """ DFLT_NAME = 'material' DFLT_SGNUM = 230 DFLT_LPARMS = [
pwA(foundSpotAngs[:,0], foundSpotAngs[:,1], style='gx') # alignment is good, but definitely missing somex pwB = plotwrap.PlotWrap() pwB(predAngles[:,0], predAngles[:,2], style='r+') pwB(foundSpotAngs[:,0], foundSpotAngs[:,2], style='gx') ''' nSpotsTThAll, nSpotsTThClaimed, numClaimed, numMultiClaimed = spotsForFit.report( scaleByMultip=False) # iHKL = num.argmax(nSpotsTThAll) iHKL = num.where(nSpotsTThAll == num.unique(nSpotsTThAll)[1])[0][0] hklListForIndex = [ tuple(planeData.getHKLs()[iHKL]), ] etaTol = valunits.valWUnit('etaTol', 'angle', 0.5, 'degrees') omeTol = valunits.valWUnit('omeTol', 'angle', 0.5, 'degrees') rMat = indexer.fiberSearch(spotsForFit, hklListForIndex, iPhase=planeData.phaseID, nsteps=720, minCompleteness=0.70, minPctClaimed=0.75, friedelOnly=False, dspTol=None, etaTol=etaTol, omeTol=omeTol, debug=True, doMultiProc=opts.doMultiProc, doRefinement=False) print 'found %d grains' % (rMat.shape[0])
def mockup_experiment(): # user options # each grain is provided in the form of a quaternion. # The following array contains the quaternions for the array. Note that the # quaternions are in the columns, with the first row (row 0) being the real # part w. We assume that we are dealing with unit quaternions quats = np.array([[ 0.91836393, 0.90869942], [ 0.33952917, 0.1834835 ], [ 0.17216207, 0.10095837], [ 0.10811041, 0.36111851]]) n_grains = quats.shape[-1] # last dimension provides the number of grains phis = 2.*np.arccos(quats[0, :]) # phis are the angles for the quaternion ns = mutil.unitVector(quats[1:, :]) # ns contains the rotation axis as an unit vector exp_maps = np.array([phis[i]*ns[:, i] for i in range(n_grains)]) rMat_c = rot.rotMatOfQuat(quats) cvec = np.arange(-25, 26) X, Y, Z = np.meshgrid(cvec, cvec, cvec) crd0 = 1e-3*np.vstack([X.flatten(), Y.flatten(), Z.flatten()]).T crd1 = crd0 + np.r_[0.100, 0.100, 0] crds = np.array([crd0, crd1]) # make grain parameters grain_params = [] for i in range(n_grains): for j in range(len(crd0)): grain_params.append( np.hstack([exp_maps[i, :], crds[i][j, :], xf.vInv_ref.flatten()]) ) # scan range and period ome_period = (0, 2*np.pi) ome_range = [ome_period,] ome_step = np.radians(1.) nframes = 0 for i in range(len(ome_range)): del_ome = ome_range[i][1]-ome_range[i][0] nframes += int((ome_range[i][1]-ome_range[i][0])/ome_step) ome_edges = np.arange(nframes+1)*ome_step # instrument with open('./retiga.yml', 'r') as fildes: instr_cfg = yaml.load(fildes) tiltAngles = instr_cfg['detector']['transform']['tilt_angles'] tVec_d = np.array(instr_cfg['detector']['transform']['t_vec_d']).reshape(3,1) chi = instr_cfg['oscillation_stage']['chi'] tVec_s = np.array(instr_cfg['oscillation_stage']['t_vec_s']).reshape(3,1) rMat_d = xfcapi.makeDetectorRotMat(tiltAngles) rMat_s = xfcapi.makeOscillRotMat([chi, 0.]) pixel_size = instr_cfg['detector']['pixels']['size'] nrows = instr_cfg['detector']['pixels']['rows'] ncols = instr_cfg['detector']['pixels']['columns'] col_ps = pixel_size[1] row_ps = pixel_size[0] row_dim = row_ps*nrows # in mm col_dim = col_ps*ncols # in mm panel_dims = [(-0.5*ncols*col_ps, -0.5*nrows*row_ps), ( 0.5*ncols*col_ps, 0.5*nrows*row_ps)] x_col_edges = col_ps * (np.arange(ncols + 1) - 0.5*ncols) y_row_edges = row_ps * (np.arange(nrows, -1, -1) - 0.5*nrows) #x_col_edges = np.arange(panel_dims[0][0], panel_dims[1][0] + 0.5*col_ps, col_ps) #y_row_edges = np.arange(panel_dims[0][1], panel_dims[1][1] + 0.5*row_ps, row_ps) rx, ry = np.meshgrid(x_col_edges, y_row_edges) gcrds = xfcapi.detectorXYToGvec(np.vstack([rx.flatten(), ry.flatten()]).T, rMat_d, rMat_s, tVec_d, tVec_s, np.zeros(3)) max_pixel_tth = np.amax(gcrds[0][0]) detector_params = np.hstack([tiltAngles, tVec_d.flatten(), chi, tVec_s.flatten()]) distortion = None # a different parametrization for the sensor (makes for faster quantization) base = np.array([x_col_edges[0], y_row_edges[0], ome_edges[0]]) deltas = np.array([x_col_edges[1] - x_col_edges[0], y_row_edges[1] - y_row_edges[0], ome_edges[1] - ome_edges[0]]) inv_deltas = 1.0/deltas clip_vals = np.array([ncols, nrows]) # dilation max_diameter = np.sqrt(3)*0.005 row_dilation = np.ceil(0.5 * max_diameter/row_ps) col_dilation = np.ceil(0.5 * max_diameter/col_ps) # crystallography data from hexrd import valunits gold = material.Material('gold') gold.sgnum = 225 gold.latticeParameters = [4.0782, ] gold.hklMax = 200 gold.beamEnergy = valunits.valWUnit("wavelength", "ENERGY", 52, "keV") gold.planeData.exclusions = None gold.planeData.tThMax = max_pixel_tth #note this comes from info in the detector ns = argparse.Namespace() # grains related information ns.n_grains = n_grains # this can be derived from other values... ns.rMat_c = rMat_c # n_grains rotation matrices (one per grain) ns.exp_maps = exp_maps # n_grains exp_maps -angle * rotation axis- (one per grain) ns.plane_data = gold.planeData ns.detector_params = detector_params ns.pixel_size = pixel_size ns.ome_range = ome_range ns.ome_period = ome_period ns.x_col_edges = x_col_edges ns.y_row_edges = y_row_edges ns.ome_edges = ome_edges ns.ncols = ncols ns.nrows = nrows ns.nframes = nframes # used only in simulate... ns.rMat_d = rMat_d ns.tVec_d = tVec_d ns.chi = chi # note this is used to compute S... why is it needed? ns.tVec_s = tVec_s # ns.rMat_s = rMat_s # ns.tVec_s = tVec_s ns.rMat_c = rMat_c ns.row_dilation = row_dilation ns.col_dilation = col_dilation ns.distortion = distortion ns.panel_dims = panel_dims # used only in simulate... ns.base = base ns.inv_deltas = inv_deltas ns.clip_vals = clip_vals return grain_params, ns
def _angstroms(x): return valWUnit('lp', 'length', x, 'angstrom')
def _nm(x): return valWUnit('lp', 'length', x, 'nm')
class Material(object): """Simple class for holding lattice parameters, accessible by name. The class references materials by name and contains lattice and space group data. default data is for nickel, but name is material """ DFLT_NAME = 'material.xtal' DFLT_XTAL = 'Ni' DFLT_SGNUM = 225 DFLT_LPARMS = [ _angstroms(3.61), _angstroms(3.61), _angstroms(3.61), _degrees(90.0), _degrees(90.0), _degrees(90.0) ] DFLT_SSMAX = 100 DFLT_KEV = valWUnit('wavelength', 'energy', 80.725e0, 'keV') DFLT_STR = 0.0025 DFLT_TTH = numpy.radians(0.25) DFLT_TTHMAX = numpy.radians(160.0) """ ATOMINFO Fractional Atom Position of an atom in the unit cell followed by the site occupany and debye waller (U) factor in A^(-2) B is related to U by B = 8 pi^2 U ATOMTYPE atomic number of all the different species in the unitcell """ DFLT_ATOMINFO = numpy.array([[0., 0., 0., 1.]]) DFLT_U = numpy.array([4.18e-7]) DFLT_ATOMTYPE = numpy.array([28]) ''' the dmin parameter is used to figure out the maximum sampling for g-vectors this parameter is in angstroms ''' DFLT_DMIN = _angstroms(1.0) ''' some materials have more than one space group setting. for ex the diamond cubic system has two settings with the origin either at (0,0,0) or at (1/4,1/4,1/4) etc. this handle takes care of these cases. but the defaiult is always 0 default space group setting ''' DFLT_SGSETTING = 0 def __init__(self, name=None, material_file=None, dmin=DFLT_DMIN, kev=DFLT_KEV, sgsetting=DFLT_SGSETTING): """Constructor for Material name -- (str) name of crystal material_file -- (str) name of the material file which contains the crystal. this could be either cif or hdf5 """ self.name = name self.description = '' self._dmin = dmin self._beamEnergy = kev self.sgsetting = sgsetting if material_file: # Get values from configuration # self._readCfg(material_file) # >> @ date 08/20/2020 SS removing dependence on hklmax #self._hklMax = Material.DFLT_SSMAX # self._beamEnergy = Material.DFLT_KEV form = Path(material_file).suffix[1:] if (form == 'cif'): self._readCif(material_file) elif (form in ['h5', 'hdf5', 'xtal']): self._readHDFxtal(fhdf=material_file, xtal=name) else: # Use default values self._lparms = Material.DFLT_LPARMS # self._hklMax = Material.DFLT_SSMAX # self.description = '' # self.sgnum = Material.DFLT_SGNUM self._sgsetting = Material.DFLT_SGSETTING # self._atominfo = Material.DFLT_ATOMINFO # self._U = Material.DFLT_U # self._atomtype = Material.DFLT_ATOMTYPE # self.unitcell = unitcell.unitcell(self._lparms, self.sgnum, self._atomtype, self._atominfo, self._U, self._dmin.getVal('nm'), self._beamEnergy.value, self._sgsetting) self._newPdata() self.update_structure_factor() def __str__(self): """String representation""" s = 'Material: %s\n' % self.name if self.description: s += ' description: %s\n' % self.description pass s += ' plane Data: %s' % str(self.planeData) return s def _newPdata(self): """Create a new plane data instance""" # spaceGroup module calulates forbidden reflections ''' >> @date 08/20/2020 SS removing dependence of planeData initialization on the spaceGroup module. everything is initialized using the unitcell module now ''' hkls = self.unitcell.getHKLs(self._dmin.getVal('nm')).T lprm = [ self._lparms[i] for i in unitcell._rqpDict[self.unitcell.latticeType][0] ] laue = self.unitcell._laueGroup self._pData = PData(hkls, lprm, laue, self._beamEnergy, Material.DFLT_STR, tThWidth=Material.DFLT_TTH, tThMax=Material.DFLT_TTHMAX) ''' Set default exclusions all reflections with two-theta smaller than 90 degrees ''' tth = numpy.array( [hkldata['tTheta'] for hkldata in self._pData.hklDataList]) dflt_excl = numpy.ones(tth.shape, dtype=numpy.bool) dflt_excl[~numpy.isnan(tth)] = ~( (tth[~numpy.isnan(tth)] >= 0.0) & \ (tth[~numpy.isnan(tth)] <= numpy.pi/2.0) ) dflt_excl[0] = False self._pData.exclusions = dflt_excl return def update_structure_factor(self): hkls = self.planeData.getHKLs(allHKLs=True) sf = numpy.zeros([ hkls.shape[0], ]) for i, g in enumerate(hkls): sf[i] = self.unitcell.CalcXRSF(g) self.planeData.set_structFact(sf[~self.planeData.exclusions]) def _readCif(self, fcif=DFLT_NAME + '.cif'): """ >> @AUTHOR: Saransh Singh, Lawrence Livermore National Lab, [email protected] >> @DATE: 10/16/2019 SS 1.0 original >> @DETAILS: hexrd3 will have real structure factors and will require the overhaul of the crystallography. In this effort, we will have a cif reader and also the HDF5 format reader in the material class. We will be using pycifrw for i/o """ # make sure file exists etc. if (fcif == Material.DFLT_NAME + '.cif'): try: cif = ReadCif(fcif) except (OSError): raise RuntimeError( 'OS Error: No file name supplied and default file name not found.' ) else: try: cif = ReadCif(fcif) except (OSError): raise RuntimeError('OS Error: File not found') # read the file for k in cif.keys(): if ('_cell_length_a' in cif[k]): m = k break cifdata = cif[m] # cifdata = cif[cif.keys()[0]] # make sure the space group is present in the cif file, either as # international table number, hermann-maguain or hall symbol sgkey = [ '_space_group_IT_number', '_symmetry_space_group_name_h-m', '_symmetry_space_group_name_hall', '_symmetry_Int_Tables_number' ] sgdata = False for key in sgkey: sgdata = sgdata or (key in cifdata) if (sgdata): skey = key break if (not (sgdata)): raise RuntimeError(' No space group information in CIF file! ') sgnum = 0 if skey is sgkey[0]: sgnum = int(cifdata[sgkey[0]]) elif (skey is sgkey[1]): HM = cifdata[sgkey[1]] HM = HM.replace(" ", "") sgnum = HM_to_sgnum[HM] elif (skey is sgkey[2]): hall = cifdata[sgkey[2]] hall = hall.replace(" ", "") sgnum = Hall_to_sgnum[HM] elif (skey is sgkey[3]): sgnum = int(cifdata[sgkey[3]]) # lattice parameters lparms = [] lpkey = ['_cell_length_a', '_cell_length_b', \ '_cell_length_c', '_cell_angle_alpha', \ '_cell_angle_beta', '_cell_angle_gamma'] for key in lpkey: n = cifdata[key].find('(') if (n != -1): lparms.append(float(cifdata[key][:n])) else: lparms.append(float(cifdata[key])) for i in range(6): if (i < 3): lparms[i] = _angstroms(lparms[i]) else: lparms[i] = _degrees(lparms[i]) self._lparms = lparms self.sgnum = sgnum # fractional atomic site, occ and vibration amplitude fracsitekey = ['_atom_site_fract_x', '_atom_site_fract_y',\ '_atom_site_fract_z',] occ_U = ['_atom_site_occupancy',\ '_atom_site_u_iso_or_equiv','_atom_site_U_iso_or_equiv'] sitedata = True for key in fracsitekey: sitedata = sitedata and (key in cifdata) if (not (sitedata)): raise RuntimeError( ' fractional site position is not present or incomplete in the CIF file! ' ) atompos = [] for key in fracsitekey: slist = cifdata[key] pos = [] for p in slist: n = p.find('(') if (n != -1): pos.append(p[:n]) else: pos.append(p) ''' sometimes cif files have negative values so need to bring them back to fractional coordinates between 0-1 ''' pos = numpy.asarray(pos).astype(numpy.float64) pos, _ = numpy.modf(pos + 100.0) atompos.append(pos) """note that the vibration amplitude, U is just the amplitude (in A) to convert to the typical B which occurs in the debye-waller factor, we will use the following formula B = 8 * pi ^2 * < U_av^2 > this will be done here so we dont have to worry about it later """ pocc = (occ_U[0] in cifdata.keys()) pU = (occ_U[1] in cifdata.keys()) or (occ_U[2] in cifdata.keys()) if (not pocc): warn('occupation fraction not present. setting it to 1') occ = numpy.ones(atompos[0].shape) atompos.append(occ) else: slist = cifdata[occ_U[0]] occ = [] for p in slist: n = p.find('(') if (n != -1): occ.append(p[:n]) else: occ.append(p) atompos.append(numpy.asarray(occ).astype(numpy.float64)) if (not pU): warn( 'Debye-Waller factors not present. setting to same values for all atoms.' ) U = 1.0 / numpy.pi / 2. / numpy.sqrt(2.) * numpy.ones( atompos[0].shape) self._U = U else: if (occ_U[1] in cifdata.keys()): k = occ_U[1] else: k = occ_U[2] slist = cifdata[k] U = [] for p in slist: n = p.find('(') if (n != -1): U.append(p[:n]) else: U.append(p) self._U = numpy.asarray(U).astype(numpy.float64) ''' format everything in the right shape etc. ''' self._atominfo = numpy.asarray(atompos).T ''' get atome types here i.e. the atomic number of atoms at each site ''' atype = '_atom_site_type_symbol' patype = (atype in cifdata) if (not patype): raise RuntimeError('atom types not defined in cif file.') satype = cifdata[atype] atomtype = [] for s in satype: atomtype.append(ptable[s]) self._atomtype = numpy.asarray(atomtype).astype(numpy.int32) self._sgsetting = 0 def _readHDFxtal(self, fhdf=DFLT_NAME, xtal=DFLT_NAME): """ >> @AUTHOR: Saransh Singh, Lawrence Livermore National Lab, [email protected] >> @DATE: 10/17/2019 SS 1.0 original >> @DETAILS: hexrd3 will have real structure factors and will require the overhaul of the crystallography. In this effort, we will have a HDF file reader. the file will be the same as the EMsoft xtal file. h5py will be used for i/o """ fexist = path.exists(fhdf) if (fexist): fid = h5py.File(fhdf, 'r') xtal = "/" + xtal if xtal not in fid: raise IOError('crystal doesn' 't exist in material file.') else: raise IOError('material file does not exist.') gid = fid.get(xtal) sgnum = numpy.asscalar(numpy.array(gid.get('SpaceGroupNumber'), \ dtype = numpy.int32)) """ IMPORTANT NOTE: note that the latice parameters is nm by default hexrd on the other hand uses A as the default units, so we need to be careful and convert it right here, so there is no confusion later on """ lparms = list(gid.get('LatticeParameters')) for i in range(6): if (i < 3): lparms[i] = _angstroms(lparms[i] * 10.0) else: lparms[i] = _degrees(lparms[i]) self._lparms = lparms #self._lparms = self._toSixLP(sgnum, lparms) # fill space group and lattice parameters self.sgnum = sgnum # the U factors are related to B by the relation B = 8pi^2 U self._atominfo = numpy.transpose( numpy.array(gid.get('AtomData'), dtype=numpy.float64)) self._U = numpy.transpose( numpy.array(gid.get('U'), dtype=numpy.float64)) # read atom types (by atomic number, Z) self._atomtype = numpy.array(gid.get('Atomtypes'), dtype=numpy.int32) self._atom_ntype = self._atomtype.shape[0] self._sgsetting = numpy.asscalar(numpy.array(gid.get('SpaceGroupSetting'), \ dtype = numpy.int32)) fid.close() def dump_material(self, filename): ''' get the atominfo dictionaary aand the lattice parameters ''' AtomInfo = {} AtomInfo['file'] = filename AtomInfo['xtalname'] = self.name AtomInfo['xtal_sys'] = xtal_sys_dict[self.unitcell.latticeType.lower()] AtomInfo['Z'] = self.unitcell.atom_type AtomInfo['SG'] = self.unitcell.sgnum AtomInfo['SGsetting'] = self.unitcell.sgsetting AtomInfo['APOS'] = self.unitcell.atom_pos AtomInfo['U'] = self.unitcell.U ''' lattice parameters ''' lat_param = { 'a': self.unitcell.a, 'b': self.unitcell.b, 'c': self.unitcell.c, 'alpha': self.unitcell.alpha, 'beta': self.unitcell.beta, 'gamma': self.unitcell.gamma } Write2H5File(AtomInfo, lat_param) # ============================== API # # ========== Properties # # property: spaceGroup @property def spaceGroup(self): """(read only) Space group""" return self._spaceGroup @property def vol(self): return self.unitcell.vol # property: sgnum def _get_sgnum(self): """Get method for sgnum""" return self._sgnum def _set_sgnum(self, v): """Set method for sgnum >> @date 08/20/2020 SS removed planedata initialization everytime sgnum is updated singe everything is initialized using unitcell now """ self._sgnum = v # Update the unit cell if there is one if hasattr(self, 'unitcell'): self.unitcell.sgnum = v sgnum = property(_get_sgnum, _set_sgnum, None, "Space group number") # property: beamEnergy def _get_beamEnergy(self): """Get method for beamEnergy""" return self._beamEnergy def _set_beamEnergy(self, keV): """ Set method for beamEnergy * note that units are assumed to be keV for float arguments. Also can take a valWUnit instance """ self._beamEnergy = keV self.planeData.wavelength = keV return beamEnergy = property(_get_beamEnergy, _set_beamEnergy, None, "Beam energy in keV") #>> @date 08/20/2020 removing dependence on hklmax # property: hklMax # def _get_hklMax(self): # """Get method for hklMax""" # return self._hklMax # def _set_hklMax(self, v): # """Set method for hklMax""" # self._hklMax = v # self._newPdata() # update planeData # return # hklMax = property(_get_hklMax, _set_hklMax, None, # "Max sum of squares for HKLs") # property: planeData @property def planeData(self): """(read only) Return the planeData attribute (lattice parameters)""" return self._pData # property: latticeParameters def _get_latticeParameters(self): """Get method for latticeParameters""" return self._lparms def _set_latticeParameters(self, v): """Set method for latticeParameters""" if (len(v) != 6): v = unitcell._rqpDict[self.unitcell.latticeType][1](v) lp = [_angstroms(v[i]) for i in range(3)] for i in range(3, 6): lp.append(_degrees(v[i])) self._lparms = lp rq_lp = unitcell._rqpDict[self.unitcell.latticeType][0] for i, vv in enumerate(lp): if (vv.isLength()): val = vv.value / 10.0 else: val = vv.value setattr(self.unitcell, unitcell._lpname[i], val) v2 = [lp[x].value for x in rq_lp] self.planeData.lparms = v2 return lpdoc = r"""Lattice parameters On output, all six paramters are returned. On input, either all six or a minimal set is accepted. The values have units attached, i.e. they are valWunit instances. """ latticeParameters = property(_get_latticeParameters, _set_latticeParameters, None, lpdoc) # property: "name" def _get_name(self): """Set method for name""" return self._name def _set_name(self, v): """Set method for name""" self._name = v return name = property(_get_name, _set_name, None, "Name of material") @property def dmin(self): return self._dmin @dmin.setter def dmin(self, v): if self._dmin == v: return self._dmin = v # Update the unit cell self.unitcell.dmin = v.getVal('nm') self._newPdata() self.update_structure_factor() # property: "atominfo" def _get_atominfo(self): """Set method for name""" return self._atominfo def _set_atominfo(self, v): """Set method for name""" if v.shape[1] == 4: self._atominfo = v else: print("Improper syntax, array must be n x 4") return atominfo = property( _get_atominfo, _set_atominfo, None, "Information about atomic positions and electron number") # # ========== Methods # # pass # end class
def beam_energy(self, x): if not isinstance(x, valWUnit): x = valWUnit("beam energy", "energy", x, "keV") for matl in self.materials.values(): matl.beamEnergy = x
class Material(object): """Simple class for holding lattice parameters, accessible by name. The class references materials by name and contains lattice and space group data. default data is for nickel, but name is material """ DFLT_NAME = 'material.xtal' DFLT_XTAL = 'Ni' DFLT_SGNUM = 225 ''' some materials have more than one space group setting. for ex the diamond cubic system has two settings with the origin either at (0,0,0) or at (1/4,1/4,1/4) etc. this handle takes care of these cases. but the defaiult is always 1 ''' DFLT_SGSETTING = 1 DFLT_LPARMS = [_angstroms(3.61), _angstroms(3.61), _angstroms(3.61), _degrees(90.0), _degrees(90.0), _degrees(90.0)] DFLT_SSMAX = 100 DFLT_KEV = valWUnit('wavelength', 'energy', 80.725e0, 'keV') DFLT_STR = 0.0025 DFLT_TTH = numpy.radians(0.25) DFLT_TTHMAX = numpy.radians(160.0) """ ATOMINFO Fractional Atom Position of an atom in the unit cell followed by the site occupany and debye waller (B) factor in nm^(-2) ATOMTYPE atomic number of all the different species in the unitcell """ DFLT_ATOMINFO = numpy.array([[0, 0, 0, 1, 0.033]]) DFLT_ATOMTYPE = numpy.array([28]) ''' the dmin parameter is used to figure out the maximum sampling for g-vectors this parameter is in angstroms ''' DFLT_DMIN = _angstroms(0.2) ''' default space group setting ''' DFLT_SGSETTING = 0 def __init__(self, name=None, cfgP=None, dmin=DFLT_DMIN, kev=DFLT_KEV, sgsetting=DFLT_SGSETTING): """Constructor for Material name -- (str) name of material cfgP -- (instance) configuration file parser with -- the material name as a section """ self.name = name self.description = '' self._dmin = dmin self._beamEnergy = kev self.sgsetting = sgsetting if(self._dmin.unit == 'angstrom'): # convert to nm uc_dmin = self._dmin.value * 0.1 elif(self._dmin.unit == 'nm'): uc_dmin = self._dmin.value if cfgP: # Get values from configuration # self._readCfg(cfgP) self._hklMax = Material.DFLT_SSMAX # self._beamEnergy = Material.DFLT_KEV n = cfgP.find('.') form = cfgP[n+1:] if(form == 'cif'): self._readCif(cfgP) elif(form in ['h5', 'hdf5', 'xtal']): self._readHDFxtal(fhdf=cfgP, xtal=name) else: # Use default values self._lparms = Material.DFLT_LPARMS self._hklMax = Material.DFLT_SSMAX # self.description = '' # self.sgnum = Material.DFLT_SGNUM self._sgsetting = Material.DFLT_SGSETTING # self._atominfo = Material.DFLT_ATOMINFO # self._atomtype = Material.DFLT_ATOMTYPE # self.unitcell = unitcell(self._lparms, self.sgnum, self._atomtype, self._atominfo, self._U, uc_dmin, self._beamEnergy.value, self._sgsetting) hkls = self.planeData.getHKLs(allHKLs=True) sf = numpy.zeros([hkls.shape[0],]) for i,g in enumerate(hkls): sf[i] = self.unitcell.CalcXRSF(g) self.planeData.set_structFact(sf[~self.planeData.exclusions]) return def __str__(self): """String representation""" s = 'Material: %s\n' % self.name if self.description: s += ' description: %s\n' % self.description pass s += ' plane Data: %s' % str(self.planeData) return s def _readCfg(self, p): """Read values from config parser""" # Lattice parameters lpStrings = ( ('a-in-angstroms', _angstroms), ('b-in-angstroms', _angstroms), ('c-in-angstroms', _angstroms), ('alpha-in-degrees', _degrees), ('beta-in-degrees', _degrees), ('gamma-in-degrees', _degrees) ) sgnum = p.getint(self.name, 'space-group') tmpSG = SG(sgnum) try: hklMax = p.getint(self.name, 'hkls-ssmax') except: hklMax = Material.DFLT_SSMAX try: beamEnergy = p.getfloat(self.name, 'beam-energy') except: beamEnergy = Material.DFLT_KEV lparams = [] for ind in tmpSG.reqParams: param, unit = lpStrings[ind] lparams.append(unit(p.getfloat(self.name, param))) pass # Initialize self._hklMax = hklMax self._beamEnergy = beamEnergy self._lparms = self._toSixLP(sgnum, lparams) self.sgnum = sgnum self.description = p.get(self.name, 'description') return def _newPdata(self): """Create a new plane data instance""" # spaceGroup module calulates forbidden reflections hkls = numpy.array(self.spaceGroup.getHKLs(self.hklMax)).T lprm = [self._lparms[i] for i in self.spaceGroup.reqParams] laue = self.spaceGroup.laueGroup self._pData = PData(hkls, lprm, laue, self._beamEnergy, Material.DFLT_STR, tThWidth=Material.DFLT_TTH, tThMax=Material.DFLT_TTHMAX) ''' Set default exclusions all reflections with two-theta smaller than 90 degrees ''' tth = numpy.array([hkldata['tTheta'] for hkldata in self._pData.hklDataList]) dflt_excl = numpy.ones(tth.shape,dtype=numpy.bool) dflt_excl[~numpy.isnan(tth)] = ~( (tth[~numpy.isnan(tth)] >= 0.0) & \ (tth[~numpy.isnan(tth)] <= numpy.pi/2.0) ) self._pData.exclusions = dflt_excl return def _toSixLP(self, sgn, lp): """ Generate all six lattice parameters, making sure units are attached. """ tmpSG = SG(sgn) lp6 = list(tmpSG.sixLatticeParams(lp)) # make sure angles have attached units for i in range(6): if not hasattr(lp6[i], 'getVal'): if i in range(3): lp6[i] = _angstroms(lp6[i]) else: lp6[i] = _degrees(lp6[i]) pass pass pass return lp6 def _readCif(self, fcif=DFLT_NAME+'.cif'): """ >> @AUTHOR: Saransh Singh, Lawrence Livermore National Lab, [email protected] >> @DATE: 10/16/2019 SS 1.0 original >> @DETAILS: hexrd3 will have real structure factors and will require the overhaul of the crystallography. In this effort, we will have a cif reader and also the HDF5 format reader in the material class. We will be using pycifrw for i/o """ # make sure file exists etc. if(fcif == Material.DFLT_NAME+'.cif'): try: cif = ReadCif(fcif) except(OSError): raise RuntimeError('OS Error: No file name supplied and default file name not found.') else: try: cif = ReadCif(fcif) except(OSError): raise RuntimeError('OS Error: File not found') # read the file cifdata = cif[cif.keys()[0]] # make sure the space group is present in the cif file, either as # international table number, hermann-maguain or hall symbol sgkey = ['_space_group_IT_number', '_symmetry_space_group_name_h-m', \ '_symmetry_space_group_name_hall'] sgdata = False for key in sgkey: sgdata = sgdata or (key in cifdata) if(sgdata): skey = key break if(not(sgdata)): raise RuntimeError(' No space group information in CIF file! ') sgnum = 0 if skey is sgkey[0]: sgnum = int(cifdata[sgkey[0]]) elif (skey is sgkey[1]): HM = cifdata[sgkey[1]] sgnum = sg.HM_to_sgnum[HM] elif (skey is sgkey[2]): hall = cifdata[sgkey[2]] sgnum = sg.Hall_to_sgnum[HM] # lattice parameters lparms = [] lpkey = ['_cell_length_a', '_cell_length_b', \ '_cell_length_c', '_cell_angle_alpha', \ '_cell_angle_beta', '_cell_angle_gamma'] for key in lpkey: n = cifdata[key].find('(') if(n != -1): lparms.append(float(cifdata[key][:n])) else: lparms.append(float(cifdata[key])) for i in range(6): if(i < 3): lparms[i] = _angstroms(lparms[i]) else: lparms[i] = _degrees(lparms[i]) self._lparms = lparms self.sgnum = sgnum # fractional atomic site, occ and vibration amplitude fracsitekey = ['_atom_site_fract_x', '_atom_site_fract_y',\ '_atom_site_fract_z',] occ_U = ['_atom_site_occupancy',\ '_atom_site_u_iso_or_equiv','_atom_site_U_iso_or_equiv'] sitedata = True for key in fracsitekey: sitedata = sitedata and (key in cifdata) if(not(sitedata)): raise RuntimeError(' fractional site position is not present or incomplete in the CIF file! ') atompos = [] for key in fracsitekey: slist = cifdata[key] pos = [] for p in slist: n = p.find('(') if(n != -1): pos.append(p[:n]) else: pos.append(p) ''' sometimes cif files have negative values so need to bring them back to fractional coordinates between 0-1 ''' pos = numpy.asarray(pos).astype(numpy.float64) pos,_ = numpy.modf(pos+100.0) atompos.append(pos) """note that the vibration amplitude, U is just the amplitude (in A) to convert to the typical B which occurs in the debye-waller factor, we will use the following formula B = 8 * pi ^2 * < U_av^2 > this will be done here so we dont have to worry about it later """ pocc = (occ_U[0] in cifdata.keys()) pU = (occ_U[1] in cifdata.keys()) or (occ_U[2] in cifdata.keys()) if(not pocc): warn('occupation fraction not present. setting it to 1') occ = numpy.ones(atompos[0].shape) atompos.append(occ) else: slist = cifdata[occ_U[0]] occ = [] for p in slist: n = p.find('(') if(n != -1): occ.append(p[:n]) else: occ.append(p) atompos.append(numpy.asarray(occ).astype(numpy.float64)) if(not pU): warn('Debye-Waller factors not present. setting to same values for all atoms.') U = 1.0/numpy.pi/2./numpy.sqrt(2.) * numpy.ones(atompos[0].shape) atompos.append(U) else: if(occ_U[1] in cifdata.keys()): k = occ_U[1] else: k = occ_U[2] slist = cifdata[k] U = [] for p in slist: n = p.find('(') if(n != -1): U.append(p[:n]) else: U.append(p) atompos.append(numpy.asarray(U).astype(numpy.float64)) ''' format everything in the right shape etc. ''' self._atominfo = numpy.asarray(atompos).T self._atominfo[:,4] = 8.0 * (numpy.pi**2) * (self._atominfo[:,4]**2) * 1E-2 ''' get atome types here i.e. the atomic number of atoms at each site ''' atype = '_atom_site_type_symbol' patype = (atype in cifdata) if(not patype): raise RuntimeError('atom types not defined in cif file.') satype = cifdata[atype] atomtype = [] for s in satype: atomtype.append(ptable[s]) self._atomtype = numpy.asarray(atomtype).astype(numpy.int32) self._sgsetting = 0 def _readHDFxtal(self, fhdf=DFLT_NAME, xtal=DFLT_NAME): """ >> @AUTHOR: Saransh Singh, Lawrence Livermore National Lab, [email protected] >> @DATE: 10/17/2019 SS 1.0 original >> @DETAILS: hexrd3 will have real structure factors and will require the overhaul of the crystallography. In this effort, we will have a HDF file reader. the file will be the same as the EMsoft xtal file. h5py will be used for i/o """ fexist = path.exists(fhdf) if(fexist): fid = h5py.File(fhdf, 'r') xtal = "/"+xtal if xtal not in fid: raise IOError('crystal doesn''t exist in material file.') else: raise IOError('material file does not exist.') gid = fid.get(xtal) sgnum = numpy.asscalar(numpy.array(gid.get('SpaceGroupNumber'), \ dtype = numpy.int32)) """ IMPORTANT NOTE: note that the latice parameters in EMsoft is nm by default hexrd on the other hand uses A as the default units, so we need to be careful and convert it right here, so there is no confusion later on """ lparms = list(gid.get('LatticeParameters')) for i in range(6): if(i < 3): lparms[i] = _angstroms(lparms[i]*10.0) else: lparms[i] = _degrees(lparms[i]) self._lparms = lparms #self._lparms = self._toSixLP(sgnum, lparms) # fill space group and lattice parameters self.sgnum = sgnum # the U factors are related to B by the relation B = 8pi^2 U self._atominfo = numpy.transpose(numpy.array(gid.get('AtomData'), dtype = numpy.float64)) self._U = numpy.transpose(numpy.array(gid.get('U'), dtype = numpy.float64)) # read atom types (by atomic number, Z) self._atomtype = numpy.array(gid.get('Atomtypes'), dtype = numpy.int32) self._atom_ntype = self._atomtype.shape[0] self._sgsetting = numpy.asscalar(numpy.array(gid.get('SpaceGroupSetting'), \ dtype = numpy.int32)) self._sgsetting -= 1 fid.close() # ============================== API # # ========== Properties # # property: spaceGroup @property def spaceGroup(self): """(read only) Space group""" return self._spaceGroup @property def vol(self): return self.unitcell.vol # property: sgnum def _get_sgnum(self): """Get method for sgnum""" return self._sgnum def _set_sgnum(self, v): """Set method for sgnum""" self._sgnum = v self._spaceGroup = SG(self._sgnum) self._newPdata() return sgnum = property(_get_sgnum, _set_sgnum, None, "Space group number") # property: beamEnergy def _get_beamEnergy(self): """Get method for beamEnergy""" return self._beamEnergy def _set_beamEnergy(self, keV): """ Set method for beamEnergy * note that units are assumed to be keV for float arguments. Also can take a valWUnit instance """ self._beamEnergy = keV self.planeData.wavelength = keV return beamEnergy = property(_get_beamEnergy, _set_beamEnergy, None, "Beam energy in keV") # property: hklMax def _get_hklMax(self): """Get method for hklMax""" return self._hklMax def _set_hklMax(self, v): """Set method for hklMax""" self._hklMax = v self._newPdata() # update planeData return hklMax = property(_get_hklMax, _set_hklMax, None, "Max sum of squares for HKLs") # property: planeData @property def planeData(self): """(read only) Return the planeData attribute (lattice parameters)""" return self._pData # property: latticeParameters def _get_latticeParameters(self): """Get method for latticeParameters""" return self._lparms def _set_latticeParameters(self, v): """Set method for latticeParameters""" self._lparms = self._toSixLP(self.sgnum, v) # self._newPdata() self.planeData.lparms = v return lpdoc = r"""Lattice parameters On output, all six paramters are returned. On input, either all six or a minimal set is accepted. The values have units attached, i.e. they are valWunit instances. """ latticeParameters = property( _get_latticeParameters, _set_latticeParameters, None, lpdoc) # property: "name" def _get_name(self): """Set method for name""" return self._name def _set_name(self, v): """Set method for name""" self._name = v return name = property(_get_name, _set_name, None, "Name of material") # property: "atominfo" def _get_atominfo(self): """Set method for name""" return self._atominfo def _set_atominfo(self, v): """Set method for name""" if v.shape[1] == 4: self._atominfo = v else: print("Improper syntax, array must be n x 4") return atominfo = property( _get_atominfo, _set_atominfo, None, "Information about atomic positions and electron number") # # ========== Methods # # pass # end class
np.zeros(3)) pixel_tth = gcrds[0][0] detector_params = np.hstack( [tiltAngles, tVec_d.flatten(), chi, tVec_s.flatten()]) distortion = None #============================================================================== # %% crystallography data #============================================================================== gold = make_matl('gold', 225, [ 4.0782, ], hkl_ssq_max=200) gold.beamEnergy = valunits.valWUnit("wavelength", "ENERGY", 52, "keV") pd = gold.planeData pd.exclusions = np.zeros(len(pd.exclusions), dtype=bool) pd.tThMax = np.amax(pixel_tth) #============================================================================== # %% DIFFRACTION SIMULATION #============================================================================== def simulate_diffractions(grain_params): pbar = ProgressBar(widgets=['simulate_diffractions', Percentage(), Bar()], maxval=len(grain_params)).start() image_stack = np.zeros((nframes, nrows, ncols), dtype=bool)
class Material(object): """Simple class for holding lattice parameters, accessible by name. The class references materials by name and contains lattice and space group data. """ DFLT_NAME = 'material' DFLT_SGNUM = 230 DFLT_LPARMS = [ _angstroms(1.0), _angstroms(1.0), _angstroms(1.0), _degrees(90.0), _degrees(90.0), _degrees(90.0) ] DFLT_SSMAX = 50 DFLT_KEV = valWUnit('wavelength', 'energy', 80.725e0, 'keV') DFLT_STR = 0.0025 DFLT_TTH = numpy.radians(0.25) DFLT_ATOMINFO = numpy.array([[0, 0, 0, 1]]) """Fractional Atom Position of an atom in the unit cell followed by the number of electrons within that atom. The max number of electrons is 96. """ def __init__(self, name=DFLT_NAME, cfgP=None): """Constructor for Material name -- (str) name of material cfgP -- (instance) configuration file parser with -- the material name as a section """ self.name = name self.description = '' if cfgP: # Get values from configuration self._readCfg(cfgP) pass else: # Use default values self._lparms = Material.DFLT_LPARMS self._hklMax = Material.DFLT_SSMAX # self._beamEnergy = Material.DFLT_KEV # self.description = '' # self.sgnum = Material.DFLT_SGNUM # self._atominfo = Material.DFLT_ATOMINFO # pass return def __str__(self): """String representation""" s = 'Material: %s\n' % self.name if self.description: s += ' description: %s\n' % self.description pass s += ' plane Data: %s' % str(self.planeData) return s def _readCfg(self, p): """Read values from config parser""" # Lattice parameters lpStrings = (('a-in-angstroms', _angstroms), ('b-in-angstroms', _angstroms), ('c-in-angstroms', _angstroms), ('alpha-in-degrees', _degrees), ('beta-in-degrees', _degrees), ('gamma-in-degrees', _degrees)) sgnum = p.getint(self.name, 'space-group') tmpSG = SG(sgnum) try: hklMax = p.getint(self.name, 'hkls-ssmax') except: hklMax = Material.DFLT_SSMAX try: beamEnergy = p.getfloat(self.name, 'beam-energy') except: beamEnergy = Material.DFLT_KEV lparams = [] for ind in tmpSG.reqParams: param, unit = lpStrings[ind] lparams.append(unit(p.getfloat(self.name, param))) pass # Initialize self._hklMax = hklMax self._beamEnergy = beamEnergy self._lparms = self._toSixLP(sgnum, lparams) self.sgnum = sgnum self.description = p.get(self.name, 'description') return def _newPdata(self): """Create a new plane data instance""" # spaceGroup module calculates forbidden reflections hkls = numpy.array(self.spaceGroup.getHKLs(self.hklMax)).T lprm = [self._lparms[i] for i in self.spaceGroup.reqParams] laue = self.spaceGroup.laueGroup self._pData = PData(hkls, lprm, laue, self._beamEnergy, Material.DFLT_STR, tThWidth=Material.DFLT_TTH) # # Set default exclusions # dflt_excl = numpy.array( [1 for i in range(len(self._pData.exclusions))], dtype=bool) dflt_excl[:5] = False self._pData.exclusions = dflt_excl return def _toSixLP(self, sgn, lp): """ Generate all six lattice parameters, making sure units are attached. """ tmpSG = SG(sgn) lp6 = list(tmpSG.sixLatticeParams(lp)) # make sure angles have attached units for i in range(6): if not hasattr(lp6[i], 'getVal'): if i in range(3): lp6[i] = _angstroms(lp6[i]) else: lp6[i] = _degrees(lp6[i]) pass pass pass return lp6 # # ============================== API # # ========== Properties # # property: spaceGroup @property def spaceGroup(self): """(read only) Space group""" return self._spaceGroup # property: sgnum def _get_sgnum(self): """Get method for sgnum""" return self._sgnum def _set_sgnum(self, v): """Set method for sgnum""" self._sgnum = v self._spaceGroup = SG(self._sgnum) self._newPdata() return sgnum = property(_get_sgnum, _set_sgnum, None, "Space group number") # property: beamEnergy def _get_beamEnergy(self): """Get method for beamEnergy""" return self._beamEnergy def _set_beamEnergy(self, keV): """ Set method for beamEnergy * note that units are assumed to be keV for float arguments. Also can take a valWUnit instance """ self._beamEnergy = keV self.planeData.wavelength = keV return beamEnergy = property(_get_beamEnergy, _set_beamEnergy, None, "Beam energy in keV") # property: hklMax def _get_hklMax(self): """Get method for hklMax""" return self._hklMax def _set_hklMax(self, v): """Set method for hklMax""" self._hklMax = v self._newPdata() # update planeData return hklMax = property(_get_hklMax, _set_hklMax, None, "Max sum of squares for HKLs") # property: planeData @property def planeData(self): """(read only) Return the planeData attribute (lattice parameters)""" return self._pData # property: latticeParameters def _get_latticeParameters(self): """Get method for latticeParameters""" return self._lparms def _set_latticeParameters(self, v): """Set method for latticeParameters""" self._lparms = self._toSixLP(self.sgnum, v) # self._newPdata() self.planeData.lparms = v return lpdoc = r"""Lattice parameters On output, all six paramters are returned. On input, either all six or a minimal set is accepted. The values have units attached, i.e. they are valWunit instances. """ latticeParameters = property(_get_latticeParameters, _set_latticeParameters, None, lpdoc) # property: "name" def _get_name(self): """Set method for name""" return self._name def _set_name(self, v): """Set method for name""" self._name = v return name = property(_get_name, _set_name, None, "Name of material") # property: "atominfo" def _get_atominfo(self): """Set method for name""" return self._atominfo def _set_atominfo(self, v): """Set method for name""" if v.shape[1] == 4: self._atominfo = v else: print("Improper syntax, array must be n x 4") return atominfo = property( _get_atominfo, _set_atominfo, None, "Information about atomic positions and electron number") # # ========== Methods # # pass # end class
def gen_trial_exp_data(grain_out_file,det_file,mat_file, x_ray_energy, mat_name, max_tth, comp_thresh, chi2_thresh, misorientation_bnd, \ misorientation_spacing,ome_range_deg, nframes, beam_stop_width): print('Loading Grain Data.....') #gen_grain_data ff_data = np.loadtxt(grain_out_file) #ff_data=np.atleast_2d(ff_data[2,:]) exp_maps = ff_data[:, 3:6] t_vec_ds = ff_data[:, 6:9] # completeness = ff_data[:, 1] chi2 = ff_data[:, 2] n_grains = exp_maps.shape[0] rMat_c = rot.rotMatOfExpMap(exp_maps.T) cut = np.where( np.logical_and(completeness > comp_thresh, chi2 < chi2_thresh))[0] exp_maps = exp_maps[cut, :] t_vec_ds = t_vec_ds[cut, :] chi2 = chi2[cut] # Add Misorientation mis_amt = misorientation_bnd * np.pi / 180. spacing = misorientation_spacing * np.pi / 180. mis_steps = int(misorientation_bnd / misorientation_spacing) ori_pts = np.arange(-mis_amt, (mis_amt + (spacing * 0.999)), spacing) num_ori_grid_pts = ori_pts.shape[0]**3 num_oris = exp_maps.shape[0] XsO, YsO, ZsO = np.meshgrid(ori_pts, ori_pts, ori_pts) grid0 = np.vstack([XsO.flatten(), YsO.flatten(), ZsO.flatten()]).T exp_maps_expanded = np.zeros([num_ori_grid_pts * num_oris, 3]) t_vec_ds_expanded = np.zeros([num_ori_grid_pts * num_oris, 3]) for ii in np.arange(num_oris): pts_to_use = np.arange(num_ori_grid_pts) + ii * num_ori_grid_pts exp_maps_expanded[pts_to_use, :] = grid0 + np.r_[exp_maps[ii, :]] t_vec_ds_expanded[pts_to_use, :] = np.r_[t_vec_ds[ii, :]] exp_maps = exp_maps_expanded t_vec_ds = t_vec_ds_expanded n_grains = exp_maps.shape[0] rMat_c = rot.rotMatOfExpMap(exp_maps.T) print('Loading Instrument Data.....') instr_cfg = yaml.load(open(det_file, 'r')) tiltAngles = instr_cfg['detector']['transform']['tilt_angles'] tVec_d = np.array(instr_cfg['detector']['transform']['t_vec_d']).reshape( 3, 1) #tVec_d[0] = -0.05 chi = instr_cfg['oscillation_stage']['chi'] tVec_s = np.array(instr_cfg['oscillation_stage']['t_vec_s']).reshape(3, 1) rMat_d = makeDetectorRotMat(tiltAngles) rMat_s = makeOscillRotMat([chi, 0.]) pixel_size = instr_cfg['detector']['pixels']['size'] nrows = instr_cfg['detector']['pixels']['rows'] ncols = instr_cfg['detector']['pixels']['columns'] # row_dim = pixel_size[0]*nrows # in mm # col_dim = pixel_size[1]*ncols # in mm x_col_edges = pixel_size[1] * (np.arange(ncols + 1) - 0.5 * ncols) y_row_edges = pixel_size[0] * (np.arange(nrows + 1) - 0.5 * nrows)[::-1] panel_dims = [(-0.5 * ncols * pixel_size[1], -0.5 * nrows * pixel_size[0]), (0.5 * ncols * pixel_size[1], 0.5 * nrows * pixel_size[0])] # a bit overkill, but grab max two-theta from all pixel transforms rx, ry = np.meshgrid(x_col_edges, y_row_edges) gcrds = detectorXYToGvec( np.vstack([rx.flatten(), ry.flatten()]).T, rMat_d, rMat_s, tVec_d, tVec_s, np.zeros(3)) pixel_tth = gcrds[0][0] detector_params = np.hstack( [tiltAngles, tVec_d.flatten(), chi, tVec_s.flatten()]) ome_period_deg = (ome_range_deg[0][0], (ome_range_deg[0][0] + 360.) ) #degrees ome_step_deg = (ome_range_deg[0][1] - ome_range_deg[0][0]) / nframes #degrees ome_period = (ome_period_deg[0] * np.pi / 180., ome_period_deg[1] * np.pi / 180.) ome_range = [(ome_range_deg[0][0] * np.pi / 180., ome_range_deg[0][1] * np.pi / 180.)] ome_step = ome_step_deg * np.pi / 180. ome_edges = np.arange(nframes + 1) * ome_step + ome_range[0][0] #fixed 2/26/17 base = np.array([x_col_edges[0], y_row_edges[0], ome_edges[0]]) deltas = np.array([ x_col_edges[1] - x_col_edges[0], y_row_edges[1] - y_row_edges[0], ome_edges[1] - ome_edges[0] ]) inv_deltas = 1.0 / deltas clip_vals = np.array([ncols, nrows]) print('Loading Material Data.....') #Load Material Data materials = cpl.load(open(mat_file, "rb")) check = np.zeros(len(materials)) for ii in np.arange(len(materials)): #print materials[ii].name check[ii] = materials[ii].name == mat_name mat_used = materials[np.where(check)[0][0]] #niti_mart.beamEnergy = valunits.valWUnit("wavelength","ENERGY",61.332,"keV") mat_used.beamEnergy = valunits.valWUnit("wavelength", "ENERGY", x_ray_energy, "keV") mat_used.planeData.exclusions = np.zeros(len( mat_used.planeData.exclusions), dtype=bool) if max_tth > 0.: mat_used.planeData.tThMax = np.amax(np.radians(max_tth)) else: mat_used.planeData.tThMax = np.amax(pixel_tth) pd = mat_used.planeData print('Final Assembly.....') experiment = argparse.Namespace() # grains related information experiment.n_grains = n_grains # this can be derived from other values... experiment.rMat_c = rMat_c # n_grains rotation matrices (one per grain) experiment.exp_maps = exp_maps # n_grains exp_maps -angle * rotation axis- (one per grain) experiment.plane_data = pd experiment.detector_params = detector_params experiment.pixel_size = pixel_size experiment.ome_range = ome_range experiment.ome_period = ome_period experiment.x_col_edges = x_col_edges experiment.y_row_edges = y_row_edges experiment.ome_edges = ome_edges experiment.ncols = ncols experiment.nrows = nrows experiment.nframes = nframes # used only in simulate... experiment.rMat_d = rMat_d experiment.tVec_d = np.atleast_2d(detector_params[3:6]).T experiment.chi = detector_params[ 6] # note this is used to compute S... why is it needed? experiment.tVec_s = np.atleast_2d(detector_params[7:]).T experiment.rMat_c = rMat_c experiment.distortion = None experiment.panel_dims = panel_dims # used only in simulate... experiment.base = base experiment.inv_deltas = inv_deltas experiment.clip_vals = clip_vals experiment.bsw = beam_stop_width if mis_steps == 0: nf_to_ff_id_map = cut else: nf_to_ff_id_map = np.tile(cut, 27 * mis_steps) return experiment, nf_to_ff_id_map
pwA = plotwrap.PlotWrap() pwA(predAngles[:,0], predAngles[:,1], style='r+') pwA(foundSpotAngs[:,0], foundSpotAngs[:,1], style='gx') # alignment is good, but definitely missing somex pwB = plotwrap.PlotWrap() pwB(predAngles[:,0], predAngles[:,2], style='r+') pwB(foundSpotAngs[:,0], foundSpotAngs[:,2], style='gx') ''' nSpotsTThAll, nSpotsTThClaimed, numClaimed, numMultiClaimed = spotsForFit.report(scaleByMultip=False) # iHKL = num.argmax(nSpotsTThAll) iHKL = num.where(nSpotsTThAll == num.unique(nSpotsTThAll)[1])[0][0] hklListForIndex = [ tuple(planeData.getHKLs()[iHKL]), ] etaTol = valunits.valWUnit('etaTol', 'angle', 0.5, 'degrees') omeTol = valunits.valWUnit('omeTol', 'angle', 0.5, 'degrees') rMat = indexer.fiberSearch(spotsForFit, hklListForIndex, iPhase=planeData.phaseID, nsteps=720, minCompleteness=0.70, minPctClaimed=0.75, friedelOnly=False, dspTol=None, etaTol=etaTol, omeTol=omeTol, debug=True, doMultiProc=opts.doMultiProc, doRefinement=False) print 'found %d grains' % (rMat.shape[0])
def mockup_experiment(): # user options # each grain is provided in the form of a quaternion. # The following array contains the quaternions for the array. Note that the # quaternions are in the columns, with the first row (row 0) being the real # part w. We assume that we are dealing with unit quaternions quats = np.array([[0.91836393, 0.90869942], [0.33952917, 0.1834835], [0.17216207, 0.10095837], [0.10811041, 0.36111851]]) n_grains = quats.shape[-1] # last dimension provides the number of grains phis = 2. * np.arccos( quats[0, :]) # phis are the angles for the quaternion ns = mutil.unitVector( quats[1:, :]) # ns contains the rotation axis as an unit vector exp_maps = np.array([phis[i] * ns[:, i] for i in range(n_grains)]) rMat_c = rot.rotMatOfQuat(quats) cvec = np.arange(-25, 26) X, Y, Z = np.meshgrid(cvec, cvec, cvec) crd0 = 1e-3 * np.vstack([X.flatten(), Y.flatten(), Z.flatten()]).T crd1 = crd0 + np.r_[0.100, 0.100, 0] crds = np.array([crd0, crd1]) # make grain parameters grain_params = [] for i in range(n_grains): for j in range(len(crd0)): grain_params.append( np.hstack( [exp_maps[i, :], crds[i][j, :], xf.vInv_ref.flatten()])) # scan range and period ome_period = (0, 2 * np.pi) ome_range = [ ome_period, ] ome_step = np.radians(1.) nframes = 0 for i in range(len(ome_range)): del_ome = ome_range[i][1] - ome_range[i][0] nframes += int((ome_range[i][1] - ome_range[i][0]) / ome_step) ome_edges = np.arange(nframes + 1) * ome_step # instrument with open('./retiga.yml', 'r') as fildes: instr_cfg = yaml.load(fildes) tiltAngles = instr_cfg['detector']['transform']['tilt_angles'] tVec_d = np.array(instr_cfg['detector']['transform']['t_vec_d']).reshape( 3, 1) chi = instr_cfg['oscillation_stage']['chi'] tVec_s = np.array(instr_cfg['oscillation_stage']['t_vec_s']).reshape(3, 1) rMat_d = xfcapi.makeDetectorRotMat(tiltAngles) rMat_s = xfcapi.makeOscillRotMat([chi, 0.]) pixel_size = instr_cfg['detector']['pixels']['size'] nrows = instr_cfg['detector']['pixels']['rows'] ncols = instr_cfg['detector']['pixels']['columns'] col_ps = pixel_size[1] row_ps = pixel_size[0] row_dim = row_ps * nrows # in mm col_dim = col_ps * ncols # in mm panel_dims = [(-0.5 * ncols * col_ps, -0.5 * nrows * row_ps), (0.5 * ncols * col_ps, 0.5 * nrows * row_ps)] x_col_edges = col_ps * (np.arange(ncols + 1) - 0.5 * ncols) y_row_edges = row_ps * (np.arange(nrows, -1, -1) - 0.5 * nrows) #x_col_edges = np.arange(panel_dims[0][0], panel_dims[1][0] + 0.5*col_ps, col_ps) #y_row_edges = np.arange(panel_dims[0][1], panel_dims[1][1] + 0.5*row_ps, row_ps) rx, ry = np.meshgrid(x_col_edges, y_row_edges) gcrds = xfcapi.detectorXYToGvec( np.vstack([rx.flatten(), ry.flatten()]).T, rMat_d, rMat_s, tVec_d, tVec_s, np.zeros(3)) max_pixel_tth = np.amax(gcrds[0][0]) detector_params = np.hstack( [tiltAngles, tVec_d.flatten(), chi, tVec_s.flatten()]) distortion = None # a different parametrization for the sensor (makes for faster quantization) base = np.array([x_col_edges[0], y_row_edges[0], ome_edges[0]]) deltas = np.array([ x_col_edges[1] - x_col_edges[0], y_row_edges[1] - y_row_edges[0], ome_edges[1] - ome_edges[0] ]) inv_deltas = 1.0 / deltas clip_vals = np.array([ncols, nrows]) # dilation max_diameter = np.sqrt(3) * 0.005 row_dilation = np.ceil(0.5 * max_diameter / row_ps) col_dilation = np.ceil(0.5 * max_diameter / col_ps) # crystallography data from hexrd import valunits gold = material.Material('gold') gold.sgnum = 225 gold.latticeParameters = [ 4.0782, ] gold.hklMax = 200 gold.beamEnergy = valunits.valWUnit("wavelength", "ENERGY", 52, "keV") gold.planeData.exclusions = None gold.planeData.tThMax = max_pixel_tth #note this comes from info in the detector ns = argparse.Namespace() # grains related information ns.n_grains = n_grains # this can be derived from other values... ns.rMat_c = rMat_c # n_grains rotation matrices (one per grain) ns.exp_maps = exp_maps # n_grains exp_maps -angle * rotation axis- (one per grain) ns.plane_data = gold.planeData ns.detector_params = detector_params ns.pixel_size = pixel_size ns.ome_range = ome_range ns.ome_period = ome_period ns.x_col_edges = x_col_edges ns.y_row_edges = y_row_edges ns.ome_edges = ome_edges ns.ncols = ncols ns.nrows = nrows ns.nframes = nframes # used only in simulate... ns.rMat_d = rMat_d ns.tVec_d = tVec_d ns.chi = chi # note this is used to compute S... why is it needed? ns.tVec_s = tVec_s # ns.rMat_s = rMat_s # ns.tVec_s = tVec_s ns.rMat_c = rMat_c ns.row_dilation = row_dilation ns.col_dilation = col_dilation ns.distortion = distortion ns.panel_dims = panel_dims # used only in simulate... ns.base = base ns.inv_deltas = inv_deltas ns.clip_vals = clip_vals return grain_params, ns
# a bit overkill, but grab max two-theta from all pixel transforms rx, ry = np.meshgrid(x_col_edges, y_row_edges) gcrds = detectorXYToGvec(np.vstack([rx.flatten(), ry.flatten()]).T, rMat_d, rMat_s, tVec_d, tVec_s, np.zeros(3)) pixel_tth = gcrds[0][0] detector_params = np.hstack([tiltAngles, tVec_d.flatten(), chi, tVec_s.flatten()]) distortion = None #============================================================================== # %% crystallography data #============================================================================== gold = make_matl('gold', 225, [4.0782,], hkl_ssq_max=200) gold.beamEnergy = valunits.valWUnit("wavelength","ENERGY",52,"keV") pd = gold.planeData pd.exclusions = np.zeros(len(pd.exclusions), dtype=bool) pd.tThMax = np.amax(pixel_tth) #============================================================================== # %% DIFFRACTION SIMULATION #============================================================================== def simulate_diffractions(grain_params): pbar = ProgressBar(widgets=['simulate_diffractions', Percentage(), Bar()], maxval=len(grain_params)).start() image_stack = np.zeros((nframes, nrows, ncols), dtype=bool) for i in range(len(grain_params)): sim_results = xrdutil.simulateGVecs(pd, detector_params,
from hexrd.xrd import detector from hexrd.xrd import grain as G from hexrd.xrd import indexer from hexrd.xrd import rotations as ROT from hexrd.xrd import spotfinder as SPT from hexrd.xrd.hydra import Hydra from hexrd.xrd.material import Material, loadMaterialList from math import pi numpy.seterr(invalid='ignore') r2d = 180. / pi d2r = pi / 180. OMEGA_PERIOD = (-pi, pi) coarse_ang_tol = valunits.valWUnit('coarse tol', 'angle', 1.0, 'degrees') fine_ang_tol = valunits.valWUnit('fine tol', 'angle', 0.5, 'degrees') # ============================================================================= # Defaults (will eventually make to a config file) # ============================================================================= HERE = os.path.dirname(__file__) toMatFile = os.path.join(HERE, '..', 'data', 'materials.cfg') DFLT_MATFILE = os.path.normpath(toMatFile) # check whether it exists matfileOK = os.access(DFLT_MATFILE, os.F_OK) if not matfileOK: # use relative path DFLT_MATFILE = os.path.join('data', 'materials.cfg') pass matfileOK = os.access(DFLT_MATFILE, os.F_OK) if not matfileOK: # set to null
def _kev(x): return valWUnit('beamenergy', 'energy', x, 'keV')
def _degrees(x): return valWUnit('lp', 'angle', x, 'degrees')
def gen_trial_exp_data(grain_out_file,det_file,mat_file, x_ray_energy, mat_name, max_tth, comp_thresh, chi2_thresh, misorientation_bnd, \ misorientation_spacing,ome_range_deg, nframes, beam_stop_width): print('Loading Grain Data.....') #gen_grain_data ff_data=np.loadtxt(grain_out_file) #ff_data=np.atleast_2d(ff_data[2,:]) exp_maps=ff_data[:,3:6] t_vec_ds=ff_data[:,6:9] # completeness=ff_data[:,1] chi2=ff_data[:,2] n_grains=exp_maps.shape[0] rMat_c = rot.rotMatOfExpMap(exp_maps.T) cut=np.where(np.logical_and(completeness>comp_thresh,chi2<chi2_thresh))[0] exp_maps=exp_maps[cut,:] t_vec_ds=t_vec_ds[cut,:] chi2=chi2[cut] # Add Misorientation mis_amt=misorientation_bnd*np.pi/180. spacing=misorientation_spacing*np.pi/180. ori_pts = np.arange(-mis_amt, (mis_amt+(spacing*0.999)),spacing) num_ori_grid_pts=ori_pts.shape[0]**3 num_oris=exp_maps.shape[0] XsO, YsO, ZsO = np.meshgrid(ori_pts, ori_pts, ori_pts) grid0 = np.vstack([XsO.flatten(), YsO.flatten(), ZsO.flatten()]).T exp_maps_expanded=np.zeros([num_ori_grid_pts*num_oris,3]) t_vec_ds_expanded=np.zeros([num_ori_grid_pts*num_oris,3]) for ii in np.arange(num_oris): pts_to_use=np.arange(num_ori_grid_pts)+ii*num_ori_grid_pts exp_maps_expanded[pts_to_use,:]=grid0+np.r_[exp_maps[ii,:] ] t_vec_ds_expanded[pts_to_use,:]=np.r_[t_vec_ds[ii,:] ] exp_maps=exp_maps_expanded t_vec_ds=t_vec_ds_expanded n_grains=exp_maps.shape[0] rMat_c = rot.rotMatOfExpMap(exp_maps.T) print('Loading Instrument Data.....') instr_cfg = yaml.load(open(det_file, 'r')) tiltAngles = instr_cfg['detector']['transform']['tilt_angles'] tVec_d = np.array(instr_cfg['detector']['transform']['t_vec_d']).reshape(3, 1) #tVec_d[0] = -0.05 chi = instr_cfg['oscillation_stage']['chi'] tVec_s = np.array(instr_cfg['oscillation_stage']['t_vec_s']).reshape(3, 1) rMat_d = makeDetectorRotMat(tiltAngles) rMat_s = makeOscillRotMat([chi, 0.]) pixel_size = instr_cfg['detector']['pixels']['size'] nrows = instr_cfg['detector']['pixels']['rows'] ncols = instr_cfg['detector']['pixels']['columns'] # row_dim = pixel_size[0]*nrows # in mm # col_dim = pixel_size[1]*ncols # in mm x_col_edges = pixel_size[1]*(np.arange(ncols+1) - 0.5*ncols) y_row_edges = pixel_size[0]*(np.arange(nrows+1) - 0.5*nrows)[::-1] panel_dims = [(-0.5*ncols*pixel_size[1], -0.5*nrows*pixel_size[0]), ( 0.5*ncols*pixel_size[1], 0.5*nrows*pixel_size[0])] # a bit overkill, but grab max two-theta from all pixel transforms rx, ry = np.meshgrid(x_col_edges, y_row_edges) gcrds = detectorXYToGvec(np.vstack([rx.flatten(), ry.flatten()]).T, rMat_d, rMat_s, tVec_d, tVec_s, np.zeros(3)) pixel_tth = gcrds[0][0] detector_params = np.hstack([tiltAngles, tVec_d.flatten(), chi, tVec_s.flatten()]) ome_period_deg=(ome_range_deg[0][0], (ome_range_deg[0][0]+360.)) #degrees ome_step_deg=(ome_range_deg[0][1]-ome_range_deg[0][0])/nframes #degrees ome_period = (ome_period_deg[0]*np.pi/180.,ome_period_deg[1]*np.pi/180.) ome_range = [(ome_range_deg[0][0]*np.pi/180.,ome_range_deg[0][1]*np.pi/180.)] ome_step = ome_step_deg*np.pi/180. ome_edges = np.arange(nframes+1)*ome_step+ome_range[0][0]#fixed 2/26/17 base = np.array([x_col_edges[0], y_row_edges[0], ome_edges[0]]) deltas = np.array([x_col_edges[1] - x_col_edges[0], y_row_edges[1] - y_row_edges[0], ome_edges[1] - ome_edges[0]]) inv_deltas = 1.0/deltas clip_vals = np.array([ncols, nrows]) print('Loading Material Data.....') #Load Material Data materials=cpl.load(open( mat_file, "rb" )) check=np.zeros(len(materials)) for ii in np.arange(len(materials)): #print materials[ii].name check[ii]=materials[ii].name==mat_name mat_used=materials[np.where(check)[0][0]] #niti_mart.beamEnergy = valunits.valWUnit("wavelength","ENERGY",61.332,"keV") mat_used.beamEnergy = valunits.valWUnit("wavelength","ENERGY",x_ray_energy,"keV") mat_used.planeData.exclusions = np.zeros(len(mat_used.planeData.exclusions), dtype=bool) if max_tth>0.: mat_used.planeData.tThMax = np.amax(np.radians(max_tth)) else: mat_used.planeData.tThMax = np.amax(pixel_tth) pd=mat_used.planeData print('Final Assembly.....') experiment = argparse.Namespace() # grains related information experiment.n_grains = n_grains # this can be derived from other values... experiment.rMat_c = rMat_c # n_grains rotation matrices (one per grain) experiment.exp_maps = exp_maps # n_grains exp_maps -angle * rotation axis- (one per grain) experiment.plane_data = pd experiment.detector_params = detector_params experiment.pixel_size = pixel_size experiment.ome_range = ome_range experiment.ome_period = ome_period experiment.x_col_edges = x_col_edges experiment.y_row_edges = y_row_edges experiment.ome_edges = ome_edges experiment.ncols = ncols experiment.nrows = nrows experiment.nframes = nframes# used only in simulate... experiment.rMat_d = rMat_d experiment.tVec_d = np.atleast_2d(detector_params[3:6]).T experiment.chi = detector_params[6] # note this is used to compute S... why is it needed? experiment.tVec_s = np.atleast_2d(detector_params[7:]).T experiment.rMat_c = rMat_c experiment.distortion = None experiment.panel_dims = panel_dims # used only in simulate... experiment.base = base experiment.inv_deltas = inv_deltas experiment.clip_vals = clip_vals experiment.bsw = beam_stop_width nf_to_ff_id_map=cut return experiment, nf_to_ff_id_map
Use the Material class directly for new materials. Known materials are defined by name in materialDict. """ from ConfigParser import SafeConfigParser as Parser import numpy from hexrd.xrd.crystallography import PlaneData as PData from hexrd.xrd.spacegroup import SpaceGroup as SG from hexrd.valunits import valWUnit # __all__ = ['Material', 'loadMaterialList'] # # ================================================== Module Data # angstroms = lambda x: valWUnit('lp', 'length', x, 'angstrom') degrees = lambda x: valWUnit('lp', 'angle', x, 'degrees') # # ---------------------------------------------------CLASS: Material # class Material(object): """Simple class for holding lattice parameters, accessible by name. The class references materials by name and contains lattice and space group data. """ DFLT_NAME = 'material' DFLT_SGNUM = 230 DFLT_LPARMS = [angstroms(1.0), angstroms(1.0), angstroms(1.0), degrees(90.0), degrees(90.0), degrees(90.0)] DFLT_SSMAX = 50
def mockup_experiment(): # user options # each grain is provided in the form of a quaternion. # The following array contains the quaternions for the array. Note that the # quaternions are in the columns, with the first row (row 0) being the real # part w. We assume that we are dealing with unit quaternions quats = np.array([[0.91836393, 0.90869942], [0.33952917, 0.18348350], [0.17216207, 0.10095837], [0.10811041, 0.36111851]]) n_grains = quats.shape[-1] # last dimension provides the number of grains phis = 2. * np.arccos( quats[0, :]) # phis are the angles for the quaternion # ns contains the rotation axis as an unit vector ns = hexrd.matrixutil.unitVector(quats[1:, :]) exp_maps = np.array([phis[i] * ns[:, i] for i in range(n_grains)]) rMat_c = rotations.rotMatOfQuat(quats) cvec = np.arange(-25, 26) X, Y, Z = np.meshgrid(cvec, cvec, cvec) crd0 = 1e-3 * np.vstack([X.flatten(), Y.flatten(), Z.flatten()]).T crd1 = crd0 + np.r_[0.100, 0.100, 0] crds = np.array([crd0, crd1]) # make grain parameters grain_params = [] for i in range(n_grains): for j in range(len(crd0)): grain_params.append( np.hstack([exp_maps[i, :], crds[i][j, :], vInv_ref])) # scan range and period ome_period = (0, 2 * np.pi) ome_range = [ ome_period, ] ome_step = np.radians(1.) nframes = 0 for i in range(len(ome_range)): nframes += int((ome_range[i][1] - ome_range[i][0]) / ome_step) ome_edges = np.arange(nframes + 1) * ome_step # instrument with open('./retiga.yml', 'r') as fildes: instr = instrument.HEDMInstrument(yaml.safe_load(fildes)) panel = next(iter(instr.detectors.values())) # !!! there is only 1 # tranform paramters # Sample chi = instr.chi tVec_s = instr.tvec # Detector rMat_d = panel.rmat tilt_angles_xyzp = np.asarray(rotations.angles_from_rmat_xyz(rMat_d)) tVec_d = panel.tvec # pixels row_ps = panel.pixel_size_row col_ps = panel.pixel_size_col pixel_size = (row_ps, col_ps) nrows = panel.rows ncols = panel.cols # panel dimensions panel_dims = [tuple(panel.corner_ll), tuple(panel.corner_ur)] x_col_edges = panel.col_edge_vec y_row_edges = panel.row_edge_vec rx, ry = np.meshgrid(x_col_edges, y_row_edges) max_pixel_tth = instrument.max_tth(instr) detector_params = np.hstack([tilt_angles_xyzp, tVec_d, chi, tVec_s]) distortion = panel.distortion # !!! must be None for now # a different parametrization for the sensor # (makes for faster quantization) base = np.array([x_col_edges[0], y_row_edges[0], ome_edges[0]]) deltas = np.array([ x_col_edges[1] - x_col_edges[0], y_row_edges[1] - y_row_edges[0], ome_edges[1] - ome_edges[0] ]) inv_deltas = 1.0 / deltas clip_vals = np.array([ncols, nrows]) # dilation max_diameter = np.sqrt(3) * 0.005 row_dilation = int(np.ceil(0.5 * max_diameter / row_ps)) col_dilation = int(np.ceil(0.5 * max_diameter / col_ps)) # crystallography data beam_energy = valunits.valWUnit("beam_energy", "energy", instr.beam_energy, "keV") beam_wavelength = constants.keVToAngstrom(beam_energy.getVal('keV')) dmin = valunits.valWUnit( "dmin", "length", 0.5 * beam_wavelength / np.sin(0.5 * max_pixel_tth), "angstrom") gold = material.Material() gold.latticeParameters = [4.0782] gold.dmin = dmin gold.beamEnergy = beam_energy gold.planeData.exclusions = None gold.planeData.tThMax = max_pixel_tth # note this comes detector ns = argparse.Namespace() # grains related information ns.n_grains = n_grains # this can be derived from other values... ns.rMat_c = rMat_c # n_grains rotation matrices (one per grain) ns.exp_maps = exp_maps # n_grains exp_maps (one per grain) ns.plane_data = gold.planeData ns.detector_params = detector_params ns.pixel_size = pixel_size ns.ome_range = ome_range ns.ome_period = ome_period ns.x_col_edges = x_col_edges ns.y_row_edges = y_row_edges ns.ome_edges = ome_edges ns.ncols = ncols ns.nrows = nrows ns.nframes = nframes # used only in simulate... ns.rMat_d = rMat_d ns.tVec_d = tVec_d ns.chi = chi # note this is used to compute S... why is it needed? ns.tVec_s = tVec_s ns.rMat_c = rMat_c ns.row_dilation = row_dilation ns.col_dilation = col_dilation ns.distortion = distortion ns.panel_dims = panel_dims # used only in simulate... ns.base = base ns.inv_deltas = inv_deltas ns.clip_vals = clip_vals return grain_params, ns
materials are defined by name in materialDict. """ from ConfigParser import SafeConfigParser as Parser import numpy from hexrd.xrd.crystallography import PlaneData as PData from hexrd.xrd.spacegroup import SpaceGroup as SG from hexrd.valunits import valWUnit # __all__ = ["Material", "loadMaterialList"] # # ================================================== Module Data # angstroms = lambda x: valWUnit("lp", "length", x, "angstrom") degrees = lambda x: valWUnit("lp", "angle", x, "degrees") # # ---------------------------------------------------CLASS: Material # class Material(object): """Simple class for holding lattice parameters, accessible by name. The class references materials by name and contains lattice and space group data. """ DFLT_NAME = "material" DFLT_SGNUM = 230 DFLT_LPARMS = [angstroms(1.0), angstroms(1.0), angstroms(1.0), degrees(90.0), degrees(90.0), degrees(90.0)] DFLT_SSMAX = 50