def detector_borders(self, det): panel = self.detectors[det] row_vec, col_vec = panel.row_pixel_vec, panel.col_pixel_vec x_start, x_stop = col_vec[0], col_vec[-1] y_start, y_stop = row_vec[0], row_vec[-1] # Create the borders in Cartesian borders = [[[x, y_start] for x in col_vec], [[x, y_stop] for x in col_vec], [[x_start, y] for y in row_vec], [[x_stop, y] for y in row_vec]] # Convert each border to angles for i, border in enumerate(borders): angles, _ = detectorXYToGvec(border, panel.rmat, ct.identity_3x3, panel.tvec, ct.zeros_3, ct.zeros_3, beamVec=panel.bvec, etaVec=panel.evec) # Convert to degrees, and keep them as lists for # easier modification later borders[i] = np.degrees(angles).tolist() # Here, we are going to remove points that are out-of-bounds, # and we are going to insert None in between points that are far # apart (in the y component), so that they are not connected in the # plot. This happens for detectors that are wrapped in the image. x_range = np.degrees((self.tth_min, self.tth_max)) y_range = np.degrees((self.eta_min, self.eta_max)) # "Far apart" is currently defined as half of the y range max_y_distance = abs(y_range[1] - y_range[0]) / 2.0 for j in range(4): border_x, border_y = borders[j][0], borders[j][1] i = 0 # These should be the same length, but just in case... while i < len(border_x) and i < len(border_y): x, y = border_x[i], border_y[i] if (not x_range[0] <= x <= x_range[1] or not y_range[0] <= y <= y_range[1]): # The point is out of bounds, remove it del border_x[i], border_y[i] continue if i != 0 and abs(y - border_y[i - 1]) > max_y_distance: # Points are too far apart. Insert a None border_x.insert(i, None) border_y.insert(i, None) i += 1 i += 1 return borders
def angularPixelSize(xy_det, xy_pixelPitch, rMat_d, rMat_s, tVec_d, tVec_s, tVec_c, distortion=None, beamVec=None, etaVec=None): """ Calculate angular pixel sizes on a detector. * choices to beam vector and eta vector specs have been supressed * assumes xy_det in UNWARPED configuration """ xy_det = np.atleast_2d(xy_det) if distortion is not None: # !!! check this logic xy_det = distortion.apply(xy_det) if beamVec is None: beamVec = xfcapi.bVec_ref if etaVec is None: etaVec = xfcapi.eta_ref xp = np.r_[-0.5, 0.5, 0.5, -0.5] * xy_pixelPitch[0] yp = np.r_[-0.5, -0.5, 0.5, 0.5] * xy_pixelPitch[1] diffs = np.array([[3, 3, 2, 1], [2, 0, 1, 0]]) ang_pix = np.zeros((len(xy_det), 2)) for ipt, xy in enumerate(xy_det): xc = xp + xy[0] yc = yp + xy[1] tth_eta, gHat_l = xfcapi.detectorXYToGvec(np.vstack([xc, yc]).T, rMat_d, rMat_s, tVec_d, tVec_s, tVec_c, beamVec=beamVec, etaVec=etaVec) delta_tth = np.zeros(4) delta_eta = np.zeros(4) for j in range(4): delta_tth[j] = abs(tth_eta[0][diffs[0, j]] - tth_eta[0][diffs[1, j]]) delta_eta[j] = xf.angularDifference(tth_eta[1][diffs[0, j]], tth_eta[1][diffs[1, j]]) ang_pix[ipt, 0] = np.amax(delta_tth) ang_pix[ipt, 1] = np.amax(delta_eta) return ang_pix
def angularPixelSize(xy_det, xy_pixelPitch, rMat_d, rMat_s, tVec_d, tVec_s, tVec_c, distortion=None, beamVec=None, etaVec=None): """ Calculate angular pixel sizes on a detector. * choices to beam vector and eta vector specs have been supressed * assumes xy_det in UNWARPED configuration """ xy_det = np.atleast_2d(xy_det) if distortion is not None: # !!! check this logic xy_det = distortion.apply(xy_det) if beamVec is None: beamVec = xfcapi.bVec_ref if etaVec is None: etaVec = xfcapi.eta_ref xy_expanded = np.empty((len(xy_det) * 4, 2), dtype=xy_det.dtype) xy_expanded = _expand_pixels(xy_det, xy_pixelPitch[0], xy_pixelPitch[1], xy_expanded) gvec_space, _ = xfcapi.detectorXYToGvec(xy_expanded, rMat_d, rMat_s, tVec_d, tVec_s, tVec_c, beamVec=beamVec, etaVec=etaVec) result = np.empty_like(xy_det) return _compute_max(gvec_space[0], gvec_space[1], result)
def overlay(self, display_mode=ViewType.raw): sim_data = self.instrument.simulate_laue_pattern( self.plane_data, minEnergy=self.min_energy, maxEnergy=self.max_energy, rmat_s=self.sample_rmat, grain_params=[ self.crystal_params, ]) point_groups = {} keys = ['spots', 'ranges', 'hkls'] for det_key, psim in sim_data.items(): point_groups[det_key] = {key: [] for key in keys} # grab panel and split out simulation results # !!! note that the sim results are lists over number of grains # and here we explicitly have one. panel = self.instrument.detectors[det_key] xy_det, hkls_in, angles, dspacing, energy = psim # find valid points idx = ~np.isnan(energy[0]) # there is only one grain here # filter (tth, eta) results xy_data = xy_det[0][idx, :] angles = angles[0][idx, :] # these are in radians point_groups[det_key]['hkls'] = hkls_in[0][:, idx].T angles[:, 1] = xfcapi.mapAngle(angles[:, 1], np.radians(self.eta_period), units='radians') # !!! apply offset corrections to angles # convert to angles in LAB ref angles_corr, _ = xfcapi.detectorXYToGvec( xy_data, panel.rmat, self.sample_rmat, panel.tvec, self.instrument.tvec, constants.zeros_3, beamVec=self.instrument.beam_vector, etaVec=self.instrument.eta_vector) # FIXME modify output to be array angles_corr = np.vstack(angles_corr).T angles_corr[:, 1] = xfcapi.mapAngle(angles_corr[:, 1], np.radians(self.eta_period), units='radians') if display_mode == ViewType.polar: range_corners = self.range_corners(angles_corr) # Save the Laue spots as a list instead of a numpy array, # so that we can predictably get the id() of spots inside. # Numpy arrays do fancy optimizations that break this. spots = np.degrees(angles_corr).tolist() point_groups[det_key]['spots'] = spots point_groups[det_key]['ranges'] = np.degrees(range_corners) elif display_mode in [ViewType.raw, ViewType.cartesian]: # !!! verify this range_corners = self.range_corners(angles) panel = self.instrument.detectors[det_key] data = xy_data if display_mode == ViewType.raw: # Convert to pixel coordinates data = panel.cartToPixel(data) # Swap x and y, they are flipped data[:, [0, 1]] = data[:, [1, 0]] point_groups[det_key]['spots'] = data point_groups[det_key]['ranges'] = self.range_data( range_corners, display_mode, panel) return point_groups
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 _convert_angles(tth_eta, detector, rmat_s, tvec_s, tvec_c, beam_vector=constants.beam_vec, eta_vector=constants.eta_vec): """ Coverts frame-local angles to effective angles in the LAB reference frame. Operates on a detector instance in lieu of instrument. Parameters ---------- tth_eta : TYPE DESCRIPTION. detector : TYPE DESCRIPTION. rmat_s : TYPE DESCRIPTION. tvec_c : TYPE DESCRIPTION. beam_vector : TYPE, optional DESCRIPTION. The default is constants.beam_vec. eta_vector : TYPE, optional DESCRIPTION. The default is constants.eta_vec. Returns ------- tth_eta_ref : TYPE DESCRIPTION. Notes ----- FIXME: This API won't work for rotation series data """ tth_eta = np.atleast_2d(tth_eta) chi = np.arctan2(rmat_s[2, 1], rmat_s[1, 1]) ome = np.arctan2(rmat_s[0, 2], rmat_s[0, 0]) # !!! reform rmat_s to be consistent with def in geometric model rmat_s = xfcapi.makeOscillRotMat(np.r_[chi, ome]) rmat_c = constants.identity_3x3 # tvec_s = constants.zeros_3 tvec_c_ref = constants.zeros_3 # FIXME: doesn't work for rotation series with different ome yet. full_angs = np.hstack([tth_eta, ome * np.ones((len(tth_eta), 1))]) # convert to gvectors using trivial crystal frame gvec_s = xfcapi.anglesToGVec(full_angs, bHat_l=beam_vector, eHat_l=eta_vector, chi=chi) # convert to detector points det_xys = xfcapi.gvecToDetectorXY(gvec_s, detector.rmat, rmat_s, rmat_c, detector.tvec, tvec_s, tvec_c, beamVec=beam_vector) # convert to angles in LAB ref tth_eta_ref, _ = xfcapi.detectorXYToGvec(det_xys, detector.rmat, rmat_s, detector.tvec, tvec_s, tvec_c_ref, beamVec=beam_vector, etaVec=eta_vector) return np.vstack(tth_eta_ref).T