def simulate_diffractions(grain_params, experiment, controller): """actual forward simulation of the diffraction""" # use a packed array for the image_stack array_dims = (experiment.nframes, experiment.ncols, ((experiment.nrows - 1) // 8) + 1) image_stack = np.zeros(array_dims, dtype=np.uint8) count = len(grain_params) subprocess = 'simulate diffractions' _project = xrdutil._project_on_detector_plane rD = experiment.rMat_d chi = experiment.chi tD = experiment.tVec_d tS = experiment.tVec_s distortion = experiment.distortion eta_range = [ (-np.pi, np.pi), ] ome_range = experiment.ome_range ome_period = (-np.pi, np.pi) full_hkls = xrdutil._fetch_hkls_from_planedata(experiment.plane_data) bMat = experiment.plane_data.latVecOps['B'] wlen = experiment.plane_data.wavelength controller.start(subprocess, count) for i in range(count): rC = xfcapi.makeRotMatOfExpMap(grain_params[i][0:3]) tC = np.ascontiguousarray(grain_params[i][3:6]) vInv_s = np.ascontiguousarray(grain_params[i][6:12]) ang_list = np.vstack( xfcapi.oscillAnglesOfHKLs(full_hkls[:, 1:], chi, rC, bMat, wlen, vInv=vInv_s)) # hkls not needed here all_angs, _ = xrdutil._filter_hkls_eta_ome(full_hkls, ang_list, eta_range, ome_range) all_angs[:, 2] = xfcapi.mapAngle(all_angs[:, 2], ome_period) proj_pts = _project(all_angs, rD, rC, chi, tD, tC, tS, distortion) det_xy = proj_pts[0] _write_pixels(det_xy, all_angs[:, 2], image_stack, experiment.base, experiment.inv_deltas, experiment.clip_vals) controller.update(i + 1) controller.finish(subprocess) return image_stack
def extract_detector_transformation(detector_params): """ Construct arrays from detector parameters. goes from 10 vector of detector parames OR instrument config dictionary (from YAML spec) to affine transformation arrays Parameters ---------- detector_params : TYPE DESCRIPTION. Returns ------- rMat_d : TYPE DESCRIPTION. tVec_d : TYPE DESCRIPTION. chi : TYPE DESCRIPTION. tVec_s : TYPE DESCRIPTION. """ # extract variables for convenience if isinstance(detector_params, dict): rMat_d = xfcapi.makeRotMatOfExpMap( np.array(detector_params['detector']['transform']['tilt'])) tVec_d = np.r_[detector_params['detector']['transform']['translation']] chi = detector_params['oscillation_stage']['chi'] tVec_s = np.r_[detector_params['oscillation_stage']['translation']] else: assert len(detector_params >= 10), \ "list of detector parameters must have length >= 10" rMat_d = xfcapi.makeRotMatOfExpMap(detector_params[:3]) tVec_d = np.ascontiguousarray(detector_params[3:6]) chi = detector_params[6] tVec_s = np.ascontiguousarray(detector_params[7:10]) return rMat_d, tVec_d, chi, tVec_s
def convert_tilt_convention(iconfig, old_convention, new_convention): """ convert the tilt angles from an old convention to a new convention This should work for both configs with statuses and without """ if new_convention == old_convention: return def _get_tilt_array(data): # This works for both a config with statuses, and without if isinstance(data, dict): return data.get('value') return data def _set_tilt_array(data, val): # This works for both a config with statuses, and without if isinstance(data, dict): data['value'] = val else: data.clear() data.extend(val) old_axes, old_extrinsic = old_convention new_axes, new_extrinsic = new_convention det_keys = iconfig['detectors'].keys() if old_axes is not None and old_extrinsic is not None: # First, convert these to the matrix invariants rme = RotMatEuler(np.zeros(3), old_axes, old_extrinsic) for key in det_keys: tilts = iconfig['detectors'][key]['transform']['tilt'] rme.angles = np.array(_get_tilt_array(tilts)) phi, n = angleAxisOfRotMat(rme.rmat) _set_tilt_array(tilts, (phi * n.flatten()).tolist()) if new_axes is None or new_extrinsic is None: # We are done return # Update to the new mapping rme = RotMatEuler(np.zeros(3), new_axes, new_extrinsic) for key in det_keys: tilts = iconfig['detectors'][key]['transform']['tilt'] tilt = np.array(_get_tilt_array(tilts)) rme.rmat = makeRotMatOfExpMap(tilt) # Use np.ndarray.tolist() to convert back to native python types _set_tilt_array(tilts, np.array(rme.angles).tolist())
def convert_angle_convention(angles, old_convention, new_convention): if old_convention is not None: # First, convert these to the matrix invariants rme = RotMatEuler(np.zeros(3), **old_convention) rme.angles = np.array(angles) phi, n = angleAxisOfRotMat(rme.rmat) angles = (phi * n.flatten()).tolist() if new_convention is None: # We are done return angles # Update to the new mapping rme = RotMatEuler(np.zeros(3), **new_convention) rme.rmat = makeRotMatOfExpMap(np.array(angles)) return np.array(rme.angles).tolist()
def objFuncFitGrain(gFit, gFull, gFlag, instrument, reflections_dict, bMat, wavelength, omePeriod, simOnly=False, return_value_flag=return_value_flag): """ Calculate residual between measured and simulated ff-HEDM G-vectors. 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) Parameters ---------- gFit : TYPE DESCRIPTION. gFull : TYPE DESCRIPTION. gFlag : TYPE DESCRIPTION. instrument : TYPE DESCRIPTION. reflections_dict : TYPE DESCRIPTION. bMat : TYPE DESCRIPTION. wavelength : TYPE DESCRIPTION. omePeriod : TYPE DESCRIPTION. simOnly : TYPE, optional DESCRIPTION. The default is False. return_value_flag : TYPE, optional DESCRIPTION. The default is return_value_flag. Raises ------ RuntimeError DESCRIPTION. Returns ------- retval : TYPE DESCRIPTION. """ 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.items(): 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]]) # distortion handling if panel.distortion is not None: meas_omes = meas_xyo[:, 2] xy_unwarped = panel.distortion.apply(meas_xyo[:, :2]) 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 = xfcapi.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 make_reflection_patches(instr_cfg, tth_eta, ang_pixel_size, omega=None, tth_tol=0.2, eta_tol=1.0, rmat_c=np.eye(3), tvec_c=np.zeros((3, 1)), npdiv=1, quiet=False, compute_areas_func=gutil.compute_areas): """ Make angular patches on a detector. panel_dims are [(xmin, ymin), (xmax, ymax)] in mm pixel_pitch is [row_size, column_size] in mm FIXME: DISTORTION HANDING IS STILL A KLUDGE!!! patches are: delta tth d ------------- ... ------------- e | x | x | x | ... | x | x | x | l ------------- ... ------------- t . a . . e ------------- ... ------------- t | x | x | x | ... | x | x | x | a ------------- ... ------------- outputs are: (tth_vtx, eta_vtx), (x_vtx, y_vtx), connectivity, subpixel_areas, (x_center, y_center), (i_row, j_col) """ npts = len(tth_eta) # detector quantities rmat_d = xfcapi.makeRotMatOfExpMap( np.r_[instr_cfg['detector']['transform']['tilt']]) tvec_d = np.r_[instr_cfg['detector']['transform']['translation']] pixel_size = instr_cfg['detector']['pixels']['size'] frame_nrows = instr_cfg['detector']['pixels']['rows'] frame_ncols = instr_cfg['detector']['pixels']['columns'] panel_dims = ( -0.5 * np.r_[frame_ncols * pixel_size[1], frame_nrows * pixel_size[0]], 0.5 * np.r_[frame_ncols * pixel_size[1], frame_nrows * pixel_size[0]]) row_edges = np.arange(frame_nrows + 1)[::-1]*pixel_size[1] \ + panel_dims[0][1] col_edges = np.arange(frame_ncols + 1)*pixel_size[0] \ + panel_dims[0][0] # handle distortion distortion = None if distortion_key in instr_cfg['detector']: distortion_cfg = instr_cfg['detector'][distortion_key] if distortion_cfg is not None: try: func_name = distortion_cfg['function_name'] dparams = distortion_cfg['parameters'] distortion = distortion_pkg.get_mapping(func_name, dparams) except (KeyError): raise RuntimeError("problem with distortion specification") # sample frame chi = instr_cfg['oscillation_stage']['chi'] tvec_s = np.r_[instr_cfg['oscillation_stage']['translation']] # beam vector bvec = np.r_[instr_cfg['beam']['vector']] # data to loop # ??? WOULD IT BE CHEAPER TO CARRY ZEROS OR USE CONDITIONAL? if omega is None: full_angs = np.hstack([tth_eta, np.zeros((npts, 1))]) else: full_angs = np.hstack([tth_eta, omega.reshape(npts, 1)]) patches = [] for angs, pix in zip(full_angs, ang_pixel_size): # calculate bin edges for patch based on local angular pixel size # tth ntths, tth_edges = gutil.make_tolerance_grid(bin_width=np.degrees( pix[0]), window_width=tth_tol, num_subdivisions=npdiv) # eta netas, eta_edges = gutil.make_tolerance_grid(bin_width=np.degrees( pix[1]), window_width=eta_tol, num_subdivisions=npdiv) # FOR ANGULAR MESH conn = gutil.cellConnectivity(netas, ntths, origin='ll') # meshgrid args are (cols, rows), a.k.a (fast, slow) m_tth, m_eta = np.meshgrid(tth_edges, eta_edges) npts_patch = m_tth.size # calculate the patch XY coords from the (tth, eta) angles # !!! will CHEAT and ignore the small perturbation the different # omega angle values causes and simply use the central value gVec_angs_vtx = np.tile(angs, (npts_patch, 1)) \ + np.radians( np.vstack([m_tth.flatten(), m_eta.flatten(), np.zeros(npts_patch) ]).T ) xy_eval_vtx, rmats_s, on_plane = _project_on_detector_plane( gVec_angs_vtx, rmat_d, rmat_c, chi, tvec_d, tvec_c, tvec_s, distortion, beamVec=bvec) areas = compute_areas_func(xy_eval_vtx, conn) # EVALUATION POINTS # !!! for lack of a better option will use centroids tth_eta_cen = gutil.cellCentroids(np.atleast_2d(gVec_angs_vtx[:, :2]), conn) gVec_angs = np.hstack( [tth_eta_cen, np.tile(angs[2], (len(tth_eta_cen), 1))]) xy_eval, rmats_s, on_plane = _project_on_detector_plane(gVec_angs, rmat_d, rmat_c, chi, tvec_d, tvec_c, tvec_s, distortion, beamVec=bvec) row_indices = gutil.cellIndices(row_edges, xy_eval[:, 1]) col_indices = gutil.cellIndices(col_edges, xy_eval[:, 0]) # append patch data to list patches.append( ((gVec_angs_vtx[:, 0].reshape(m_tth.shape), gVec_angs_vtx[:, 1].reshape(m_tth.shape)), (xy_eval_vtx[:, 0].reshape(m_tth.shape), xy_eval_vtx[:, 1].reshape(m_tth.shape)), conn, areas.reshape(netas, ntths), (xy_eval[:, 0].reshape(netas, ntths), xy_eval[:, 1].reshape(netas, ntths)), (row_indices.reshape(netas, ntths), col_indices.reshape(netas, ntths)))) pass # close loop over angles return patches
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: dpts = distortion.apply_inverse(dpts) # 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 simulateGVecs(pd, detector_params, grain_params, ome_range=[ (-np.pi, np.pi), ], ome_period=(-np.pi, np.pi), eta_range=[ (-np.pi, np.pi), ], panel_dims=[(-204.8, -204.8), (204.8, 204.8)], pixel_pitch=(0.2, 0.2), distortion=None): """ returns valid_ids, valid_hkl, valid_ang, valid_xy, ang_ps panel_dims are [(xmin, ymin), (xmax, ymax)] in mm pixel_pitch is [row_size, column_size] in mm simulate the monochormatic scattering for a specified - space group - wavelength - orientation - strain - position - detector parameters - oscillation axis tilt (chi) subject to - omega (oscillation) ranges (list of (min, max) tuples) - eta (azimuth) ranges pd................a hexrd.crystallography.PlaneData instance detector_params...a (10,) ndarray containing the tilt angles (3), translation (3), chi (1), and sample frame translation (3) parameters grain_params......a (12,) ndarray containing the exponential map (3), translation (3), and inverse stretch tensor compnents in Mandel-Voigt notation (6). * currently only one panel is supported, but this will likely change soon """ bMat = pd.latVecOps['B'] wlen = pd.wavelength full_hkls = _fetch_hkls_from_planedata(pd) # extract variables for convenience rMat_d = xfcapi.makeDetectorRotMat(detector_params[:3]) tVec_d = np.ascontiguousarray(detector_params[3:6]) chi = detector_params[6] tVec_s = np.ascontiguousarray(detector_params[7:10]) rMat_c = xfcapi.makeRotMatOfExpMap(grain_params[:3]) tVec_c = np.ascontiguousarray(grain_params[3:6]) vInv_s = np.ascontiguousarray(grain_params[6:12]) # first find valid G-vectors angList = np.vstack( xfcapi.oscillAnglesOfHKLs(full_hkls[:, 1:], chi, rMat_c, bMat, wlen, vInv=vInv_s)) allAngs, allHKLs = _filter_hkls_eta_ome(full_hkls, angList, eta_range, ome_range) if len(allAngs) == 0: valid_ids = [] valid_hkl = [] valid_ang = [] valid_xy = [] ang_ps = [] else: # ??? preallocate for speed? det_xy, rMat_s, on_plane = _project_on_detector_plane( allAngs, rMat_d, rMat_c, chi, tVec_d, tVec_c, tVec_s, distortion) # on_panel_x = np.logical_and(det_xy[:, 0] >= panel_dims[0][0], det_xy[:, 0] <= panel_dims[1][0]) on_panel_y = np.logical_and(det_xy[:, 1] >= panel_dims[0][1], det_xy[:, 1] <= panel_dims[1][1]) on_panel = np.logical_and(on_panel_x, on_panel_y) # op_idx = np.where(on_panel)[0] # valid_ang = allAngs[op_idx, :] valid_ang[:, 2] = xf.mapAngle(valid_ang[:, 2], ome_period) valid_ids = allHKLs[op_idx, 0] valid_hkl = allHKLs[op_idx, 1:] valid_xy = det_xy[op_idx, :] ang_ps = angularPixelSize(valid_xy, pixel_pitch, rMat_d, rMat_s, tVec_d, tVec_s, tVec_c, distortion=distortion) return valid_ids, valid_hkl, valid_ang, valid_xy, ang_ps
def simulateOmeEtaMaps(omeEdges, etaEdges, planeData, expMaps, chi=0., etaTol=None, omeTol=None, etaRanges=None, omeRanges=None, bVec=xf.bVec_ref, eVec=xf.eta_ref, vInv=xf.vInv_ref): """ Simulate spherical maps. Parameters ---------- omeEdges : TYPE DESCRIPTION. etaEdges : TYPE DESCRIPTION. planeData : TYPE DESCRIPTION. expMaps : (3, n) ndarray DESCRIPTION. chi : TYPE, optional DESCRIPTION. The default is 0.. etaTol : TYPE, optional DESCRIPTION. The default is None. omeTol : TYPE, optional DESCRIPTION. The default is None. etaRanges : TYPE, optional DESCRIPTION. The default is None. omeRanges : TYPE, optional DESCRIPTION. The default is None. bVec : TYPE, optional DESCRIPTION. The default is xf.bVec_ref. eVec : TYPE, optional DESCRIPTION. The default is xf.eta_ref. vInv : TYPE, optional DESCRIPTION. The default is xf.vInv_ref. Returns ------- eta_ome : TYPE DESCRIPTION. Notes ----- all angular info is entered in degrees ??? might want to creat module-level angluar unit flag ??? might want to allow resvers delta omega """ # convert to radians etaEdges = np.radians(np.sort(etaEdges)) omeEdges = np.radians(np.sort(omeEdges)) omeIndices = list(range(len(omeEdges))) etaIndices = list(range(len(etaEdges))) i_max = omeIndices[-1] j_max = etaIndices[-1] etaMin = etaEdges[0] etaMax = etaEdges[-1] omeMin = omeEdges[0] omeMax = omeEdges[-1] if omeRanges is None: omeRanges = [ [omeMin, omeMax], ] if etaRanges is None: etaRanges = [ [etaMin, etaMax], ] # signed deltas IN RADIANS del_ome = omeEdges[1] - omeEdges[0] del_eta = etaEdges[1] - etaEdges[0] delOmeSign = np.sign(del_eta) # tolerances are in degrees (easier) if omeTol is None: omeTol = abs(del_ome) else: omeTol = np.radians(omeTol) if etaTol is None: etaTol = abs(del_eta) else: etaTol = np.radians(etaTol) # pixel dialtions dpix_ome = round(omeTol / abs(del_ome)) dpix_eta = round(etaTol / abs(del_eta)) i_dil, j_dil = np.meshgrid(np.arange(-dpix_ome, dpix_ome + 1), np.arange(-dpix_eta, dpix_eta + 1)) # get symmetrically expanded hkls from planeData sym_hkls = planeData.getSymHKLs() nhkls = len(sym_hkls) # make things C-contiguous for use in xfcapi functions expMaps = np.array(expMaps.T, order='C') nOrs = len(expMaps) bMat = np.array(planeData.latVecOps['B'], order='C') wlen = planeData.wavelength bVec = np.array(bVec.flatten(), order='C') eVec = np.array(eVec.flatten(), order='C') vInv = np.array(vInv.flatten(), order='C') eta_ome = np.zeros((nhkls, max(omeIndices), max(etaIndices)), order='C') for iHKL in range(nhkls): these_hkls = np.ascontiguousarray(sym_hkls[iHKL].T, dtype=float) for iOr in range(nOrs): rMat_c = xfcapi.makeRotMatOfExpMap(expMaps[iOr, :]) angList = np.vstack( xfcapi.oscillAnglesOfHKLs(these_hkls, chi, rMat_c, bMat, wlen, beamVec=bVec, etaVec=eVec, vInv=vInv)) if not np.all(np.isnan(angList)): # angList[:, 1] = xf.mapAngle( angList[:, 1], [etaEdges[0], etaEdges[0] + 2 * np.pi]) angList[:, 2] = xf.mapAngle( angList[:, 2], [omeEdges[0], omeEdges[0] + 2 * np.pi]) # # do eta ranges angMask_eta = np.zeros(len(angList), dtype=bool) for etas in etaRanges: angMask_eta = np.logical_or( angMask_eta, xf.validateAngleRanges(angList[:, 1], etas[0], etas[1])) # do omega ranges ccw = True angMask_ome = np.zeros(len(angList), dtype=bool) for omes in omeRanges: if omes[1] - omes[0] < 0: ccw = False angMask_ome = np.logical_or( angMask_ome, xf.validateAngleRanges(angList[:, 2], omes[0], omes[1], ccw=ccw)) # mask angles list, hkls angMask = np.logical_and(angMask_eta, angMask_ome) culledTTh = angList[angMask, 0] culledEta = angList[angMask, 1] culledOme = angList[angMask, 2] for iTTh in range(len(culledTTh)): culledEtaIdx = np.where(etaEdges - culledEta[iTTh] > 0)[0] if len(culledEtaIdx) > 0: culledEtaIdx = culledEtaIdx[0] - 1 if culledEtaIdx < 0: culledEtaIdx = None else: culledEtaIdx = None culledOmeIdx = np.where(omeEdges - culledOme[iTTh] > 0)[0] if len(culledOmeIdx) > 0: if delOmeSign > 0: culledOmeIdx = culledOmeIdx[0] - 1 else: culledOmeIdx = culledOmeIdx[-1] if culledOmeIdx < 0: culledOmeIdx = None else: culledOmeIdx = None if culledEtaIdx is not None and culledOmeIdx is not None: if dpix_ome > 0 or dpix_eta > 0: i_sup = omeIndices[culledOmeIdx] + \ np.array([i_dil.flatten()], dtype=int) j_sup = etaIndices[culledEtaIdx] + \ np.array([j_dil.flatten()], dtype=int) # catch shit that falls off detector... # maybe make this fancy enough to wrap at 2pi? idx_mask = np.logical_and( np.logical_and(i_sup >= 0, i_sup < i_max), np.logical_and(j_sup >= 0, j_sup < j_max)) eta_ome[iHKL, i_sup[idx_mask], j_sup[idx_mask]] = 1. else: eta_ome[iHKL, omeIndices[culledOmeIdx], etaIndices[culledEtaIdx]] = 1. pass # close conditional on pixel dilation pass # close conditional on ranges pass # close for loop on valid reflections pass # close conditional for valid angles return eta_ome