/
sample_generators.py
226 lines (192 loc) · 6.89 KB
/
sample_generators.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
# -*- coding: utf-8 -*-
# Copyright 2018-2024 the orix developers
#
# This file is part of orix.
#
# orix 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.
#
# orix 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 orix. If not, see <http://www.gnu.org/licenses/>.
from typing import Optional, Union
import numpy as np
from orix.quaternion import OrientationRegion, Rotation, Symmetry
from orix.quaternion.symmetry import C1, get_point_group
from orix.sampling import sample_S2
from orix.sampling.SO3_sampling import _three_uniform_samples_method, uniform_SO3_sample
from orix.sampling._cubochoric_sampling import cubochoric_sampling
def get_sample_fundamental(
resolution: Union[int, float] = 2,
point_group: Optional[Symmetry] = None,
space_group: Optional[int] = None,
method: str = "cubochoric",
**kwargs
) -> Rotation:
"""Return an equispaced grid of rotations within a fundamental zone.
Parameters
----------
resolution
The characteristic distance between a rotation and its neighbour
in degrees. Default is 2 degrees.
point_group
One of the 11 proper point groups. If not given, ``space_group``
must be.
space_group
Between 1 and 231. Must be given if ``point_group`` is not.
method
``"cubochoric"`` (default), ``"haar_euler"`` or
``"quaternion"``. See :func:`~orix.sampling.uniform_SO3_sample`
for details.
**kwargs
Keyword arguments passed on to the sampling method.
Returns
-------
rot
Grid of rotations lying within the specified fundamental zone.
See Also
--------
orix.sampling.uniform_SO3_sample
Examples
--------
>>> from orix.quaternion.symmetry import Oh
>>> from orix.sampling import get_sample_fundamental
>>> rot = get_sample_fundamental(5, point_group=Oh)
>>> rot
Rotation (6579,)
[[ 0.877 -0.2774 -0.2774 -0.2774]
[ 0.877 -0.2884 -0.2884 -0.2538]
[ 0.877 -0.2986 -0.2986 -0.2291]
...
[ 0.877 0.2986 0.2986 0.2291]
[ 0.877 0.2884 0.2884 0.2538]
[ 0.877 0.2774 0.2774 0.2774]]
"""
if point_group is None:
point_group = get_point_group(space_group, proper=True)
# TODO: provide some subspace selection options
rot = uniform_SO3_sample(resolution, method=method, unique=False, **kwargs)
fundamental_region = OrientationRegion.from_symmetry(point_group)
rot = rot[rot < fundamental_region]
rot = rot.unique()
return rot
def get_sample_local(
resolution: Union[int, float] = 2,
center: Optional[Rotation] = None,
grid_width: Union[int, float] = 10,
method: str = "cubochoric",
**kwargs
) -> Rotation:
"""Return a grid of rotations about a given rotation.
Parameters
----------
resolution
The characteristic distance between a rotation and its neighbour
in degrees. Default is 2 degrees.
center
The rotation at which the grid is centered. The identity is used
if not given.
grid_width
The largest angle of rotation in degrees away from center that
is acceptable. Default is 10 degrees.
method
``"cubochoric"`` (default), ``"haar_euler"`` or
``"quaternion"``. See :func:`~orix.sampling.uniform_SO3_sample`
for details.
**kwargs
Keyword arguments passed on to the sampling method.
Returns
-------
rot
Grid of rotations lying within ``grid_width`` of center.
See Also
--------
orix.sampling.uniform_SO3_sample
"""
if method == "haar_euler":
rot = uniform_SO3_sample(resolution, method=method, unique=False)
elif method == "quaternion":
rot = _three_uniform_samples_method(
resolution, unique=False, max_angle=grid_width
)
else: # method == "cubochoric"
rot = cubochoric_sampling(resolution=resolution, **kwargs)
rot = _remove_larger_than_angle(rot, grid_width)
rot = rot.unique()
if center is not None:
rot = center * rot
return rot
def _remove_larger_than_angle(rot: Rotation, max_angle: Union[int, float]) -> Rotation:
"""Remove large angle rotations from a sample of rotations.
Parameters
----------
rot
Sample of rotations.
max_angle
Maximum allowable angle (in degrees) from which a rotation can
differ from the origin.
Returns
-------
rot_out
Rotations lying within the desired region.
"""
half_angle = np.deg2rad(max_angle / 2)
half_angles = np.arccos(rot.a) # Returns between 0 and pi
mask = half_angles < half_angle
rot_out = rot[mask]
return rot_out
def get_sample_reduced_fundamental(
resolution: float = 2,
method: Optional[str] = None,
point_group: Optional[Symmetry] = None,
) -> Rotation:
r"""Return a grid of rotations that rotate the Z-vector (0, 0, 1)
into the fundamental sector of a point group's Laue group.
The rotations are constrained in that the first Euler angle is
:math:`\phi_1 = 0^{\circ}`.
Parameters
----------
resolution
The characteristic distance between a rotation and its neighbour
in degrees. Default is 2 degrees.
method
Name of method to mesh the unit sphere. See
:func:`orix.sampling.sample_S2` for options. If not given, a
suitable default is chosen given by the crystal system of the
given point group.
point_group
Point group with symmetry operations that define the
:attr:`~orix.quaternion.symmetry.Symmetry.fundamental_sector`
on the unit sphere. If not given, rotations that rotate the
Z-vector onto the whole sphere are returned.
Returns
-------
R
Rotations of shape ``(n, 3)``.
"""
if point_group is None:
point_group = C1
if method is None:
sampling_method = {
"triclinic": "icosahedral",
"monoclinic": "icosahedral",
"orthorhombic": "spherified_cube_edge",
"tetragonal": "spherified_cube_edge",
"cubic": "spherified_cube_edge",
"trigonal": "hexagonal",
"hexagonal": "hexagonal",
}
method = sampling_method[point_group.system]
v = sample_S2(resolution, method=method)
v_fs = v[v <= point_group.fundamental_sector]
phi = v_fs.polar
phi2 = (np.pi / 2 - v_fs.azimuth) % (2 * np.pi)
phi1 = np.zeros(phi2.shape[0])
euler = np.vstack([phi1, phi, phi2]).T
return Rotation.from_euler(euler, degrees=False)