def quatAverage(q_in, qsym): """ """ assert q_in.ndim == 2, 'input must be 2-s hstacked quats' # renormalize q_in = unitVector(q_in) # check to see num of quats is > 1 if q_in.shape[1] < 3: if q_in.shape[1] == 1: q_bar = q_in else: ma, mq = misorientation(q_in[:, 0].reshape(4, 1), q_in[:, 1].reshape(4, 1), (qsym, )) q_bar = quatProduct( q_in[:, 0].reshape(4, 1), quatOfExpMap(0.5 * ma * unitVector(mq[1:].reshape(3, 1)))) else: # use first quat as initial guess phi = 2. * arccos(q_in[0, 0]) if phi <= finfo(float).eps: x0 = zeros(3) else: n = unitVector(q_in[1:, 0].reshape(3, 1)) x0 = phi * n.flatten() results = leastsq(quatAverage_obj, x0, args=(q_in, qsym)) phi = sqrt(sum(results[0] * results[0])) if phi <= finfo(float).eps: q_bar = c_[1., 0., 0., 0.].T else: n = results[0] / phi q_bar = hstack([cos(0.5 * phi), sin(0.5 * phi) * n]).reshape(4, 1) return q_bar
def quatAverage(q_in, qsym): """ """ assert q_in.ndim == 2, 'input must be 2-s hstacked quats' # renormalize q_in = unitVector(q_in) # check to see num of quats is > 1 if q_in.shape[1] < 3: if q_in.shape[1] == 1: q_bar = q_in else: ma, mq = misorientation(q_in[:, 0].reshape(4, 1), q_in[:, 1].reshape(4, 1), (qsym,)) q_bar = quatProduct(q_in[:, 0].reshape(4, 1), quatOfExpMap(0.5*ma*unitVector(mq[1:].reshape(3, 1)))) else: # use first quat as initial guess phi = 2. * arccos(q_in[0, 0]) if phi <= finfo(float).eps: x0 = zeros(3) else: n = unitVector(q_in[1:, 0].reshape(3, 1)) x0 = phi*n.flatten() results = leastsq(quatAverage_obj, x0, args=(q_in, qsym)) phi = sqrt(sum(results[0]*results[0])) if phi <= finfo(float).eps: q_bar = c_[1., 0., 0., 0.].T else: n = results[0] / phi q_bar = hstack([cos(0.5*phi), sin(0.5*phi)*n]).reshape(4, 1) return q_bar
def quatAverageCluster(q_in, qsym): """ """ assert q_in.ndim == 2, 'input must be 2-s hstacked quats' # renormalize q_in = unitVector(q_in) # check to see num of quats is > 1 if q_in.shape[1] < 3: if q_in.shape[1] == 1: q_bar = q_in else: ma, mq = misorientation(q_in[:, 0].reshape(4, 1), q_in[:, 1].reshape(4, 1), (qsym, )) q_bar = quatProduct( q_in[:, 0].reshape(4, 1), quatOfExpMap(0.5 * ma * unitVector(mq[1:].reshape(3, 1)))) else: # first drag to origin using first quat (arb!) q0 = q_in[:, 0].reshape(4, 1) qrot = dot(quatProductMatrix(invertQuat(q0), mult='left'), q_in) # second, re-cast to FR qrot = symmetry.toFundamentalRegion(qrot.squeeze(), crysSym=qsym) # compute arithmetic average q_bar = unitVector(average(qrot, axis=1).reshape(4, 1)) # unrotate! q_bar = dot(quatProductMatrix(q0, mult='left'), q_bar) # re-map q_bar = symmetry.toFundamentalRegion(q_bar, crysSym=qsym) return q_bar
def gen_schmid_tensors(pd,uvw,hkl): # slip plane directions slipdir = mutil.unitVector( np.dot( pd.latVecOps['F'], uvw) ) # 2 -1 -1 0 slipdir_sym = sym.applySym(slipdir, pd.getQSym(), csFlag=False, cullPM=True, tol=1e-08) # slip plane plane normals n_plane = mutil.unitVector( np.dot( pd.latVecOps['B'], hkl ) ) n_plane_sym = sym.applySym(n_plane, pd.getQSym(), csFlag=False, cullPM=True, tol=1e-08) num_slip_plane= n_plane_sym.shape[1] num_slip_sys=0 for i in range(num_slip_plane): planeID = np.where(abs(np.dot(n_plane_sym[:, i],slipdir_sym)) < 1.e-8)[0] num_slip_sys +=planeID.shape[0] T= np.zeros((num_slip_sys, 3, 3)) counter=0 # for i in range(num_slip_plane): planeID = np.where(abs(np.dot(n_plane_sym[:, i],slipdir_sym)) < 1.e-8)[0] for j in np.arange(planeID.shape[0]): T[counter, :, :] = np.dot(slipdir_sym[:, planeID[j]].reshape(3, 1), n_plane_sym[:, i].reshape(1, 3)) counter+=1 #Clean some round off errors round_off_err=np.where(abs(T)<1e-8) T[round_off_err[0],round_off_err[1],round_off_err[2]]=0. return T
def distanceToFiber(c, s, q, qsym, **kwargs): """ """ csymFlag = False B = I3 arglen = len(kwargs) if len(c) != 3 or len(s) != 3: raise RuntimeError('c and/or s are not 3-vectors') # argument handling if arglen > 0: argkeys = list(kwargs.keys()) for i in range(arglen): if argkeys[i] == 'centrosymmetry': csymFlag = kwargs[argkeys[i]] elif argkeys[i] == 'bmatrix': B = kwargs[argkeys[i]] else: raise RuntimeError("keyword arg \'%s\' is not recognized" % (argkeys[i])) c = unitVector(dot(B, asarray(c))) s = unitVector(asarray(s).reshape(3, 1)) nq = q.shape[1] # number of quaternions rmats = rotMatOfQuat(q) # (nq, 3, 3) csym = applySym(c, qsym, csymFlag) # (3, m) m = csym.shape[1] # multiplicity if nq == 1: rc = dot(rmats, csym) # apply q's to c's sdotrc = dot(s.T, rc).max() else: rc = multMatArray( rmats, tile(csym, (nq, 1, 1)) ) # apply q's to c's sdotrc = dot( s.T, rc.swapaxes(1, 2).reshape(nq*m, 3).T ).reshape(nq, m).max(1) d = arccosSafe(array(sdotrc)) return d
def quatOfAngleAxis(angle, rotaxis): """ make an hstacked array of quaternions from arrays of angle/axis pairs """ if isinstance(angle, list): n = len(angle) angle = asarray(angle) elif isinstance(angle, float) or isinstance(angle, int): n = 1 elif isinstance(angle, ndarray): n = angle.shape[0] else: raise RuntimeError, "angle argument is of incorrect type. " \ "must be a list, int, float, or ndarray." if rotaxis.shape[1] == 1: rotaxis = tile(rotaxis, (1, n)) else: if rotaxis.shape[1] != n: raise RuntimeError, "rotation axes argument has incompatible shape" halfangle = 0.5*angle cphiby2 = cos(halfangle) sphiby2 = sin(halfangle) quat = vstack([cphiby2, tile(sphiby2, (3, 1)) * unitVector(rotaxis)]) return fixQuat(quat)
def quaternion_ball(qref, radius, num=500): """ Makes a ball of randomly sampled quaternions within the specified misorientation of the supplied reference. Parameters ---------- qref : array_like, (4,) Unit quaternion defining the reference orientation. radius : scalar Maximum misorientation in degrees. num : int, optional The number of orientations to generate. The default is 500. Returns ------- The (4, num) array of quaternions around qref. """ # make random angle/axis pairs rand_angs = np.radians(radius) * np.random.rand(num) rand_axes = mutil.unitVector(np.random.randn(3, num)) # form quats qball = rot.quatOfAngleAxis(rand_angs, rand_axes) # recenter around reference orientation qref_mat = rot.quatProductMatrix(np.atleast_1d(qref).reshape(4, 1), mult='right').squeeze() return np.dot(qref_mat, qball)
def quatOfAngleAxis(angle, rotaxis): """ make an hstacked array of quaternions from arrays of angle/axis pairs """ if isinstance(angle, list): n = len(angle) angle = asarray(angle) elif isinstance(angle, float) or isinstance(angle, int): n = 1 elif isinstance(angle, ndarray): n = angle.shape[0] else: raise RuntimeError, "angle argument is of incorrect type. " \ "must be a list, int, float, or ndarray." if rotaxis.shape[1] == 1: rotaxis = tile(rotaxis, (1, n)) else: if rotaxis.shape[1] != n: raise RuntimeError, "rotation axes argument has incompatible shape" halfangle = 0.5 * angle cphiby2 = cos(halfangle) sphiby2 = sin(halfangle) quat = vstack([cphiby2, tile(sphiby2, (3, 1)) * unitVector(rotaxis)]) return fixQuat(quat)
def contained_in_sst(vectors, vertices_ccw): """ checks hstack array of unit vectors !!! inputs must both be unit vectors !!! vertices must be CCW """ # column-wise normals to the spherical triangle edges sst_normals = np.array([ np.cross(vertices_ccw[:, i[0]], vertices_ccw[:, i[1]]) for i in [(0, 1), (1, 2), (2, 0)] ]).T sst_normals_unit = mutil.unitVector(sst_normals) angles = np.arcsin(mutil.columnNorm(sst_normals)) edges = [] for i, ang in enumerate(angles): sub_ang = np.linspace(0, ang, endpoint=True) for j in sub_ang: rm = rot.rotMatOfExpMap(j * sst_normals_unit[:, i].reshape(3, 1)) edges.append(np.dot(vertices_ccw[:, i], rm.T)) edges.append(np.nan * np.ones(3)) dim, n = vectors.shape contained = [] for v in vectors.T: d0 = np.dot(sst_normals_unit[:, 0], v) d1 = np.dot(sst_normals_unit[:, 1], v) d2 = np.dot(sst_normals_unit[:, 2], v) contained.append(np.all([d0 > 0, d1 > 0, d2 > 0])) return contained, np.vstack(edges)
def objFuncSX(pFit, pFull, pFlag, dFunc, dFlag, xyo_det, hkls_idx, bMat, vInv, wavelength, bVec, eVec, omePeriod, simOnly=False, returnScalarValue=returnScalarValue): """ """ npts = len(xyo_det) refineFlag = np.hstack([pFlag, dFlag]) # pFull[refineFlag] = pFit/scl[refineFlag] pFull[refineFlag] = pFit dParams = pFull[-len(dFlag):] xy_unwarped = dFunc(xyo_det[:, :2], dParams) # detector quantities rMat_d = xf.makeDetectorRotMat(pFull[:3]) tVec_d = pFull[3:6].reshape(3, 1) # sample quantities chi = pFull[6] tVec_s = pFull[7:10].reshape(3, 1) # crystal quantities rMat_c = xf.makeRotMatOfExpMap(pFull[10:13]) tVec_c = pFull[13:16].reshape(3, 1) gVec_c = np.dot(bMat, hkls_idx) vMat_s = mutil.vecMVToSymm(vInv) # stretch tensor comp matrix from MV notation in SAMPLE frame gVec_s = np.dot(vMat_s, np.dot(rMat_c, gVec_c)) # reciprocal lattice vectors in SAMPLE frame gHat_s = mutil.unitVector(gVec_s) # unit reciprocal lattice vectors in SAMPLE frame gHat_c = np.dot(rMat_c.T, gHat_s) # unit reciprocal lattice vectors in CRYSTAL frame match_omes, calc_omes = matchOmegas(xyo_det, hkls_idx, chi, rMat_c, bMat, wavelength, vInv=vInv, beamVec=bVec, etaVec=eVec, omePeriod=omePeriod) calc_xy = np.zeros((npts, 2)) for i in range(npts): rMat_s = xfcapi.makeOscillRotMat([chi, calc_omes[i]]) calc_xy[i, :] = xfcapi.gvecToDetectorXY(gHat_c[:, i], rMat_d, rMat_s, rMat_c, tVec_d, tVec_s, tVec_c, beamVec=bVec).flatten() pass if np.any(np.isnan(calc_xy)): print "infeasible pFull: may want to scale back finite difference step size" # return values if simOnly: retval = np.hstack([calc_xy, calc_omes.reshape(npts, 1)]) else: diff_vecs_xy = calc_xy - xy_unwarped[:, :2] diff_ome = xf.angularDifference( calc_omes, xyo_det[:, 2] ) retval = np.hstack([diff_vecs_xy, diff_ome.reshape(npts, 1) ]).flatten() if returnScalarValue: retval = sum( retval ) return retval
def quatOfExpMap(expMap): """ """ angles = columnNorm(expMap) axes = unitVector(expMap) quats = quatOfAngleAxis(angles, axes) return quats
def angleAxisOfRotMat(R): """ """ if not isinstance(R, ndarray): raise RuntimeError, "Input must be a 2 or 3-d ndarray" else: rdim = R.ndim if rdim == 2: nrot = 1 R = tile(R, (1, 1, 1)) elif rdim == 3: nrot = R.shape[0] else: raise RuntimeError, "R array must be (3, 3) or (n, 3, 3); input has dimension %d" % (rdim) # # Find angle of rotation. # ca = 0.5 * (R[:, 0, 0] + R[:, 1, 1] + R[:, 2, 2] - 1) angle = arccosSafe(ca) # # Three cases for the angle: # # * near zero -- matrix is effectively the identity # * near pi -- binary rotation; need to find axis # * neither -- general case; can use skew part # tol = 1.0e-4 anear0 = angle < tol angle[anear0] = 0 raxis = vstack([R[:, 2, 1] - R[:, 1, 2], R[:, 0, 2] - R[:, 2, 0], R[:, 1, 0] - R[:, 0, 1]]) raxis[:, anear0] = 1 special = angle > pi - tol nspec = special.sum() if nspec > 0: tmp = R[special, :, :] + tile(I3, (nspec, 1, 1)) tmpr = tmp.transpose(0, 2, 1).reshape(nspec * 3, 3).T tmpnrm = (tmpr * tmpr).sum(0).reshape(3, nspec) mx = tmpnrm.max(0) # remap indices maxInd = (tmpnrm == mx).nonzero() maxInd = c_[maxInd[0], maxInd[1]] tmprColInd = sort(maxInd[:, 0] + maxInd[:, 1] * nspec) saxis = tmpr[:, tmprColInd] raxis[:, special] = saxis return angle, unitVector(raxis)
def makePlaneData(hkls, lparms, qsym, symmGroup, strainMag, wavelength): # spots """ hkls : need to work with crystallography.latticePlanes lparms : need to work with crystallography.latticePlanes laueGroup : see symmetry module wavelength : wavelength strainMag : swag of strian magnitudes """ tempSetOutputDegrees(False) latPlaneData = latticePlanes(hkls, lparms, ltype=symmGroup, strainMag=strainMag, wavelength=wavelength) latVecOps = latticeVectors(lparms, symmGroup) hklDataList = [] for iHKL in range(len(hkls.T)): # need transpose because of convention for hkls ordering # JVB # latVec = latPlaneData['normals'][:,iHKL] # JVB # # ... if not spots, may be able to work with a subset of these # JVB # latPlnNrmlList = symmetry.applySym(num.c_[latVec], qsym, csFlag=True, cullPM=False) # returns UN-NORMALIZED lattice plane normals latPlnNrmls = symmetry.applySym( num.dot(latVecOps['B'], hkls[:,iHKL].reshape(3, 1)), qsym, csFlag=True, cullPM=False) # check for +/- in symmetry group latPlnNrmlsM = symmetry.applySym( num.dot(latVecOps['B'], hkls[:,iHKL].reshape(3, 1)), qsym, csFlag=False, cullPM=False) csRefl = latPlnNrmls.shape[1] == latPlnNrmlsM.shape[1] # added this so that I retain the actual symmetric integer hkls as well symHKLs = num.array( num.round( num.dot(latVecOps['F'].T, latPlnNrmls) ), dtype='int' ) hklDataList.append({ 'hklID' : iHKL , 'hkl' : hkls[:,iHKL] , 'tTheta' : latPlaneData['tThetas'][iHKL] , 'dSpacings' : latPlaneData['dspacings'][iHKL], 'tThetaLo' : latPlaneData['tThetasLo'][iHKL], 'tThetaHi' : latPlaneData['tThetasHi'][iHKL], 'latPlnNrmls' : unitVector(latPlnNrmls) , 'symHKLs' : symHKLs, 'centrosym' : csRefl }) revertOutputDegrees() return latPlaneData, latVecOps, hklDataList
def distanceToFiber(c, s, q, qsym, **kwargs): """ """ csymFlag = False B = I3 arglen = len(kwargs) if len(c) != 3 or len(s) != 3: raise RuntimeError, 'c and/or s are not 3-vectors' # argument handling if arglen > 0: argkeys = kwargs.keys() for i in range(arglen): if argkeys[i] == 'centrosymmetry': csymFlag = kwargs[argkeys[i]] elif argkeys[i] == 'bmatrix': B = kwargs[argkeys[i]] else: raise RuntimeError('keyword arg \'%s\' is not recognized' \ % (argkeys[i])) c = unitVector( dot(B, asarray(c)) ) s = unitVector( asarray(s).reshape(3, 1) ) nq = q.shape[1] # number of quaternions rmats = rotMatOfQuat(q) # (nq, 3, 3) csym = applySym(c, qsym, csymFlag) # (3, m) m = csym.shape[1] # multiplicity if nq == 1: rc = dot(rmats, csym) # apply q's to c's sdotrc = dot( s.T, rc ).max() else: rc = multMatArray( rmats, tile(csym, (nq, 1, 1)) ) # apply q's to c's sdotrc = dot( s.T, rc.swapaxes(1, 2).reshape(nq*m, 3).T ).reshape(nq, m).max(1) d = arccosSafe( array(sdotrc) ) return d
def quatAverageCluster(q_in, qsym): """ """ from symmetry import toFundamentalRegion assert q_in.ndim == 2, "input must be 2-s hstacked quats" # renormalize q_in = unitVector(q_in) # check to see num of quats is > 1 if q_in.shape[1] < 3: if q_in.shape[1] == 1: q_bar = q_in else: ma, mq = misorientation(q_in[:, 0].reshape(4, 1), q_in[:, 1].reshape(4, 1), (qsym,)) q_bar = quatProduct(q_in[:, 0].reshape(4, 1), quatOfExpMap(0.5 * ma * unitVector(mq[1:].reshape(3, 1)))) else: # use first quat as initial guess phi = 2.0 * arccos(q_in[0, 0]) if phi <= finfo(float).eps: x0 = zeros(3) else: n = unitVector(q_in[1:, 0].reshape(3, 1)) x0 = phi * n.flatten() # first drag to origin using first quat (arb!) q0 = q_in[:, 0].reshape(4, 1) qrot = dot(quatProductMatrix(invertQuat(q0), mult="right"), q_in) # second, re-cast to FR qrot = toFundamentalRegion(qrot.squeeze(), crysSym=qsym) # compute arithmetic average q_bar = unitVector(average(qrot, axis=1).reshape(4, 1)) # unrotate! q_bar = dot(quatProductMatrix(q0, mult="right"), q_bar) # re-map q_bar = toFundamentalRegion(q_bar, crysSym=qsym) return q_bar
def gen_schmid_tensors(pd, uvw, hkl): # slip plane directions slipdir = mutil.unitVector(np.dot(pd.latVecOps['F'], uvw)) # 2 -1 -1 0 slipdir_sym = sym.applySym(slipdir, pd.getQSym(), csFlag=False, cullPM=True, tol=1e-08) # slip plane plane normals n_plane = mutil.unitVector(np.dot(pd.latVecOps['B'], hkl)) n_plane_sym = sym.applySym(n_plane, pd.getQSym(), csFlag=False, cullPM=True, tol=1e-08) num_slip_plane = n_plane_sym.shape[1] num_slip_sys = 0 for i in range(num_slip_plane): planeID = np.where( abs(np.dot(n_plane_sym[:, i], slipdir_sym)) < 1.e-8)[0] num_slip_sys += planeID.shape[0] T = np.zeros((num_slip_sys, 3, 3)) counter = 0 # for i in range(num_slip_plane): planeID = np.where( abs(np.dot(n_plane_sym[:, i], slipdir_sym)) < 1.e-8)[0] for j in np.arange(planeID.shape[0]): T[counter, :, :] = np.dot(slipdir_sym[:, planeID[j]].reshape(3, 1), n_plane_sym[:, i].reshape(1, 3)) counter += 1 #Clean some round off errors round_off_err = np.where(abs(T) < 1e-8) T[round_off_err[0], round_off_err[1], round_off_err[2]] = 0. return T
def quatAverageCluster(q_in, qsym): """ """ from symmetry import toFundamentalRegion assert q_in.ndim == 2, 'input must be 2-s hstacked quats' # renormalize q_in = unitVector(q_in) # check to see num of quats is > 1 if q_in.shape[1] < 3: if q_in.shape[1] == 1: q_bar = q_in else: ma, mq = misorientation(q_in[:, 0].reshape(4, 1), q_in[:, 1].reshape(4, 1), (qsym,)) q_bar = quatProduct(q_in[:, 0].reshape(4, 1), quatOfExpMap(0.5*ma*unitVector(mq[1:].reshape(3, 1)))) else: # first drag to origin using first quat (arb!) q0 = q_in[:, 0].reshape(4, 1) qrot = dot( quatProductMatrix( invertQuat( q0 ), mult='left' ), q_in) # second, re-cast to FR qrot = toFundamentalRegion(qrot.squeeze(), crysSym=qsym) # compute arithmetic average q_bar = unitVector(average(qrot, axis=1).reshape(4, 1)) # unrotate! q_bar = dot( quatProductMatrix( q0, mult='left' ), q_bar) # re-map q_bar = toFundamentalRegion(q_bar, crysSym=qsym) return q_bar
def drawLines(pw, pointLists=[], netStyle = None, netNDiv = 12, netAlpha = 0.5, rMat=None, southern=False, invertFromSouthern=True, origin=(0.,0.), r=1.0): x0, y0 = origin lines = pw.a.get_lines() for line in lines: line.remove() ringAngs = num.linspace(0,num.pi*2.,181) if netStyle: arcAngsM = num.linspace(-num.pi/2.,num.pi/2.,91) arcAngsL = num.linspace(0, num.pi, 181) rMat_yDiv = ors.RotInv(-num.pi/netNDiv, [0,1,0]).toMatrix() pw(r*num.cos(ringAngs)+x0,r*num.sin(ringAngs)+y0,style='k-') if netStyle: nVMerid = num.vstack( ( num.cos(arcAngsM), num.sin(arcAngsM), num.zeros(len(arcAngsM)) ) ) for iDiv in range(netNDiv-1): nVMerid = num.dot(rMat_yDiv, nVMerid) eaProj = n2eap(nVMerid, flip=False) pw(r*eaProj[0,:]+x0, r*eaProj[1,:]+y0, alpha=netAlpha, style=netStyle) for latAng in num.pi/netNDiv*num.arange(1,netNDiv): polZ = num.cos(latAng) polR = num.sin(latAng) nVLat = num.vstack( ( polR*num.cos(arcAngsL), polZ*num.ones(len(arcAngsL)), polR*num.sin(arcAngsL)) ) eaProj = n2eap(nVLat, flip=False) pw(r*eaProj[0,:]+x0, r*eaProj[1,:]+y0, alpha=netAlpha, style=netStyle) 'done with drawing net' for points, pwKWArgs in pointLists: nVecs = matrixutil.unitVector(points) if rMat is not None: 'rotate as did elsewhere' nVecs = num.dot(rMat, nVecs) bNVecsS = nVecs[2,:] < 0 if southern: nVecsS = nVecs[:,bNVecsS] nVecsS = fromSouthern(nVecsS, invertFromSouthern) eaProj = n2eap(nVecsS, flip=False) pw(r*eaProj[0,:]+x0, r*eaProj[1,:]+y0, **pwKWArgs) else: nVecsN = nVecs[:,num.logical_not(bNVecsS)] eaProj = n2eap(nVecsN, flip=False) pw(r*eaProj[0,:]+x0, r*eaProj[1,:]+x0, **pwKWArgs) 'done with pointLists' return
def _compute_centroids_dense(cl, qfib_r, qsym): """compute centroids when clusters are compact""" if np.any(cl == -1): nblobs = len(np.unique(cl)) - 1 else: nblobs = len(np.unique(cl)) qbar = np.zeros((4, nblobs)) for i in range(nblobs): cluster_indices = (cl == i + 1) this_cluster = qfib_r[:, cluster_indices] qbar[:, i] = np.average(np.atleast_2d(this_cluster), axis=1) qbar = sym.toFundamentalRegion(mutil.unitVector(qbar), crysSym=qsym) return np.atleast_2d(qbar)
def millerBravaisDirectionToVector(dir_ind, a=1.0, c=1.0): """ converts direction indices into a unit vector in the crystal frame INPUT [uv.w] the Miller-Bravais convention in the hexagonal basis {a1, a2, a3, c}. The basis for the output, {o1, o2, o3}, is chosen such that o1 || a1 o3 || c o2 = o3 ^ o1 """ dir_ind = num.atleast_2d(dir_ind) num_in = len(dir_ind) u = dir_ind[:, 0] v = dir_ind[:, 1] w = dir_ind[:, 2] return unitVector(num.vstack([1.5 * u * a, sqrt3by2 * (2.0 * v + u) * a, w * c]).reshape(3, num_in))
def fixQuat(q): """ flip to positive q0 and normalize """ qdims = q.ndim if qdims == 3: l, m, n = q.shape assert m == 4, 'your 3-d quaternion array isn\'t the right shape' q = q.transpose(0, 2, 1).reshape(l*n, 4).T qfix = unitVector(q) q0negative = qfix[0, ] < 0 qfix[:, q0negative] = -1*qfix[:, q0negative] if qdims == 3: qfix = qfix.T.reshape(l, n, 4).transpose(0, 2, 1) return qfix
def fixQuat(q): """ flip to positive q0 and normalize """ qdims = q.ndim if qdims == 3: l, m, n = q.shape assert m == 4, 'your 3-d quaternion array isn\'t the right shape' q = q.transpose(0, 2, 1).reshape(l * n, 4).T qfix = unitVector(q) q0negative = qfix[0, ] < 0 qfix[:, q0negative] = -1 * qfix[:, q0negative] if qdims == 3: qfix = qfix.T.reshape(l, n, 4).transpose(0, 2, 1) return qfix
def assemble_grain_data(grain_data_list, pos_offset=None, rotation_offset=None): num_grain_files = len(grain_data_list) num_grains_list = [None] * num_grain_files for i in np.arange(num_grain_files): num_grains_list[i] = grain_data_list[i].shape[0] num_grains = np.sum(num_grains_list) grain_data = np.zeros([num_grains, grain_data_list[0].shape[1]]) for i in np.arange(num_grain_files): tmp = copy.copy(grain_data_list[i]) if pos_offset is not None: pos_tile = np.tile(pos_offset[:, i], [num_grains_list[i], 1]) tmp[:, 6:9] = tmp[:, 6:9] + pos_tile #Needs Testing if rotation_offset is not None: rot_tile = np.tile( np.atleast_2d(rotation_offset[:, i]).T, [1, num_grains_list[i]]) quat_tile = rot.quatOfExpMap(rot_tile) grain_quats = rot.quatOfExpMap(tmp[:, 3:6].T) new_quats = rot.quatProduct(grain_quats, quat_tile) sinang = mutil.columnNorm(new_quats[1:, :]) ang = 2. * np.arcsin(sinang) axis = mutil.unitVector(new_quats[1:, :]) tmp[:, 3:6] = np.tile(np.atleast_2d(ang).T, [1, 3]) * axis.T grain_data[int(np.sum(num_grains_list[:i]) ):int(np.sum(num_grains_list[:(i + 1)])), :] = tmp old_grain_numbers = copy.copy(grain_data[:, 0]) grain_data[:, 0] = np.arange(num_grains) return grain_data, old_grain_numbers
def run_cluster(complPG, qfib, qsym, cl_radius=cl_radius, min_compl=min_compl): """ """ start = time.clock() # time this # # use transforms module for distance # quatDistance = lambda x, y: xf.quat_distance(x, y, qsym) # use compiled module for distance # just to be safe, must order qsym as C-contiguous qsym = np.array(qsym.T, order='C').T quatDistance = lambda x, y: xfcapi.quat_distance(np.array(x, order='C'), \ np.array(y, order='C'), \ qsym) qfib_r = qfib[:, np.r_[complPG] > min_compl] print "Feeding %d orientations above %.1f%% to clustering" % (qfib_r.shape[1], 100*min_compl) if haveScikit: print "Using scikit..." pdist = pairwise_distances(qfib_r.T, metric=quatDistance, n_jobs=-1) core_samples, labels = dbscan(pdist, eps=d2r*cl_radius, min_samples=1, metric='precomputed') cl = np.array(labels, dtype=int) + 1 else: print "Using fclusterdata with a tolerance of %f degrees..." % (cl_radius) cl = cluster.hierarchy.fclusterdata(qfib_r.T, d2r*cl_radius, criterion='distance', metric=quatDistance) nblobs = len(np.unique(cl)) qbar = np.zeros((4, nblobs)) for i in range(nblobs): npts = sum(cl == i + 1) qbar[:, i] = mutil.unitVector( np.sum(qfib[:, np.r_[complPG] > min_compl][:, cl == i + 1].reshape(4, npts), axis=1).reshape(4, 1)).flatten() elapsed = (time.clock() - start) print "clustering took %f seconds" % (elapsed) return qbar, cl
def assemble_grain_data(grain_data_list,pos_offset=None,rotation_offset=None): num_grain_files=len(grain_data_list) num_grains_list=[None]*num_grain_files for i in np.arange(num_grain_files): num_grains_list[i]=grain_data_list[i].shape[0] num_grains=np.sum(num_grains_list) grain_data=np.zeros([num_grains,grain_data_list[0].shape[1]]) for i in np.arange(num_grain_files): tmp=copy.copy(grain_data_list[i]) if pos_offset is not None: pos_tile=np.tile(pos_offset[:,i],[num_grains_list[i],1]) tmp[:,6:9]=tmp[:,6:9]+pos_tile #Needs Testing if rotation_offset is not None: rot_tile=np.tile(np.atleast_2d(rotation_offset[:,i]).T,[1,num_grains_list[i]]) quat_tile=rot.quatOfExpMap(rot_tile) grain_quats=rot.quatOfExpMap(tmp[:,3:6].T) new_quats=rot.quatProduct(grain_quats,quat_tile) sinang = mutil.columnNorm(new_quats[1:,:]) ang=2.*np.arcsin(sinang) axis = mutil.unitVector(new_quats[1:,:]) tmp[:,3:6]=np.tile(np.atleast_2d(ang).T,[1,3])*axis.T grain_data[int(np.sum(num_grains_list[:i])):int(np.sum(num_grains_list[:(i+1)])),:]=tmp old_grain_numbers=copy.copy(grain_data[:,0]) grain_data[:,0]=np.arange(num_grains) return grain_data,old_grain_numbers
def find_orientations(cfg, hkls=None, clean=False, profile=False, nsim=100): print('ready to run find_orientations') # %% # ============================================================================= # SEARCH SPACE GENERATION # ============================================================================= hedm = cfg.instrument.hedm plane_data = cfg.material.plane_data ncpus = cfg.multiprocessing # for indexing active_hkls = cfg.find_orientations.orientation_maps.active_hkls fiber_ndiv = cfg.find_orientations.seed_search.fiber_ndiv fiber_seeds = cfg.find_orientations.seed_search.hkl_seeds on_map_threshold = cfg.find_orientations.threshold # for clustering cl_radius = cfg.find_orientations.clustering.radius min_compl = cfg.find_orientations.clustering.completeness compl_thresh = cfg.find_orientations.clustering.completeness eta_ome = get_eta_ome(cfg, clean=clean) print("INFO:\tgenerating search quaternion list using %d processes" % ncpus) start = timeit.default_timer() qfib = generate_orientation_fibers(eta_ome, hedm.chi, on_map_threshold, fiber_seeds, fiber_ndiv, ncpus=ncpus) print("INFO:\t\t...took %f seconds" % (timeit.default_timer() - start)) print("INFO: will test %d quaternions using %d processes" % (qfib.shape[1], ncpus)) # %% # ============================================================================= # ORIENTATION SCORING # ============================================================================= scoredq_filename = 'scored_orientations_' + analysis_id(cfg) + '.npz' print("INFO:\tusing map search with paintGrid on %d processes" % ncpus) start = timeit.default_timer() completeness = indexer.paintGrid( qfib, eta_ome, etaRange=np.radians(cfg.find_orientations.eta.range), omeTol=np.radians(cfg.find_orientations.omega.tolerance), etaTol=np.radians(cfg.find_orientations.eta.tolerance), omePeriod=np.radians(cfg.find_orientations.omega.period), threshold=on_map_threshold, doMultiProc=ncpus > 1, nCPUs=ncpus) print("INFO:\t\t...took %f seconds" % (timeit.default_timer() - start)) completeness = np.array(completeness) # export scored orientations np.savez_compressed(scoredq_filename, quaternions=qfib, completeness=completeness) print("INFO:\tsaved scored orientations to file: '%s'" % (scoredq_filename)) # %% # ============================================================================= # CLUSTERING AND GRAINS OUTPUT # ============================================================================= if not os.path.exists(cfg.analysis_dir): os.makedirs(cfg.analysis_dir) qbar_filename = 'accepted_orientations_' + analysis_id(cfg) + '.dat' print("INFO:\trunning clustering using '%s'" % cfg.find_orientations.clustering.algorithm) start = timeit.default_timer() # Simulate N random grains to get neighborhood size print("INFO:\trunning %d simulations to determine neighborhood size" % nsim) seed_hkl_ids = [ plane_data.hklDataList[active_hkls[i]]['hklID'] for i in fiber_seeds ] # need ome_ranges from imageseries # CAVEAT: assumes that all imageseries have same omega ranges!!! oims = OmegaImageSeries(cfg.image_series.itervalues().next()) ome_ranges = [(np.radians([i['ostart'], i['ostop']])) for i in oims.omegawedges.wedges] if seed_hkl_ids is not None: rand_q = mutil.unitVector(np.random.randn(4, nsim)) rand_e = np.tile(2.*np.arccos(rand_q[0, :]), (3, 1)) \ * mutil.unitVector(rand_q[1:, :]) refl_per_grain = np.zeros(nsim) num_seed_refls = np.zeros(nsim) grain_param_list = np.vstack([ rand_e, np.zeros((3, nsim)), np.tile(cnst.identity_6x1, (nsim, 1)).T ]).T sim_results = hedm.simulate_rotation_series( plane_data, grain_param_list, eta_ranges=np.radians(cfg.find_orientations.eta.range), ome_ranges=ome_ranges, ome_period=np.radians(cfg.find_orientations.omega.period)) refl_per_grain = np.zeros(nsim) seed_refl_per_grain = np.zeros(nsim) for sim_result in sim_results.itervalues(): for i, refl_ids in enumerate(sim_result[0]): refl_per_grain[i] += len(refl_ids) seed_refl_per_grain[i] += np.sum( [sum(refl_ids == hkl_id) for hkl_id in seed_hkl_ids]) min_samples = max( int( np.floor(0.5 * cfg.find_orientations.clustering.completeness * min(seed_refl_per_grain))), 2) mean_rpg = int(np.round(np.average(refl_per_grain))) else: min_samples = 1 mean_rpg = 1 print("INFO:\tmean reflections per grain: %d" % mean_rpg) print("INFO:\tneighborhood size: %d" % min_samples) qbar, cl = run_cluster(completeness, qfib, plane_data.getQSym(), cfg, min_samples=min_samples, compl_thresh=compl_thresh, radius=cl_radius) print("INFO:\t\t...took %f seconds" % (timeit.default_timer() - start)) print("INFO:\tfound %d grains; saved to file: '%s'" % (qbar.shape[1], qbar_filename)) np.savetxt(qbar_filename, qbar.T, fmt='%.18e', delimiter='\t') gw = instrument.GrainDataWriter( os.path.join(cfg.analysis_dir, 'grains.out')) grain_params_list = [] for gid, q in enumerate(qbar.T): phi = 2 * np.arccos(q[0]) n = xfcapi.unitRowVector(q[1:]) grain_params = np.hstack([phi * n, cnst.zeros_3, cnst.identity_6x1]) gw.dump_grain(gid, 1., 0., grain_params) grain_params_list.append(grain_params) gw.close()
def check_indexing_plots(cfg_filename, plot_trials=False, plot_from_grains=False): cfg = config.open(cfg_filename)[0] # use first block, like indexing working_dir = cfg.working_dir analysis_dir = os.path.join(working_dir, cfg.analysis_name) #instrument parameters icfg = get_instrument_parameters(cfg) chi = icfg['oscillation_stage']['chi'] # load maps that were used oem = cPickle.load( open(cfg.find_orientations.orientation_maps.file, 'r') ) nmaps = len(oem.dataStore) omeEdges = np.degrees(oem.omeEdges); nome = len(omeEdges) - 1 etaEdges = np.degrees(oem.etaEdges); neta = len(etaEdges) - 1 delta_ome = abs(omeEdges[1]-omeEdges[0]) full_ome_range = xf.angularDifference(omeEdges[0], omeEdges[-1]) == 0 full_eta_range = xf.angularDifference(etaEdges[0], etaEdges[-1]) == 0 # grab plane data and figure out IDs of map HKLS pd = oem.planeData gvids = [pd.hklDataList[i]['hklID'] for i in np.where(pd.exclusions == False)[0].tolist()] # load orientations quats = np.atleast_2d(np.loadtxt(os.path.join(working_dir, 'accepted_orientations.dat'))) if plot_trials: scored_trials = np.load(os.path.join(working_dir, 'scored_orientations.dat')) quats = scored_orientations[:4, scored_orientations[-1, :] >= cfg.find_orientations.clustering.completeness] pass expMaps = np.tile(2. * np.arccos(quats[:, 0]), (3, 1))*unitVector(quats[:, 1:].T) ########################################## # SPECIAL CASE FOR FIT GRAINS # ########################################## if plot_from_grains: distortion = (GE_41RT, icfg['detector']['distortion']['parameters']) # grain_table = np.atleast_2d(np.loadtxt(os.path.join(analysis_dir, 'grains.out'))) ngrains = len(grain_table) # expMaps = grain_table[:, 3:6] tVec_c = grain_table[:, 6:9] vInv = grain_table[:, 6:12] # rMat_d = xf.makeDetectorRotMat(icfg['detector']['transform']['tilt_angles']) tVec_d = np.vstack(icfg['detector']['transform']['t_vec_d']) # chi = icfg['oscillation_stage']['chi'] tVec_s = np.vstack(icfg['oscillation_stage']['t_vec_s']) # oes = np.zeros(oem.dataStore.shape) for i_grn in range(ngrains): spots_table = np.loadtxt(os.path.join(analysis_dir, 'spots_%05d.out' %i_grn)) idx_m = spots_table[:, 0] >= 0 for i_map in range(nmaps): idx_g = spots_table[:, 1] == gvids[i_map] idx = np.logical_and(idx_m, idx_g) nrefl = sum(idx) omes_fit = xf.mapAngle(spots_table[idx, 9], np.radians(cfg.find_orientations.omega.period), units='radians') xy_det = spots_table[idx, -3:] xy_det[:, 2] = np.zeros(nrefl) rMat_s_array = xfcapi.makeOscillRotMatArray(chi, omes_fit) # form in-plane vectors for detector points list in DETECTOR FRAME P2_d = xy_det.T # in LAB FRAME P2_l = np.dot(rMat_d, P2_d) + tVec_d # point on detector P0_l = np.hstack( [tVec_s + np.dot(rMat_s_array[j], tVec_c[i_grn, :].reshape(3, 1)) for j in range(nrefl)] ) # origin of CRYSTAL FRAME # diffraction unit vector components in LAB FRAME dHat_l = unitVector(P2_l - P0_l) P2_l = np.dot(rMat_d, xy_det.T) + tVec_d # angles for reference frame dHat_ref_l = unitVector(P2_l) # append etas and omes etas_fit = np.arctan2(dHat_ref_l[1, :], dHat_ref_l[0, :]).flatten() # find indices, then truncate or wrap i_ome = cellIndices(oem.omeEdges, omes_fit) if full_ome_range: i_ome[i_ome < 0] = np.mod(i_ome, nome) + 1 i_ome[i_ome >= nome] = np.mod(i_ome, nome) else: incl = np.logical_or(i_ome >= 0, i_ome < nome) i_ome = i_ome[incl] j_eta = cellIndices(oem.etaEdges, etas_fit) if full_eta_range: j_eta[j_eta < 0] = np.mod(j_eta, neta) + 1 j_eta[j_eta >= neta] = np.mod(j_eta, neta) else: incl = np.logical_or(j_eta >= 0, j_eta < neta) j_eta = j_eta[incl] #if np.max(i_ome) >= nome or np.min(i_ome) < 0 or np.max(j_eta) >= neta or np.min(j_eta) < 0: # import pdb; pdb.set_trace() # add to map oes[i_map][i_ome, j_eta] = 1 pass pass # simulate quaternion points if not plot_from_grains: oes = simulateOmeEtaMaps(omeEdges, etaEdges, pd, expMaps, chi=chi, etaTol=0.01, omeTol=0.01, etaRanges=None, omeRanges=None, bVec=xf.bVec_ref, eVec=xf.eta_ref, vInv=xf.vInv_ref) # tick labling omes = np.degrees(oem.omeEdges) etas = np.degrees(oem.etaEdges) num_ticks = 7 xmin = np.amin(etas); xmax = np.amax(etas) dx = (xmax - xmin) / (num_ticks - 1.); dx1 = (len(etas) - 1) / (num_ticks - 1.) xtlab = ["%.0f" % (xmin + i*dx) for i in range(num_ticks)] xtloc = np.array([i*dx1 for i in range(num_ticks)]) - 0.5 ymin = np.amin(omes); ymax = np.amax(omes) dy = (ymax - ymin) / (num_ticks - 1.); dy1 = (len(omes) - 1) / (num_ticks - 1.) ytlab = ["%.0f" % (ymin + i*dy) for i in range(num_ticks)] ytloc = np.array([i*dy1 for i in range(num_ticks)]) - 0.5 # Plot the three kernel density estimates n_maps = len(oem.iHKLList) fig_list =[plt.figure(num=i+1) for i in range(n_maps)] ax_list = [fig_list[i].gca() for i in range(n_maps)] for i_map in range(n_maps): y, x = np.where(oes[i_map] > 0) ax_list[i_map].hold(True) ax_list[i_map].imshow(oem.dataStore[i_map] > 0.1, cmap=cm.bone) ax_list[i_map].set_title(r'Map for $\{%d %d %d\}$' %tuple(pd.hkls[:, i_map])) ax_list[i_map].set_xlabel(r'Azimuth channel, $\eta$; $\Delta\eta=%.3f$' %delta_ome) ax_list[i_map].set_ylabel(r'Rotation channel, $\omega$; $\Delta\omega=%.3f$' %delta_ome) ax_list[i_map].plot(x, y, 'c+') ax_list[i_map].xaxis.set_ticks(xtloc) ax_list[i_map].xaxis.set_ticklabels(xtlab) ax_list[i_map].yaxis.set_ticks(ytloc) ax_list[i_map].yaxis.set_ticklabels(ytlab) ax_list[i_map].axis('tight') plt.show() return fig_list, oes
def simulate_laue_pattern(self, crystal_data, minEnergy=5., maxEnergy=35., rmat_s=None, tvec_s=None, grain_params=None, beam_vec=None): """ """ if isinstance(crystal_data, PlaneData): plane_data = crystal_data # grab the expanded list of hkls from plane_data hkls = np.hstack(plane_data.getSymHKLs()) # and the unit plane normals (G-vectors) in CRYSTAL FRAME gvec_c = np.dot(plane_data.latVecOps['B'], hkls) elif len(crystal_data) == 2: # !!! should clean this up hkls = np.array(crystal_data[0]) bmat = crystal_data[1] gvec_c = np.dot(bmat, hkls) else: raise(RuntimeError, 'argument list not understood') nhkls_tot = hkls.shape[1] # parse energy ranges # TODO: allow for spectrum parsing multipleEnergyRanges = False if hasattr(maxEnergy, '__len__'): assert len(maxEnergy) == len(minEnergy), \ 'energy cutoff ranges must have the same length' multipleEnergyRanges = True lmin = [] lmax = [] for i in range(len(maxEnergy)): lmin.append(ct.keVToAngstrom(maxEnergy[i])) lmax.append(ct.keVToAngstrom(minEnergy[i])) else: lmin = ct.keVToAngstrom(maxEnergy) lmax = ct.keVToAngstrom(minEnergy) # parse grain parameters kwarg if grain_params is None: grain_params = np.atleast_2d( np.hstack([np.zeros(6), ct.identity_6x1]) ) n_grains = len(grain_params) # sample rotation if rmat_s is None: rmat_s = ct.identity_3x3 # dummy translation vector... make input if tvec_s is None: tvec_s = ct.zeros_3 # beam vector if beam_vec is None: beam_vec = ct.beam_vec # ========================================================================= # LOOP OVER GRAINS # ========================================================================= # pre-allocate output arrays xy_det = np.nan*np.ones((n_grains, nhkls_tot, 2)) hkls_in = np.nan*np.ones((n_grains, 3, nhkls_tot)) angles = np.nan*np.ones((n_grains, nhkls_tot, 2)) dspacing = np.nan*np.ones((n_grains, nhkls_tot)) energy = np.nan*np.ones((n_grains, nhkls_tot)) for iG, gp in enumerate(grain_params): rmat_c = makeRotMatOfExpMap(gp[:3]) tvec_c = gp[3:6].reshape(3, 1) vInv_s = mutil.vecMVToSymm(gp[6:].reshape(6, 1)) # stretch them: V^(-1) * R * Gc gvec_s_str = np.dot(vInv_s, np.dot(rmat_c, gvec_c)) ghat_c_str = mutil.unitVector(np.dot(rmat_c.T, gvec_s_str)) # project dpts = gvecToDetectorXY(ghat_c_str.T, self.rmat, rmat_s, rmat_c, self.tvec, tvec_s, tvec_c, beamVec=beam_vec) # check intersections with detector plane canIntersect = ~np.isnan(dpts[:, 0]) npts_in = sum(canIntersect) if np.any(canIntersect): dpts = dpts[canIntersect, :].reshape(npts_in, 2) dhkl = hkls[:, canIntersect].reshape(3, npts_in) # back to angles tth_eta, gvec_l = detectorXYToGvec( dpts, self.rmat, rmat_s, self.tvec, tvec_s, tvec_c, beamVec=beam_vec) tth_eta = np.vstack(tth_eta).T # warp measured points if self.distortion is not None: if len(self.distortion) == 2: dpts = self.distortion[0]( dpts, self.distortion[1], invert=True) else: raise(RuntimeError, "something is wrong with the distortion") # plane spacings and energies dsp = 1. / rowNorm(gvec_s_str[:, canIntersect].T) wlen = 2*dsp*np.sin(0.5*tth_eta[:, 0]) # clip to detector panel _, on_panel = self.clip_to_panel(dpts, buffer_edges=True) if multipleEnergyRanges: validEnergy = np.zeros(len(wlen), dtype=bool) for i in range(len(lmin)): in_energy_range = np.logical_and( wlen >= lmin[i], wlen <= lmax[i]) validEnergy = validEnergy | in_energy_range pass else: validEnergy = np.logical_and(wlen >= lmin, wlen <= lmax) pass # index for valid reflections keepers = np.where(np.logical_and(on_panel, validEnergy))[0] # assign output arrays xy_det[iG][keepers, :] = dpts[keepers, :] hkls_in[iG][:, keepers] = dhkl[:, keepers] angles[iG][keepers, :] = tth_eta[keepers, :] dspacing[iG, keepers] = dsp[keepers] energy[iG, keepers] = ct.keVToAngstrom(wlen[keepers]) pass # close conditional on valids pass # close loop on grains return xy_det, hkls_in, angles, dspacing, energy
def latticePlanes(hkls, lparms, ltype='cubic', wavelength=1.54059292, strainMag=None): """ Generates lattice plane data in the direct lattice for a given set of Miller indices. Vector components are written in the crystal-relative RHON basis, X. The convention for fixing X to the lattice is such that a || x1 and c* || x3, where a and c* are direct and reciprocal lattice vectors, respectively. USAGE: planeInfo = latticePlanes(hkls, lparms, **kwargs) INPUTS: 1) hkls (3 x n float ndarray) is the array of Miller indices for the planes of interest. The vectors are assumed to be concatenated along the 1-axis (horizontal). 2) lparms (1 x m float list) is the array of lattice parameters, where m depends on the symmetry group (see below). 3) The following optional keyword arguments are recognized: *) ltype=(string) is a string representing the symmetry type of the implied Laue group. The 11 available choices are shown below. The default value is 'cubic'. Note that each group expects a lattice parameter array of the indicated length and order. latticeType lparms ----------- ------------ 'cubic' a 'hexagonal' a, c 'trigonal' a, c 'rhombohedral' a, alpha (in degrees) 'tetragonal' a, c 'orthorhombic' a, b, c 'monoclinic' a, b, c, beta (in degrees) 'triclinic' a, b, c, alpha, beta, gamma (in degrees) *) wavelength=<float> is a value represented the wavelength in Angstroms to calculate bragg angles for. The default value is for Cu K-alpha radiation (1.54059292 Angstrom) *) strainMag=None OUTPUTS: 1) planeInfo is a dictionary containing the following keys/items: normals (3, n) double array array of the components to the unit normals for each {hkl} in X (horizontally concatenated) dspacings (n, ) double array array of the d-spacings for each {hkl} 2thetas (n, ) double array array of the Bragg angles for each {hkl} relative to the specified wavelength NOTES: *) This function is effectively a wrapper to 'latticeVectors'. See 'help(latticeVectors)' for additional info. *) Lattice plane d-spacings are calculated from the reciprocal lattice vectors specified by {hkl} as shown in Appendix 1 of [1]. REFERENCES: [1] B. D. Cullity, ``Elements of X-Ray Diffraction, 2 ed.''. Addison-Wesley Publishing Company, Inc., 1978. ISBN 0-201-01174-3 """ location = 'latticePlanes' assert hkls.shape[0] == 3, "hkls aren't column vectors in call to '%s'!" % (location) tag = ltype wlen = wavelength # get B L = latticeVectors(lparms, tag) # get G-vectors -- reciprocal vectors in crystal frame G = num.dot(L['B'], hkls) # magnitudes d = 1 / num.sqrt(num.sum(G**2, 0)) angConv = 1. if outputDegrees: angConv = r2d # two thetas tth = angConv * 2 * num.arcsin(wlen / 2 / d) p = {'normals':unitVector(G), 'dspacings':d, 'tThetas':tth} if strainMag is not None: p['tThetasLo'] = angConv * 2 * num.arcsin(wlen / 2 / (d*(1.+strainMag))) p['tThetasHi'] = angConv * 2 * num.arcsin(wlen / 2 / (d*(1.-strainMag))) return p
def objFuncFitGrain(gFit, gFull, gFlag, instrument, reflections_dict, bMat, wavelength, omePeriod, simOnly=False, return_value_flag=return_value_flag): """ gFull[0] = expMap_c[0] gFull[1] = expMap_c[1] gFull[2] = expMap_c[2] gFull[3] = tVec_c[0] gFull[4] = tVec_c[1] gFull[5] = tVec_c[2] gFull[6] = vInv_MV[0] gFull[7] = vInv_MV[1] gFull[8] = vInv_MV[2] gFull[9] = vInv_MV[3] gFull[10] = vInv_MV[4] gFull[11] = vInv_MV[5] OLD CALL objFuncFitGrain(gFit, gFull, gFlag, detectorParams, xyo_det, hkls_idx, bMat, wavelength, bVec, eVec, dFunc, dParams, omePeriod, simOnly=False, return_value_flag=return_value_flag) """ bVec = instrument.beam_vector eVec = instrument.eta_vector # fill out parameters gFull[gFlag] = gFit # map parameters to functional arrays rMat_c = xfcapi.makeRotMatOfExpMap(gFull[:3]) tVec_c = gFull[3:6].reshape(3, 1) vInv_s = gFull[6:] vMat_s = mutil.vecMVToSymm(vInv_s) # NOTE: Inverse of V from F = V * R # loop over instrument panels # CAVEAT: keeping track of key ordering in the "detectors" attribute of # instrument here because I am not sure if instatiating them using # dict.fromkeys() preserves the same order if using iteration... # <JVB 2017-10-31> calc_omes_dict = dict.fromkeys(instrument.detectors, []) calc_xy_dict = dict.fromkeys(instrument.detectors) meas_xyo_all = [] det_keys_ordered = [] for det_key, panel in instrument.detectors.iteritems(): det_keys_ordered.append(det_key) rMat_d, tVec_d, chi, tVec_s = extract_detector_transformation( instrument.detector_parameters[det_key]) results = reflections_dict[det_key] if len(results) == 0: continue """ extract data from results list fields: refl_id, gvec_id, hkl, sum_int, max_int, pred_ang, meas_ang, meas_xy or array from spots tables: 0:5 ID PID H K L 5:7 sum(int) max(int) 7:10 pred tth pred eta pred ome 10:13 meas tth meas eta meas ome 13:15 pred X pred Y 15:17 meas X meas Y """ if isinstance(results, list): # WARNING: hkls and derived vectors below must be columnwise; # strictly necessary??? change affected APIs instead? # <JVB 2017-03-26> hkls = np.atleast_2d( np.vstack([x[2] for x in results]) ).T meas_xyo = np.atleast_2d( np.vstack([np.r_[x[7], x[6][-1]] for x in results]) ) elif isinstance(results, np.ndarray): hkls = np.atleast_2d(results[:, 2:5]).T meas_xyo = np.atleast_2d(results[:, [15, 16, 12]]) # FIXME: distortion handling must change to class-based if panel.distortion is not None: meas_omes = meas_xyo[:, 2] xy_unwarped = panel.distortion[0]( meas_xyo[:, :2], panel.distortion[1]) meas_xyo = np.vstack([xy_unwarped.T, meas_omes]).T pass # append to meas_omes meas_xyo_all.append(meas_xyo) # G-vectors: # 1. calculate full g-vector components in CRYSTAL frame from B # 2. rotate into SAMPLE frame and apply stretch # 3. rotate back into CRYSTAL frame and normalize to unit magnitude # IDEA: make a function for this sequence of operations with option for # choosing ouput frame (i.e. CRYSTAL vs SAMPLE vs LAB) gVec_c = np.dot(bMat, hkls) gVec_s = np.dot(vMat_s, np.dot(rMat_c, gVec_c)) gHat_c = mutil.unitVector(np.dot(rMat_c.T, gVec_s)) # !!!: check that this operates on UNWARPED xy match_omes, calc_omes = matchOmegas( meas_xyo, hkls, chi, rMat_c, bMat, wavelength, vInv=vInv_s, beamVec=bVec, etaVec=eVec, omePeriod=omePeriod) # append to omes dict calc_omes_dict[det_key] = calc_omes # TODO: try Numba implementations rMat_s = xfcapi.makeOscillRotMatArray(chi, calc_omes) calc_xy = xfcapi.gvecToDetectorXYArray(gHat_c.T, rMat_d, rMat_s, rMat_c, tVec_d, tVec_s, tVec_c, beamVec=bVec) # append to xy dict calc_xy_dict[det_key] = calc_xy pass # stack results to concatenated arrays calc_omes_all = np.hstack([calc_omes_dict[k] for k in det_keys_ordered]) tmp = [] for k in det_keys_ordered: if calc_xy_dict[k] is not None: tmp.append(calc_xy_dict[k]) calc_xy_all = np.vstack(tmp) meas_xyo_all = np.vstack(meas_xyo_all) npts = len(meas_xyo_all) if np.any(np.isnan(calc_xy)): raise RuntimeError( "infeasible pFull: may want to scale" + "back finite difference step size") # return values if simOnly: # return simulated values if return_value_flag in [None, 1]: retval = np.hstack([calc_xy_all, calc_omes_all.reshape(npts, 1)]) else: rd = dict.fromkeys(det_keys_ordered) for det_key in det_keys_ordered: rd[det_key] = {'calc_xy': calc_xy_dict[det_key], 'calc_omes': calc_omes_dict[det_key]} retval = rd else: # return residual vector # IDEA: try angles instead of xys? diff_vecs_xy = calc_xy_all - meas_xyo_all[:, :2] diff_ome = xf.angularDifference(calc_omes_all, meas_xyo_all[:, 2]) retval = np.hstack([diff_vecs_xy, diff_ome.reshape(npts, 1) ]).flatten() if return_value_flag == 1: # return scalar sum of squared residuals retval = sum(abs(retval)) elif return_value_flag == 2: # return DOF-normalized chisq # TODO: check this calculation denom = 3*npts - len(gFit) - 1. if denom != 0: nu_fac = 1. / denom else: nu_fac = 1. retval = nu_fac * sum(retval**2) return retval
def simulateLauePattern(self, planeData, minEnergy=5, maxEnergy=25, rMat_s=np.eye(3), rMat=None, vInv=None, doGnomonic=False): multipleEnergyRanges = False if hasattr(maxEnergy, '__len__'): assert len(maxEnergy) == len(minEnergy), 'energy cutoff ranges must have the same length' multipleEnergyRanges = True; lmin = []; lmax = [] for i in range(len(maxEnergy)): lmin.append(processWavelength(maxEnergy[i])) lmax.append(processWavelength(minEnergy[i])) else: lmin = processWavelength(maxEnergy) lmax = processWavelength(minEnergy) gvec_c = planeData.getPlaneNormals() hkls = planeData.getSymHKLs() dsp = planeData.getPlaneSpacings() if rMat is None: rMat = [np.eye(3),] if vInv is None: vInv = [vInv_ref,] # rMult = planeData.getMultiplicity() # nHKLs_tot = rMult.sum() # rMask = np.ones(nHKLs_tot, dtype=bool) retval = [] for iG in range(len(rMat)): tmp = {'detXY':[], 'gnoXY':[], 'angles':[], 'dspacing':[], 'hkl':[], 'energy':[]} for iHKL in range(planeData.nHKLs): # stretch them: V^(-1) * R * Gc gvec_s_str = mutil.unitVector( np.dot( vInv[iG], np.dot( rMat[iG], gvec_c[iHKL] ) ) ) gvec_c_str = np.dot(rMat[iG].T, gvec_s_str) gvec_l_str = np.dot(rMat_s, gvec_s_str) # # dpts = self.gVecToDet(gvec_c_str, rMat=rMat[iG], rMat_s=rMat_s) # gpts = self.gVecToDet(gvec_c_str, rMat=rMat[iG], rMat_s=rMat_s, doGnomonic=True) dpts = self.gVecToDet(gvec_c_str, rMat=rMat[iG], rMat_s=rMat_s) gpts = self.gVecToDet(gvec_c_str, rMat=rMat[iG], rMat_s=rMat_s, doGnomonic=True) canIntersect = -np.isnan(dpts[0, :]) npts_in = sum(canIntersect) if np.any(canIntersect): dpts = dpts[:, canIntersect].reshape(3, npts_in) dhkl = hkls[iHKL][:, canIntersect].reshape(3, npts_in) gvl_hat = gvec_l_str[:, canIntersect].reshape(3, npts_in) gvl_xy = gvec_l_str[:2, canIntersect].reshape(2, npts_in) # dot with the beam dotWbeam = np.dot(Z_ref.T, gvl_hat).flatten() # angles theta = piby2 - rot.arccosSafe( dotWbeam ) wlen = 2*dsp[iHKL]*np.sin(theta) eta = np.arccos(gvl_xy[0, :]) # find on spatial extent of detector # for corner # xTest = np.logical_and(dpts[0, :] > 0, dpts[0, :] < self.cdim) # for corner # yTest = np.logical_and(dpts[1, :] > 0, dpts[1, :] < self.rdim) xTest = np.logical_and(dpts[0, :] > -0.5 * self.cdim, dpts[0, :] < 0.5 * self.cdim) yTest = np.logical_and(dpts[1, :] > -0.5 * self.rdim, dpts[1, :] < 0.5 * self.rdim) onDetector = np.logical_and(xTest, yTest) if multipleEnergyRanges: validEnergy = np.zeros(len(wlen), dtype=bool) for i in range(len(lmin)): validEnergy = validEnergy | np.logical_and(wlen >= lmin[i], wlen <= lmax[i]) pass else: validEnergy = np.logical_and(wlen >= lmin, wlen <= lmax) pass keepers = np.logical_and(onDetector, validEnergy) dsp_this = 1. / mutil.columnNorm(np.dot(planeData.latVecOps['B'], dhkl[:, keepers])) tmp['detXY'].append(dpts[:2, keepers]) tmp['gnoXY'].append(gpts[:2, keepers]) tmp['angles'].append(np.vstack([2*theta[keepers]*r2d, eta[keepers]*r2d])) tmp['hkl'].append(dhkl[:, keepers]) tmp['dspacing'].append(dsp_this) tmp['energy'].append(processWavelength(wlen[keepers])) else: tmp['detXY'].append(np.empty((2, 0))) tmp['gnoXY'].append(np.empty((2, 0))) tmp['angles'].append(np.empty((2, 0))) tmp['hkl'].append(np.empty((3, 0))) tmp['dspacing'].append(np.empty(0)) tmp['energy'].append(np.empty(0)) pass pass retval.append(tmp) return retval
def simulate_laue_pattern(self, crystal_data, minEnergy=5., maxEnergy=35., rmat_s=None, tvec_s=None, grain_params=None, beam_vec=None): """ """ if isinstance(crystal_data, PlaneData): plane_data = crystal_data # grab the expanded list of hkls from plane_data hkls = np.hstack(plane_data.getSymHKLs()) # and the unit plane normals (G-vectors) in CRYSTAL FRAME gvec_c = np.dot(plane_data.latVecOps['B'], hkls) elif len(crystal_data) == 2: # !!! should clean this up hkls = np.array(crystal_data[0]) bmat = crystal_data[1] gvec_c = np.dot(bmat, hkls) else: raise (RuntimeError, 'argument list not understood') nhkls_tot = hkls.shape[1] # parse energy ranges # TODO: allow for spectrum parsing multipleEnergyRanges = False if hasattr(maxEnergy, '__len__'): assert len(maxEnergy) == len(minEnergy), \ 'energy cutoff ranges must have the same length' multipleEnergyRanges = True lmin = [] lmax = [] for i in range(len(maxEnergy)): lmin.append(ct.keVToAngstrom(maxEnergy[i])) lmax.append(ct.keVToAngstrom(minEnergy[i])) else: lmin = ct.keVToAngstrom(maxEnergy) lmax = ct.keVToAngstrom(minEnergy) # parse grain parameters kwarg if grain_params is None: grain_params = np.atleast_2d( np.hstack([np.zeros(6), ct.identity_6x1])) n_grains = len(grain_params) # sample rotation if rmat_s is None: rmat_s = ct.identity_3x3 # dummy translation vector... make input if tvec_s is None: tvec_s = ct.zeros_3 # beam vector if beam_vec is None: beam_vec = ct.beam_vec # ========================================================================= # LOOP OVER GRAINS # ========================================================================= # pre-allocate output arrays xy_det = np.nan * np.ones((n_grains, nhkls_tot, 2)) hkls_in = np.nan * np.ones((n_grains, 3, nhkls_tot)) angles = np.nan * np.ones((n_grains, nhkls_tot, 2)) dspacing = np.nan * np.ones((n_grains, nhkls_tot)) energy = np.nan * np.ones((n_grains, nhkls_tot)) for iG, gp in enumerate(grain_params): rmat_c = makeRotMatOfExpMap(gp[:3]) tvec_c = gp[3:6].reshape(3, 1) vInv_s = mutil.vecMVToSymm(gp[6:].reshape(6, 1)) # stretch them: V^(-1) * R * Gc gvec_s_str = np.dot(vInv_s, np.dot(rmat_c, gvec_c)) ghat_c_str = mutil.unitVector(np.dot(rmat_c.T, gvec_s_str)) # project dpts = gvecToDetectorXY(ghat_c_str.T, self.rmat, rmat_s, rmat_c, self.tvec, tvec_s, tvec_c, beamVec=beam_vec) # check intersections with detector plane canIntersect = ~np.isnan(dpts[:, 0]) npts_in = sum(canIntersect) if np.any(canIntersect): dpts = dpts[canIntersect, :].reshape(npts_in, 2) dhkl = hkls[:, canIntersect].reshape(3, npts_in) # back to angles tth_eta, gvec_l = detectorXYToGvec(dpts, self.rmat, rmat_s, self.tvec, tvec_s, tvec_c, beamVec=beam_vec) tth_eta = np.vstack(tth_eta).T # warp measured points if self.distortion is not None: if len(self.distortion) == 2: dpts = self.distortion[0](dpts, self.distortion[1], invert=True) else: raise (RuntimeError, "something is wrong with the distortion") # plane spacings and energies dsp = 1. / rowNorm(gvec_s_str[:, canIntersect].T) wlen = 2 * dsp * np.sin(0.5 * tth_eta[:, 0]) # clip to detector panel _, on_panel = self.clip_to_panel(dpts, buffer_edges=True) if multipleEnergyRanges: validEnergy = np.zeros(len(wlen), dtype=bool) for i in range(len(lmin)): in_energy_range = np.logical_and( wlen >= lmin[i], wlen <= lmax[i]) validEnergy = validEnergy | in_energy_range pass else: validEnergy = np.logical_and(wlen >= lmin, wlen <= lmax) pass # index for valid reflections keepers = np.where(np.logical_and(on_panel, validEnergy))[0] # assign output arrays xy_det[iG][keepers, :] = dpts[keepers, :] hkls_in[iG][:, keepers] = dhkl[:, keepers] angles[iG][keepers, :] = tth_eta[keepers, :] dspacing[iG, keepers] = dsp[keepers] energy[iG, keepers] = ct.keVToAngstrom(wlen[keepers]) pass # close conditional on valids pass # close loop on grains return xy_det, hkls_in, angles, dspacing, energy
def find_orientations(cfg, hkls=None, clean=False, profile=False): """ Takes a config dict as input, generally a yml document NOTE: single cfg instance, not iterator! """ # ...make this an attribute in cfg? analysis_id = '%s_%s' %( cfg.analysis_name.strip().replace(' ', '-'), cfg.material.active.strip().replace(' ', '-'), ) # grab planeData object matl = cPickle.load(open('materials.cpl', 'r')) md = dict(zip([matl[i].name for i in range(len(matl))], matl)) pd = md[cfg.material.active].planeData # make image_series image_series = cfg.image_series.omegaseries # need instrument cfg later on down... instr_cfg = get_instrument_parameters(cfg) detector_params = np.hstack([ instr_cfg['detector']['transform']['tilt_angles'], instr_cfg['detector']['transform']['t_vec_d'], instr_cfg['oscillation_stage']['chi'], instr_cfg['oscillation_stage']['t_vec_s'], ]) rdim = cfg.instrument.detector.pixels.size[0]*cfg.instrument.detector.pixels.rows cdim = cfg.instrument.detector.pixels.size[1]*cfg.instrument.detector.pixels.columns panel_dims = ((-0.5*cdim, -0.5*rdim), ( 0.5*cdim, 0.5*rdim), ) # UGH! hard-coded distortion... if instr_cfg['detector']['distortion']['function_name'] == 'GE_41RT': distortion = (dFuncs.GE_41RT, instr_cfg['detector']['distortion']['parameters'], ) else: distortion = None min_compl = cfg.find_orientations.clustering.completeness # start logger logger.info("beginning analysis '%s'", cfg.analysis_name) # load the eta_ome orientation maps eta_ome = load_eta_ome_maps(cfg, pd, image_series, hkls=hkls, clean=clean) ome_range = ( np.min(eta_ome.omeEdges), np.max(eta_ome.omeEdges) ) try: # are we searching the full grid of orientation space? qgrid_f = cfg.find_orientations.use_quaternion_grid quats = np.load(qgrid_f) logger.info("Using %s for full quaternion search", qgrid_f) hkl_ids = None except (IOError, ValueError, AttributeError): # or doing a seeded search? logger.info("Defaulting to seeded search") hkl_seeds = cfg.find_orientations.seed_search.hkl_seeds hkl_ids = [ eta_ome.planeData.hklDataList[i]['hklID'] for i in hkl_seeds ] hklseedstr = ', '.join( [str(i) for i in eta_ome.planeData.hkls.T[hkl_seeds]] ) logger.info( "Seeding search using hkls from %s: %s", cfg.find_orientations.orientation_maps.file, hklseedstr ) quats = generate_orientation_fibers( eta_ome, detector_params[6], cfg.find_orientations.threshold, cfg.find_orientations.seed_search.hkl_seeds, cfg.find_orientations.seed_search.fiber_ndiv, ncpus=cfg.multiprocessing, ) if save_as_ascii: np.savetxt( os.path.join(cfg.working_dir, 'trial_orientations.dat'), quats.T, fmt="%.18e", delimiter="\t" ) pass pass # close conditional on grid search # generate the completion maps logger.info("Running paintgrid on %d trial orientations", quats.shape[1]) if profile: logger.info("Profiling mode active, forcing ncpus to 1") ncpus = 1 else: ncpus = cfg.multiprocessing logger.info( "%d of %d available processors requested", ncpus, mp.cpu_count() ) compl = idx.paintGrid( quats, eta_ome, etaRange=np.radians(cfg.find_orientations.eta.range), omeTol=np.radians(cfg.find_orientations.omega.tolerance), etaTol=np.radians(cfg.find_orientations.eta.tolerance), omePeriod=np.radians(cfg.find_orientations.omega.period), threshold=cfg.find_orientations.threshold, doMultiProc=ncpus > 1, nCPUs=ncpus ) if save_as_ascii: np.savetxt(os.path.join(cfg.working_dir, 'completeness.dat'), compl) else: np.save( os.path.join( cfg.working_dir, 'scored_orientations_%s.npy' %analysis_id ), np.vstack([quats, compl]) ) ########################################################## ## Simulate N random grains to get neighborhood size ## ########################################################## if hkl_ids is not None: ngrains = 100 rand_q = mutil.unitVector(np.random.randn(4, ngrains)) rand_e = np.tile(2.*np.arccos(rand_q[0, :]), (3, 1)) \ * mutil.unitVector(rand_q[1:, :]) refl_per_grain = np.zeros(ngrains) num_seed_refls = np.zeros(ngrains) print('fo: hklids = ', hkl_ids) for i in range(ngrains): grain_params = np.hstack([rand_e[:, i], xf.zeroVec.flatten(), xf.vInv_ref.flatten() ]) sim_results = simulateGVecs(pd, detector_params, grain_params, ome_range=(ome_range,), ome_period=(ome_range[0], ome_range[0]+2*np.pi), eta_range=np.radians(cfg.find_orientations.eta.range), panel_dims=panel_dims, pixel_pitch=cfg.instrument.detector.pixels.size, distortion=distortion, ) refl_per_grain[i] = len(sim_results[0]) # lines below fix bug when sim_results[0] is empty if refl_per_grain[i] > 0: num_seed_refls[i] = np.sum([sum(sim_results[0] == hkl_id) for hkl_id in hkl_ids]) else: num_seed_refls[i] = 0 #min_samples = 2 min_samples = max( int(np.floor(0.5*min_compl*min(num_seed_refls))), 2 ) mean_rpg = int(np.round(np.average(refl_per_grain))) else: min_samples = 1 mean_rpg = 1 logger.info("mean number of reflections per grain is %d", mean_rpg) logger.info("neighborhood size estimate is %d points", min_samples) # cluster analysis to identify orientation blobs, the final output: qbar, cl = run_cluster(compl, quats, pd.getQSym(), cfg, min_samples=min_samples) analysis_id = '%s_%s' %( cfg.analysis_name.strip().replace(' ', '-'), cfg.material.active.strip().replace(' ', '-'), ) np.savetxt( os.path.join( cfg.working_dir, 'accepted_orientations_%s.dat' %analysis_id ), qbar.T, fmt="%.18e", delimiter="\t") return
Zl = np.c_[0, 0, 1].T eVec = Xl.flatten() chi_b = 15 ome_b = 5 rMat_b = np.dot(rot.rotMatOfExpMap(d2r * chi_b * Xl), rot.rotMatOfExpMap(d2r * ome_b * Yl)) bVec = np.dot(rMat_b, -Zl) beam_len = 10 tVec_d = np.c_[-5.0, 3.0, -10.0].T tVec_s = np.c_[3.0, 0.2, -1.5].T tVec_c = np.c_[2.0, 0.8, -0.7].T rMat_d = rot.rotMatOfExpMap(d2r * 21 * mutil.unitVector(np.c_[2, 3, 1].T)) chi = 21 * d2r ome = 67 * d2r rMat_s = np.dot(rot.rotMatOfExpMap(chi * Xl), rot.rotMatOfExpMap(ome * Yl)) rMat_c = rot.rotMatOfExpMap(d2r * 36 * mutil.unitVector(np.c_[1, 2, 3].T)) det_size = (6, 4) det_pt = (-1.15, 0.875) def gen_sk(fname, det_size, det_pt, eVec, beam_len, bVec, rMat_b, rMat_d, rMat_s, rMat_c, tVec_d, tVec_s, tVec_c): if isinstance(fname, file): fid = f
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
rmats_final = rot.rotMatOfExpMap(np.array(expmaps_final)[:, grainlist]) # getting symmetry group directly here; could also grab from planeData object # qsym = symm.quatOfLaueGroup('d6h') plane_data = load_pdata( '/Users/rachellim/Documents/Research/CHESS_Jun17/2020-08-03/materials2.hexrd', 'ti7al') qsym = plane_data.getQSym() bmat = plane_data.latVecOps['B'] # this was for 001 triangle # sst_vertices = mutil.unitVector(matfuncs.triu(np.ones((3, 3))).T) # this if for the standard triangle # sst_vertices = mutil.unitVector(matfuncs.triu(np.ones((3, 3)))) sst_vertices = mutil.unitVector( np.dot(bmat, np.array([[0, 0, 1], [2, -1, 0], [1, 0, 0]]).T)) sst_normals = np.array([ np.cross(sst_vertices[:, i[0]], sst_vertices[:, i[1]]) for i in [(0, 1), (1, 2), (2, 0)] ]).T # DEFINE SAMPLE DIRECTION YOU WANT FOR IPF THEN MOVE THE CRYSTAL FRAME s = mutil.unitVector(np.c_[0., 1., 0.].T) # sample Y a.k.a. loading s_c_init = np.zeros((len(rmats_init), 3)) for i, rm in enumerate(rmats_init): s_c_init[i] = np.dot(rm.T, s).flatten() # APPLY SYMMETRIES AND PICK REPRESENTATIVE IN SPECIFED SST sst_list = [] csym_init = []
def create_clustering_parameters(cfg, eta_ome): """ Compute min samples and mean reflections per grain for clustering Parameters ---------- cfg : TYPE DESCRIPTION. eta_ome : TYPE DESCRIPTION. Returns ------- Tuple of (min_samples, mean_rpg) """ # grab objects from config plane_data = cfg.material.plane_data imsd = cfg.image_series instr = cfg.instrument.hedm eta_ranges = cfg.find_orientations.eta.range compl_thresh = cfg.find_orientations.clustering.completeness # handle omega period ome_period, ome_ranges = _process_omegas(imsd) active_hkls = cfg.find_orientations.orientation_maps.active_hkls \ or eta_ome.iHKLList fiber_seeds = cfg.find_orientations.seed_search.hkl_seeds # Simulate N random grains to get neighborhood size seed_hkl_ids = [ plane_data.hklDataList[active_hkls[i]]['hklID'] for i in fiber_seeds ] # !!! default to use 100 grains ngrains = 100 rand_q = mutil.unitVector(np.random.randn(4, ngrains)) rand_e = np.tile(2.*np.arccos(rand_q[0, :]), (3, 1)) \ * mutil.unitVector(rand_q[1:, :]) grain_param_list = np.vstack([ rand_e, np.zeros((3, ngrains)), np.tile(const.identity_6x1, (ngrains, 1)).T ]).T sim_results = instr.simulate_rotation_series( plane_data, grain_param_list, eta_ranges=np.radians(eta_ranges), ome_ranges=np.radians(ome_ranges), ome_period=np.radians(ome_period)) refl_per_grain = np.zeros(ngrains) seed_refl_per_grain = np.zeros(ngrains) for sim_result in sim_results.values(): for i, refl_ids in enumerate(sim_result[0]): refl_per_grain[i] += len(refl_ids) seed_refl_per_grain[i] += np.sum( [sum(refl_ids == hkl_id) for hkl_id in seed_hkl_ids]) min_samples = max( int(np.floor(0.5 * compl_thresh * min(seed_refl_per_grain))), 2) mean_rpg = int(np.round(np.average(refl_per_grain))) return min_samples, mean_rpg
def latticePlanes(hkls, lparms, ltype="cubic", wavelength=1.54059292, strainMag=None): """ Generates lattice plane data in the direct lattice for a given set of Miller indices. Vector components are written in the crystal-relative RHON basis, X. The convention for fixing X to the lattice is such that a || x1 and c* || x3, where a and c* are direct and reciprocal lattice vectors, respectively. USAGE: planeInfo = latticePlanes(hkls, lparms, **kwargs) INPUTS: 1) hkls (3 x n float ndarray) is the array of Miller indices for the planes of interest. The vectors are assumed to be concatenated along the 1-axis (horizontal). 2) lparms (1 x m float list) is the array of lattice parameters, where m depends on the symmetry group (see below). 3) The following optional keyword arguments are recognized: *) ltype=(string) is a string representing the symmetry type of the implied Laue group. The 11 available choices are shown below. The default value is 'cubic'. Note that each group expects a lattice parameter array of the indicated length and order. latticeType lparms ----------- ------------ 'cubic' a 'hexagonal' a, c 'trigonal' a, c 'rhombohedral' a, alpha (in degrees) 'tetragonal' a, c 'orthorhombic' a, b, c 'monoclinic' a, b, c, beta (in degrees) 'triclinic' a, b, c, alpha, beta, gamma (in degrees) *) wavelength=<float> is a value represented the wavelength in Angstroms to calculate bragg angles for. The default value is for Cu K-alpha radiation (1.54059292 Angstrom) *) strainMag=None OUTPUTS: 1) planeInfo is a dictionary containing the following keys/items: normals (3, n) double array array of the components to the unit normals for each {hkl} in X (horizontally concatenated) dspacings (n, ) double array array of the d-spacings for each {hkl} 2thetas (n, ) double array array of the Bragg angles for each {hkl} relative to the specified wavelength NOTES: *) This function is effectively a wrapper to 'latticeVectors'. See 'help(latticeVectors)' for additional info. *) Lattice plane d-spacings are calculated from the reciprocal lattice vectors specified by {hkl} as shown in Appendix 1 of [1]. REFERENCES: [1] B. D. Cullity, ``Elements of X-Ray Diffraction, 2 ed.''. Addison-Wesley Publishing Company, Inc., 1978. ISBN 0-201-01174-3 """ location = "latticePlanes" assert hkls.shape[0] == 3, "hkls aren't column vectors in call to '%s'!" % (location) tag = ltype wlen = wavelength # get B L = latticeVectors(lparms, tag) # get G-vectors -- reciprocal vectors in crystal frame G = num.dot(L["B"], hkls) # magnitudes d = 1 / num.sqrt(num.sum(G ** 2, 0)) angConv = 1.0 if outputDegrees: angConv = r2d # two thetas tth = angConv * 2 * num.arcsin(wlen / 2 / d) p = {"normals": unitVector(G), "dspacings": d, "tThetas": tth} if strainMag is not None: p["tThetasLo"] = angConv * 2 * num.arcsin(wlen / 2 / (d * (1.0 + strainMag))) p["tThetasHi"] = angConv * 2 * num.arcsin(wlen / 2 / (d * (1.0 - strainMag))) return p
def objFuncFitGrain(gFit, gFull, gFlag, detectorParams, xyo_det, hkls_idx, bMat, wavelength, bVec, eVec, dFunc, dParams, omePeriod, simOnly=False, returnScalarValue=returnScalarValue): """ gFull[0] = expMap_c[0] gFull[1] = expMap_c[1] gFull[2] = expMap_c[2] gFull[3] = tVec_c[0] gFull[4] = tVec_c[1] gFull[5] = tVec_c[2] gFull[6] = vInv_MV[0] gFull[7] = vInv_MV[1] gFull[8] = vInv_MV[2] gFull[9] = vInv_MV[3] gFull[10] = vInv_MV[4] gFull[11] = vInv_MV[5] detectorParams[0] = tiltAngles[0] detectorParams[1] = tiltAngles[1] detectorParams[2] = tiltAngles[2] detectorParams[3] = tVec_d[0] detectorParams[4] = tVec_d[1] detectorParams[5] = tVec_d[2] detectorParams[6] = chi detectorParams[7] = tVec_s[0] detectorParams[8] = tVec_s[1] detectorParams[9] = tVec_s[2] """ npts = len(xyo_det) gFull[gFlag] = gFit xy_unwarped = dFunc(xyo_det[:, :2], dParams) rMat_d = xfcapi.makeDetectorRotMat(detectorParams[:3]) tVec_d = detectorParams[3:6].reshape(3, 1) chi = detectorParams[6] tVec_s = detectorParams[7:10].reshape(3, 1) rMat_c = xfcapi.makeRotMatOfExpMap(gFull[:3]) tVec_c = gFull[3:6].reshape(3, 1) vInv_s = gFull[6:] vMat_s = mutil.vecMVToSymm(vInv_s) # NOTE: Inverse of V from F = V * R gVec_c = np.dot(bMat, hkls_idx) # gVecs with magnitudes in CRYSTAL frame gVec_s = np.dot(vMat_s, np.dot(rMat_c, gVec_c)) # stretched gVecs in SAMPLE frame gHat_c = mutil.unitVector( np.dot(rMat_c.T, gVec_s)) # unit reciprocal lattice vectors in CRYSTAL frame match_omes, calc_omes = matchOmegas(xyo_det, hkls_idx, chi, rMat_c, bMat, wavelength, vInv=vInv_s, beamVec=bVec, etaVec=eVec, omePeriod=omePeriod) calc_xy = np.zeros((npts, 2)) for i in range(npts): rMat_s = xfcapi.makeOscillRotMat([chi, calc_omes[i]]) calc_xy[i, :] = xfcapi.gvecToDetectorXY(gHat_c[:, i], rMat_d, rMat_s, rMat_c, tVec_d, tVec_s, tVec_c, beamVec=bVec).flatten() pass if np.any(np.isnan(calc_xy)): print "infeasible pFull" # return values if simOnly: retval = np.hstack([calc_xy, calc_omes.reshape(npts, 1)]) else: diff_vecs_xy = calc_xy - xy_unwarped[:, :2] diff_ome = xf.angularDifference( calc_omes, xyo_det[:, 2] ) retval = mutil.rowNorm( np.hstack([diff_vecs_xy, diff_ome.reshape(npts, 1) ]) ).flatten() if returnScalarValue: retval = sum( retval ) return retval
def objFuncFitGrain(gFit, gFull, gFlag, detectorParams, xyo_det, hkls_idx, bMat, wavelength, bVec, eVec, dFunc, dParams, omePeriod, simOnly=False, return_value_flag=return_value_flag): """ gFull[0] = expMap_c[0] gFull[1] = expMap_c[1] gFull[2] = expMap_c[2] gFull[3] = tVec_c[0] gFull[4] = tVec_c[1] gFull[5] = tVec_c[2] gFull[6] = vInv_MV[0] gFull[7] = vInv_MV[1] gFull[8] = vInv_MV[2] gFull[9] = vInv_MV[3] gFull[10] = vInv_MV[4] gFull[11] = vInv_MV[5] detectorParams[0] = tiltAngles[0] detectorParams[1] = tiltAngles[1] detectorParams[2] = tiltAngles[2] detectorParams[3] = tVec_d[0] detectorParams[4] = tVec_d[1] detectorParams[5] = tVec_d[2] detectorParams[6] = chi detectorParams[7] = tVec_s[0] detectorParams[8] = tVec_s[1] detectorParams[9] = tVec_s[2] """ npts = len(xyo_det) gFull[gFlag] = gFit xy_unwarped = dFunc(xyo_det[:, :2], dParams) rMat_d = xfcapi.makeDetectorRotMat(detectorParams[:3]) tVec_d = detectorParams[3:6].reshape(3, 1) chi = detectorParams[6] tVec_s = detectorParams[7:10].reshape(3, 1) rMat_c = xfcapi.makeRotMatOfExpMap(gFull[:3]) tVec_c = gFull[3:6].reshape(3, 1) vInv_s = gFull[6:] vMat_s = mutil.vecMVToSymm(vInv_s) # NOTE: Inverse of V from F = V * R gVec_c = np.dot(bMat, hkls_idx) # gVecs with magnitudes in CRYSTAL frame gVec_s = np.dot(vMat_s, np.dot(rMat_c, gVec_c)) # stretched gVecs in SAMPLE frame gHat_c = mutil.unitVector(np.dot( rMat_c.T, gVec_s)) # unit reciprocal lattice vectors in CRYSTAL frame match_omes, calc_omes = matchOmegas(xyo_det, hkls_idx, chi, rMat_c, bMat, wavelength, vInv=vInv_s, beamVec=bVec, etaVec=eVec, omePeriod=omePeriod) rMat_s = xfcapi.makeOscillRotMatArray(chi, calc_omes) calc_xy = xfcapi.gvecToDetectorXYArray(gHat_c.T, rMat_d, rMat_s, rMat_c, tVec_d, tVec_s, tVec_c, beamVec=bVec) if np.any(np.isnan(calc_xy)): print "infeasible pFull" # return values if simOnly: retval = np.hstack([calc_xy, calc_omes.reshape(npts, 1)]) else: diff_vecs_xy = calc_xy - xy_unwarped[:, :2] diff_ome = xf.angularDifference(calc_omes, xyo_det[:, 2]) retval = np.hstack([diff_vecs_xy, diff_ome.reshape(npts, 1)]).flatten() if return_value_flag == 1: retval = sum(abs(retval)) elif return_value_flag == 2: denom = npts - len(gFit) - 1. if denom != 0: nu_fac = 1. / denom else: nu_fac = 1. retval = nu_fac * sum(retval**2 / abs( np.hstack([calc_xy, calc_omes.reshape(npts, 1)]).flatten())) return retval
def find_orientations(cfg, hkls=None, clean=False, profile=False): """ Takes a config dict as input, generally a yml document NOTE: single cfg instance, not iterator! """ # ...make this an attribute in cfg? analysis_id = '%s_%s' % ( cfg.analysis_name.strip().replace(' ', '-'), cfg.material.active.strip().replace(' ', '-'), ) # grab planeData object matl = cPickle.load(open('materials.cpl', 'r')) md = dict(zip([matl[i].name for i in range(len(matl))], matl)) pd = md[cfg.material.active].planeData # make image_series image_series = cfg.image_series.omegaseries # need instrument cfg later on down... instr_cfg = get_instrument_parameters(cfg) detector_params = np.hstack([ instr_cfg['detector']['transform']['tilt_angles'], instr_cfg['detector']['transform']['t_vec_d'], instr_cfg['oscillation_stage']['chi'], instr_cfg['oscillation_stage']['t_vec_s'], ]) rdim = cfg.instrument.detector.pixels.size[ 0] * cfg.instrument.detector.pixels.rows cdim = cfg.instrument.detector.pixels.size[ 1] * cfg.instrument.detector.pixels.columns panel_dims = ( (-0.5 * cdim, -0.5 * rdim), (0.5 * cdim, 0.5 * rdim), ) # UGH! hard-coded distortion... if instr_cfg['detector']['distortion']['function_name'] == 'GE_41RT': distortion = ( dFuncs.GE_41RT, instr_cfg['detector']['distortion']['parameters'], ) else: distortion = None min_compl = cfg.find_orientations.clustering.completeness # start logger logger.info("beginning analysis '%s'", cfg.analysis_name) # load the eta_ome orientation maps eta_ome = load_eta_ome_maps(cfg, pd, image_series, hkls=hkls, clean=clean) ome_range = (np.min(eta_ome.omeEdges), np.max(eta_ome.omeEdges)) try: # are we searching the full grid of orientation space? qgrid_f = cfg.find_orientations.use_quaternion_grid quats = np.load(qgrid_f) logger.info("Using %s for full quaternion search", qgrid_f) hkl_ids = None except (IOError, ValueError, AttributeError): # or doing a seeded search? logger.info("Defaulting to seeded search") hkl_seeds = cfg.find_orientations.seed_search.hkl_seeds hkl_ids = [ eta_ome.planeData.hklDataList[i]['hklID'] for i in hkl_seeds ] hklseedstr = ', '.join( [str(i) for i in eta_ome.planeData.hkls.T[hkl_seeds]]) logger.info("Seeding search using hkls from %s: %s", cfg.find_orientations.orientation_maps.file, hklseedstr) quats = generate_orientation_fibers( eta_ome, detector_params[6], cfg.find_orientations.threshold, cfg.find_orientations.seed_search.hkl_seeds, cfg.find_orientations.seed_search.fiber_ndiv, ncpus=cfg.multiprocessing, ) if save_as_ascii: np.savetxt(os.path.join(cfg.working_dir, 'trial_orientations.dat'), quats.T, fmt="%.18e", delimiter="\t") pass pass # close conditional on grid search # generate the completion maps logger.info("Running paintgrid on %d trial orientations", quats.shape[1]) if profile: logger.info("Profiling mode active, forcing ncpus to 1") ncpus = 1 else: ncpus = cfg.multiprocessing logger.info("%d of %d available processors requested", ncpus, mp.cpu_count()) compl = idx.paintGrid( quats, eta_ome, etaRange=np.radians(cfg.find_orientations.eta.range), omeTol=np.radians(cfg.find_orientations.omega.tolerance), etaTol=np.radians(cfg.find_orientations.eta.tolerance), omePeriod=np.radians(cfg.find_orientations.omega.period), threshold=cfg.find_orientations.threshold, doMultiProc=ncpus > 1, nCPUs=ncpus) if save_as_ascii: np.savetxt(os.path.join(cfg.working_dir, 'completeness.dat'), compl) else: np.save( os.path.join(cfg.working_dir, 'scored_orientations_%s.npy' % analysis_id), np.vstack([quats, compl])) ########################################################## ## Simulate N random grains to get neighborhood size ## ########################################################## if hkl_ids is not None: ngrains = 100 rand_q = mutil.unitVector(np.random.randn(4, ngrains)) rand_e = np.tile(2.*np.arccos(rand_q[0, :]), (3, 1)) \ * mutil.unitVector(rand_q[1:, :]) refl_per_grain = np.zeros(ngrains) num_seed_refls = np.zeros(ngrains) print('fo: hklids = ', hkl_ids) for i in range(ngrains): grain_params = np.hstack( [rand_e[:, i], xf.zeroVec.flatten(), xf.vInv_ref.flatten()]) sim_results = simulateGVecs( pd, detector_params, grain_params, ome_range=(ome_range, ), ome_period=(ome_range[0], ome_range[0] + 2 * np.pi), eta_range=np.radians(cfg.find_orientations.eta.range), panel_dims=panel_dims, pixel_pitch=cfg.instrument.detector.pixels.size, distortion=distortion, ) refl_per_grain[i] = len(sim_results[0]) # lines below fix bug when sim_results[0] is empty if refl_per_grain[i] > 0: num_seed_refls[i] = np.sum( [sum(sim_results[0] == hkl_id) for hkl_id in hkl_ids]) else: num_seed_refls[i] = 0 #min_samples = 2 min_samples = max(int(np.floor(0.5 * min_compl * min(num_seed_refls))), 2) mean_rpg = int(np.round(np.average(refl_per_grain))) else: min_samples = 1 mean_rpg = 1 logger.info("mean number of reflections per grain is %d", mean_rpg) logger.info("neighborhood size estimate is %d points", min_samples) # cluster analysis to identify orientation blobs, the final output: qbar, cl = run_cluster(compl, quats, pd.getQSym(), cfg, min_samples=min_samples) analysis_id = '%s_%s' % ( cfg.analysis_name.strip().replace(' ', '-'), cfg.material.active.strip().replace(' ', '-'), ) np.savetxt(os.path.join(cfg.working_dir, 'accepted_orientations_%s.dat' % analysis_id), qbar.T, fmt="%.18e", delimiter="\t") return
def makeScatteringVectors(hkls, rMat_c, bMat, wavelength, chiTilt=None): """ modeled after QFromU.m """ # basis vectors bHat_l = num.c_[ 0., 0., -1.].T eHat_l = num.c_[ 1., 0., 0.].T zTol = 1.0e-7 # zero tolerance for checking vectors gVec_s = [] oangs0 = [] oangs1 = [] # these are the reciprocal lattice vectors in the CRYSTAL FRAME # ** NOTE ** # if strained, assumes that you handed it a bMat calculated from # strained [a, b, c] gVec_c = num.dot( bMat, hkls ) gHat_c = unitVector(gVec_c) dim0, nRefl = gVec_c.shape assert dim0 == 3, "Looks like something is wrong with your lattice plane normals son!" # extract 1/dspacing and sin of bragg angle dSpacingi = columnNorm(gVec_c).flatten() sintht = 0.5 * wavelength * dSpacingi # move reciprocal lattice vectors to sample frame gHat_s = num.dot(rMat_c.squeeze(), gHat_c) if chiTilt is None: cchi = 1. schi = 0. rchi = num.eye(3) else: cchi = num.cos(chiTilt) schi = num.sin(chiTilt) rchi = num.array([[ 1., 0., 0.], [ 0., cchi, -schi], [ 0., schi, cchi]]) pass a = cchi * gHat_s[0, :] b = -cchi * gHat_s[2, :] c = schi * gHat_s[1, :] - sintht # 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) ome0 = rhsAng - phaseAng ome1 = num.pi - rhsAng - phaseAng goodOnes_s = -num.isnan(ome0) eta0 = num.nan * num.ones_like(ome0) eta1 = num.nan * num.ones_like(ome1) # mark feasible reflections goodOnes = num.tile(goodOnes_s, (1, 2)).flatten() numGood_s = sum(goodOnes_s) numGood = 2 * numGood_s tmp_eta = num.empty(numGood) tmp_gvec = num.tile(gHat_c, (1, 2))[:, goodOnes] allome = num.hstack([ome0, ome1]) for i in range(numGood): come = num.cos(allome[goodOnes][i]) some = num.sin(allome[goodOnes][i]) rome = num.array([[ come, 0., some], [ 0., 1., 0.], [-some, 0., come]]) rMat_s = num.dot(rchi, rome) gVec_l = num.dot(rMat_s, num.dot(rMat_c, tmp_gvec[:, i].reshape(3, 1) ) ) tmp_eta[i] = num.arctan2(gVec_l[1], gVec_l[0]) pass eta0[goodOnes_s] = tmp_eta[:numGood_s] eta1[goodOnes_s] = tmp_eta[numGood_s:] # make assoc tTh array tTh = 2.*num.arcsin(sintht).flatten() tTh0 = tTh; tTh0[-goodOnes_s] = num.nan gVec_s = num.tile(dSpacingi, (3, 1)) * gHat_s oangs0 = num.vstack([tTh0.flatten(), eta0.flatten(), ome0.flatten()]) oangs1 = num.vstack([tTh0.flatten(), eta1.flatten(), ome1.flatten()]) return gVec_s, oangs0, oangs1
def simulateLauePattern(self, planeData, minEnergy=5, maxEnergy=25, rMat_s=np.eye(3), rMat=None, vInv=None, doGnomonic=False): multipleEnergyRanges = False if hasattr(maxEnergy, '__len__'): assert len(maxEnergy) == len( minEnergy), 'energy cutoff ranges must have the same length' multipleEnergyRanges = True lmin = [] lmax = [] for i in range(len(maxEnergy)): lmin.append(processWavelength(maxEnergy[i])) lmax.append(processWavelength(minEnergy[i])) else: lmin = processWavelength(maxEnergy) lmax = processWavelength(minEnergy) gvec_c = planeData.getPlaneNormals() hkls = planeData.getSymHKLs() dsp = planeData.getPlaneSpacings() if rMat is None: rMat = [ np.eye(3), ] if vInv is None: vInv = [ vInv_ref, ] # rMult = planeData.getMultiplicity() # nHKLs_tot = rMult.sum() # rMask = np.ones(nHKLs_tot, dtype=bool) retval = [] for iG in range(len(rMat)): tmp = { 'detXY': [], 'gnoXY': [], 'angles': [], 'dspacing': [], 'hkl': [], 'energy': [] } for iHKL in range(planeData.nHKLs): # stretch them: V^(-1) * R * Gc gvec_s_str = mutil.unitVector( np.dot(vInv[iG], np.dot(rMat[iG], gvec_c[iHKL]))) gvec_c_str = np.dot(rMat[iG].T, gvec_s_str) gvec_l_str = np.dot(rMat_s, gvec_s_str) # # dpts = self.gVecToDet(gvec_c_str, rMat=rMat[iG], rMat_s=rMat_s) # gpts = self.gVecToDet(gvec_c_str, rMat=rMat[iG], rMat_s=rMat_s, doGnomonic=True) dpts = self.gVecToDet(gvec_c_str, rMat=rMat[iG], rMat_s=rMat_s) gpts = self.gVecToDet(gvec_c_str, rMat=rMat[iG], rMat_s=rMat_s, doGnomonic=True) canIntersect = -np.isnan(dpts[0, :]) npts_in = sum(canIntersect) if np.any(canIntersect): dpts = dpts[:, canIntersect].reshape(3, npts_in) dhkl = hkls[iHKL][:, canIntersect].reshape(3, npts_in) gvl_hat = gvec_l_str[:, canIntersect].reshape(3, npts_in) gvl_xy = gvec_l_str[:2, canIntersect].reshape(2, npts_in) # dot with the beam dotWbeam = np.dot(Z_ref.T, gvl_hat).flatten() # angles theta = piby2 - rot.arccosSafe(dotWbeam) wlen = 2 * dsp[iHKL] * np.sin(theta) eta = np.arccos(gvl_xy[0, :]) # find on spatial extent of detector # for corner # xTest = np.logical_and(dpts[0, :] > 0, dpts[0, :] < self.cdim) # for corner # yTest = np.logical_and(dpts[1, :] > 0, dpts[1, :] < self.rdim) xTest = np.logical_and(dpts[0, :] > -0.5 * self.cdim, dpts[0, :] < 0.5 * self.cdim) yTest = np.logical_and(dpts[1, :] > -0.5 * self.rdim, dpts[1, :] < 0.5 * self.rdim) onDetector = np.logical_and(xTest, yTest) if multipleEnergyRanges: validEnergy = np.zeros(len(wlen), dtype=bool) for i in range(len(lmin)): validEnergy = validEnergy | np.logical_and( wlen >= lmin[i], wlen <= lmax[i]) pass else: validEnergy = np.logical_and(wlen >= lmin, wlen <= lmax) pass keepers = np.logical_and(onDetector, validEnergy) dsp_this = 1. / mutil.columnNorm( np.dot(planeData.latVecOps['B'], dhkl[:, keepers])) tmp['detXY'].append(dpts[:2, keepers]) tmp['gnoXY'].append(gpts[:2, keepers]) tmp['angles'].append( np.vstack( [2 * theta[keepers] * r2d, eta[keepers] * r2d])) tmp['hkl'].append(dhkl[:, keepers]) tmp['dspacing'].append(dsp_this) tmp['energy'].append(processWavelength(wlen[keepers])) else: tmp['detXY'].append(np.empty((2, 0))) tmp['gnoXY'].append(np.empty((2, 0))) tmp['angles'].append(np.empty((2, 0))) tmp['hkl'].append(np.empty((3, 0))) tmp['dspacing'].append(np.empty(0)) tmp['energy'].append(np.empty(0)) pass pass retval.append(tmp) return retval
def oe_pfig(self): """Make an omega-eta polefigure""" # some constants deg2rad = numpy.pi / 180.0 radius = numpy.sqrt(2) offset = 2.5 * radius nocolor = 'none' pdir_cho = 'X' # to come from choice interactor # parent window and data exp = wx.GetApp().ws p = self.GetParent() ome_eta = p.data hkldata = ome_eta.getData(self.idata) # axes/figure p.figure.delaxes(p.axes) p.axes = p.figure.gca() p.axes.set_autoscale_on(True) p.axes.set_aspect('equal') p.axes.axis([-2.0, offset + 2.0, -1.5, 1.5]) # outlines for pole figure C1 = Circle((0, 0), radius) C2 = Circle((offset, 0), radius) outline_circles = [C1, C2] pc_outl = PatchCollection(outline_circles, facecolors=nocolor) p.axes.add_collection(pc_outl) # build the rectangles pf_rects = [] tTh = exp.activeMaterial.planeData.getTTh()[self.idata] etas = ome_eta.etaEdges netas = len(etas) - 1 deta = abs(etas[1] - etas[0]) omes = ome_eta.omeEdges nomes = len(omes) - 1 dome = abs(omes[1] - omes[0]) if pdir_cho == 'X': pdir = numpy.c_[1, 0, 0].T # X elif pdir_cho == 'Y': pdir = numpy.c_[0, 1, 0].T # X elif pdir_cho == 'Z': pdir = numpy.c_[0, 0, 1].T # X pass ii = 0 for i in range(nomes): for j in range(netas): qc = makeMSV(tTh, etas[j] + 0.5 * deta, omes[i] + 0.5 * dome) qll = makeMSV(tTh, etas[j], omes[i]) qlr = makeMSV(tTh, etas[j] + deta, omes[i]) qur = makeMSV(tTh, etas[j] + deta, omes[i] + dome) qul = makeMSV(tTh, etas[j], omes[i] + dome) pdot_p = numpy.dot(qll.T, pdir) >= 0 \ and numpy.dot(qlr.T, pdir) >= 0 \ and numpy.dot(qur.T, pdir) >= 0 \ and numpy.dot(qul.T, pdir) >= 0 pdot_m = numpy.dot(qll.T, pdir) < 0 \ and numpy.dot(qlr.T, pdir) < 0 \ and numpy.dot(qur.T, pdir) < 0 \ and numpy.dot(qul.T, pdir) < 0 if pdot_p: sgn = 1.0 ii += 1 elif pdot_m: sgn = -1.0 ii += 1 elif not pdot_p and not pdot_m: continue # the vertex chords qll = makeMSV(tTh, etas[j], omes[i]) - sgn * pdir qlr = makeMSV(tTh, etas[j] + deta, omes[i]) - sgn * pdir qur = makeMSV(tTh, etas[j] + deta, omes[i] + dome) - sgn * pdir qul = makeMSV(tTh, etas[j], omes[i] + dome) - sgn * pdir nll = columnNorm(qll) nlr = columnNorm(qlr) nur = columnNorm(qur) nul = columnNorm(qul) if pdir_cho == 'X': pqll = nll * unitVector(qll[[1, 2]].reshape(2, 1)) pqlr = nlr * unitVector(qlr[[1, 2]].reshape(2, 1)) pqur = nur * unitVector(qur[[1, 2]].reshape(2, 1)) pqul = nul * unitVector(qul[[1, 2]].reshape(2, 1)) elif pdir_cho == 'Y': pqll = nll * unitVector(qll[[0, 2]].reshape(2, 1)) pqlr = nlr * unitVector(qlr[[0, 2]].reshape(2, 1)) pqur = nur * unitVector(qur[[0, 2]].reshape(2, 1)) pqul = nul * unitVector(qul[[0, 2]].reshape(2, 1)) elif pdir_cho == 'Z': pqll = nll * unitVector(qll[[0, 1]].reshape(2, 1)) pqlr = nlr * unitVector(qlr[[0, 1]].reshape(2, 1)) pqur = nur * unitVector(qur[[0, 1]].reshape(2, 1)) pqul = nul * unitVector(qul[[0, 1]].reshape(2, 1)) xy = numpy.hstack([pqll, pqlr, pqur, pqul]).T if sgn == -1: xy[:, 0] = xy[:, 0] + offset pf_rects.append(Polygon(xy, aa=False)) pass pass cmap = matplotlib.cm.jet pf_coll = PatchCollection(pf_rects, cmap=cmap, edgecolors='None') pf_coll.set_array(numpy.array(hkldata.T.flatten())) p.axes.add_collection(pf_coll) p.canvas.draw() p.axes.axis('off') return
def angleAxisOfRotMat(R): """ """ if not isinstance(R, ndarray): raise RuntimeError, 'Input must be a 2 or 3-d ndarray' else: rdim = R.ndim if rdim == 2: nrot = 1 R = tile(R, (1, 1, 1)) elif rdim == 3: nrot = R.shape[0] else: raise RuntimeError, \ "R array must be (3, 3) or (n, 3, 3); input has dimension %d" \ % (rdim) # # Find angle of rotation. # ca = 0.5 * (R[:, 0, 0] + R[:, 1, 1] + R[:, 2, 2] - 1) angle = arccosSafe(ca) # # Three cases for the angle: # # * near zero -- matrix is effectively the identity # * near pi -- binary rotation; need to find axis # * neither -- general case; can use skew part # tol = 1.0e-4 anear0 = angle < tol angle[anear0] = 0 raxis = vstack([ R[:, 2, 1] - R[:, 1, 2], R[:, 0, 2] - R[:, 2, 0], R[:, 1, 0] - R[:, 0, 1] ]) raxis[:, anear0] = 1 special = angle > pi - tol nspec = special.sum() if nspec > 0: tmp = R[special, :, :] + tile(I3, (nspec, 1, 1)) tmpr = tmp.transpose(0, 2, 1).reshape(nspec * 3, 3).T tmpnrm = (tmpr * tmpr).sum(0).reshape(3, nspec) mx = tmpnrm.max(0) # remap indices maxInd = (tmpnrm == mx).nonzero() maxInd = c_[maxInd[0], maxInd[1]] tmprColInd = sort(maxInd[:, 0] + maxInd[:, 1] * nspec) saxis = tmpr[:, tmprColInd] raxis[:, special] = saxis return angle, unitVector(raxis)
import sys, os, time import numpy as np from hexrd import matrixutil as mutil from hexrd.xrd import rotations as rot from hexrd.xrd import transforms_CAPI as xfcapi n_quats = 1000000 rq = mutil.unitVector(np.random.randn(4, n_quats)) quats = np.array(rq.T, dtype=float, order='C') # phi = 2. * rot.arccosSafe(rq[0, :]) # n = np.tile(1. / np.sin(0.5*phi), (3, 1)) * rq[1:, :] start0 = time.clock() # time this rMats0 = rot.rotMatOfQuat(rq) elapsed0 = (time.clock() - start0) # rMats1 = np.zeros((n_quats, 3, 3)) # for i in range(n_quats): # rMats1[i, :, :] = xfcapi.makeRotMatOfQuat(quats[i, :]) start1 = time.time() # time this rMats1 = xfcapi.makeRotMatOfQuat(quats) elapsed1 = (time.time() - start1) print "Time for %d quats:\t%g v. %g (%f)" % ( n_quats, elapsed0 / float(n_quats), elapsed1 / float(n_quats), elapsed0 / elapsed1) print "Maximum discrepancy:\t%f" % (np.amax(abs(rMats0 - rMats1)))
def discreteFiber(c, s, B=I3, ndiv=120, invert=False, csym=None, ssym=None): """ """ import symmetry as S ztol = 1.e-8 # arg handling for c if hasattr(c, '__len__'): if hasattr(c, 'shape'): assert c.shape[0] == 3, \ 'scattering vector must be 3-d; yours is %d-d' \ % (c.shape[0]) if len(c.shape) == 1: c = c.reshape(3, 1) elif len(c.shape) > 2: raise RuntimeError, \ 'incorrect arg shape; must be 1-d or 2-d, yours is %d-d' \ % (len(c.shape)) else: # convert list input to array and transpose if len(c) == 3 and isscalar(c[0]): c = asarray(c).reshape(3, 1) else: c = asarray(c).T else: raise RuntimeError, 'input must be array-like' # arg handling for s if hasattr(s, '__len__'): if hasattr(s, 'shape'): assert s.shape[0] == 3, \ 'scattering vector must be 3-d; yours is %d-d' \ % (s.shape[0]) if len(s.shape) == 1: s = s.reshape(3, 1) elif len(s.shape) > 2: raise RuntimeError, \ 'incorrect arg shape; must be 1-d or 2-d, yours is %d-d' \ % (len(s.shape)) else: # convert list input to array and transpose if len(s) == 3 and isscalar(s[0]): s = asarray(s).reshape(3, 1) else: s = asarray(s).T else: raise RuntimeError, 'input must be array-like' nptc = c.shape[1] npts = s.shape[1] c = unitVector(dot(B, c)) # turn c hkls into unit vector in crys frame s = unitVector(s) # convert s to unit vector in samp frame retval = [] for i_c in range(nptc): dupl_c = tile(c[:, i_c], (npts, 1)).T ax = s + dupl_c anrm = columnNorm(ax).squeeze() # should be 1-d okay = anrm > ztol nokay = okay.sum() if nokay == npts: ax = ax / tile(anrm, (3, 1)) else: nspace = nullSpace(c[:, i_c].reshape(3, 1)) hperp = nspace[:, 0].reshape(3, 1) if nokay == 0: ax = tile(hperp, (1, npts)) else: ax[:, okay] = ax[:, okay] / tile(anrm[okay], (3, 1)) ax[:, not okay] = tile(hperp, (1, npts - nokay)) q0 = vstack([zeros(npts), ax]) # find rotations # note: the following line fixes bug with use of arange with float increments phi = arange(0, ndiv) * (2 * pi / float(ndiv)) qh = quatOfAngleAxis(phi, tile(c[:, i_c], (ndiv, 1)).T) # the fibers, arraged as (npts, 4, ndiv) qfib = dot(quatProductMatrix(qh, mult='right'), q0).transpose(2, 1, 0) if csym is not None: retval.append( S.toFundamentalRegion(qfib.squeeze(), crysSym=csym, sampSym=ssym)) else: retval.append(fixQuat(qfib).squeeze()) return retval
def simulateLauePattern(hkls, bMat, rmat_d, tvec_d, panel_dims, panel_buffer=5, minEnergy=8, maxEnergy=24, rmat_s=np.eye(3), grain_params=None, distortion=None, beamVec=None): if beamVec is None: beamVec = xfcapi.bVec_ref # parse energy ranges multipleEnergyRanges = False if hasattr(maxEnergy, '__len__'): assert len(maxEnergy) == len(minEnergy), \ 'energy cutoff ranges must have the same length' multipleEnergyRanges = True lmin = [] lmax = [] for i in range(len(maxEnergy)): lmin.append(processWavelength(maxEnergy[i])) lmax.append(processWavelength(minEnergy[i])) else: lmin = processWavelength(maxEnergy) lmax = processWavelength(minEnergy) # process crystal rmats and inverse stretches if grain_params is None: grain_params = np.atleast_2d( [0., 0., 0., 0., 0., 0., 1., 1., 1., 0., 0., 0.]) n_grains = len(grain_params) # dummy translation vector... make input tvec_s = np.zeros((3, 1)) # number of hkls nhkls_tot = hkls.shape[1] # unit G-vectors in crystal frame ghat_c = mutil.unitVector(np.dot(bMat, hkls)) # pre-allocate output arrays xy_det = np.nan * np.ones((n_grains, nhkls_tot, 2)) hkls_in = np.nan * np.ones((n_grains, 3, nhkls_tot)) angles = np.nan * np.ones((n_grains, nhkls_tot, 2)) dspacing = np.nan * np.ones((n_grains, nhkls_tot)) energy = np.nan * np.ones((n_grains, nhkls_tot)) """ LOOP OVER GRAINS """ for iG, gp in enumerate(grain_params): rmat_c = xfcapi.makeRotMatOfExpMap(gp[:3]) tvec_c = gp[3:6].reshape(3, 1) vInv_s = mutil.vecMVToSymm(gp[6:].reshape(6, 1)) # stretch them: V^(-1) * R * Gc ghat_s_str = mutil.unitVector(np.dot(vInv_s, np.dot(rmat_c, ghat_c))) ghat_c_str = np.dot(rmat_c.T, ghat_s_str) # project dpts = xfcapi.gvecToDetectorXY(ghat_c_str.T, rmat_d, rmat_s, rmat_c, tvec_d, tvec_s, tvec_c, beamVec=beamVec).T # check intersections with detector plane canIntersect = ~np.isnan(dpts[0, :]) npts_in = sum(canIntersect) if np.any(canIntersect): dpts = dpts[:, canIntersect].reshape(2, npts_in) dhkl = hkls[:, canIntersect].reshape(3, npts_in) # back to angles tth_eta, gvec_l = xfcapi.detectorXYToGvec(dpts.T, rmat_d, rmat_s, tvec_d, tvec_s, tvec_c, beamVec=beamVec) tth_eta = np.vstack(tth_eta).T # warp measured points if distortion is not None: if len(distortion) == 2: dpts = distortion[0](dpts, distortion[1], invert=True) # plane spacings and energies dsp = 1. / mutil.columnNorm(np.dot(bMat, dhkl)) wlen = 2 * dsp * np.sin(0.5 * tth_eta[:, 0]) # find on spatial extent of detector xTest = np.logical_and( dpts[0, :] >= -0.5 * panel_dims[1] + panel_buffer, dpts[0, :] <= 0.5 * panel_dims[1] - panel_buffer) yTest = np.logical_and( dpts[1, :] >= -0.5 * panel_dims[0] + panel_buffer, dpts[1, :] <= 0.5 * panel_dims[0] - panel_buffer) onDetector = np.logical_and(xTest, yTest) if multipleEnergyRanges: validEnergy = np.zeros(len(wlen), dtype=bool) for i in range(len(lmin)): validEnergy = validEnergy | \ np.logical_and(wlen >= lmin[i], wlen <= lmax[i]) pass else: validEnergy = np.logical_and(wlen >= lmin, wlen <= lmax) pass # index for valid reflections keepers = np.where(np.logical_and(onDetector, validEnergy))[0] # assign output arrays xy_det[iG][keepers, :] = dpts[:, keepers].T hkls_in[iG][:, keepers] = dhkl[:, keepers] angles[iG][keepers, :] = tth_eta[keepers, :] dspacing[iG, keepers] = dsp[keepers] energy[iG, keepers] = processWavelength(wlen[keepers]) pass pass return xy_det, hkls_in, angles, dspacing, energy
def run_cluster(compl, qfib, qsym, cfg, min_samples=None, compl_thresh=None, radius=None): """ """ algorithm = cfg.find_orientations.clustering.algorithm cl_radius = cfg.find_orientations.clustering.radius min_compl = cfg.find_orientations.clustering.completeness # check for override on completeness threshold if compl_thresh is not None: min_compl = compl_thresh # check for override on radius if radius is not None: cl_radius = radius start = time.clock() # time this num_above = sum(np.array(compl) > min_compl) if num_above == 0: # nothing to cluster qbar = cl = np.array([]) elif num_above == 1: # short circuit qbar = qfib[:, np.array(compl) > min_compl] cl = [1] else: # use compiled module for distance # just to be safe, must order qsym as C-contiguous qsym = np.array(qsym.T, order='C').T def quat_distance(x, y): return xfcapi.quat_distance(np.array(x, order='C'), np.array(y, order='C'), qsym) qfib_r = qfib[:, np.array(compl) > min_compl] num_ors = qfib_r.shape[1] if num_ors > 25000: if algorithm == 'sph-dbscan' or algorithm == 'fclusterdata': logger.info("defaulting to orthographic DBSCAN") algorithm = 'ort-dbscan' #raise RuntimeError, \ # "Requested clustering of %d orientations, which would be too slow!" %qfib_r.shape[1] logger.info( "Feeding %d orientations above %.1f%% to clustering", num_ors, 100*min_compl ) if algorithm == 'dbscan' and not have_sklearn: algorithm = 'fclusterdata' logger.warning( "sklearn >= 0.14 required for dbscan; using fclusterdata" ) if algorithm == 'dbscan' or algorithm == 'ort-dbscan' or algorithm == 'sph-dbscan': # munge min_samples according to options if min_samples is None or cfg.find_orientations.use_quaternion_grid is not None: min_samples = 1 if algorithm == 'sph-dbscan': # compute distance matrix pdist = pairwise_distances( qfib_r.T, metric=quat_distance, n_jobs=1 ) # run dbscan core_samples, labels = dbscan( pdist, eps=np.radians(cl_radius), min_samples=min_samples, metric='precomputed' ) else: if algorithm == 'ort-dbscan': pts = qfib_r[1:, :].T else: pts = qfib_r.T # run dbscan core_samples, labels = dbscan( pts, eps=0.5*np.radians(cl_radius), min_samples=min_samples, metric='minkowski', p=2, ) # extract cluster labels cl = np.array(labels, dtype=int) # convert to array noise_points = cl == -1 # index for marking noise cl += 1 # move index to 1-based instead of 0 cl[noise_points] = -1 # re-mark noise as -1 logger.info("dbscan found %d noise points", sum(noise_points)) elif algorithm == 'fclusterdata': cl = cluster.hierarchy.fclusterdata( qfib_r.T, np.radians(cl_radius), criterion='distance', metric=quat_distance ) else: raise RuntimeError( "Clustering algorithm %s not recognized" % algorithm ) # extract number of clusters if np.any(cl == -1): nblobs = len(np.unique(cl)) - 1 else: nblobs = len(np.unique(cl)) #import pdb; pdb.set_trace() """ PERFORM AVERAGING TO GET CLUSTER CENTROIDS """ qbar = np.zeros((4, nblobs)) if algorithm == 'sph-dbscan' or algorithm == 'fclusterdata': # here clusters can be split across fr for i in range(nblobs): npts = sum(cl == i + 1) qbar[:, i] = rot.quatAverageCluster( qfib_r[:, cl == i + 1].reshape(4, npts), qsym ).flatten() pass else: # here clusters are ompact by construction for i in range(nblobs): qbar[:, i] = np.average(np.atleast_2d(qfib_r[:, cl == i + 1]), axis=1) pass qbar = sym.toFundamentalRegion(mutil.unitVector(qbar), crysSym=qsym) logger.info("clustering took %f seconds", time.clock() - start) logger.info( "Found %d orientation clusters with >=%.1f%% completeness" " and %2f misorientation", qbar.size/4, 100.*min_compl, cl_radius ) return np.atleast_2d(qbar), cl
def objFuncSX(pFit, pFull, pFlag, dFunc, dFlag, xyo_det, hkls_idx, bMat, vInv, bVec, eVec, omePeriod, simOnly=False, return_value_flag=return_value_flag): """ """ npts = len(xyo_det) refineFlag = np.array(np.hstack([pFlag, dFlag]), dtype=bool) print refineFlag # pFull[refineFlag] = pFit/scl[refineFlag] pFull[refineFlag] = pFit if dFunc is not None: dParams = pFull[-len(dFlag):] xys = dFunc(xyo_det[:, :2], dParams) else: xys = xyo_det[:, :2] # detector quantities wavelength = pFull[0] rMat_d = xf.makeDetectorRotMat(pFull[1:4]) tVec_d = pFull[4:7].reshape(3, 1) # sample quantities chi = pFull[7] tVec_s = pFull[8:11].reshape(3, 1) # crystal quantities rMat_c = xf.makeRotMatOfExpMap(pFull[11:14]) tVec_c = pFull[14:17].reshape(3, 1) # stretch tensor comp matrix from MV notation in SAMPLE frame vMat_s = mutil.vecMVToSymm(vInv) # g-vectors: # 1. calculate full g-vector components in CRYSTAL frame from B # 2. rotate into SAMPLE frame and apply stretch # 3. rotate back into CRYSTAL frame and normalize to unit magnitude # IDEA: make a function for this sequence of operations with option for # choosing ouput frame (i.e. CRYSTAL vs SAMPLE vs LAB) gVec_c = np.dot(bMat, hkls_idx) gVec_s = np.dot(vMat_s, np.dot(rMat_c, gVec_c)) gHat_c = mutil.unitVector(np.dot(rMat_c.T, gVec_s)) match_omes, calc_omes = matchOmegas( xyo_det, hkls_idx, chi, rMat_c, bMat, wavelength, vInv=vInv, beamVec=bVec, etaVec=eVec, omePeriod=omePeriod) calc_xy = np.zeros((npts, 2)) for i in range(npts): rMat_s = xfcapi.makeOscillRotMat([chi, calc_omes[i]]) calc_xy[i, :] = xfcapi.gvecToDetectorXY(gHat_c[:, i], rMat_d, rMat_s, rMat_c, tVec_d, tVec_s, tVec_c, beamVec=bVec).flatten() pass if np.any(np.isnan(calc_xy)): raise RuntimeError( "infeasible pFull: may want to scale" + "back finite difference step size") # return values if simOnly: # return simulated values retval = np.hstack([calc_xy, calc_omes.reshape(npts, 1)]) else: # return residual vector # IDEA: try angles instead of xys? diff_vecs_xy = calc_xy - xys[:, :2] diff_ome = xf.angularDifference(calc_omes, xyo_det[:, 2]) retval = np.hstack([diff_vecs_xy, diff_ome.reshape(npts, 1) ]).flatten() if return_value_flag == 1: # return scalar sum of squared residuals retval = sum(abs(retval)) elif return_value_flag == 2: # return DOF-normalized chisq # TODO: check this calculation denom = npts - len(pFit) - 1. if denom != 0: nu_fac = 1. / denom else: nu_fac = 1. nu_fac = 1 / (npts - len(pFit) - 1.) retval = nu_fac * sum(retval**2) return retval
def oe_pfig(self): """Make an omega-eta polefigure""" # some constants deg2rad = numpy.pi /180.0 radius = numpy.sqrt(2) offset = 2.5*radius nocolor = 'none' pdir_cho = 'X' # to come from choice interactor # parent window and data exp = wx.GetApp().ws p = self.GetParent() ome_eta = p.data hkldata = ome_eta.getData(self.idata) # axes/figure p.figure.delaxes(p.axes) p.axes = p.figure.gca() p.axes.set_autoscale_on(True) p.axes.set_aspect('equal') p.axes.axis([-2.0, offset+2.0, -1.5, 1.5]) # outlines for pole figure C1 = Circle((0,0), radius) C2 = Circle((offset,0), radius) outline_circles = [C1, C2] pc_outl = PatchCollection(outline_circles, facecolors=nocolor) p.axes.add_collection(pc_outl) # build the rectangles pf_rects = [] tTh = exp.activeMaterial.planeData.getTTh()[self.idata] etas = ome_eta.etaEdges netas = len(etas) - 1 deta = abs(etas[1] - etas[0]) omes = ome_eta.omeEdges nomes = len(omes) - 1 dome = abs(omes[1] - omes[0]) if pdir_cho == 'X': pdir = numpy.c_[1, 0, 0].T # X elif pdir_cho == 'Y': pdir = numpy.c_[0, 1, 0].T # X elif pdir_cho == 'Z': pdir = numpy.c_[0, 0, 1].T # X pass ii = 0 for i in range(nomes): for j in range(netas): qc = makeMSV(tTh, etas[j] + 0.5*deta, omes[i] + 0.5*dome) qll = makeMSV(tTh, etas[j], omes[i]) qlr = makeMSV(tTh, etas[j] + deta, omes[i]) qur = makeMSV(tTh, etas[j] + deta, omes[i] + dome) qul = makeMSV(tTh, etas[j], omes[i] + dome) pdot_p = numpy.dot(qll.T, pdir) >= 0 \ and numpy.dot(qlr.T, pdir) >= 0 \ and numpy.dot(qur.T, pdir) >= 0 \ and numpy.dot(qul.T, pdir) >= 0 pdot_m = numpy.dot(qll.T, pdir) < 0 \ and numpy.dot(qlr.T, pdir) < 0 \ and numpy.dot(qur.T, pdir) < 0 \ and numpy.dot(qul.T, pdir) < 0 if pdot_p: sgn = 1.0 ii += 1 elif pdot_m: sgn = -1.0 ii += 1 elif not pdot_p and not pdot_m: continue # the vertex chords qll = makeMSV(tTh, etas[j], omes[i]) - sgn * pdir qlr = makeMSV(tTh, etas[j] + deta, omes[i]) - sgn * pdir qur = makeMSV(tTh, etas[j] + deta, omes[i] + dome) - sgn * pdir qul = makeMSV(tTh, etas[j], omes[i] + dome) - sgn * pdir nll = columnNorm(qll) nlr = columnNorm(qlr) nur = columnNorm(qur) nul = columnNorm(qul) if pdir_cho == 'X': pqll = nll*unitVector(qll[[1, 2]].reshape(2, 1)) pqlr = nlr*unitVector(qlr[[1, 2]].reshape(2, 1)) pqur = nur*unitVector(qur[[1, 2]].reshape(2, 1)) pqul = nul*unitVector(qul[[1, 2]].reshape(2, 1)) elif pdir_cho == 'Y': pqll = nll*unitVector(qll[[0, 2]].reshape(2, 1)) pqlr = nlr*unitVector(qlr[[0, 2]].reshape(2, 1)) pqur = nur*unitVector(qur[[0, 2]].reshape(2, 1)) pqul = nul*unitVector(qul[[0, 2]].reshape(2, 1)) elif pdir_cho == 'Z': pqll = nll*unitVector(qll[[0, 1]].reshape(2, 1)) pqlr = nlr*unitVector(qlr[[0, 1]].reshape(2, 1)) pqur = nur*unitVector(qur[[0, 1]].reshape(2, 1)) pqul = nul*unitVector(qul[[0, 1]].reshape(2, 1)) xy = numpy.hstack([pqll, pqlr, pqur, pqul]).T if sgn == -1: xy[:, 0] = xy[:, 0] + offset pf_rects.append(Polygon(xy, aa=False)) pass pass cmap=matplotlib.cm.jet pf_coll = PatchCollection(pf_rects, cmap=cmap, edgecolors='None') pf_coll.set_array(numpy.array(hkldata.T.flatten())) p.axes.add_collection(pf_coll) p.canvas.draw() p.axes.axis('off') return
def discreteFiber(c, s, B=I3, ndiv=120, invert=False, csym=None, ssym=None): """ """ import symmetry as S ztol = 1.e-8 # arg handling for c if hasattr(c, '__len__'): if hasattr(c, 'shape'): assert c.shape[0] == 3, \ 'scattering vector must be 3-d; yours is %d-d' \ % (c.shape[0]) if len(c.shape) == 1: c = c.reshape(3, 1) elif len(c.shape) > 2: raise RuntimeError, \ 'incorrect arg shape; must be 1-d or 2-d, yours is %d-d' \ % (len(c.shape)) else: # convert list input to array and transpose if len(c) == 3 and isscalar(c[0]): c = asarray(c).reshape(3, 1) else: c = asarray(c).T else: raise RuntimeError, 'input must be array-like' # arg handling for s if hasattr(s, '__len__'): if hasattr(s, 'shape'): assert s.shape[0] == 3, \ 'scattering vector must be 3-d; yours is %d-d' \ % (s.shape[0]) if len(s.shape) == 1: s = s.reshape(3, 1) elif len(s.shape) > 2: raise RuntimeError, \ 'incorrect arg shape; must be 1-d or 2-d, yours is %d-d' \ % (len(s.shape)) else: # convert list input to array and transpose if len(s) == 3 and isscalar(s[0]): s = asarray(s).reshape(3, 1) else: s = asarray(s).T else: raise RuntimeError, 'input must be array-like' nptc = c.shape[1] npts = s.shape[1] c = unitVector(dot(B, c)) # turn c hkls into unit vector in crys frame s = unitVector(s) # convert s to unit vector in samp frame retval = [] for i_c in range(nptc): dupl_c = tile(c[:, i_c], (npts, 1)).T ax = s + dupl_c anrm = columnNorm(ax).squeeze() # should be 1-d okay = anrm > ztol nokay = okay.sum() if nokay == npts: ax = ax / tile(anrm, (3, 1)) else: nspace = nullSpace(c[:, i_c].reshape(3, 1)) hperp = nspace[:, 0].reshape(3, 1) if nokay == 0: ax = tile(hperp, (1, npts)) else: ax[:, okay] = ax[:, okay] / tile(anrm[okay], (3, 1)) ax[:, not okay] = tile(hperp, (1, npts - nokay)) q0 = vstack( [ zeros(npts), ax ] ) # find rotations # note: the following line fixes bug with use of arange with float increments phi = arange(0, ndiv) * (2*pi/float(ndiv)) qh = quatOfAngleAxis(phi, tile(c[:, i_c], (ndiv, 1)).T) # the fibers, arraged as (npts, 4, ndiv) qfib = dot( quatProductMatrix(qh, mult='right'), q0 ).transpose(2, 1, 0) if csym is not None: retval.append(S.toFundamentalRegion(qfib.squeeze(), crysSym=csym, sampSym=ssym)) else: retval.append(fixQuat(qfib).squeeze()) return retval
def sxcal_obj_func(plist_fit, plist_full, param_flags, dfuncs, dparam_flags, ndparams, instr, xyo_det, hkls_idx, bmat, vinv_s, ome_period, bvec, evec, sim_only=False, return_value_flag=None): """ """ # stack flags and force bool repr refine_flags = np.array(np.hstack([param_flags, dparam_flags]), dtype=bool) # fill out full parameter list # !!! no scaling for now plist_full[refine_flags] = plist_fit # instrument quantities wavelength = plist_full[0] chi = plist_full[1] tvec_s = plist_full[2:5] # calibration crystal quantities rmat_c = xfcapi.makeRotMatOfExpMap(plist_full[5:8]) tvec_c = plist_full[8:11] # right now just stuck on the end and assumed # to all be the same length... FIX THIS dparams_all = plist_full[-len(dparam_flags):] xy_unwarped = {} meas_omes = {} calc_omes = {} calc_xy = {} ii = 11 # offset to start of panels... jj = 0 npts_tot = 0 for det_key, panel in instr.detectors.iteritems(): xy_unwarped[det_key] = xyo_det[det_key][:, :2] npts_tot += len(xyo_det[det_key]) dfunc = dfuncs[det_key] len_these_dps = ndparams[det_key] if dfunc is not None: # do unwarping dparams = dparams_all[jj:jj + len_these_dps] jj += len_these_dps xy_unwarped[det_key] = dfunc(xy_unwarped[det_key], dparams) pass meas_omes[det_key] = xyo_det[det_key][:, 2] # get these panel params for convenience gparams = plist_full[ii:ii + 6] rmat_d = xfcapi.makeDetectorRotMat(gparams[:3]) tvec_d = gparams[3:].reshape(3, 1) # transform G-vectors: # 1) convert inv. stretch tensor from MV notation in to 3x3 # 2) take reciprocal lattice vectors from CRYSTAL to SAMPLE frame # 3) apply stretch tensor # 4) normalize reciprocal lattice vectors in SAMPLE frame # 5) transform unit reciprocal lattice vetors back to CRYSAL frame gvec_c = np.dot(bmat, hkls_idx[det_key].T) vmat_s = mutil.vecMVToSymm(vinv_s) ghat_s = mutil.unitVector(np.dot(vmat_s, np.dot(rmat_c, gvec_c))) ghat_c = np.dot(rmat_c.T, ghat_s) match_omes, calc_omes_tmp = fitting.matchOmegas(xyo_det[det_key], hkls_idx[det_key].T, chi, rmat_c, bmat, wavelength, vInv=vinv_s, beamVec=bvec, etaVec=evec, omePeriod=ome_period) rmat_s_arr = xfcapi.makeOscillRotMatArray( chi, np.ascontiguousarray(calc_omes_tmp)) calc_xy_tmp = xfcapi.gvecToDetectorXYArray(ghat_c.T, rmat_d, rmat_s_arr, rmat_c, tvec_d, tvec_s, tvec_c) if np.any(np.isnan(calc_xy_tmp)): print("infeasible parameters: " + "may want to scale back finite difference step size") calc_omes[det_key] = calc_omes_tmp calc_xy[det_key] = calc_xy_tmp ii += 6 pass # return values if sim_only: retval = {} for det_key in calc_xy.keys(): # ??? calc_xy is always 2-d retval[det_key] = np.vstack( [calc_xy[det_key].T, calc_omes[det_key]]).T else: meas_xy_all = [] calc_xy_all = [] meas_omes_all = [] calc_omes_all = [] for det_key in xy_unwarped.keys(): meas_xy_all.append(xy_unwarped[det_key]) calc_xy_all.append(calc_xy[det_key]) meas_omes_all.append(meas_omes[det_key]) calc_omes_all.append(calc_omes[det_key]) pass meas_xy_all = np.vstack(meas_xy_all) calc_xy_all = np.vstack(calc_xy_all) meas_omes_all = np.hstack(meas_omes_all) calc_omes_all = np.hstack(calc_omes_all) diff_vecs_xy = calc_xy_all - meas_xy_all diff_ome = xfcapi.angularDifference(calc_omes_all, meas_omes_all) retval = np.hstack([diff_vecs_xy, diff_ome.reshape(npts_tot, 1)]).flatten() if return_value_flag == 1: retval = sum(abs(retval)) elif return_value_flag == 2: denom = npts_tot - len(plist_fit) - 1. if denom != 0: nu_fac = 1. / denom else: nu_fac = 1. nu_fac = 1 / (npts_tot - len(plist_fit) - 1.) retval = nu_fac * sum(retval**2) return retval
import sys, os, time import numpy as np from hexrd import matrixutil as mutil from hexrd.xrd import rotations as rot from hexrd.xrd import transforms_CAPI as xfcapi n_quats = 1e6 rq = mutil.unitVector(np.random.randn(4, n_quats)) quats = np.array(rq.T, dtype=float, order='C') """ NUMPY """ start0 = time.clock() # time this rMats0 = rot.rotMatOfQuat(rq) elapsed0 = (time.clock() - start0) """ CAPI """ start1 = time.time() # time this rMats1 = xfcapi.makeRotMatOfQuat(quats) # rMats1 = np.zeros((n_quats, 3, 3)) # for i in range(n_quats): # rMats1[i, :, :] = xfcapi.makeRotMatOfQuat(quats[i, :]) elapsed1 = (time.time() - start1) print "Time for %d quats:\t%g v. %g (%f)"%(n_quats, elapsed0/float(n_quats), elapsed1/float(n_quats), elapsed0/elapsed1) print "Maximum discrepancy:\t%f" % (np.amax(abs(rMats0 - rMats1))) """ NOTES If I call xfcapi.makeRotMatOfQuat(quats) I get:
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