Skip to content

Commit 54b6f07

Browse files
committed
Getting closer, still some tests left
1 parent be19d4f commit 54b6f07

File tree

3 files changed

+52
-64
lines changed

3 files changed

+52
-64
lines changed

diffsims/crystallography/reciprocal_lattice_vector.py

Lines changed: 38 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
from copy import deepcopy
2121

2222
from diffpy.structure.symmetryutilities import expandPosition
23-
from diffpy.structure import Structure
23+
from diffpy.structure import Structure, Lattice
2424
import numba as nb
2525
import numpy as np
2626
from orix.vector import Miller, Vector3d
@@ -696,33 +696,17 @@ def calculate_structure_factor(
696696
"""
697697

698698
# Compute one structure factor per set {hkl}
699-
hkl_sets = self.get_hkl_sets()
700-
701-
# For each set, get the indices of the first vector in the
702-
# present vectors, accounting for potential multiple dimensions
703-
# and avoding computing the unique vectors again
704-
first_idx = []
705-
for arr in list(hkl_sets.values()):
706-
i = []
707-
for arr_i in arr:
708-
i.append(arr_i[0])
709-
first_idx.append(i)
710-
first_idx_arr = np.array(first_idx).T
711-
712-
# Get 2D array of unique vectors, one for each set
713-
hkl_unique = self.hkl[tuple(first_idx_arr)]
714-
699+
unique, inds = self.unique(use_symmetry=True, return_inverse=True)
700+
hkl_unique = unique.hkl
715701
structure_factor = _get_kinematical_structure_factor(
716702
structure=self.phase.structure,
717703
g_indices=hkl_unique,
718704
g_hkls_array=self.phase.structure.lattice.rnorm(hkl_unique),
719705
debye_waller_factors=debye_waller_factors,
720706
scattering_params=scattering_params,
721707
)
722-
723708
# Set structure factors of all symmetrically equivalent vectors
724-
for i, idx in enumerate(hkl_sets.values()):
725-
self._structure_factor[idx] = structure_factor[i]
709+
self._structure_factor[:] = structure_factor[inds]
726710

727711
def calculate_theta(self, voltage):
728712
r"""Populate :attr:`theta` with the Bragg angle :math:`theta_B`
@@ -1517,7 +1501,7 @@ def transpose(self, *axes):
15171501
new._update_shapes()
15181502
return new
15191503

1520-
def unique(self, use_symmetry=False, return_index=False):
1504+
def unique(self, use_symmetry=False, return_index=False, return_inverse=False):
15211505
"""The unique vectors.
15221506
15231507
Parameters
@@ -1528,48 +1512,51 @@ def unique(self, use_symmetry=False, return_index=False):
15281512
return_index : bool, optional
15291513
Whether to return the indices of the (flattened) data where
15301514
the unique entries were found. Default is ``False``.
1515+
return_inverse: bool, optional
1516+
Whether to also return the indices to reconstruct the
1517+
(flattened) data from the unique data.
15311518
15321519
Returns
15331520
-------
15341521
ReciprocalLatticeVector
15351522
Flattened unique vectors.
15361523
idx : numpy.ndarray
15371524
Indices of the unique data in the (flattened) array.
1538-
1525+
inv : numpy.ndarray
1526+
Indices to reconstruct the original vectors from the unique
15391527
"""
15401528

15411529
# TODO: Reduce floating point precision in orix!
15421530
def miller_unique(miller, use_symmetry=False):
1543-
v, idx = Vector3d(miller).unique(return_index=True)
1531+
v, idx, inv = Vector3d(miller).unique(return_index=True, return_inverse=True)
1532+
idx = idx[::-1]
1533+
inv = inv[::-1]
15441534

15451535
if use_symmetry:
15461536
operations = miller.phase.point_group
15471537
n_v = v.size
1548-
v2 = operations.outer(v).flatten().reshape(*(n_v, operations.size))
1538+
v2 = operations.outer(v[::-1]).flatten().reshape(*(n_v, operations.size))
15491539
data = v2.data.round(6) # Reduced precision
1550-
data_sorted = np.zeros_like(data)
1551-
for i in range(n_v):
1552-
a = data[i]
1553-
order = np.lexsort(a.T) # Sort by column 1, 2, then 3
1554-
data_sorted[i] = a[order]
1555-
_, idx = np.unique(data_sorted, return_index=True, axis=0)
1556-
v = v[idx[::-1]]
1540+
_, idx, inv = np.unique(data, return_index=True, return_inverse=True, axis=0)
1541+
v = v[idx]
15571542

15581543
m = miller.__class__(xyz=v.data, phase=miller.phase)
15591544
m.coordinate_format = miller.coordinate_format
1560-
return m, idx
15611545

1562-
# kwargs = dict(use_symmetry=use_symmetry, return_index=True)
1563-
# miller, idx = self.to_miller().unique(**kwargs)
1564-
miller, idx = miller_unique(self.to_miller(), use_symmetry)
1565-
idx = idx[::-1]
1546+
return m, idx, inv
1547+
1548+
miller, idx, inv = miller_unique(self.to_miller(), use_symmetry)
15661549

15671550
new_rlv = self.from_miller(miller)
15681551
new_rlv._structure_factor = self.structure_factor.ravel()[idx]
15691552
new_rlv._theta = self.theta.ravel()[idx]
15701553

1571-
if return_index:
1554+
if return_index and return_inverse:
1555+
return new_rlv, idx, inv
1556+
elif return_index:
15721557
return new_rlv, idx
1558+
elif return_inverse:
1559+
return new_rlv, inv
15731560
else:
15741561
return new_rlv
15751562

@@ -1623,7 +1610,7 @@ def _atom_eq(atom1, atom2):
16231610

16241611
return (
16251612
atom1.element == atom2.element
1626-
and np.allclose(atom1.xyz, atom2.xyz, atol=1e-7)
1613+
and np.allclose(atom1.xyz_cartn, atom2.xyz_cartn, atol=1e-7)
16271614
and atom1.occupancy == atom2.occupancy
16281615
and np.allclose(atom1.U, atom2.U, atol=1e-7)
16291616
and np.allclose(atom1.Uisoequiv, atom2.Uisoequiv, atol=1e-7)
@@ -1646,9 +1633,17 @@ def _expand_unit_cell(space_group, structure):
16461633
new_structure : diffpy.structure.Structure
16471634
16481635
"""
1649-
1636+
# Transform to diffpy axis conventions
1637+
structure_matrix = structure.lattice.base
1638+
pos = structure.xyz_cartn
1639+
structure = Structure(
1640+
atoms=list(structure),
1641+
lattice=Lattice(*structure.lattice.abcABG()),
1642+
)
16501643
new_structure = Structure(lattice=structure.lattice)
1644+
structure.xyz_cartn = pos
16511645

1646+
# Perform symmetry expansion
16521647
for atom in structure:
16531648
equal = []
16541649
for atom2 in new_structure:
@@ -1660,6 +1655,10 @@ def _expand_unit_cell(space_group, structure):
16601655
new_atom.xyz = new_position
16611656
new_structure.append(new_atom)
16621657

1658+
# Transform back to orix convention
1659+
old_xyz_cartn = new_structure.xyz_cartn
1660+
new_structure.lattice.setLatBase(structure_matrix)
1661+
new_structure.xyz_cartn = old_xyz_cartn
16631662
return new_structure
16641663

16651664

diffsims/generators/simulation_generator.py

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -133,9 +133,7 @@ def wavelength(self):
133133
def calculate_diffraction2d(
134134
self,
135135
phase: Union[Phase, Sequence[Phase]],
136-
rotation: Union[Rotation, Sequence[Rotation]] = Rotation.from_euler(
137-
(0, 0, 0), degrees=True
138-
),
136+
rotation: Union[Rotation, Sequence[Rotation]] = Rotation.identity(),
139137
reciprocal_radius: float = 1.0,
140138
with_direct_beam: bool = True,
141139
max_excitation_error: float = 1e-2,
@@ -201,12 +199,10 @@ def calculate_diffraction2d(
201199
include_zero_vector=with_direct_beam,
202200
)
203201
recip.sanitise_phase()
204-
# No need to pre-calculate structure factors if only a few rotations are present
205-
if rotate.size > 4:
206-
recip.calculate_structure_factor(
207-
scattering_params=self.scattering_params,
208-
debye_waller_factors=debye_waller_factors,
209-
)
202+
recip.calculate_structure_factor(
203+
scattering_params=self.scattering_params,
204+
debye_waller_factors=debye_waller_factors,
205+
)
210206
phase_vectors = []
211207
for rot in rotate:
212208
# Calculate the reciprocal lattice vectors that intersect the Ewald sphere.
@@ -227,15 +223,7 @@ def calculate_diffraction2d(
227223
excitation_error, max_excitation_error, r_spot, shape_factor_width
228224
)
229225
# Calculate intensity
230-
if rotate.size <= 4:
231-
v = recip[intersection]
232-
v.calculate_structure_factor(
233-
scattering_params=self.scattering_params,
234-
debye_waller_factors=debye_waller_factors,
235-
)
236-
f_hkls = v.structure_factor
237-
else:
238-
f_hkls = recip[intersection].structure_factor
226+
f_hkls = recip.structure_factor[intersection]
239227
intensities = (f_hkls * f_hkls.conjugate()).real * shape_factor
240228

241229
# Threshold peaks included in simulation as factor of zero beam intensity.

diffsims/tests/generators/test_simulation_generator.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -196,12 +196,13 @@ def test_appropriate_scaling(self, diffraction_calculator: SimulationGenerator):
196196
big_diffraction = diffraction_calculator.calculate_diffraction2d(
197197
phase=big_silicon, reciprocal_radius=5.0
198198
)
199-
indices = [tuple(i) for i in diffraction.coordinates.hkl]
200-
big_indices = [tuple(i) for i in big_diffraction.coordinates.hkl]
201-
assert (2, 2, 0) in indices
202-
assert (2, 2, 0) in big_indices
203-
coordinates = diffraction.coordinates[indices.index((2, 2, 0))]
204-
big_coordinates = big_diffraction.coordinates[big_indices.index((2, 2, 0))]
199+
indices = [tuple(i.tolist()) for i in diffraction.coordinates.hkl.astype(int)]
200+
big_indices = [tuple(i.tolist()) for i in big_diffraction.coordinates.hkl.astype(int)]
201+
target = (2, 2, 0)
202+
assert target in indices
203+
assert target in big_indices
204+
coordinates = diffraction.coordinates[indices.index(target)]
205+
big_coordinates = big_diffraction.coordinates[big_indices.index(target)]
205206
assert np.allclose(coordinates.data, big_coordinates.data * 2)
206207

207208
def test_appropriate_intensities(self, diffraction_calculator, local_structure):
@@ -345,7 +346,7 @@ def test_same_simulation_results():
345346
# old_data = diff_lib["Graphite"]["simulations"][0].get_diffraction_pattern(shape=shape, sigma=sigma)
346347

347348
# New
348-
p = Phase("Graphite", structure=structure_matrix, space_group=1) # Space group 1 ensures no symmetry is applied
349+
p = Phase("Graphite", structure=structure_matrix, space_group=194)
349350
gen = SimulationGenerator(**generator_kwargs)
350351
rot = Rotation.from_euler(euler_angles_new, degrees=True)
351352
sim = gen.calculate_diffraction2d(

0 commit comments

Comments
 (0)