Exemplo n.º 1
0
    def build(self, positions: Sequence[Sequence[float]] = None) -> Waves:
        """
        Build probe wave functions at the provided positions.

        :param positions: Positions of the probe wave functions
        :return: Probe wave functions as a Waves object.
        """

        self.grid.check_is_defined()
        self.accelerator.check_is_defined()
        xp = get_array_module_from_device(self._device)
        fft2 = get_device_function(xp, 'fft2')

        if positions is None:
            positions = xp.zeros((1, 2), dtype=xp.float32)
        else:
            positions = xp.array(positions, dtype=xp.float32)

        if len(positions.shape) == 1:
            positions = xp.expand_dims(positions, axis=0)

        array = fft2(self._evaluate_ctf(xp) *
                     self._fourier_translation_operator(positions),
                     overwrite_x=True)

        return Waves(array, extent=self.extent, energy=self.energy)
Exemplo n.º 2
0
 def build(self) -> Waves:
     """
     :return: Wave function as a Waves object.
     """
     xp = get_array_module_from_device(self._device)
     self.grid.check_is_defined()
     array = xp.ones((self.gpts[0], self.gpts[1]), dtype=xp.complex64)
     return Waves(array, extent=self.extent, energy=self.energy)
Exemplo n.º 3
0
    def build(self) -> SMatrix:
        self.grid.check_is_defined()
        self.accelerator.check_is_defined()

        xp = get_array_module(self._device)
        storage_xp = get_array_module_from_device(self._storage)
        complex_exponential = get_device_function(xp, 'complex_exponential')

        n_max = int(
            xp.ceil(self.expansion_cutoff / 1000. /
                    (self.wavelength / self.extent[0] * self.interpolation)))
        m_max = int(
            xp.ceil(self.expansion_cutoff / 1000. /
                    (self.wavelength / self.extent[1] * self.interpolation)))

        n = xp.arange(-n_max, n_max + 1, dtype=xp.float32)
        w = xp.asarray(self.extent[0], dtype=xp.float32)
        m = xp.arange(-m_max, m_max + 1, dtype=xp.float32)
        h = xp.asarray(self.extent[1], dtype=xp.float32)

        kx = n / w * xp.float32(self.interpolation)
        ky = m / h * xp.float32(self.interpolation)

        mask = kx[:, None]**2 + ky[None, :]**2 < (self.expansion_cutoff /
                                                  1000. / self.wavelength)**2
        kx, ky = xp.meshgrid(kx, ky, indexing='ij')
        kx = kx[mask]
        ky = ky[mask]

        x, y = coordinates(extent=self.extent,
                           gpts=self.gpts,
                           endpoint=self.grid.endpoint)
        x = xp.asarray(x)
        y = xp.asarray(y)

        array = storage_xp.zeros((len(kx), ) + (self.gpts[0], self.gpts[1]),
                                 dtype=np.complex64)

        for i in range(len(kx)):
            array[i] = copy_to_device(
                complex_exponential(
                    -2 * np.pi * kx[i, None, None] * x[:, None]) *
                complex_exponential(
                    -2 * np.pi * ky[i, None, None] * y[None, :]),
                self._storage)

        return SMatrix(array,
                       expansion_cutoff=self.expansion_cutoff,
                       interpolation=self.interpolation,
                       extent=self.extent,
                       energy=self.energy,
                       k=(kx, ky))
Exemplo n.º 4
0
    def build(self,
              pbar: Union[bool, ProgressBar] = False) -> 'ArrayPotential':
        self.grid.check_is_defined()

        storage_xp = get_array_module_from_device(self._storage)
        array = storage_xp.zeros(
            (self.num_slices, ) + (self.gpts[0], self.gpts[1]),
            dtype=np.float32)
        slice_thicknesses = np.zeros(self.num_slices)

        if isinstance(pbar, bool):
            pbar = ProgressBar(total=len(self),
                               desc='Potential',
                               disable=not pbar)

        pbar.reset()
        for i, potential_slice in enumerate(self.generate_slices()):
            array[i] = copy_to_device(potential_slice.array, self._storage)
            slice_thicknesses[i] = potential_slice.thickness
            pbar.update(1)

        pbar.refresh()

        return ArrayPotential(array, slice_thicknesses, self.extent)
Exemplo n.º 5
0
    def _generate_slices_finite(self,
                                first_slice=0,
                                last_slice=None,
                                max_batch=1) -> Generator:
        xp = get_array_module_from_device(self._device)

        interpolate_radial_functions = get_device_function(
            xp, 'interpolate_radial_functions')

        atoms = self.atoms.copy()
        atoms.wrap()
        indices_by_number = {
            number: np.where(atoms.numbers == number)[0]
            for number in np.unique(atoms.numbers)
        }

        start, end = next(
            generate_batches(last_slice - first_slice,
                             max_batch=max_batch,
                             start=first_slice))
        array = xp.zeros((end - start, ) + self.gpts, dtype=xp.float32)

        slice_edges = np.linspace(0, self.atoms.cell[2, 2],
                                  self.num_slices + 1)

        for start, end in generate_batches(last_slice - first_slice,
                                           max_batch=max_batch,
                                           start=first_slice):
            array[:] = 0.

            for number, indices in indices_by_number.items():
                species_atoms = atoms[indices]
                integrator = self.get_integrator(number)
                disc_indices = xp.asarray(
                    self._get_radial_interpolation_points(number))

                a = slice_edges[start]
                b = slice_edges[end]
                chunk_atoms = species_atoms[
                    (species_atoms.positions[:, 2] > a - integrator.cutoff) *
                    (species_atoms.positions[:, 2] < b + integrator.cutoff)]
                chunk_atoms = pad_atoms(chunk_atoms, integrator.cutoff)
                chunk_positions = chunk_atoms.positions

                if len(chunk_atoms) == 0:
                    continue

                positions = np.zeros((0, 3), dtype=xp.float32)
                A = np.zeros((0, ), dtype=xp.float32)
                B = np.zeros((0, ), dtype=xp.float32)
                run_length_enconding = np.zeros((end - start + 1, ),
                                                dtype=xp.int32)

                for i, j in enumerate(range(start, end)):
                    a = slice_edges[j]
                    b = slice_edges[j + 1]
                    slice_positions = chunk_positions[
                        (chunk_positions[:, 2] > a - integrator.cutoff) *
                        (chunk_positions[:, 2] < b + integrator.cutoff)]

                    positions = np.vstack((positions, slice_positions))
                    A = np.concatenate((A, [a] * len(slice_positions)))
                    B = np.concatenate((B, [b] * len(slice_positions)))

                    run_length_enconding[
                        i + 1] = run_length_enconding[i] + len(slice_positions)

                vr, dvdr = integrator.integrate(positions[:, 2], A, B, xp=xp)

                vr = xp.asarray(vr, dtype=xp.float32)
                dvdr = xp.asarray(dvdr, dtype=xp.float32)
                r = xp.asarray(integrator.r, dtype=xp.float32)
                sampling = xp.asarray(self.sampling, dtype=xp.float32)

                interpolate_radial_functions(array, run_length_enconding,
                                             disc_indices, positions, vr, r,
                                             dvdr, sampling)

            slice_thicknesses = [
                self.get_slice_thickness(i) for i in range(start, end)
            ]

            yield start, end, PotentialArray(array[:end - start] / kappa,
                                             slice_thicknesses,
                                             extent=self.extent)
Exemplo n.º 6
0
    def _generate_slices_infinite(self,
                                  first_slice=0,
                                  last_slice=None,
                                  max_batch=1) -> Generator:
        xp = get_array_module_from_device(self._device)

        fft2_convolve = get_device_function(xp, 'fft2_convolve')

        atoms = self.atoms.copy()
        atoms.wrap()
        positions = atoms.get_positions().astype(np.float32)
        numbers = atoms.get_atomic_numbers()
        unique = np.unique(numbers)
        order = np.argsort(positions[:, 2])

        positions = positions[order]
        numbers = numbers[order]

        kx = xp.fft.fftfreq(self.gpts[0], self.sampling[0])
        ky = xp.fft.fftfreq(self.gpts[1], self.sampling[1])
        kx, ky = xp.meshgrid(kx, ky, indexing='ij')
        k = xp.sqrt(kx**2 + ky**2)

        sinc = xp.sinc(
            xp.sqrt((kx * self.sampling[0])**2 + (kx * self.sampling[1])**2))

        scattering_factors = {}
        for atomic_number in unique:
            f = kirkland_projected_fourier(k, self.parameters[atomic_number])
            scattering_factors[atomic_number] = (
                f /
                (sinc * self.sampling[0] * self.sampling[1] * kappa)).astype(
                    xp.complex64)

        slice_idx = np.floor(positions[:, 2] / atoms.cell[2, 2] *
                             self.num_slices).astype(np.int)

        start, end = next(
            generate_batches(last_slice - first_slice,
                             max_batch=max_batch,
                             start=first_slice))

        array = xp.zeros((end - start, ) + self.gpts, dtype=xp.complex64)
        temp = xp.zeros((end - start, ) + self.gpts, dtype=xp.complex64)

        for start, end in generate_batches(last_slice - first_slice,
                                           max_batch=max_batch,
                                           start=first_slice):
            array[:] = 0.
            start_idx = np.searchsorted(slice_idx, start)
            end_idx = np.searchsorted(slice_idx, end)

            if start_idx != end_idx:
                for j, number in enumerate(unique):
                    temp[:] = 0.
                    chunk_positions = positions[start_idx:end_idx]
                    chunk_slice_idx = slice_idx[start_idx:end_idx] - start

                    if len(unique) > 1:
                        chunk_positions = chunk_positions[
                            numbers[start_idx:end_idx] == number]
                        chunk_slice_idx = chunk_slice_idx[
                            numbers[start_idx:end_idx] == number]

                    chunk_positions = xp.asarray(chunk_positions[:, :2] /
                                                 self.sampling)

                    superpose_deltas(chunk_positions, chunk_slice_idx, temp)
                    fft2_convolve(temp, scattering_factors[number])

                    array += temp

            slice_thicknesses = [
                self.get_slice_thickness(i) for i in range(start, end)
            ]
            yield start, end, PotentialArray(array.real[:end - start],
                                             slice_thicknesses,
                                             extent=self.extent)
Exemplo n.º 7
0
    def build(
        self,
        first_slice: int = 0,
        last_slice: int = None,
        energy: float = None,
        max_batch: int = None,
        pbar: Union[bool, ProgressBar] = False,
    ) -> 'PotentialArray':
        """
        Precalcaulate the potential as a potential array.

        Parameters
        ----------
        first_slice: int
            First potential slice to generate.
        last_slice: int, optional
            Last potential slice generate.
        energy: float
            Electron energy [eV]. If given, the transmission functions will be returned.
        max_batch: int
            Maximum number of potential slices calculated in parallel.
        pbar: bool
            If true, show progress bar.

        Returns
        -------
        PotentialArray object
        """

        self.grid.check_is_defined()

        if last_slice is None:
            last_slice = len(self)

        if max_batch is None:
            max_batch = self._estimate_max_batch()

        storage_xp = get_array_module_from_device(self._storage)

        if energy is None:
            array = storage_xp.zeros(
                (last_slice - first_slice, ) + (self.gpts[0], self.gpts[1]),
                dtype=np.float32)
            generator = self.generate_slices(max_batch=max_batch,
                                             first_slice=first_slice,
                                             last_slice=last_slice)
        else:
            array = storage_xp.zeros(
                (last_slice - first_slice, ) + (self.gpts[0], self.gpts[1]),
                dtype=np.complex64)
            generator = self.generate_transmission_functions(
                energy=energy,
                max_batch=max_batch,
                first_slice=first_slice,
                last_slice=last_slice)

        slice_thicknesses = np.zeros(last_slice - first_slice)

        if isinstance(pbar, bool):
            pbar = ProgressBar(total=len(self),
                               desc='Potential',
                               disable=not pbar)
            close_pbar = True
        else:
            close_pbar = False

        pbar.reset()
        for start, end, potential_slice in generator:
            array[start:end] = copy_to_device(potential_slice.array,
                                              self._storage)
            slice_thicknesses[start:end] = potential_slice.slice_thicknesses
            pbar.update(end - start)

        pbar.refresh()

        if close_pbar:
            pbar.close()

        if energy is None:
            return PotentialArray(array,
                                  slice_thicknesses=slice_thicknesses,
                                  extent=self.extent)
        else:
            return TransmissionFunction(array,
                                        slice_thicknesses=slice_thicknesses,
                                        extent=self.extent,
                                        energy=energy)
Exemplo n.º 8
0
    def _generate_slices_finite(self,
                                first_slice=0,
                                last_slice=None,
                                max_batch=1) -> Generator:
        sliced_atoms = SlicedAtoms(self.atoms, self.slice_thickness)

        xp = get_array_module_from_device(self._device)
        interpolate_radial_functions = get_device_function(
            xp, 'interpolate_radial_functions')

        array = None
        unique = np.unique(self.atoms.numbers)

        for start, end in generate_batches(last_slice - first_slice,
                                           max_batch=max_batch,
                                           start=first_slice):
            if array is None:
                array = xp.zeros((end - start, ) + self.gpts, dtype=xp.float32)
            else:
                array[:] = 0.

            for number in unique:
                integrator = self.get_integrator(number)
                disc_indices = xp.asarray(
                    self._get_radial_interpolation_points(number))
                chunk_atoms = sliced_atoms.get_subsliced_atoms(
                    start, end, number, z_margin=integrator.cutoff)

                if len(chunk_atoms) == 0:
                    continue

                positions = np.zeros((0, 3), dtype=xp.float32)
                slice_entrances = np.zeros((0, ), dtype=xp.float32)
                slice_exits = np.zeros((0, ), dtype=xp.float32)
                run_length_enconding = np.zeros((end - start + 1, ),
                                                dtype=xp.int32)

                for i, slice_idx in enumerate(range(start, end)):
                    slice_atoms = chunk_atoms.get_subsliced_atoms(
                        slice_idx,
                        padding=integrator.cutoff,
                        z_margin=integrator.cutoff)

                    slice_positions = slice_atoms.positions
                    slice_entrance = slice_atoms.get_slice_entrance(slice_idx)
                    slice_exit = slice_atoms.get_slice_exit(slice_idx)

                    positions = np.vstack((positions, slice_positions))
                    slice_entrances = np.concatenate(
                        (slice_entrances,
                         [slice_entrance] * len(slice_positions)))
                    slice_exits = np.concatenate(
                        (slice_exits, [slice_exit] * len(slice_positions)))

                    run_length_enconding[
                        i + 1] = run_length_enconding[i] + len(slice_positions)

                vr, dvdr = integrator.integrate(positions[:, 2],
                                                slice_entrances, slice_exits)

                vr = xp.asarray(vr, dtype=xp.float32)
                dvdr = xp.asarray(dvdr, dtype=xp.float32)
                r = xp.asarray(integrator.r, dtype=xp.float32)
                sampling = xp.asarray(self.sampling, dtype=xp.float32)

                interpolate_radial_functions(array, run_length_enconding,
                                             disc_indices, positions, vr, r,
                                             dvdr, sampling)

            slice_thicknesses = [
                self.get_slice_thickness(i) for i in range(start, end)
            ]

            yield start, end, PotentialArray(array[:end - start] / kappa,
                                             slice_thicknesses,
                                             extent=self.extent)
Exemplo n.º 9
0
    def _generate_slices_infinite(self,
                                  first_slice=0,
                                  last_slice=None,
                                  max_batch=1) -> Generator:
        xp = get_array_module_from_device(self._device)

        fft2_convolve = get_device_function(xp, 'fft2_convolve')

        atoms = self.atoms.copy()
        atoms.wrap()
        indices_by_number = {
            number: np.where(atoms.numbers == number)[0]
            for number in np.unique(atoms.numbers)
        }

        kx = xp.fft.fftfreq(self.gpts[0], self.sampling[0])
        ky = xp.fft.fftfreq(self.gpts[1], self.sampling[1])
        kx, ky = xp.meshgrid(kx, ky, indexing='ij')
        k = xp.sqrt(kx**2 + ky**2)

        sinc = xp.sinc(
            xp.sqrt((kx * self.sampling[0])**2 + (kx * self.sampling[1])**2))

        scattering_factors = {}
        for atomic_number in indices_by_number.keys():
            f = kirkland_projected_fourier(k, self.parameters[atomic_number])
            scattering_factors[atomic_number] = (
                f /
                (sinc * self.sampling[0] * self.sampling[1] * kappa)).astype(
                    xp.complex64)

        slice_idx = np.floor(atoms.positions[:, 2] / atoms.cell[2, 2] *
                             self.num_slices).astype(np.int)

        start, end = next(
            generate_batches(last_slice - first_slice,
                             max_batch=max_batch,
                             start=first_slice))

        array = xp.zeros((end - start, ) + self.gpts, dtype=xp.complex64)
        temp = xp.zeros((end - start, ) + self.gpts, dtype=xp.complex64)

        for start, end in generate_batches(last_slice - first_slice,
                                           max_batch=max_batch,
                                           start=first_slice):
            array[:] = 0.

            for j, (number, indices) in enumerate(indices_by_number.items()):
                temp[:] = 0.
                in_slice = (slice_idx >= start) * (slice_idx < end) * (
                    atoms.numbers == number)
                slice_atoms = atoms[in_slice]
                positions = xp.asarray(slice_atoms.positions[:, :2] /
                                       self.sampling)

                superpose_deltas(positions, slice_idx[in_slice] - start, temp)
                fft2_convolve(temp, scattering_factors[number])

                array += temp

            slice_thicknesses = [
                self.get_slice_thickness(i) for i in range(start, end)
            ]
            yield start, end, PotentialArray(array.real[:end - start],
                                             slice_thicknesses,
                                             extent=self.extent)