Skip to content

Commit

Permalink
Merge ea1c515 into b25eb5d
Browse files Browse the repository at this point in the history
  • Loading branch information
robtovey committed Dec 3, 2019
2 parents b25eb5d + ea1c515 commit 4b2e18a
Show file tree
Hide file tree
Showing 19 changed files with 2,646 additions and 22 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,5 @@ src/

# VScode project settings
.vscode/
/.project
/.pydevproject
3 changes: 2 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ language: python

env:
global:
- DEPS="psutil numba"
- TEST_DEPS="pytest pytest-cov coveralls"

matrix:
Expand Down Expand Up @@ -33,7 +34,7 @@ before_install:
- df -h

install:
- conda install -y $TEST_DEPS;
- conda install -y $DEPS $TEST_DEPS;
- pip install .

script:
Expand Down
3 changes: 2 additions & 1 deletion appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
environment:

global:
DEPS: "psutil numba"
TEST_DEPS: "pytest"
MPLBACKEND: "agg"

Expand All @@ -23,7 +24,7 @@ install:
- "conda activate testenv"

# Install the dependencies of diffsims.
- 'conda install -yq %TEST_DEPS%'
- 'conda install -yq %DEPS% %TEST_DEPS%'

# Install our package
- 'pip install -e .'
Expand Down
4 changes: 3 additions & 1 deletion diffsims/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,14 @@

import numpy as np

from .generators.diffraction_generator import DiffractionGenerator
from .generators.diffraction_generator import DiffractionGenerator, AtomicDiffractionGenerator
from .generators.library_generator import DiffractionLibraryGenerator
from .generators.library_generator import VectorLibraryGenerator

from .sims.diffraction_simulation import DiffractionSimulation

from .utils.probe_utils import probeFunction, BesselProbe

from . import release_info

__version__ = release_info.version
Expand Down
128 changes: 123 additions & 5 deletions diffsims/generators/diffraction_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,10 @@
from diffsims.sims.diffraction_simulation import ProfileSimulation

from diffsims.utils.atomic_scattering_params import ATOMIC_SCATTERING_PARAMS
from diffsims.utils.sim_utils import get_electron_wavelength,\
from diffsims.utils.sim_utils import get_electron_wavelength, \
get_kinematical_intensities, get_unique_families, get_points_in_sphere, \
get_vectorized_list_for_atomic_scattering_factors, is_lattice_hexagonal
from diffsims.utils.fourier_transform import fromFreq


class DiffractionGenerator(object):
Expand Down Expand Up @@ -121,7 +122,7 @@ def calculate_ed_data(self,
# excitation error and store the magnitude of their excitation error.
r_sphere = 1 / wavelength
r_spot = np.sqrt(np.sum(np.square(cartesian_coordinates[:, :2]), axis=1))
z_sphere = -np.sqrt(r_sphere**2 - r_spot**2) + r_sphere
z_sphere = -np.sqrt(r_sphere ** 2 - r_spot ** 2) + r_sphere
proximity = np.absolute(z_sphere - cartesian_coordinates[:, 2])
intersection = proximity < max_excitation_error
# Mask parameters corresponding to excited reflections.
Expand Down Expand Up @@ -201,7 +202,7 @@ def calculate_profile_data(self, structure,
d_hkl = 1 / g_hkl

# Bragg condition
#theta = asin(wavelength * g_hkl / 2)
# theta = asin(wavelength * g_hkl / 2)

# s = sin(theta) / wavelength = 1 / 2d = |ghkl| / 2 (d =
# 1/|ghkl|)
Expand All @@ -228,11 +229,11 @@ def calculate_profile_data(self, structure,
# Intensity for hkl is modulus square of structure factor.
i_hkl = (f_hkl * f_hkl.conjugate()).real

#two_theta = degrees(2 * theta)
# two_theta = degrees(2 * theta)

if is_hex:
# Use Miller-Bravais indices for hexagonal lattices.
hkl = (hkl[0], hkl[1], - hkl[0] - hkl[1], hkl[2])
hkl = (hkl[0], hkl[1], -hkl[0] - hkl[1], hkl[2])

peaks[g_hkl] = [i_hkl, [tuple(hkl)], d_hkl]

Expand All @@ -254,3 +255,120 @@ def calculate_profile_data(self, structure,
y = y / max(y) * 100

return ProfileSimulation(x, y, hkls)


class AtomicDiffractionGenerator:
'''
Computes electron diffraction patterns for an atomic lattice.
Parameters
----------
accelerating_voltage : float (or 'inf')
The accelerating voltage of the microscope in kV
detector : list of 1D ndarrays
List of mesh vectors defining the (flat) detector size and sensor positions
reciprocal_mesh : bool
If True then <detector> is assumed to be a reciprocal grid, else
(default) it is assumed to be a real grid.
'''

def __init__(self, accelerating_voltage, detector,
reciprocal_mesh=False, debye_waller_factors=None):
self.wavelength = get_electron_wavelength(accelerating_voltage)
# Always store a 'real' mesh
self.detector = detector if not reciprocal_mesh else fromFreq(detector)

if debye_waller_factors:
raise NotImplementedError('Not implemented for this simulator')
self.debye_waller_factors = debye_waller_factors or {}

def calculate_ed_data(self, structure, probe, slice_thickness,
probe_centre=None, precessed=False, dtype='float64',
ZERO=1e-14, mode='kinematic', **kwargs):
'''
Calculates single electron diffraction image for particular atomic
structure and probe.
Parameters
----------
coordinates : ndarray of floats, shape [n_atoms, 3]
List of atomic coordinates, i.e. atom i is centred at <coordinates>[i]
species : ndarray of integers, shape [n_atoms]
List of atomic numbers, i.e. atom i has atomic number <species>[i]
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]
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
Returns
-------
ndarray
Diffraction data to be interpreted as a discretisation on the original
detector mesh.
'''
# TODO: document kwargs?..

species = structure.element
coordinates = structure.xyz_cartn.reshape(species.size, -1)
dim = coordinates.shape[1]
assert dim == 3

if probe_centre is None:
probe_centre = np.zeros(dim)
elif len(probe_centre) < dim:
probe_centre = np.array(list(probe_centre) + [0])
probe_centre = np.array(probe_centre)

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)

assert ZERO > 0

# Filter list of atoms
for d in range(dim - 1):
ind = coordinates[:, d] >= self.detector[d].min() + probe_centre[d] - 20
coordinates, species = coordinates[ind, :], species[ind]
ind = coordinates[:, d] <= self.detector[d].max() + probe_centre[d] + 20
coordinates, species = coordinates[ind, :], species[ind]

# Add z-coordinate
x = list(self.detector) + [np.linspace(coordinates[:, -1].min() - 20,
coordinates[:, -1].max() + 20,
slice_thickness)]

if mode == 'kinematic':
from diffsims.sims import kinematic_simulation 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)

0 comments on commit 4b2e18a

Please sign in to comment.