def test_get_extent(self): dect = Detector(pixel1=1e-4, pixel2=1e-4) ai = AzimuthalIntegrator(detector=dect, dist=0.1) ai.setFit2D(directDist=1000, centerX=50.5, centerY=50.5) extent = _get_radial_extent(ai=ai, shape=(100, 100), unit="2th_rad") max_rad = 50 * np.sqrt(2) calc_extent = np.arctan(max_rad * 1e-4 / 1) np.testing.assert_almost_equal( extent[1], calc_extent, )
def get_azimuthal_integral2d( self, npt_rad, npt_azim=360, center=None, affine=None, mask=None, radial_range=None, azimuth_range=None, wavelength=None, unit="pyxem", inplace=False, method="splitpixel", map_kwargs={}, detector=None, detector_dist=None, correctSolidAngle=True, ai_kwargs={}, integrate2d_kwargs={}, ): """Creates a polar reprojection using pyFAI's azimuthal integrate 2d. This function is designed to be fairly flexible to account for 2 different cases: 1 - If the unit is "pyxem" then it lets pyXEM take the lead. If wavelength is none in that case it doesn't account for the Ewald sphere. 2 - If unit is any of the options from pyFAI then detector cannot be None and the handling of units is passed to pyxem and those units are used. Parameters --------------- npt_rad: int The number of radial points to calculate npt_azim: int The number of azimuthal points to calculate center: None or (x,y) or BaseSignal The center of the pattern in pixels to preform the integration around affine: 3x3 array or BaseSignal An affine transformation to apply during the transformation (creates a spline map that is used by pyFAI) mask: boolean array or BaseSignal A boolean mask to apply to the data to exclude some points. If mask is a baseSignal then it is itereated over as well. radial_range: None or (float, float) The radial range over which to perform the integration. Default is the full frame azim_range:None or (float, float) The azimuthal range over which to perform the integration. Default is from -pi to pi wavelength: None or float The wavelength of for the microscope. Has to be in the same units as the pyxem units if you want it to properly work. unit: str The unit can be "pyxem" to use the pyxem units and “q_nm^-1”, “q_A^-1”, “2th_deg”, “2th_rad”, “r_mm” if pyFAI is used for unit handling inplace: bool If the signal is overwritten or copied to a new signal detector: pyFai.detector.Detector The detector set up to be used by the integrator detector_dist: float distance sample - detector plan (orthogonal distance, not along the beam), in meter. map_kwargs: dict Any other keyword arguments for hyperspys map function integrate2d_kwargs:dict Any keyword arguements for PyFAI's integrate2d function Returns ---------- polar: PolarDiffraction2D A polar diffraction signal Examples ---------- Basic case using "2th_deg" units (no wavelength needed) >>> ds.unit = "2th_deg" >>> ds.get_azimuthal_integral2d(npt_rad=100) Basic case using a curved Ewald Sphere approximation and pyXEM units (wavelength needed) >>> ds.unit = "k_nm^-1" # setting units >>> ds.get_azimuthal_integral1d(npt_rad=100, wavelength=2.5e-12) Using pyFAI to define a detector case using a curved Ewald Sphere approximation and pyXEM units >>> from pyFAI.detectors import Detector >>> det = Detector(pixel1=1e-4, pixel2=1e-4) >>> ds.get_azimuthal_integral1d(npt_rad=100, detector_dist=.2, detector= det, wavelength=2.508e-12) """ pyxem_units = False sig_shape = self.axes_manager.signal_shape if unit == "pyxem": pyxem_units = True pixel_scale = [ self.axes_manager.signal_axes[0].scale, self.axes_manager.signal_axes[1].scale, ] if wavelength is None and self.unit not in ["2th_deg", "2th_rad"]: print('if the unit is not "2th_deg", "2th_rad"' "then a wavelength must be given. ") return setup = _get_setup(wavelength, self.unit, pixel_scale, radial_range) detector, detector_dist, radial_range, unit, scale_factor = setup use_iterate = any([ isinstance(mask, BaseSignal), isinstance(affine, BaseSignal), isinstance(center, BaseSignal), ]) if use_iterate: if radial_range is None: # need consistent range if isinstance(center, BaseSignal): ind = (0, ) * len(self.axes_manager.navigation_shape) cen = center.inav[ind].data else: cen = center ai = get_azimuthal_integrator( detector=detector, detector_distance=detector_dist, shape=sig_shape, center=cen, wavelength=wavelength, ) # take 1st center radial_range = _get_radial_extent(ai=ai, shape=sig_shape, unit=unit) radial_range[0] = 0 integration = self.map(azimuthal_integrate2d_slow, npt_azim=npt_azim, detector=detector, center=center, mask=mask, affine=affine, detector_distance=detector_dist, npt_rad=npt_rad, wavelength=wavelength, radial_range=radial_range, azimuth_range=azimuth_range, inplace=inplace, unit=unit, method=method, correctSolidAngle=correctSolidAngle, **integrate2d_kwargs, **map_kwargs) # Uses slow methodology else: # much simpler and no changing integrator without using map iterate ai = get_azimuthal_integrator(detector=detector, detector_distance=detector_dist, shape=sig_shape, center=center, affine=affine, mask=mask, wavelength=wavelength, **ai_kwargs) if radial_range is None: radial_range = _get_radial_extent(ai=ai, shape=sig_shape, unit=unit) radial_range[0] = 0 integration = self.map(azimuthal_integrate2d_fast, azimuthal_integrator=ai, npt_rad=npt_rad, npt_azim=npt_azim, azimuth_range=azimuth_range, radial_range=radial_range, method=method, inplace=inplace, unit=unit, correctSolidAngle=correctSolidAngle, **integrate2d_kwargs, **map_kwargs) # Dealing with axis changes if inplace: t_axis = self.axes_manager.signal_axes[0] k_axis = self.axes_manager.signal_axes[1] self.set_signal_type("polar_diffraction") else: transfer_navigation_axes(integration, self) integration.set_signal_type("polar_diffraction") t_axis = integration.axes_manager.signal_axes[0] k_axis = integration.axes_manager.signal_axes[1] t_axis.name = "Radians" if azimuth_range is None: t_axis.scale = np.pi * 2 / npt_azim t_axis.offset = -np.pi else: t_axis.scale = (azimuth_range[1] - azimuth_range[0]) / npt_rad t_axis.offset = azimuth_range[0] k_axis.name = "Radius" if pyxem_units: k_axis.scale = (radial_range[1] - radial_range[0]) / npt_rad / scale_factor k_axis.offset = radial_range[0] / scale_factor else: k_axis.scale = (radial_range[1] - radial_range[0]) / npt_rad k_axis.units = unit k_axis.offset = radial_range[0] return integration