def test_get_diffraction_image(n, vol_shape, grid_shape, precession, wavelength): coords, species = create_atoms(n, vol_shape) x = [np.linspace(0, vol_shape[i], grid_shape[i]) for i in range(3)] if precession: precession = (1e-3, 20) else: precession = (0, 1) params = { "dtype": ("f4", "c8"), "ZERO": 1e-10, "GPU": False, "pointwise": True } val1 = get_diffraction_image(coords, species, probe, x, wavelength, precession, **params) val2 = get_diffraction_image(coords, species, probe, x, 0, (0, 1), **params) if precession[0] > 0: val1 = val1[2:-2, 2:-2] val2 = val2[2:-2, 2:-2] assert val1.shape == val2.shape if precession[0] == 0: assert val1.shape == grid_shape[:2] np.testing.assert_allclose(val1, val2, 1e-2, 1e-4)
def calculate_ed_data( self, structure, probe, slice_thickness, probe_centre=None, z_range=200, precessed=False, dtype="float64", ZERO=1e-14, mode="kinematic", **kwargs, ): """ Calculates single electron diffraction image for particular atomic structure and probe. Parameters ---------- structure : Structure The structure for upon which to perform the calculation probe : instance of probeFunction Function representing 3D shape of beam slice_thickness : float Discretisation thickness in the z-axis probe_centre : ndarray (or iterable), shape [3] or [2] Translation vector for the probe. Either of the same dimension of the space or the dimension of the detector. default=None focusses the probe at [0,0,0] zrange : float z-thickness to discretise. Only required if sample is not thick enough to fully resolve the Ewald-sphere. Default value is 200. precessed : bool, float, or (float, int) Dictates whether beam precession is simulated. If False or the float is 0 then no precession is computed. If <precessed> = (alpha, n) then the precession arc of tilt alpha (in degrees) is discretised into n projections. If n is not provided then default of 30 is used. dtype : str or numpy.dtype Defines the precision to use whilst computing diffraction image. ZERO : float > 0 Rounding error permitted in computation of atomic density. This value is the smallest value rounded to 0. Default is 1e-14. mode : str Only <mode>='kinematic' is currently supported. kwargs : dictionary Extra key-word arguments to pass to child simulator. For kinematic: **GPU** (bool): Flag to use GPU if available, default is True. **pointwise** (bool): Flag to evaluate charge pointwise on voxels rather than average, default is False. Returns ------- ndarray Diffraction data to be interpreted as a discretisation on the original detector mesh. """ species = structure.element coordinates = structure.xyz_cartn.reshape(species.size, -1) dim = coordinates.shape[1] # guarenteed to be 3 if not ZERO > 0: raise ValueError("The value of the ZERO argument must be greater than 0") if probe_centre is None: probe_centre = np.zeros(dim) elif len(probe_centre) == (dim - 1): probe_centre = np.array(list(probe_centre) + [0]) coordinates = coordinates - probe_centre[None] if not precessed: precessed = (float(0), 1) elif np.isscalar(precessed): precessed = (float(precessed), 30) dtype = np.dtype(dtype) dtype = round(dtype.itemsize / (1 if dtype.kind == "f" else 2)) dtype = "f" + str(dtype), "c" + str(2 * dtype) # Filter list of atoms for d in range(dim - 1): ind = coordinates[:, d] >= self.detector[d].min() - 20 coordinates, species = coordinates[ind, :], species[ind] ind = coordinates[:, d] <= self.detector[d].max() + 20 coordinates, species = coordinates[ind, :], species[ind] # Add z-coordinate z_range = max( z_range, coordinates[:, -1].ptp() ) # enforce minimal resolution in reciprocal space x = [ self.detector[0], self.detector[1], np.arange( coordinates[:, -1].min() - 20, coordinates[:, -1].min() + z_range + 20, slice_thickness, ), ] if mode == "kinematic": from diffsims.utils import kinematic_simulation_utils as simlib else: raise NotImplementedError( "<mode> = %s is not currently supported" % repr(mode) ) kwargs["dtype"] = dtype kwargs["ZERO"] = ZERO return simlib.get_diffraction_image( coordinates, species, probe, x, self.wavelength, precessed, **kwargs )