def writeGVE(spotsArray, fileroot, **kwargs): """ write Fable gve file from Spots object fileroot is the root string used to write the gve and ini files Outputs: No return value, but writes the following files: <fileroot>.gve <fileroot>_grainSpotter.ini (points to --> <fileroot>_grainSpotter.log) Keyword arguments: Mainly for GrainSpotter .ini file, but some are needed for gve files keyword default definitions ----------------------------------------------------------------------------------------- 'sgNum': <225> 'phaseID': <None> 'cellString': <F> 'omeRange': <-60, 60, 120, 240> the oscillation range(s) **currently pulls from spots 'deltaOme': <0.25, 0.25> the oscillation delta(s) **currently pulls from spots 'minMeas': <24> 'minCompl': <0.7> 'minUniqn': <0.5> 'uncertainty': <[0.10, 0.25, .50]> the min [tTh, eta, ome] uncertainties in degrees 'eulStep': <2> 'nSigmas': <2> 'minFracG': <0.90> 'nTrials': <100000> 'positionfit': <True> Notes: *) The omeRange is currently pulled from the spotsArray input; the kwarg has no effect as of now. Will change this to 'override' the spots info if the user, say, wants to pare down the range. *) There is no etaRange argument yet, but presumably GrainSpotter knows how to deal with this. Pending feature... """ # check on fileroot assert isinstance(fileroot, str) # keyword argument processing phaseID = None sgNum = 225 cellString = 'P' omeRange = num.r_[-60, 60] # in DEGREES deltaOme = 0.25 # in DEGREES minMeas = 24 minCompl = 0.7 minUniqn = 0.5 uncertainty = [0.10, 0.25, .50] # in DEGREES eulStep = 2 # in DEGREES nSigmas = 2 minFracG = 0.90 numTrials = 100000 positionFit = True kwarglen = len(kwargs) if kwarglen > 0: argkeys = kwargs.keys() for i in range(kwarglen): if argkeys[i] == 'sgNum': sgNum = kwargs[argkeys[i]] elif argkeys[i] == 'phaseID': phaseID = kwargs[argkeys[i]] elif argkeys[i] == 'cellString': cellString = kwargs[argkeys[i]] elif argkeys[i] == 'omeRange': omeRange = kwargs[argkeys[i]] elif argkeys[i] == 'deltaOme': deltaOme = kwargs[argkeys[i]] elif argkeys[i] == 'minMeas': minMeas = kwargs[argkeys[i]] elif argkeys[i] == 'minCompl': minCompl = kwargs[argkeys[i]] elif argkeys[i] == 'minUniqn': minUniqn = kwargs[argkeys[i]] elif argkeys[i] == 'uncertainty': uncertainty = kwargs[argkeys[i]] elif argkeys[i] == 'eulStep': eulStep = kwargs[argkeys[i]] elif argkeys[i] == 'nSigmas': nSigmas = kwargs[argkeys[i]] elif argkeys[i] == 'minFracG': minFracG = kwargs[argkeys[i]] elif argkeys[i] == 'nTrials': numTrials = kwargs[argkeys[i]] elif argkeys[i] == 'positionfit': positionFit = kwargs[argkeys[i]] else: raise RuntimeError, "Unrecognized keyword argument '%s'" % (argkeys[i]) # grab some detector geometry parameters for gve file header mmPerPixel = float(spotsArray.detectorGeom.pixelPitch) # ...these are still hard-coded to be square nrows_p = spotsArray.detectorGeom.nrows - 1 ncols_p = spotsArray.detectorGeom.ncols - 1 row_p, col_p = spotsArray.detectorGeom.pixelIndicesOfCartesianCoords(spotsArray.detectorGeom.xc, spotsArray.detectorGeom.yc) yc_p = ncols_p - col_p zc_p = nrows_p - row_p wd_mu = spotsArray.detectorGeom.workDist * 1e3 # in microns (Soeren) osc_axis = num.dot(fableSampCOB.T, Yl).flatten() # start grabbing stuff from planeData planeData = spotsArray.getPlaneData(phaseID=phaseID) cellp = planeData.latVecOps['dparms'] U0 = planeData.latVecOps['U0'] wlen = planeData.wavelength dsp = planeData.getPlaneSpacings() fHKLs = planeData.getSymHKLs() tThRng = planeData.getTThRanges() symTag = planeData.getLaueGroup() tThMin, tThMax = (r2d*tThRng.min(), r2d*tThRng.max()) # single range should be ok since entering hkls etaMin, etaMax = (0, 360) # not sure when this will ever *NOT* be the case, so setting it omeMin = spotsArray.getOmegaMins() omeMax = spotsArray.getOmegaMaxs() omeRangeString = '' for iOme in range(len(omeMin)): if hasattr(omeMin[iOme], 'getVal'): omeRangeString += 'omegarange %g %g\n' % (omeMin[iOme].getVal('degrees'), omeMax[iOme].getVal('degrees')) else: omeRangeString += 'omegarange %g %g\n' % (omeMin[iOme] * r2d, omeMax[iOme] * r2d) # convert angles cellp[3:] = r2d*cellp[3:] # make the theoretical hkls string gvecHKLString = '' for i in range(len(dsp)): for j in range(fHKLs[i].shape[1]): gvecHKLString += '%1.8f %d %d %d\n' % (1/dsp[i], fHKLs[i][0, j], fHKLs[i][1, j], fHKLs[i][2, j]) # now for the measured data section # xr yr zr xc yc ds eta omega gvecString = '' spotsIter = spotsArray.getIterPhase(phaseID, returnBothCoordTypes=True) for iSpot, angCOM, xyoCOM in spotsIter: sR, sC, sOme = xyoCOM # detector coords sTTh, sEta, sOme = angCOM # angular coords (radians) sDsp = wlen / 2. / num.sin(0.5*sTTh) # dspacing # get raw y, z (Fable frame) yraw = ncols_p - sC zraw = nrows_p - sR # convert eta to fable frame rEta = mapAngle(90. - r2d*sEta, [0, 360], units='degrees') # make mesaured G vector components in fable frame mGvec = makeMeasuredScatteringVectors(sTTh, sEta, sOme, convention='fable', frame='sample') # full Gvec components in fable lab frame (for grainspotter position fit) gveXYZ = spotsArray.detectorGeom.angToXYO(sTTh, sEta, sOme, outputGve=True) # no 4*pi mGvec = mGvec / sDsp # make contribution gvecString += '%1.8f %1.8f %1.8f %1.8f %1.8f %1.8f %1.8f %1.8f %d %1.8f %1.8f %1.8f\n' \ % (mGvec[0], mGvec[1], mGvec[2], \ sR, sC, \ 1/sDsp, rEta, r2d*sOme, \ iSpot, \ gveXYZ[0, :], gveXYZ[1, :], gveXYZ[2, :]) pass # write gve file for grainspotter fid = open(fileroot+'.gve', 'w') print >> fid, '%1.8f %1.8f %1.8f %1.8f %1.8f %1.8f ' % tuple(cellp) + \ cellString + '\n' + \ '# wavelength = %1.8f\n' % (wlen) + \ '# wedge = 0.000000\n' + \ '# axis = %d %d %d\n' % tuple(osc_axis) + \ '# cell__a %1.4f\n' %(cellp[0]) + \ '# cell__b %1.4f\n' %(cellp[1]) + \ '# cell__c %1.4f\n' %(cellp[2]) + \ '# cell_alpha %1.4f\n' %(cellp[3]) + \ '# cell_beta %1.4f\n' %(cellp[4]) + \ '# cell_gamma %1.4f\n' %(cellp[5]) + \ '# cell_lattice_[P,A,B,C,I,F,R] %s\n' %(cellString) + \ '# chi 0.0\n' + \ '# distance %.4f\n' %(wd_mu) + \ '# fit_tolerance 0.5\n' + \ '# o11 1\n' + \ '# o12 0\n' + \ '# o21 0\n' + \ '# o22 -1\n' + \ '# omegasign %1.1f\n' %(num.sign(deltaOme)) + \ '# t_x 0\n' + \ '# t_y 0\n' + \ '# t_z 0\n' + \ '# tilt_x 0.000000\n' + \ '# tilt_y 0.000000\n' + \ '# tilt_z 0.000000\n' + \ '# y_center %.6f\n' %(yc_p) + \ '# y_size %.6f\n' %(mmPerPixel*1.e3) + \ '# z_center %.6f\n' %(zc_p) + \ '# z_size %.6f\n' %(mmPerPixel*1.e3) + \ '# ds h k l\n' + \ gvecHKLString + \ '# xr yr zr xc yc ds eta omega\n' + \ gvecString fid.close() ############################################################### # GrainSpotter ini parameters # # fileroot = tempfile.mktemp() if positionFit: positionString = 'positionfit' else: positionString = '!positionfit' if numTrials == 0: randomString = '!random\n' else: randomString = 'random %g\n' % (numTrials) fid = open(fileroot+'_grainSpotter.ini', 'w') # self.__tempFNameList.append(fileroot) print >> fid, \ 'spacegroup %d\n' % (sgNum) + \ 'tthrange %g %g\n' % (tThMin, tThMax) + \ 'etarange %g %g\n' % (etaMin, etaMax) + \ 'domega %g\n' % (deltaOme) + \ omeRangeString + \ 'filespecs %s.gve %s_grainSpotter.log\n' % (fileroot, fileroot) + \ 'cuts %d %g %g\n' % (minMeas, minCompl, minUniqn) + \ 'eulerstep %g\n' % (eulStep)+ \ 'uncertainties %g %g %g\n' % (uncertainty[0], uncertainty[1], uncertainty[2]) + \ 'nsigmas %d\n' % (nSigmas) + \ 'minfracg %g\n' % (minFracG) + \ randomString + \ positionString + '\n' fid.close() return
def getFriedelPair(tth0, eta0, *ome0, **kwargs): """ Get the diffractometer angular coordinates in degrees for the Friedel pair of a given reflection (min angular distance). AUTHORS: J. V. Bernier -- 10 Nov 2009 USAGE: ome1, eta1 = getFriedelPair(tth0, eta0, *ome0, display=False, units='degrees', convention='hexrd') INPUTS: 1) tth0 is a list (or ndarray) of 1 or n the bragg angles (2theta) for the n reflections (tiled to match eta0 if only 1 is given). 2) eta0 is a list (or ndarray) of 1 or n azimuthal coordinates for the n reflections (tiled to match tth0 if only 1 is given). 3) ome0 is a list (or ndarray) of 1 or n reference oscillation angles for the n reflections (denoted omega in [1]). This argument is optional. 4) Keyword arguments may be one of the following: Keyword Values|{default} Action -------------- -------------- -------------- 'display' True|{False} toggles display info to cmd line 'units' 'radians'|{'degrees'} sets units for input angles 'convention' 'fable'|{'hexrd'} sets conventions defining the angles (see below) 'chiTilt' None the inclination (about Xlab) of the oscillation axis OUTPUTS: 1) ome1 contains the oscialltion angle coordinates of the Friedel pairs associated with the n input reflections, relative to ome0 (i.e. ome1 = <result> + ome0). Output is in DEGREES! 2) eta1 contains the azimuthal coordinates of the Friedel pairs associated with the n input reflections. Output units are controlled via the module variable 'outputDegrees' NOTES: JVB) The ouputs ome1, eta1 are written using the selected convention, but the units are alway degrees. May change this to work with Nathan's global... JVB) In the 'fable' convention [1], {XYZ} form a RHON basis where X is downstream, Z is vertical, and eta is CCW with +Z defining eta = 0. JVB) In the 'hexrd' convention [2], {XYZ} form a RHON basis where Z is upstream, Y is vertical, and eta is CCW with +X defining eta = 0. REFERENCES: [1] E. M. Lauridsen, S. Schmidt, R. M. Suter, and H. F. Poulsen, ``Tracking: a method for structural characterization of grains in powders or polycrystals''. J. Appl. Cryst. (2001). 34, 744--750 [2] J. V. Bernier, M. P. Miller, J. -S. Park, and U. Lienert, ``Quantitative Stress Analysis of Recrystallized OFHC Cu Subject to Deformed In Situ'', J. Eng. Mater. Technol. (2008). 130. DOI:10.1115/1.2870234 """ dispFlag = False fableFlag = False chi = None c1 = 1.0 c2 = pi / 180.0 zTol = 1.0e-7 # cast to arrays (in case they aren't) if num.isscalar(eta0): eta0 = [eta0] if num.isscalar(tth0): tth0 = [tth0] if num.isscalar(ome0): ome0 = [ome0] eta0 = num.asarray(eta0) tth0 = num.asarray(tth0) ome0 = num.asarray(ome0) if eta0.ndim != 1: raise RuntimeError, "your azimuthal input was not 1-D, so I do not know what you expect me to do" npts = len(eta0) if tth0.ndim != 1: raise RuntimeError, "your Bragg angle input was not 1-D, so I do not know what you expect me to do" else: if len(tth0) != npts: if len(tth0) == 1: tth0 = tth0 * num.ones(npts) elif npts == 1: npts = len(tth0) eta0 = eta0 * num.ones(npts) else: raise RuntimeError, "the azimuthal and Bragg angle inputs are inconsistent" if len(ome0) == 0: ome0 = num.zeros(npts) # dummy ome0 elif len(ome0) == 1 and npts > 1: ome0 = ome0 * num.ones(npts) else: if len(ome0) != npts: raise RuntimeError( "your oscialltion angle input is inconsistent; " + "it has length %d while it should be %d" % (len(ome0), npts) ) # keyword args processing kwarglen = len(kwargs) if kwarglen > 0: argkeys = kwargs.keys() for i in range(kwarglen): if argkeys[i] == "display": dispFlag = kwargs[argkeys[i]] elif argkeys[i] == "convention": if kwargs[argkeys[i]].lower() == "fable": fableFlag = True elif argkeys[i] == "units": if kwargs[argkeys[i]] == "radians": c1 = 180.0 / pi c2 = 1.0 elif argkeys[i] == "chiTilt": if kwargs[argkeys[i]] is not None: chi = kwargs[argkeys[i]] # a little talkback... if dispFlag: if fableFlag: print "\nUsing Fable angle convention\n" else: print "\nUsing image-based angle convention\n" # mapped eta input # - in DEGREES, thanks to c1 eta0 = mapAngle(c1 * eta0, [-180, 180], units="degrees") if fableFlag: eta0 = 90 - eta0 # must put args into RADIANS # - eta0 is in DEGREES, # - the others are in whatever was entered, hence c2 eta0 = d2r * eta0 tht0 = c2 * tth0 / 2 if chi is not None: chi = c2 * chi else: chi = 0 # --------------------- # SYSTEM SOLVE # # # cos(chi)cos(eta)cos(theta)sin(x) - cos(chi)sin(theta)cos(x) = sin(theta) - sin(chi)sin(eta)cos(theta) # # # Identity: a sin x + b cos x = sqrt(a**2 + b**2) sin (x + alfa) # # / # | atan(b/a) for a > 0 # alfa < # | pi + atan(b/a) for a < 0 # \ # # => sin (x + alfa) = c / sqrt(a**2 + b**2) # # must use both branches for sin(x) = n: x = u (+ 2k*pi) | x = pi - u (+ 2k*pi) # cchi = num.cos(chi) schi = num.sin(chi) ceta = num.cos(eta0) seta = num.sin(eta0) ctht = num.cos(tht0) stht = num.sin(tht0) nchi = num.c_[0.0, cchi, schi].T gHat0_l = -num.vstack([ceta * ctht, seta * ctht, stht]) a = cchi * ceta * ctht b = -cchi * stht c = stht + schi * seta * ctht # form solution abMag = num.sqrt(a * a + b * b) assert num.all(abMag > 0), "Beam vector specification is infeasible!" phaseAng = num.arctan2(b, a) rhs = c / abMag rhs[abs(rhs) > 1.0] = num.nan rhsAng = num.arcsin(rhs) # write ome angle output arrays (NaNs persist here) ome1 = rhsAng - phaseAng ome2 = num.pi - rhsAng - phaseAng ome1 = mapAngle(ome1, [-num.pi, num.pi], units="radians") ome2 = mapAngle(ome2, [-num.pi, num.pi], units="radians") ome_stack = num.vstack([ome1, ome2]) min_idx = num.argmin(abs(ome_stack), axis=0) ome_min = ome_stack[min_idx, range(len(ome1))] eta_min = num.nan * num.ones_like(ome_min) # mark feasible reflections goodOnes = -num.isnan(ome_min) numGood = sum(goodOnes) tmp_eta = num.empty(numGood) tmp_gvec = gHat0_l[:, goodOnes] for i in range(numGood): come = num.cos(ome_min[goodOnes][i]) some = num.sin(ome_min[goodOnes][i]) rchi = rotMatOfExpMap(num.tile(ome_min[goodOnes][i], (3, 1)) * nchi) gHat_l = num.dot(rchi, tmp_gvec[:, i].reshape(3, 1)) tmp_eta[i] = num.arctan2(gHat_l[1], gHat_l[0]) pass eta_min[goodOnes] = tmp_eta # everybody back to DEGREES! # - ome1 is in RADIANS here # - convert and put into [-180, 180] ome1 = mapAngle(mapAngle(r2d * ome_min, [-180, 180], units="degrees") + c1 * ome0, [-180, 180], units="degrees") # put eta1 in [-180, 180] eta1 = mapAngle(r2d * eta_min, [-180, 180], units="degrees") if not outputDegrees: ome1 = d2r * ome1 eta1 = d2r * eta1 return ome1, eta1
set12 = eta0 > 90.0 lw = 2 fig1 = plt.figure(1) fig1.clf() axes1 = fig1.gca() fig2 = plt.figure(2) fig2.clf() axes2 = fig2.gca() for i in range(len(chi)): ome1, eta1 = xtl.getFriedelPair(tth0, eta0, chiTilt=chi[i], units="degrees", convention="hexrd") axes1.plot(d2r * eta0[set11], ome1[set11], lt[i], label="$\chi$=%d$^\circ$" % chi[i], linewidth=lw) axes1.plot(d2r * eta0[set12], ome1[set12], lt[i], label="$\chi$=%d$^\circ$" % chi[i], linewidth=lw) # eta = np.pi - rot.angularDifference(d2r*eta0, eta1) eta = rot.mapAngle(np.pi - (d2r * eta0 - eta1)) set1 = np.logical_and(eta0 <= 0.0, eta >= 0.0) set2 = np.logical_and(eta0 <= 0.0, eta <= 0.0) set3 = eta0 >= 0.0 axes2.plot(d2r * eta0, eta, lt[i], linewidth=lw) # axes2.plot(d2r*eta0[set1], eta[set1], lt[i], linewidth=lw) # axes2.plot(d2r*eta0[set2], eta[set2], lt[i], linewidth=lw) # axes2.plot(d2r*eta0[set3], eta[set3], lt[i], linewidth=lw) axes1.grid(True) axes1.axis("tight") axes1.set_xlabel("starting azimuth, $\eta_0$") axes1.set_ylabel("minimum $\Delta\omega_{FP}$")
def writeGVE(spotsArray, fileroot, **kwargs): """ write Fable gve file from Spots object fileroot is the root string used to write the gve and ini files Outputs: No return value, but writes the following files: <fileroot>.gve <fileroot>_grainSpotter.ini (points to --> <fileroot>_grainSpotter.log) Keyword arguments: Mainly for GrainSpotter .ini file, but some are needed for gve files keyword default definitions ----------------------------------------------------------------------------------------- 'sgNum': <225> 'phaseID': <None> 'cellString': <F> 'omeRange': <-60, 60, 120, 240> the oscillation range(s) **currently pulls from spots 'deltaOme': <0.25, 0.25> the oscillation delta(s) **currently pulls from spots 'minMeas': <24> 'minCompl': <0.7> 'minUniqn': <0.5> 'uncertainty': <[0.10, 0.25, .50]> the min [tTh, eta, ome] uncertainties in degrees 'eulStep': <2> 'nSigmas': <2> 'minFracG': <0.90> 'nTrials': <100000> 'positionfit': <True> Notes: *) The omeRange is currently pulled from the spotsArray input; the kwarg has no effect as of now. Will change this to 'override' the spots info if the user, say, wants to pare down the range. *) There is no etaRange argument yet, but presumably GrainSpotter knows how to deal with this. Pending feature... """ # check on fileroot assert isinstance(fileroot, str) # keyword argument processing phaseID = None sgNum = 225 cellString = 'P' omeRange = num.r_[-60, 60] # in DEGREES deltaOme = 0.25 # in DEGREES minMeas = 24 minCompl = 0.7 minUniqn = 0.5 uncertainty = [0.10, 0.25, .50] # in DEGREES eulStep = 2 # in DEGREES nSigmas = 2 minFracG = 0.90 numTrials = 100000 positionFit = True kwarglen = len(kwargs) if kwarglen > 0: argkeys = kwargs.keys() for i in range(kwarglen): if argkeys[i] == 'sgNum': sgNum = kwargs[argkeys[i]] elif argkeys[i] == 'phaseID': phaseID = kwargs[argkeys[i]] elif argkeys[i] == 'cellString': cellString = kwargs[argkeys[i]] elif argkeys[i] == 'omeRange': omeRange = kwargs[argkeys[i]] elif argkeys[i] == 'deltaOme': deltaOme = kwargs[argkeys[i]] elif argkeys[i] == 'minMeas': minMeas = kwargs[argkeys[i]] elif argkeys[i] == 'minCompl': minCompl = kwargs[argkeys[i]] elif argkeys[i] == 'minUniqn': minUniqn = kwargs[argkeys[i]] elif argkeys[i] == 'uncertainty': uncertainty = kwargs[argkeys[i]] elif argkeys[i] == 'eulStep': eulStep = kwargs[argkeys[i]] elif argkeys[i] == 'nSigmas': nSigmas = kwargs[argkeys[i]] elif argkeys[i] == 'minFracG': minFracG = kwargs[argkeys[i]] elif argkeys[i] == 'nTrials': numTrials = kwargs[argkeys[i]] elif argkeys[i] == 'positionfit': positionFit = kwargs[argkeys[i]] else: raise RuntimeError, "Unrecognized keyword argument '%s'" % ( argkeys[i]) # grab some detector geometry parameters for gve file header mmPerPixel = float(spotsArray.detectorGeom.pixelPitch ) # ...these are still hard-coded to be square nrows_p = spotsArray.detectorGeom.nrows - 1 ncols_p = spotsArray.detectorGeom.ncols - 1 row_p, col_p = spotsArray.detectorGeom.pixelIndicesOfCartesianCoords( spotsArray.detectorGeom.xc, spotsArray.detectorGeom.yc) yc_p = ncols_p - col_p zc_p = nrows_p - row_p wd_mu = spotsArray.detectorGeom.workDist * 1e3 # in microns (Soeren) osc_axis = num.dot(fableSampCOB.T, Yl).flatten() # start grabbing stuff from planeData planeData = spotsArray.getPlaneData(phaseID=phaseID) cellp = planeData.latVecOps['dparms'] U0 = planeData.latVecOps['U0'] wlen = planeData.wavelength dsp = planeData.getPlaneSpacings() fHKLs = planeData.getSymHKLs() tThRng = planeData.getTThRanges() symTag = planeData.getLaueGroup() tThMin, tThMax = (r2d * tThRng.min(), r2d * tThRng.max() ) # single range should be ok since entering hkls etaMin, etaMax = ( 0, 360 ) # not sure when this will ever *NOT* be the case, so setting it omeMin = spotsArray.getOmegaMins() omeMax = spotsArray.getOmegaMaxs() omeRangeString = '' for iOme in range(len(omeMin)): if hasattr(omeMin[iOme], 'getVal'): omeRangeString += 'omegarange %g %g\n' % ( omeMin[iOme].getVal('degrees'), omeMax[iOme].getVal('degrees')) else: omeRangeString += 'omegarange %g %g\n' % (omeMin[iOme] * r2d, omeMax[iOme] * r2d) # convert angles cellp[3:] = r2d * cellp[3:] # make the theoretical hkls string gvecHKLString = '' for i in range(len(dsp)): for j in range(fHKLs[i].shape[1]): gvecHKLString += '%1.8f %d %d %d\n' % ( 1 / dsp[i], fHKLs[i][0, j], fHKLs[i][1, j], fHKLs[i][2, j]) # now for the measured data section # xr yr zr xc yc ds eta omega gvecString = '' spotsIter = spotsArray.getIterPhase(phaseID, returnBothCoordTypes=True) for iSpot, angCOM, xyoCOM in spotsIter: sR, sC, sOme = xyoCOM # detector coords sTTh, sEta, sOme = angCOM # angular coords (radians) sDsp = wlen / 2. / num.sin(0.5 * sTTh) # dspacing # get raw y, z (Fable frame) yraw = ncols_p - sC zraw = nrows_p - sR # convert eta to fable frame rEta = mapAngle(90. - r2d * sEta, [0, 360], units='degrees') # make mesaured G vector components in fable frame mGvec = makeMeasuredScatteringVectors(sTTh, sEta, sOme, convention='fable', frame='sample') # full Gvec components in fable lab frame (for grainspotter position fit) gveXYZ = spotsArray.detectorGeom.angToXYO(sTTh, sEta, sOme, outputGve=True) # no 4*pi mGvec = mGvec / sDsp # make contribution gvecString += '%1.8f %1.8f %1.8f %1.8f %1.8f %1.8f %1.8f %1.8f %d %1.8f %1.8f %1.8f\n' \ % (mGvec[0], mGvec[1], mGvec[2], \ sR, sC, \ 1/sDsp, rEta, r2d*sOme, \ iSpot, \ gveXYZ[0, :], gveXYZ[1, :], gveXYZ[2, :]) pass # write gve file for grainspotter fid = open(fileroot + '.gve', 'w') print >> fid, '%1.8f %1.8f %1.8f %1.8f %1.8f %1.8f ' % tuple(cellp) + \ cellString + '\n' + \ '# wavelength = %1.8f\n' % (wlen) + \ '# wedge = 0.000000\n' + \ '# axis = %d %d %d\n' % tuple(osc_axis) + \ '# cell__a %1.4f\n' %(cellp[0]) + \ '# cell__b %1.4f\n' %(cellp[1]) + \ '# cell__c %1.4f\n' %(cellp[2]) + \ '# cell_alpha %1.4f\n' %(cellp[3]) + \ '# cell_beta %1.4f\n' %(cellp[4]) + \ '# cell_gamma %1.4f\n' %(cellp[5]) + \ '# cell_lattice_[P,A,B,C,I,F,R] %s\n' %(cellString) + \ '# chi 0.0\n' + \ '# distance %.4f\n' %(wd_mu) + \ '# fit_tolerance 0.5\n' + \ '# o11 1\n' + \ '# o12 0\n' + \ '# o21 0\n' + \ '# o22 -1\n' + \ '# omegasign %1.1f\n' %(num.sign(deltaOme)) + \ '# t_x 0\n' + \ '# t_y 0\n' + \ '# t_z 0\n' + \ '# tilt_x 0.000000\n' + \ '# tilt_y 0.000000\n' + \ '# tilt_z 0.000000\n' + \ '# y_center %.6f\n' %(yc_p) + \ '# y_size %.6f\n' %(mmPerPixel*1.e3) + \ '# z_center %.6f\n' %(zc_p) + \ '# z_size %.6f\n' %(mmPerPixel*1.e3) + \ '# ds h k l\n' + \ gvecHKLString + \ '# xr yr zr xc yc ds eta omega\n' + \ gvecString fid.close() ############################################################### # GrainSpotter ini parameters # # fileroot = tempfile.mktemp() if positionFit: positionString = 'positionfit' else: positionString = '!positionfit' if numTrials == 0: randomString = '!random\n' else: randomString = 'random %g\n' % (numTrials) fid = open(fileroot + '_grainSpotter.ini', 'w') # self.__tempFNameList.append(fileroot) print >> fid, \ 'spacegroup %d\n' % (sgNum) + \ 'tthrange %g %g\n' % (tThMin, tThMax) + \ 'etarange %g %g\n' % (etaMin, etaMax) + \ 'domega %g\n' % (deltaOme) + \ omeRangeString + \ 'filespecs %s.gve %s_grainSpotter.log\n' % (fileroot, fileroot) + \ 'cuts %d %g %g\n' % (minMeas, minCompl, minUniqn) + \ 'eulerstep %g\n' % (eulStep)+ \ 'uncertainties %g %g %g\n' % (uncertainty[0], uncertainty[1], uncertainty[2]) + \ 'nsigmas %d\n' % (nSigmas) + \ 'minfracg %g\n' % (minFracG) + \ randomString + \ positionString + '\n' fid.close() return
def getFriedelPair(tth0, eta0, *ome0, **kwargs): """ Get the diffractometer angular coordinates in degrees for the Friedel pair of a given reflection (min angular distance). AUTHORS: J. V. Bernier -- 10 Nov 2009 USAGE: ome1, eta1 = getFriedelPair(tth0, eta0, *ome0, display=False, units='degrees', convention='hexrd') INPUTS: 1) tth0 is a list (or ndarray) of 1 or n the bragg angles (2theta) for the n reflections (tiled to match eta0 if only 1 is given). 2) eta0 is a list (or ndarray) of 1 or n azimuthal coordinates for the n reflections (tiled to match tth0 if only 1 is given). 3) ome0 is a list (or ndarray) of 1 or n reference oscillation angles for the n reflections (denoted omega in [1]). This argument is optional. 4) Keyword arguments may be one of the following: Keyword Values|{default} Action -------------- -------------- -------------- 'display' True|{False} toggles display info to cmd line 'units' 'radians'|{'degrees'} sets units for input angles 'convention' 'fable'|{'hexrd'} sets conventions defining the angles (see below) 'chiTilt' None the inclination (about Xlab) of the oscillation axis OUTPUTS: 1) ome1 contains the oscialltion angle coordinates of the Friedel pairs associated with the n input reflections, relative to ome0 (i.e. ome1 = <result> + ome0). Output is in DEGREES! 2) eta1 contains the azimuthal coordinates of the Friedel pairs associated with the n input reflections. Output units are controlled via the module variable 'outputDegrees' NOTES: JVB) The ouputs ome1, eta1 are written using the selected convention, but the units are alway degrees. May change this to work with Nathan's global... JVB) In the 'fable' convention [1], {XYZ} form a RHON basis where X is downstream, Z is vertical, and eta is CCW with +Z defining eta = 0. JVB) In the 'hexrd' convention [2], {XYZ} form a RHON basis where Z is upstream, Y is vertical, and eta is CCW with +X defining eta = 0. REFERENCES: [1] E. M. Lauridsen, S. Schmidt, R. M. Suter, and H. F. Poulsen, ``Tracking: a method for structural characterization of grains in powders or polycrystals''. J. Appl. Cryst. (2001). 34, 744--750 [2] J. V. Bernier, M. P. Miller, J. -S. Park, and U. Lienert, ``Quantitative Stress Analysis of Recrystallized OFHC Cu Subject to Deformed In Situ'', J. Eng. Mater. Technol. (2008). 130. DOI:10.1115/1.2870234 """ dispFlag = False fableFlag = False chi = None c1 = 1. c2 = pi/180. zTol = 1.e-7 # cast to arrays (in case they aren't) if num.isscalar(eta0): eta0 = [eta0] if num.isscalar(tth0): tth0 = [tth0] if num.isscalar(ome0): ome0 = [ome0] eta0 = num.asarray(eta0) tth0 = num.asarray(tth0) ome0 = num.asarray(ome0) if eta0.ndim != 1: raise RuntimeError, 'your azimuthal input was not 1-D, so I do not know what you expect me to do' npts = len(eta0) if tth0.ndim != 1: raise RuntimeError, 'your Bragg angle input was not 1-D, so I do not know what you expect me to do' else: if len(tth0) != npts: if len(tth0) == 1: tth0 = tth0*num.ones(npts) elif npts == 1: npts = len(tth0) eta0 = eta0*num.ones(npts) else: raise RuntimeError, 'the azimuthal and Bragg angle inputs are inconsistent' if len(ome0) == 0: ome0 = num.zeros(npts) # dummy ome0 elif len(ome0) == 1 and npts > 1: ome0 = ome0*num.ones(npts) else: if len(ome0) != npts: raise RuntimeError('your oscialltion angle input is inconsistent; ' \ + 'it has length %d while it should be %d' % (len(ome0), npts) ) # keyword args processing kwarglen = len(kwargs) if kwarglen > 0: argkeys = kwargs.keys() for i in range(kwarglen): if argkeys[i] == 'display': dispFlag = kwargs[argkeys[i]] elif argkeys[i] == 'convention': if kwargs[argkeys[i]].lower() == 'fable': fableFlag = True elif argkeys[i] == 'units': if kwargs[argkeys[i]] == 'radians': c1 = 180./pi c2 = 1. elif argkeys[i] == 'chiTilt': if kwargs[argkeys[i]] is not None: chi = kwargs[argkeys[i]] # a little talkback... if dispFlag: if fableFlag: print '\nUsing Fable angle convention\n' else: print '\nUsing image-based angle convention\n' # mapped eta input # - in DEGREES, thanks to c1 eta0 = mapAngle(c1*eta0, [-180, 180], units='degrees') if fableFlag: eta0 = 90 - eta0 # must put args into RADIANS # - eta0 is in DEGREES, # - the others are in whatever was entered, hence c2 eta0 = d2r*eta0 tht0 = c2*tth0/2 if chi is not None: chi = c2*chi else: chi = 0 # --------------------- # SYSTEM SOLVE # # # cos(chi)cos(eta)cos(theta)sin(x) - cos(chi)sin(theta)cos(x) = sin(theta) - sin(chi)sin(eta)cos(theta) # # # Identity: a sin x + b cos x = sqrt(a**2 + b**2) sin (x + alfa) # # / # | atan(b/a) for a > 0 # alfa < # | pi + atan(b/a) for a < 0 # \ # # => sin (x + alfa) = c / sqrt(a**2 + b**2) # # must use both branches for sin(x) = n: x = u (+ 2k*pi) | x = pi - u (+ 2k*pi) # cchi = num.cos(chi); schi = num.sin(chi) ceta = num.cos(eta0); seta = num.sin(eta0) ctht = num.cos(tht0); stht = num.sin(tht0) nchi = num.c_[0., cchi, schi].T gHat0_l = num.vstack([ceta * ctht, seta * ctht, stht]) a = cchi*ceta*ctht b = cchi*schi*seta*ctht + schi*schi*stht - stht c = stht + cchi*schi*seta*ctht + schi*schi*stht # form solution abMag = num.sqrt(a*a + b*b); assert num.all(abMag > 0), "Beam vector specification is infealible!" phaseAng = num.arctan2(b, a) rhs = c / abMag; rhs[abs(rhs) > 1.] = num.nan rhsAng = num.arcsin(rhs) # write ome angle output arrays (NaNs persist here) ome1 = rhsAng - phaseAng ome2 = num.pi - rhsAng - phaseAng ome1 = mapAngle(ome1, [-num.pi, num.pi], units='radians') ome2 = mapAngle(ome2, [-num.pi, num.pi], units='radians') ome_stack = num.vstack([ome1, ome2]) min_idx = num.argmin(abs(ome_stack), axis=0) ome_min = ome_stack[min_idx, range(len(ome1))] eta_min = num.nan * num.ones_like(ome_min) # mark feasible reflections goodOnes = -num.isnan(ome_min) numGood = sum(goodOnes) tmp_eta = num.empty(numGood) tmp_gvec = gHat0_l[:, goodOnes] for i in range(numGood): come = num.cos(ome_min[goodOnes][i]) some = num.sin(ome_min[goodOnes][i]) rchi = rotMatOfExpMap( num.tile(ome_min[goodOnes][i], (3, 1)) * nchi ) gHat_l = num.dot(rchi, tmp_gvec[:, i].reshape(3, 1)) tmp_eta[i] = num.arctan2(gHat_l[1], gHat_l[0]) pass eta_min[goodOnes] = tmp_eta # everybody back to DEGREES! # - ome1 is in RADIANS here # - convert and put into [-180, 180] ome1 = mapAngle( mapAngle(r2d*ome_min, [-180, 180], units='degrees') + c1*ome0, [-180, 180], units='degrees') # put eta1 in [-180, 180] eta1 = mapAngle(r2d*eta_min, [-180, 180], units='degrees') if not outputDegrees: ome1 = d2r * ome1 eta1 = d2r * eta1 return ome1, eta1
def __call__(self, spotsArray, **kwargs): """ A word on spacegroup numbers: it appears that grainspotter is using the 'VolA' tag for calls to SgInfo """ location = self.__class__.__name__ tic = time.time() # keyword argument processing phaseID = None gVecFName = 'tmpGve' sgNum = 225 cellString = 'F' omeRange = num.r_[-60, 60] # in DEGREES deltaOme = 0.25 # in DEGREES minMeas = 24 minCompl = 0.7 minUniqn = 0.5 uncertainty = [0.10, 0.25, .50] # in DEGREES eulStep = 2 # in DEGREES nSigmas = 2 minFracG = 0.90 numTrials = 100000 positionFit = False kwarglen = len(kwargs) if kwarglen > 0: argkeys = kwargs.keys() for i in range(kwarglen): if argkeys[i] == 'sgNum': sgNum = kwargs[argkeys[i]] elif argkeys[i] == 'phaseID': phaseID = kwargs[argkeys[i]] elif argkeys[i] == 'gVecFName': gVecFName = kwargs[argkeys[i]] elif argkeys[i] == 'cellString': cellString = kwargs[argkeys[i]] elif argkeys[i] == 'omeRange': omeRange = kwargs[argkeys[i]] elif argkeys[i] == 'deltaOme': deltaOme = kwargs[argkeys[i]] elif argkeys[i] == 'minMeas': minMeas = kwargs[argkeys[i]] elif argkeys[i] == 'minCompl': minCompl = kwargs[argkeys[i]] elif argkeys[i] == 'minUniqn': minUniqn = kwargs[argkeys[i]] elif argkeys[i] == 'uncertainty': uncertainty = kwargs[argkeys[i]] elif argkeys[i] == 'eulStep': eulStep = kwargs[argkeys[i]] elif argkeys[i] == 'nSigmas': nSigmas = kwargs[argkeys[i]] elif argkeys[i] == 'minFracG': minFracG = kwargs[argkeys[i]] elif argkeys[i] == 'nTrials': numTrials = kwargs[argkeys[i]] elif argkeys[i] == 'positionfit': positionFit = kwargs[argkeys[i]] else: raise RuntimeError, "Unrecognized keyword argument '%s'" % (argkeys[i]) # cleanup stuff from any previous run self.cleanup() planeData = spotsArray.getPlaneData(phaseID=phaseID) cellp = planeData.latVecOps['dparms'] U0 = planeData.latVecOps['U0'] wlen = planeData.wavelength dsp = planeData.getPlaneSpacings() fHKLs = planeData.getSymHKLs() tThRng = planeData.getTThRanges() symTag = planeData.getLaueGroup() tThMin, tThMax = (r2d*tThRng.min(), r2d*tThRng.max()) # single range should be ok since entering hkls etaMin, etaMax = (0, 360) # not sure when this will ever *NOT* be the case, so setting it omeMin = spotsArray.getOmegaMins() omeMax = spotsArray.getOmegaMaxs() omeRangeString = '' for iOme in range(len(omeMin)): omeRangeString += 'omegarange %g %g\n' % (omeMin[iOme] * r2d, omeMax[iOme] * r2d) # convert angles cellp[3:] = r2d*cellp[3:] # make the theoretical hkls string gvecHKLString = '' for i in range(len(dsp)): for j in range(fHKLs[i].shape[1]): gvecHKLString += '%1.8f %d %d %d\n' % (1/dsp[i], fHKLs[i][0, j], fHKLs[i][1, j], fHKLs[i][2, j]) # now for the measured data section # xr yr zr xc yc ds eta omega gvecString = '' ii = 0 spotsIter = spotsArray.getIterPhase(phaseID, returnBothCoordTypes=True) for iSpot, angCOM, xyoCOM in spotsIter: sX, sY, sOme = xyoCOM # detector coords sTTh, sEta, sOme = angCOM # angular coords (radians) sDsp = wlen / 2. / num.sin(0.5*sTTh) # dspacing # convert eta to risoe frame rEta = mapAngle(90. - r2d*sEta, [0, 360], units='degrees') # make mesaured G vector components in risoe frame mGvec = makeMeasuredScatteringVectors(sTTh, sEta, sOme, convention='risoe', frame='sample') # mQvec = makeMeasuredScatteringVectors(sTTh, sEta, sOme, convention='llnl', frame='lab') gveXYZ = spotsArray.detectorGeom.angToXYO(sTTh, sEta, sOme, outputGve=True) mGvec = mGvec / sDsp # mQvec = 4*num.pi*num.sin(0.5*sTTh)*mQvec/wlen # make contribution gvecString += '%1.8f %1.8f %1.8f %1.8f %1.8f %1.8f %1.8f %1.8f %d %1.8f %1.8f %1.8f\n' \ % (mGvec[0], mGvec[1], mGvec[2], \ sX, sY, \ 1/sDsp, rEta, r2d*sOme, \ iSpot, \ gveXYZ[0, :], gveXYZ[1, :], gveXYZ[2, :]) # advance counter ii += 1 # write gve file for grainspotter f = open(gVecFName+'.gve', 'w') print >> f, '%1.8f %1.8f %1.8f %1.8f %1.8f %1.8f ' % tuple(cellp) + \ cellString + '\n' + \ '# wavelength = %1.8f\n' % (wlen) + \ '# wedge = 0.000000\n# ds h k l\n' + \ gvecHKLString + \ '# xr yr zr xc yc ds eta omega\n' + \ gvecString f.close() ############################################################### # GrainSpotter ini parameters # # tempFNameIn = tempfile.mktemp() if positionFit: positionString = 'positionfit' else: positionString = '!positionfit' if numTrials == 0: randomString = '!random\n' else: randomString = 'random %g\n' % (numTrials) tempFNameIn = 'tmpIni' f = open(tempFNameIn, 'w') # self.__tempFNameList.append(tempFNameIn) print >> f, \ 'spacegroup %d\n' % (sgNum) + \ 'tthrange %g %g\n' % (tThMin, tThMax) + \ 'etarange %g %g\n' % (etaMin, etaMax) + \ 'domega %g\n' % (deltaOme) + \ omeRangeString + \ 'filespecs %s.gve %s.log\n' % (gVecFName, tempFNameIn) + \ 'cuts %d %g %g\n' % (minMeas, minCompl, minUniqn) + \ 'eulerstep %g\n' % (eulStep)+ \ 'uncertainties %g %g %g\n' % (uncertainty[0], uncertainty[1], uncertainty[2]) + \ 'nsigmas %d\n' % (nSigmas) + \ 'minfracg %g\n' % (minFracG) + \ randomString + \ positionString + '\n' f.close() toc = time.time() print 'in %s, setup took %g' % (location, toc-tic) tic = time.time() # tempFNameStdout = tempfile.mktemp() # self.__tempFNameList.append(tempFNameStdout) tempFNameStdout = 'tmp.out' # grainSpotterCmd = '%s %s > %s' % (self.__execName, tempFNameIn, tempFNameStdout) grainSpotterCmd = '%s %s' % (self.__execName, tempFNameIn) os.system(grainSpotterCmd) toc = time.time() print 'in %s, execution took %g' % (location, toc-tic) tic = time.time() # add output files to cleanup list # self.__tempFNameList += glob.glob(tempFNameIn+'.*') # collect data from gff file' gffFile = tempFNameIn+'.gff' gffData = num.loadtxt(gffFile) if gffData.ndim == 1: gffData = gffData.reshape(1, len(gffData)) gffData_U = gffData[:,6:6+9] # process for output retval = convertUToRotMat(gffData_U, U0, symTag=symTag) toc = time.time() print 'in %s, post-processing took %g' % (location, toc-tic) tic = time.time() return retval