Skip to content

Use phase class2 #205

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 36 commits into from
May 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
1fc6a87
New Feature: Add Simulation classes using Phase Class
CSSFrancis Apr 12, 2024
8282721
New Feature: Add intensity to reciprocal lattice vector
CSSFrancis Apr 12, 2024
bfe0505
Testing: Add tests for adding intensity of reciprocal_lattive_vector.py
CSSFrancis Apr 12, 2024
ac97ee6
New Feature: Added zerobeam to reciprocal lattice vector
CSSFrancis Apr 12, 2024
b0b67fa
New Feature: Moved Shape factor calculation to shape_factor_models.py
CSSFrancis Apr 12, 2024
6ec15b8
Testing: Added testing for simulation_generator.
CSSFrancis Apr 12, 2024
f3bd483
Testing: Testing the zero Beam is most intense.
CSSFrancis Apr 12, 2024
d7c86ec
Min Version: Bump orix min version
CSSFrancis Apr 17, 2024
ef03b39
Packaging: Include function and classes in init file
CSSFrancis Apr 17, 2024
d1839a5
Testing: Clean up tests due to changed function name
CSSFrancis Apr 17, 2024
3567308
Testing: Add coverage for simulation classes
CSSFrancis Apr 17, 2024
66f0133
Documentation: Fixed Broken documentation
CSSFrancis Apr 17, 2024
3d9c662
Refactor: Fixes per @viljarjf
CSSFrancis Apr 21, 2024
99327ad
Refactor: Clean up intensity in rlv class
CSSFrancis Apr 25, 2024
2ad4567
Refactor: Remove calculate structure factor
CSSFrancis Apr 25, 2024
273ffcc
Refactor: Changes from @hakonanes
CSSFrancis May 3, 2024
f32bfda
Refactor: Improve Documentation
CSSFrancis May 3, 2024
a8c68a3
Testing: Test simulation continuity
CSSFrancis May 3, 2024
3a27a7b
Documentation: Add intensity parameter
CSSFrancis May 3, 2024
9bf9404
Manifest: Add numpy old file
CSSFrancis May 3, 2024
9c216f8
Refactor: Refactor DiffractingVector code
CSSFrancis May 5, 2024
2ada567
Refactor: Refactor Rotation implementation
CSSFrancis May 5, 2024
b598a5a
BugFix: Fix HKL computation
CSSFrancis May 5, 2024
e0815d5
Refactor: Fix MANIFEST.in
CSSFrancis May 5, 2024
dff72bb
Plotting: Add interactive plotting
CSSFrancis May 6, 2024
8b92dce
Testing: no cover interactive plotting
CSSFrancis May 6, 2024
f3d9b95
Documentation: Add docstrings for interactive plotting
CSSFrancis May 6, 2024
82b3333
Documentation: Remove lattice rotation
CSSFrancis May 6, 2024
c39e357
Bugfix: Fix polar conversion
CSSFrancis May 7, 2024
05b3736
Bugfix: Fix Rotation with RLV
CSSFrancis May 8, 2024
bfbd299
Revert "Bugfix: Fix Rotation with RLV"
CSSFrancis May 8, 2024
96a48ec
Testing: Improve testing
CSSFrancis May 8, 2024
1f2150d
Refactor: Improve based on @hakonanes and @viljarjf's suggestions
CSSFrancis May 8, 2024
efde31d
Refactor: Tests including zero-vector
CSSFrancis May 8, 2024
da45a32
Refactor: Remove changes from RLV
CSSFrancis May 8, 2024
0e97f22
Refactor: suggestions for @hakonanes
CSSFrancis May 9, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ jobs:
- os: ubuntu-latest
python-version: 3.8
OLDEST_SUPPORTED_VERSION: true
DEPENDENCIES: diffpy.structure==3.0.2 matplotlib==3.5 numpy==1.17.3 orix==0.9.0 scipy==1.8 tqdm==4.9
DEPENDENCIES: diffpy.structure==3.0.2 matplotlib==3.5 numpy==1.17.3 orix==0.12.1 scipy==1.8 tqdm==4.9
LABEL: -oldest
steps:
- uses: actions/checkout@v4
Expand Down
194 changes: 194 additions & 0 deletions diffsims/crystallography/_diffracting_vector.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
# -*- coding: utf-8 -*-
# Copyright 2017-2024 The diffsims developers
#
# This file is part of diffsims.
#
# diffsims is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# diffsims is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with diffsims. If not, see <http://www.gnu.org/licenses/>.

from diffsims.crystallography import ReciprocalLatticeVector
import numpy as np
from orix.vector.miller import _transform_space
from orix.quaternion import Rotation


class DiffractingVector(ReciprocalLatticeVector):
r"""Reciprocal lattice vectors :math:`(hkl)` for use in electron
diffraction analysis and simulation.

All lengths are assumed to be given in Å or inverse Å.

This extends the :class:`ReciprocalLatticeVector` class. `DiffractingVector`
focus on the subset of reciprocal lattice vectors that are relevant for
electron diffraction based on the intersection of the Ewald sphere with the
reciprocal lattice.

This class is only used internally to store the DiffractionVectors generated from the
:class:`~diffsims.simulations.DiffractionSimulation` class. It is not (currently)
intended to be used directly by the user.

Parameters
----------
phase : orix.crystal_map.Phase
A phase with a crystal lattice and symmetry.
xyz : numpy.ndarray, list, or tuple, optional
Cartesian coordinates of indices of reciprocal lattice vector(s)
``hkl``. Default is ``None``. This, ``hkl``, or ``hkil`` is
required.
hkl : numpy.ndarray, list, or tuple, optional
Indices of reciprocal lattice vector(s). Default is ``None``.
This, ``xyz``, or ``hkil`` is required.
hkil : numpy.ndarray, list, or tuple, optional
Indices of reciprocal lattice vector(s), often preferred over
``hkl`` in trigonal and hexagonal lattices. Default is ``None``.
This, ``xyz``, or ``hkl`` is required.
intensity : numpy.ndarray, list, or tuple, optional
Intensity of the diffraction vector(s). Default is ``None``.
rotation : orix.quaternion.Rotation, optional
Rotation matrix previously applied to the reciprocal lattice vector(s) and the
lattice of the phase. Default is ``None`` which corresponds to the
identity matrix.


Examples
--------
>>> from diffpy.structure import Atom, Lattice, Structure
>>> from orix.crystal_map import Phase
>>> from diffsims.crystallography import DiffractingVector
>>> phase = Phase(
... "al",
... space_group=225,
... structure=Structure(
... lattice=Lattice(4.04, 4.04, 4.04, 90, 90, 90),
... atoms=[Atom("Al", [0, 0, 1])],
... ),
... )
>>> rlv = DiffractingVector(phase, hkl=[[1, 1, 1], [2, 0, 0]])
>>> rlv
ReciprocalLatticeVector (2,), al (m-3m)
[[1. 1. 1.]
[2. 0. 0.]]

"""

def __init__(self, phase, xyz=None, hkl=None, hkil=None, intensity=None):
super().__init__(phase, xyz=xyz, hkl=hkl, hkil=hkil)
if intensity is None:
self._intensity = np.full(self.shape, np.nan)
elif len(intensity) != self.size:
raise ValueError("Length of intensity array must match number of vectors")
else:
self._intensity = np.array(intensity)

def __getitem__(self, key):
new_data = self.data[key]
dv_new = self.__class__(self.phase, xyz=new_data)

if np.isnan(self.structure_factor).all():
dv_new._structure_factor = np.full(dv_new.shape, np.nan, dtype="complex128")

else:
dv_new._structure_factor = self.structure_factor[key]
if np.isnan(self.theta).all():
dv_new._theta = np.full(dv_new.shape, np.nan)
else:
dv_new._theta = self.theta[key]
if np.isnan(self.intensity).all():
dv_new._intensity = np.full(dv_new.shape, np.nan)
else:
slic = self.intensity[key]
if not hasattr(slic, "__len__"):
slic = np.array(
[
slic,
]
)
dv_new._intensity = slic

return dv_new

@property
def basis_rotation(self):
"""
Returns the lattice basis rotation.
"""
return Rotation.from_matrix(self.phase.structure.lattice.baserot)

def rotate_with_basis(self, rotation):
"""Rotate both vectors and the basis with a given `Rotation`.
This differs from simply multiplying with a `Rotation`,
as that would NOT update the basis.

Parameters
----------
rot : orix.quaternion.Rotation
A rotation to apply to vectors and the basis.

Returns
-------
DiffractingVector
A new DiffractingVector with the rotated vectors and basis. This maintains
the hkl indices of the vectors, but the underlying vector xyz coordinates
are rotated by the given rotation.

Notes
-----
Rotating the lattice basis may lead to undefined behavior in orix as it violates
the assumption that the basis is aligned with the crystal axes. Particularly,
applying symmetry operations to the phase may lead to unexpected results.
"""

if rotation.size != 1:
raise ValueError("Rotation must be a single rotation")
# rotate basis
new_phase = self.phase.deepcopy()
br = new_phase.structure.lattice.baserot
# In case the base rotation is set already
new_br = br @ rotation.to_matrix().squeeze()
new_phase.structure.lattice.setLatPar(baserot=new_br)
# rotate vectors
vecs = ~rotation * self.to_miller()
return ReciprocalLatticeVector(new_phase, xyz=vecs.data)

@property
def intensity(self):
return self._intensity

@intensity.setter
def intensity(self, value):
if not hasattr(value, "__len__"):
value = np.array(
[
value,
]
* self.size
)
if len(value) != self.size:
raise ValueError("Length of intensity array must match number of vectors")
self._intensity = np.array(value)

def calculate_structure_factor(self):
raise NotImplementedError(
"Structure factor calculation not implemented for DiffractionVector. "
"Use ReciprocalLatticeVector instead."
)

def to_flat_polar(self):
"""Return the vectors in polar coordinates as projected onto the x,y plane"""
flat_self = self.flatten()
r = np.linalg.norm(flat_self.data[:, :2], axis=1)
theta = np.arctan2(
flat_self.data[:, 1],
flat_self.data[:, 0],
)
return r, theta
21 changes: 16 additions & 5 deletions diffsims/crystallography/reciprocal_lattice_vector.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,6 @@ def __init__(self, phase, xyz=None, hkl=None, hkil=None):
self._coordinate_format = "hkl"
xyz = _transform_space(hkl, "r", "c", phase.structure.lattice)
super().__init__(xyz)

self._theta = np.full(self.shape, np.nan)
self._structure_factor = np.full(self.shape, np.nan, dtype="complex128")

Expand Down Expand Up @@ -1023,7 +1022,7 @@ def symmetrise(self, return_multiplicity=False, return_index=False):
return new_out

@classmethod
def from_highest_hkl(cls, phase, hkl):
def from_highest_hkl(cls, phase, hkl, include_zero_vector=False):
"""Create a set of unique reciprocal lattice vectors from three
highest indices.

Expand All @@ -1033,6 +1032,8 @@ def from_highest_hkl(cls, phase, hkl):
A phase with a crystal lattice and symmetry.
hkl : numpy.ndarray, list, or tuple
Three highest reciprocal lattice vector indices.
include_zero_vector : bool
If ``True``, include the zero vector (000) in the set of vectors.

Examples
--------
Expand Down Expand Up @@ -1067,10 +1068,14 @@ def from_highest_hkl(cls, phase, hkl):
"""

idx = _get_indices_from_highest(highest_indices=hkl)
return cls(phase, hkl=idx).unique()
new = cls(phase, hkl=idx).unique()
if include_zero_vector:
new_data = np.vstack((new.hkl, np.zeros(3, dtype=int)))
new = ReciprocalLatticeVector(phase, hkl=new_data)
return new

@classmethod
def from_min_dspacing(cls, phase, min_dspacing=0.7):
def from_min_dspacing(cls, phase, min_dspacing=0.7, include_zero_vector=False):
"""Create a set of unique reciprocal lattice vectors with a
a direct space interplanar spacing greater than a lower
threshold.
Expand All @@ -1083,6 +1088,8 @@ def from_min_dspacing(cls, phase, min_dspacing=0.7):
Smallest interplanar spacing to consider. Default is 0.7,
in the unit used to define the lattice parameters in
``phase``, which is assumed to be Ångström.
include_zero_vector: bool
If ``True``, include the zero vector (000) in the set of vectors.

Examples
--------
Expand Down Expand Up @@ -1128,7 +1135,11 @@ def from_min_dspacing(cls, phase, min_dspacing=0.7):
dspacing = 1 / phase.structure.lattice.rnorm(hkl)
idx = dspacing >= min_dspacing
hkl = hkl[idx]
return cls(phase, hkl=hkl).unique()
new = cls(phase, hkl=hkl).unique()
if include_zero_vector:
new_data = np.vstack((new.hkl, np.zeros(3, dtype=int)))
new = cls(phase, hkl=new_data)
return new

@classmethod
def from_miller(cls, miller):
Expand Down
2 changes: 2 additions & 0 deletions diffsims/generators/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,14 @@
rotation_list_generators,
sphere_mesh_generators,
zap_map_generator,
simulation_generator,
)

__all__ = [
"diffraction_generator",
"library_generator",
"rotation_list_generators",
"sphere_mesh_generators",
"simulation_generator",
"zap_map_generator",
]
Loading