Skip to content
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

New Feature: Allow passing phase to sample generation #481

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ Added
- ``Added orix.sampling.get_sample_zone_axis`` for getting zone axes for some point group.
- ``Added orix.sampling.get_sample_reduced_fundamental`` for getting reduced
fundamental zone for some point group.
- Added ``phase`` argument to ``orix.sampling.get_sample_fundamental`` and
``orix.sampling.get_sample_reduced_fundamental`` for passing a
phase to the functions rather than a space group or point group.

Changed
-------
Expand Down
19 changes: 17 additions & 2 deletions orix/sampling/sample_generators.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ def get_sample_fundamental(
resolution: Union[int, float] = 2,
point_group: Optional[Symmetry] = None,
space_group: Optional[int] = None,
phase: Optional[Phase] = None,
method: str = "cubochoric",
**kwargs
) -> Rotation:
Expand All @@ -48,6 +49,10 @@ def get_sample_fundamental(
must be.
space_group
Between 1 and 231. Must be given if ``point_group`` is not.
phase
The phase for which the fundamental zone rotations are sampled.
If not given, the point group or space group is used to determine
the crystal system.
method
``"cubochoric"`` (default), ``"haar_euler"`` or
``"quaternion"``. See :func:`~orix.sampling.uniform_SO3_sample`
Expand Down Expand Up @@ -79,6 +84,8 @@ def get_sample_fundamental(
[ 0.877 0.2884 0.2884 0.2538]
[ 0.877 0.2774 0.2774 0.2774]]
"""
if phase is not None:
point_group = phase.point_group
if point_group is None:
point_group = get_point_group(space_group, proper=True)

Expand Down Expand Up @@ -173,6 +180,7 @@ def _remove_larger_than_angle(rot: Rotation, max_angle: Union[int, float]) -> Ro
def get_sample_reduced_fundamental(
resolution: float,
mesh: str = None,
phase: Phase = None,
point_group: Symmetry = None,
) -> Rotation:
"""Produces rotations to align various crystallographic directions with
Expand All @@ -189,16 +197,23 @@ def get_sample_reduced_fundamental(
Type of meshing of the sphere that defines how the grid is created. See
orix.sampling.sample_S2 for all the options. A suitable default is
chosen depending on the crystal system.
phase
The phase for which the reduced fundamental zone rotations are
sampled. If not given, the point group is used to determine the
crystal system.
point_group
Symmetry operations that determines the unique directions. Defaults to
Symmetry operations that determines the unique directions. If ``Phase``
is given the ``Phase.point_group`` is used instead. Defaults to
no symmetry, which means sampling all 3D unit vectors.
Returns
-------
Rotation
(N, 3) array representing Euler angles for the different orientations
"""
if point_group is None:
if point_group is None and phase is None:
point_group = symmetry.C1
if phase is not None:
point_group = phase.point_group

if mesh is None:
s2_auto_sampling_map = {
Expand Down
50 changes: 33 additions & 17 deletions orix/tests/sampling/test_sampling.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,23 @@ class TestSampleFundamental:
def C6_sample(self):
return get_sample_fundamental(resolution=4, point_group=C6, method="haar_euler")

@pytest.fixture(scope="session")
def phase(self):
a = 5.431
latt = Lattice(a, a, a, 90, 90, 90)
atom_list = []
for coords in [[0, 0, 0], [0.5, 0, 0.5], [0, 0.5, 0.5], [0.5, 0.5, 0]]:
x, y, z = coords[0], coords[1], coords[2]
atom_list.append(
Atom(atype="Si", xyz=[x, y, z], lattice=latt)
) # Motif part A
atom_list.append(
Atom(atype="Si", xyz=[x + 0.25, y + 0.25, z + 0.25], lattice=latt)
) # Motif part B
struct = Structure(atoms=atom_list, lattice=latt)
p = Phase(structure=struct, space_group=227)
return p

def test_get_sample_fundamental_zone_order(self, C6_sample):
"""Cross check point counts to group order terms."""
D6_sample = get_sample_fundamental(4, point_group=D6, method="haar_euler")
Expand Down Expand Up @@ -200,30 +217,29 @@ def test_get_sample_reduced_fundamental(self):
np.abs(rotations.size / rotations6.size) - 6 < 0.1
) # about 6 times more rotations

def test_get_sample_reduced_fundamental_phase(self, phase):
rotations = get_sample_reduced_fundamental(resolution=4, phase=phase)
rotations2 = get_sample_reduced_fundamental(
resolution=4, point_group=phase.point_group
)
np.testing.assert_allclose(rotations.data, rotations2.data)

def test_get_sample_fundamental_phase(self, phase):
rotations = get_sample_fundamental(resolution=4, phase=phase)
rotations2 = get_sample_fundamental(resolution=4, point_group=phase.point_group)
np.testing.assert_allclose(rotations.data, rotations2.data)

@pytest.mark.parametrize("density", ("3", "7", "5"))
@pytest.mark.parametrize("get_directions", (True, False))
def test_get_zone_axis(self, density, get_directions):
a = 5.431
latt = Lattice(a, a, a, 90, 90, 90)
atom_list = []
for coords in [[0, 0, 0], [0.5, 0, 0.5], [0, 0.5, 0.5], [0.5, 0.5, 0]]:
x, y, z = coords[0], coords[1], coords[2]
atom_list.append(
Atom(atype="Si", xyz=[x, y, z], lattice=latt)
) # Motif part A
atom_list.append(
Atom(atype="Si", xyz=[x + 0.25, y + 0.25, z + 0.25], lattice=latt)
) # Motif part B
struct = Structure(atoms=atom_list, lattice=latt)
p = Phase(structure=struct, space_group=227)
def test_get_zone_axis(self, density, get_directions, phase):
if density == "5":
with pytest.raises(ValueError):
get_sample_zone_axis(phase=p, density=density)
get_sample_zone_axis(phase=phase, density=density)
else:
if get_directions:
rot, _ = get_sample_zone_axis(
phase=p, density=density, return_directions=True
phase=phase, density=density, return_directions=True
)
else:
rot = get_sample_zone_axis(phase=p, density=density)
rot = get_sample_zone_axis(phase=phase, density=density)
assert isinstance(rot, Rotation)
Loading