Merge branch 'typehints_orientation_rotation' into 'development'
04 First typehints for rotation and orientation modules See merge request damask/DAMASK!479
This commit is contained in:
commit
fe0ff7cab2
|
@ -2,10 +2,11 @@ from typing import Union, Dict, List, Tuple
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
|
from ._typehints import FloatSequence, CrystalFamily, CrystalLattice, CrystalKinematics
|
||||||
from . import util
|
from . import util
|
||||||
from . import Rotation
|
from . import Rotation
|
||||||
|
|
||||||
lattice_symmetries = {
|
lattice_symmetries: Dict[CrystalLattice, CrystalFamily] = {
|
||||||
'aP': 'triclinic',
|
'aP': 'triclinic',
|
||||||
|
|
||||||
'mP': 'monoclinic',
|
'mP': 'monoclinic',
|
||||||
|
@ -30,9 +31,9 @@ lattice_symmetries = {
|
||||||
class Crystal():
|
class Crystal():
|
||||||
"""Crystal lattice."""
|
"""Crystal lattice."""
|
||||||
|
|
||||||
def __init__(self,*,
|
def __init__(self, *,
|
||||||
family = None,
|
family: CrystalFamily = None,
|
||||||
lattice = None,
|
lattice: CrystalLattice = None,
|
||||||
a: float = None, b: float = None, c: float = None,
|
a: float = None, b: float = None, c: float = None,
|
||||||
alpha: float = None, beta: float = None, gamma: float = None,
|
alpha: float = None, beta: float = None, gamma: float = None,
|
||||||
degrees: bool = False):
|
degrees: bool = False):
|
||||||
|
@ -130,9 +131,8 @@ class Crystal():
|
||||||
Crystal to check for equality.
|
Crystal to check for equality.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if not isinstance(other, Crystal):
|
return NotImplemented if not isinstance(other, Crystal) else \
|
||||||
return NotImplemented
|
self.lattice == other.lattice and \
|
||||||
return self.lattice == other.lattice and \
|
|
||||||
self.parameters == other.parameters and \
|
self.parameters == other.parameters and \
|
||||||
self.family == other.family
|
self.family == other.family
|
||||||
|
|
||||||
|
@ -208,7 +208,7 @@ class Crystal():
|
||||||
... }
|
... }
|
||||||
|
|
||||||
"""
|
"""
|
||||||
_basis = {
|
_basis: Dict[CrystalFamily, Dict[str, np.ndarray]] = {
|
||||||
'cubic': {'improper':np.array([ [-1. , 0. , 1. ],
|
'cubic': {'improper':np.array([ [-1. , 0. , 1. ],
|
||||||
[ np.sqrt(2.) , -np.sqrt(2.) , 0. ],
|
[ np.sqrt(2.) , -np.sqrt(2.) , 0. ],
|
||||||
[ 0. , np.sqrt(3.) , 0. ] ]),
|
[ 0. , np.sqrt(3.) , 0. ] ]),
|
||||||
|
@ -315,19 +315,19 @@ class Crystal():
|
||||||
self.lattice[-1],None),dtype=float)
|
self.lattice[-1],None),dtype=float)
|
||||||
|
|
||||||
def to_lattice(self, *,
|
def to_lattice(self, *,
|
||||||
direction: np.ndarray = None,
|
direction: FloatSequence = None,
|
||||||
plane: np.ndarray = None) -> np.ndarray:
|
plane: FloatSequence = None) -> np.ndarray:
|
||||||
"""
|
"""
|
||||||
Calculate lattice vector corresponding to crystal frame direction or plane normal.
|
Calculate lattice vector corresponding to crystal frame direction or plane normal.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
direction|plane : numpy.ndarray of shape (...,3)
|
direction|plane : numpy.ndarray, shape (...,3)
|
||||||
Vector along direction or plane normal.
|
Vector along direction or plane normal.
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
Miller : numpy.ndarray of shape (...,3)
|
Miller : numpy.ndarray, shape (...,3)
|
||||||
Lattice vector of direction or plane.
|
Lattice vector of direction or plane.
|
||||||
Use util.scale_to_coprime to convert to (integer) Miller indices.
|
Use util.scale_to_coprime to convert to (integer) Miller indices.
|
||||||
|
|
||||||
|
@ -341,19 +341,19 @@ class Crystal():
|
||||||
|
|
||||||
|
|
||||||
def to_frame(self, *,
|
def to_frame(self, *,
|
||||||
uvw: np.ndarray = None,
|
uvw: FloatSequence = None,
|
||||||
hkl: np.ndarray = None) -> np.ndarray:
|
hkl: FloatSequence = None) -> np.ndarray:
|
||||||
"""
|
"""
|
||||||
Calculate crystal frame vector along lattice direction [uvw] or plane normal (hkl).
|
Calculate crystal frame vector along lattice direction [uvw] or plane normal (hkl).
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
uvw|hkl : numpy.ndarray of shape (...,3)
|
uvw|hkl : numpy.ndarray, shape (...,3)
|
||||||
Miller indices of crystallographic direction or plane normal.
|
Miller indices of crystallographic direction or plane normal.
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
vector : numpy.ndarray of shape (...,3)
|
vector : numpy.ndarray, shape (...,3)
|
||||||
Crystal frame vector along [uvw] direction or (hkl) plane normal.
|
Crystal frame vector along [uvw] direction or (hkl) plane normal.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
@ -366,7 +366,7 @@ class Crystal():
|
||||||
|
|
||||||
|
|
||||||
def kinematics(self,
|
def kinematics(self,
|
||||||
mode: str) -> Dict[str, List[np.ndarray]]:
|
mode: CrystalKinematics) -> Dict[str, List[np.ndarray]]:
|
||||||
"""
|
"""
|
||||||
Return crystal kinematics systems.
|
Return crystal kinematics systems.
|
||||||
|
|
||||||
|
@ -381,7 +381,7 @@ class Crystal():
|
||||||
Directions and planes of deformation mode families.
|
Directions and planes of deformation mode families.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
_kinematics = {
|
_kinematics: Dict[CrystalLattice, Dict[CrystalKinematics, List[np.ndarray]]] = {
|
||||||
'cF': {
|
'cF': {
|
||||||
'slip': [np.array([
|
'slip': [np.array([
|
||||||
[+0,+1,-1, +1,+1,+1],
|
[+0,+1,-1, +1,+1,+1],
|
||||||
|
@ -626,7 +626,7 @@ class Crystal():
|
||||||
|
|
||||||
|
|
||||||
def relation_operations(self,
|
def relation_operations(self,
|
||||||
model: str) -> Tuple[str, Rotation]:
|
model: str) -> Tuple[CrystalLattice, Rotation]:
|
||||||
"""
|
"""
|
||||||
Crystallographic orientation relationships for phase transformations.
|
Crystallographic orientation relationships for phase transformations.
|
||||||
|
|
||||||
|
@ -658,7 +658,7 @@ class Crystal():
|
||||||
https://doi.org/10.1016/j.actamat.2004.11.021
|
https://doi.org/10.1016/j.actamat.2004.11.021
|
||||||
|
|
||||||
"""
|
"""
|
||||||
_orientation_relationships = {
|
_orientation_relationships: Dict[str, Dict[CrystalLattice,np.ndarray]] = {
|
||||||
'KS': {
|
'KS': {
|
||||||
'cF' : np.array([
|
'cF' : np.array([
|
||||||
[[-1, 0, 1],[ 1, 1, 1]],
|
[[-1, 0, 1],[ 1, 1, 1]],
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
import inspect
|
import inspect
|
||||||
import copy
|
import copy
|
||||||
|
from typing import Union, Callable, List, Dict, Any, Tuple, TypeVar
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
|
from ._typehints import FloatSequence, IntSequence, CrystalFamily, CrystalLattice
|
||||||
from . import Rotation
|
from . import Rotation
|
||||||
from . import Crystal
|
from . import Crystal
|
||||||
from . import util
|
from . import util
|
||||||
|
@ -33,6 +35,7 @@ _parameter_doc = \
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
MyType = TypeVar('MyType', bound='Orientation')
|
||||||
|
|
||||||
class Orientation(Rotation,Crystal):
|
class Orientation(Rotation,Crystal):
|
||||||
"""
|
"""
|
||||||
|
@ -93,12 +96,13 @@ class Orientation(Rotation,Crystal):
|
||||||
|
|
||||||
@util.extend_docstring(_parameter_doc)
|
@util.extend_docstring(_parameter_doc)
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
rotation = np.array([1.0,0.0,0.0,0.0]), *,
|
rotation: Union[FloatSequence, Rotation] = np.array([1.,0.,0.,0.]),
|
||||||
family = None,
|
*,
|
||||||
lattice = None,
|
family: CrystalFamily = None,
|
||||||
a = None,b = None,c = None,
|
lattice: CrystalLattice = None,
|
||||||
alpha = None,beta = None,gamma = None,
|
a: float = None, b: float = None, c: float = None,
|
||||||
degrees = False):
|
alpha: float = None, beta: float = None, gamma: float = None,
|
||||||
|
degrees: bool = False):
|
||||||
"""
|
"""
|
||||||
New orientation.
|
New orientation.
|
||||||
|
|
||||||
|
@ -115,13 +119,13 @@ class Orientation(Rotation,Crystal):
|
||||||
a=a,b=b,c=c, alpha=alpha,beta=beta,gamma=gamma, degrees=degrees)
|
a=a,b=b,c=c, alpha=alpha,beta=beta,gamma=gamma, degrees=degrees)
|
||||||
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self) -> str:
|
||||||
"""Represent."""
|
"""Represent."""
|
||||||
return '\n'.join([Crystal.__repr__(self),
|
return '\n'.join([Crystal.__repr__(self),
|
||||||
Rotation.__repr__(self)])
|
Rotation.__repr__(self)])
|
||||||
|
|
||||||
|
def __copy__(self: MyType,
|
||||||
def __copy__(self,rotation=None):
|
rotation: Union[FloatSequence, Rotation] = None) -> MyType:
|
||||||
"""Create deep copy."""
|
"""Create deep copy."""
|
||||||
dup = copy.deepcopy(self)
|
dup = copy.deepcopy(self)
|
||||||
if rotation is not None:
|
if rotation is not None:
|
||||||
|
@ -131,7 +135,9 @@ class Orientation(Rotation,Crystal):
|
||||||
copy = __copy__
|
copy = __copy__
|
||||||
|
|
||||||
|
|
||||||
def __eq__(self,other):
|
|
||||||
|
def __eq__(self,
|
||||||
|
other: object) -> bool:
|
||||||
"""
|
"""
|
||||||
Equal to other.
|
Equal to other.
|
||||||
|
|
||||||
|
@ -141,12 +147,15 @@ class Orientation(Rotation,Crystal):
|
||||||
Orientation to check for equality.
|
Orientation to check for equality.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
if not isinstance(other, Orientation):
|
||||||
|
return NotImplemented
|
||||||
matching_type = self.family == other.family and \
|
matching_type = self.family == other.family and \
|
||||||
self.lattice == other.lattice and \
|
self.lattice == other.lattice and \
|
||||||
self.parameters == other.parameters
|
self.parameters == other.parameters
|
||||||
return np.logical_and(matching_type,super(self.__class__,self.reduced).__eq__(other.reduced))
|
return np.logical_and(matching_type,super(self.__class__,self.reduced).__eq__(other.reduced))
|
||||||
|
|
||||||
def __ne__(self,other):
|
def __ne__(self,
|
||||||
|
other: object) -> bool:
|
||||||
"""
|
"""
|
||||||
Not equal to other.
|
Not equal to other.
|
||||||
|
|
||||||
|
@ -156,10 +165,14 @@ class Orientation(Rotation,Crystal):
|
||||||
Orientation to check for equality.
|
Orientation to check for equality.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return np.logical_not(self==other)
|
return np.logical_not(self==other) if isinstance(other, Orientation) else NotImplemented
|
||||||
|
|
||||||
|
|
||||||
def isclose(self,other,rtol=1e-5,atol=1e-8,equal_nan=True):
|
def isclose(self: MyType,
|
||||||
|
other: MyType,
|
||||||
|
rtol: float = 1e-5,
|
||||||
|
atol: float = 1e-8,
|
||||||
|
equal_nan: bool = True) -> bool:
|
||||||
"""
|
"""
|
||||||
Report where values are approximately equal to corresponding ones of other Orientation.
|
Report where values are approximately equal to corresponding ones of other Orientation.
|
||||||
|
|
||||||
|
@ -176,7 +189,7 @@ class Orientation(Rotation,Crystal):
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
mask : numpy.ndarray bool
|
mask : numpy.ndarray of bool, shape (self.shape)
|
||||||
Mask indicating where corresponding orientations are close.
|
Mask indicating where corresponding orientations are close.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
@ -187,7 +200,11 @@ class Orientation(Rotation,Crystal):
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def allclose(self,other,rtol=1e-5,atol=1e-8,equal_nan=True):
|
def allclose(self: MyType,
|
||||||
|
other: MyType,
|
||||||
|
rtol: float = 1e-5,
|
||||||
|
atol: float = 1e-8,
|
||||||
|
equal_nan: bool = True) -> bool:
|
||||||
"""
|
"""
|
||||||
Test whether all values are approximately equal to corresponding ones of other Orientation.
|
Test whether all values are approximately equal to corresponding ones of other Orientation.
|
||||||
|
|
||||||
|
@ -208,10 +225,11 @@ class Orientation(Rotation,Crystal):
|
||||||
Whether all values are close between both orientations.
|
Whether all values are close between both orientations.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return np.all(self.isclose(other,rtol,atol,equal_nan))
|
return bool(np.all(self.isclose(other,rtol,atol,equal_nan)))
|
||||||
|
|
||||||
|
|
||||||
def __mul__(self,other):
|
def __mul__(self: MyType,
|
||||||
|
other: Union[Rotation, 'Orientation']) -> MyType:
|
||||||
"""
|
"""
|
||||||
Compose this orientation with other.
|
Compose this orientation with other.
|
||||||
|
|
||||||
|
@ -226,14 +244,15 @@ class Orientation(Rotation,Crystal):
|
||||||
Compound rotation self*other, i.e. first other then self rotation.
|
Compound rotation self*other, i.e. first other then self rotation.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if isinstance(other,Orientation) or isinstance(other,Rotation):
|
if isinstance(other, (Orientation,Rotation)):
|
||||||
return self.copy(rotation=Rotation.__mul__(self,Rotation(other.quaternion)))
|
return self.copy(Rotation(self.quaternion)*Rotation(other.quaternion))
|
||||||
else:
|
else:
|
||||||
raise TypeError('use "O@b", i.e. matmul, to apply Orientation "O" to object "b"')
|
raise TypeError('use "O@b", i.e. matmul, to apply Orientation "O" to object "b"')
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _split_kwargs(kwargs,target):
|
def _split_kwargs(kwargs: Dict[str, Any],
|
||||||
|
target: Callable) -> Tuple[Dict[str, Any], ...]:
|
||||||
"""
|
"""
|
||||||
Separate keyword arguments in 'kwargs' targeted at 'target' from general keyword arguments of Orientation objects.
|
Separate keyword arguments in 'kwargs' targeted at 'target' from general keyword arguments of Orientation objects.
|
||||||
|
|
||||||
|
@ -252,7 +271,7 @@ class Orientation(Rotation,Crystal):
|
||||||
Valid keyword arguments of Orientation object.
|
Valid keyword arguments of Orientation object.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
kws = ()
|
kws: Tuple[Dict[str, Any], ...] = ()
|
||||||
for t in (target,Orientation.__init__):
|
for t in (target,Orientation.__init__):
|
||||||
kws += ({key: kwargs[key] for key in set(inspect.signature(t).parameters) & set(kwargs)},)
|
kws += ({key: kwargs[key] for key in set(inspect.signature(t).parameters) & set(kwargs)},)
|
||||||
|
|
||||||
|
@ -264,105 +283,108 @@ class Orientation(Rotation,Crystal):
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@util.extended_docstring(Rotation.from_random,_parameter_doc)
|
@util.extended_docstring(Rotation.from_random, _parameter_doc)
|
||||||
def from_random(cls,**kwargs):
|
def from_random(cls, **kwargs) -> 'Orientation':
|
||||||
kwargs_rot,kwargs_ori = Orientation._split_kwargs(kwargs,Rotation.from_random)
|
kwargs_rot,kwargs_ori = Orientation._split_kwargs(kwargs,Rotation.from_random)
|
||||||
return cls(rotation=Rotation.from_random(**kwargs_rot),**kwargs_ori)
|
return cls(rotation=Rotation.from_random(**kwargs_rot),**kwargs_ori)
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@util.extended_docstring(Rotation.from_quaternion,_parameter_doc)
|
@util.extended_docstring(Rotation.from_quaternion,_parameter_doc)
|
||||||
def from_quaternion(cls,**kwargs):
|
def from_quaternion(cls, **kwargs) -> 'Orientation':
|
||||||
kwargs_rot,kwargs_ori = Orientation._split_kwargs(kwargs,Rotation.from_quaternion)
|
kwargs_rot,kwargs_ori = Orientation._split_kwargs(kwargs,Rotation.from_quaternion)
|
||||||
return cls(rotation=Rotation.from_quaternion(**kwargs_rot),**kwargs_ori)
|
return cls(rotation=Rotation.from_quaternion(**kwargs_rot),**kwargs_ori)
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@util.extended_docstring(Rotation.from_Euler_angles,_parameter_doc)
|
@util.extended_docstring(Rotation.from_Euler_angles,_parameter_doc)
|
||||||
def from_Euler_angles(cls,**kwargs):
|
def from_Euler_angles(cls, **kwargs) -> 'Orientation':
|
||||||
kwargs_rot,kwargs_ori = Orientation._split_kwargs(kwargs,Rotation.from_Euler_angles)
|
kwargs_rot,kwargs_ori = Orientation._split_kwargs(kwargs,Rotation.from_Euler_angles)
|
||||||
return cls(rotation=Rotation.from_Euler_angles(**kwargs_rot),**kwargs_ori)
|
return cls(rotation=Rotation.from_Euler_angles(**kwargs_rot),**kwargs_ori)
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@util.extended_docstring(Rotation.from_axis_angle,_parameter_doc)
|
@util.extended_docstring(Rotation.from_axis_angle,_parameter_doc)
|
||||||
def from_axis_angle(cls,**kwargs):
|
def from_axis_angle(cls, **kwargs) -> 'Orientation':
|
||||||
kwargs_rot,kwargs_ori = Orientation._split_kwargs(kwargs,Rotation.from_axis_angle)
|
kwargs_rot,kwargs_ori = Orientation._split_kwargs(kwargs,Rotation.from_axis_angle)
|
||||||
return cls(rotation=Rotation.from_axis_angle(**kwargs_rot),**kwargs_ori)
|
return cls(rotation=Rotation.from_axis_angle(**kwargs_rot),**kwargs_ori)
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@util.extended_docstring(Rotation.from_basis,_parameter_doc)
|
@util.extended_docstring(Rotation.from_basis,_parameter_doc)
|
||||||
def from_basis(cls,**kwargs):
|
def from_basis(cls, **kwargs) -> 'Orientation':
|
||||||
kwargs_rot,kwargs_ori = Orientation._split_kwargs(kwargs,Rotation.from_basis)
|
kwargs_rot,kwargs_ori = Orientation._split_kwargs(kwargs,Rotation.from_basis)
|
||||||
return cls(rotation=Rotation.from_basis(**kwargs_rot),**kwargs_ori)
|
return cls(rotation=Rotation.from_basis(**kwargs_rot),**kwargs_ori)
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@util.extended_docstring(Rotation.from_matrix,_parameter_doc)
|
@util.extended_docstring(Rotation.from_matrix,_parameter_doc)
|
||||||
def from_matrix(cls,**kwargs):
|
def from_matrix(cls, **kwargs) -> 'Orientation':
|
||||||
kwargs_rot,kwargs_ori = Orientation._split_kwargs(kwargs,Rotation.from_matrix)
|
kwargs_rot,kwargs_ori = Orientation._split_kwargs(kwargs,Rotation.from_matrix)
|
||||||
return cls(rotation=Rotation.from_matrix(**kwargs_rot),**kwargs_ori)
|
return cls(rotation=Rotation.from_matrix(**kwargs_rot),**kwargs_ori)
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@util.extended_docstring(Rotation.from_Rodrigues_vector,_parameter_doc)
|
@util.extended_docstring(Rotation.from_Rodrigues_vector,_parameter_doc)
|
||||||
def from_Rodrigues_vector(cls,**kwargs):
|
def from_Rodrigues_vector(cls, **kwargs) -> 'Orientation':
|
||||||
kwargs_rot,kwargs_ori = Orientation._split_kwargs(kwargs,Rotation.from_Rodrigues_vector)
|
kwargs_rot,kwargs_ori = Orientation._split_kwargs(kwargs,Rotation.from_Rodrigues_vector)
|
||||||
return cls(rotation=Rotation.from_Rodrigues_vector(**kwargs_rot),**kwargs_ori)
|
return cls(rotation=Rotation.from_Rodrigues_vector(**kwargs_rot),**kwargs_ori)
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@util.extended_docstring(Rotation.from_homochoric,_parameter_doc)
|
@util.extended_docstring(Rotation.from_homochoric,_parameter_doc)
|
||||||
def from_homochoric(cls,**kwargs):
|
def from_homochoric(cls, **kwargs) -> 'Orientation':
|
||||||
kwargs_rot,kwargs_ori = Orientation._split_kwargs(kwargs,Rotation.from_homochoric)
|
kwargs_rot,kwargs_ori = Orientation._split_kwargs(kwargs,Rotation.from_homochoric)
|
||||||
return cls(rotation=Rotation.from_homochoric(**kwargs_rot),**kwargs_ori)
|
return cls(rotation=Rotation.from_homochoric(**kwargs_rot),**kwargs_ori)
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@util.extended_docstring(Rotation.from_cubochoric,_parameter_doc)
|
@util.extended_docstring(Rotation.from_cubochoric,_parameter_doc)
|
||||||
def from_cubochoric(cls,**kwargs):
|
def from_cubochoric(cls, **kwargs) -> 'Orientation':
|
||||||
kwargs_rot,kwargs_ori = Orientation._split_kwargs(kwargs,Rotation.from_cubochoric)
|
kwargs_rot,kwargs_ori = Orientation._split_kwargs(kwargs,Rotation.from_cubochoric)
|
||||||
return cls(rotation=Rotation.from_cubochoric(**kwargs_rot),**kwargs_ori)
|
return cls(rotation=Rotation.from_cubochoric(**kwargs_rot),**kwargs_ori)
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@util.extended_docstring(Rotation.from_spherical_component,_parameter_doc)
|
@util.extended_docstring(Rotation.from_spherical_component,_parameter_doc)
|
||||||
def from_spherical_component(cls,**kwargs):
|
def from_spherical_component(cls, **kwargs) -> 'Orientation':
|
||||||
kwargs_rot,kwargs_ori = Orientation._split_kwargs(kwargs,Rotation.from_spherical_component)
|
kwargs_rot,kwargs_ori = Orientation._split_kwargs(kwargs,Rotation.from_spherical_component)
|
||||||
return cls(rotation=Rotation.from_spherical_component(**kwargs_rot),**kwargs_ori)
|
return cls(rotation=Rotation.from_spherical_component(**kwargs_rot),**kwargs_ori)
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@util.extended_docstring(Rotation.from_fiber_component,_parameter_doc)
|
@util.extended_docstring(Rotation.from_fiber_component,_parameter_doc)
|
||||||
def from_fiber_component(cls,**kwargs):
|
def from_fiber_component(cls, **kwargs) -> 'Orientation':
|
||||||
kwargs_rot,kwargs_ori = Orientation._split_kwargs(kwargs,Rotation.from_fiber_component)
|
kwargs_rot,kwargs_ori = Orientation._split_kwargs(kwargs,Rotation.from_fiber_component)
|
||||||
return cls(rotation=Rotation.from_fiber_component(**kwargs_rot),**kwargs_ori)
|
return cls(rotation=Rotation.from_fiber_component(**kwargs_rot),**kwargs_ori)
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@util.extend_docstring(_parameter_doc)
|
@util.extend_docstring(_parameter_doc)
|
||||||
def from_directions(cls,uvw,hkl,**kwargs):
|
def from_directions(cls,
|
||||||
|
uvw: FloatSequence,
|
||||||
|
hkl: FloatSequence,
|
||||||
|
**kwargs) -> 'Orientation':
|
||||||
"""
|
"""
|
||||||
Initialize orientation object from two crystallographic directions.
|
Initialize orientation object from two crystallographic directions.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
uvw : list, numpy.ndarray of shape (...,3)
|
uvw : numpy.ndarray, shape (...,3)
|
||||||
lattice direction aligned with lab frame x-direction.
|
Lattice direction aligned with lab frame x-direction.
|
||||||
hkl : list, numpy.ndarray of shape (...,3)
|
hkl : numpy.ndarray, shape (...,3)
|
||||||
lattice plane normal aligned with lab frame z-direction.
|
Lattice plane normal aligned with lab frame z-direction.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
o = cls(**kwargs)
|
o = cls(**kwargs)
|
||||||
x = o.to_frame(uvw=uvw)
|
x = o.to_frame(uvw=uvw)
|
||||||
z = o.to_frame(hkl=hkl)
|
z = o.to_frame(hkl=hkl)
|
||||||
om = np.stack([x,np.cross(z,x),z],axis=-2)
|
om = np.stack([x,np.cross(z,x),z],axis=-2)
|
||||||
return o.copy(rotation=Rotation.from_matrix(tensor.transpose(om/np.linalg.norm(om,axis=-1,keepdims=True))))
|
return o.copy(Rotation.from_matrix(tensor.transpose(om/np.linalg.norm(om,axis=-1,keepdims=True))))
|
||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def equivalent(self):
|
def equivalent(self: MyType) -> MyType:
|
||||||
"""
|
"""
|
||||||
Orientations that are symmetrically equivalent.
|
Orientations that are symmetrically equivalent.
|
||||||
|
|
||||||
|
@ -372,11 +394,11 @@ class Orientation(Rotation,Crystal):
|
||||||
"""
|
"""
|
||||||
sym_ops = self.symmetry_operations
|
sym_ops = self.symmetry_operations
|
||||||
o = sym_ops.broadcast_to(sym_ops.shape+self.shape,mode='right')
|
o = sym_ops.broadcast_to(sym_ops.shape+self.shape,mode='right')
|
||||||
return self.copy(rotation=o*Rotation(self.quaternion).broadcast_to(o.shape,mode='left'))
|
return self.copy(o*Rotation(self.quaternion).broadcast_to(o.shape,mode='left'))
|
||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def reduced(self):
|
def reduced(self: MyType) -> MyType:
|
||||||
"""Select symmetrically equivalent orientation that falls into fundamental zone according to symmetry."""
|
"""Select symmetrically equivalent orientation that falls into fundamental zone according to symmetry."""
|
||||||
eq = self.equivalent
|
eq = self.equivalent
|
||||||
ok = eq.in_FZ
|
ok = eq.in_FZ
|
||||||
|
@ -387,13 +409,13 @@ class Orientation(Rotation,Crystal):
|
||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def in_FZ(self):
|
def in_FZ(self) -> Union[np.bool_, np.ndarray]:
|
||||||
"""
|
"""
|
||||||
Check whether orientation falls into fundamental zone of own symmetry.
|
Check whether orientation falls into fundamental zone of own symmetry.
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
in : numpy.ndarray of bool, quaternion.shape
|
in : numpy.ndarray of bool, shape (self.shape)
|
||||||
Whether Rodrigues-Frank vector falls into fundamental zone.
|
Whether Rodrigues-Frank vector falls into fundamental zone.
|
||||||
|
|
||||||
Notes
|
Notes
|
||||||
|
@ -431,13 +453,13 @@ class Orientation(Rotation,Crystal):
|
||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def in_disorientation_FZ(self):
|
def in_disorientation_FZ(self) -> np.ndarray:
|
||||||
"""
|
"""
|
||||||
Check whether orientation falls into fundamental zone of disorientations.
|
Check whether orientation falls into fundamental zone of disorientations.
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
in : numpy.ndarray of bool, quaternion.shape
|
in : numpy.ndarray of bool, shape (self.shape)
|
||||||
Whether Rodrigues-Frank vector falls into disorientation FZ.
|
Whether Rodrigues-Frank vector falls into disorientation FZ.
|
||||||
|
|
||||||
References
|
References
|
||||||
|
@ -471,8 +493,9 @@ class Orientation(Rotation,Crystal):
|
||||||
else:
|
else:
|
||||||
return np.ones_like(rho[...,0],dtype=bool)
|
return np.ones_like(rho[...,0],dtype=bool)
|
||||||
|
|
||||||
|
def disorientation(self,
|
||||||
def disorientation(self,other,return_operators=False):
|
other: 'Orientation',
|
||||||
|
return_operators: bool = False) -> object:
|
||||||
"""
|
"""
|
||||||
Calculate disorientation between myself and given other orientation.
|
Calculate disorientation between myself and given other orientation.
|
||||||
|
|
||||||
|
@ -490,7 +513,7 @@ class Orientation(Rotation,Crystal):
|
||||||
-------
|
-------
|
||||||
disorientation : Orientation
|
disorientation : Orientation
|
||||||
Disorientation between self and other.
|
Disorientation between self and other.
|
||||||
operators : numpy.ndarray int of shape (...,2), conditional
|
operators : numpy.ndarray of int, shape (...,2), conditional
|
||||||
Index of symmetrically equivalent orientation that rotated vector to the SST.
|
Index of symmetrically equivalent orientation that rotated vector to the SST.
|
||||||
|
|
||||||
Notes
|
Notes
|
||||||
|
@ -557,13 +580,15 @@ class Orientation(Rotation,Crystal):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def average(self,weights=None,return_cloud=False):
|
def average(self,
|
||||||
|
weights: FloatSequence = None,
|
||||||
|
return_cloud: bool = False):
|
||||||
"""
|
"""
|
||||||
Return orientation average over last dimension.
|
Return orientation average over last dimension.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
weights : numpy.ndarray, optional
|
weights : numpy.ndarray, shape (self.shape), optional
|
||||||
Relative weights of orientations.
|
Relative weights of orientations.
|
||||||
return_cloud : bool, optional
|
return_cloud : bool, optional
|
||||||
Return the set of symmetrically equivalent orientations that was used in averaging.
|
Return the set of symmetrically equivalent orientations that was used in averaging.
|
||||||
|
@ -583,31 +608,30 @@ class Orientation(Rotation,Crystal):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
eq = self.equivalent
|
eq = self.equivalent
|
||||||
m = eq.misorientation(self[...,0].reshape((1,)+self.shape[:-1]+(1,))
|
m = eq.misorientation(self[...,0].reshape((1,)+self.shape[:-1]+(1,)) # type: ignore
|
||||||
.broadcast_to(eq.shape))\
|
.broadcast_to(eq.shape)).as_axis_angle()[...,3] # type: ignore
|
||||||
.as_axis_angle()[...,3]
|
|
||||||
r = Rotation(np.squeeze(np.take_along_axis(eq.quaternion,
|
r = Rotation(np.squeeze(np.take_along_axis(eq.quaternion,
|
||||||
np.argmin(m,axis=0)[np.newaxis,...,np.newaxis],
|
np.argmin(m,axis=0)[np.newaxis,...,np.newaxis],
|
||||||
axis=0),
|
axis=0),
|
||||||
axis=0))
|
axis=0))
|
||||||
return (
|
return ((self.copy(Rotation(r).average(weights)),self.copy(Rotation(r))) if return_cloud else
|
||||||
(self.copy(rotation=Rotation(r).average(weights)),
|
self.copy(Rotation(r).average(weights))
|
||||||
self.copy(rotation=Rotation(r)))
|
|
||||||
if return_cloud else
|
|
||||||
self.copy(rotation=Rotation(r).average(weights))
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def to_SST(self,vector,proper=False,return_operators=False):
|
def to_SST(self,
|
||||||
|
vector: FloatSequence,
|
||||||
|
proper: bool = False,
|
||||||
|
return_operators: bool = False) -> np.ndarray:
|
||||||
"""
|
"""
|
||||||
Rotate vector to ensure it falls into (improper or proper) standard stereographic triangle of crystal symmetry.
|
Rotate vector to ensure it falls into (improper or proper) standard stereographic triangle of crystal symmetry.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
vector : numpy.ndarray of shape (...,3)
|
vector : numpy.ndarray, shape (...,3)
|
||||||
Lab frame vector to align with crystal frame direction.
|
Lab frame vector to align with crystal frame direction.
|
||||||
Shape of vector blends with shape of own rotation array.
|
Shape of vector blends with shape of own rotation array.
|
||||||
For example, a rotation array of shape (3,2) and a (2,4) vector array result in (3,2,4) outputs.
|
For example, a rotation array of shape (3,2) and a vector array of shape (2,4) result in (3,2,4) outputs.
|
||||||
proper : bool, optional
|
proper : bool, optional
|
||||||
Consider only vectors with z >= 0, hence combine two neighboring SSTs.
|
Consider only vectors with z >= 0, hence combine two neighboring SSTs.
|
||||||
Defaults to False.
|
Defaults to False.
|
||||||
|
@ -617,15 +641,18 @@ class Orientation(Rotation,Crystal):
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
vector_SST : numpy.ndarray of shape (...,3)
|
vector_SST : numpy.ndarray, shape (...,3)
|
||||||
Rotated vector falling into SST.
|
Rotated vector falling into SST.
|
||||||
operators : numpy.ndarray int of shape (...), conditional
|
operators : numpy.ndarray of int, shape (...), conditional
|
||||||
Index of symmetrically equivalent orientation that rotated vector to SST.
|
Index of symmetrically equivalent orientation that rotated vector to SST.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
vector_ = np.array(vector,float)
|
||||||
|
if vector_.shape[-1] != 3:
|
||||||
|
raise ValueError('input is not a field of three-dimensional vectors')
|
||||||
eq = self.equivalent
|
eq = self.equivalent
|
||||||
blend = util.shapeblender(eq.shape,np.array(vector).shape[:-1])
|
blend = util.shapeblender(eq.shape,vector_.shape[:-1])
|
||||||
poles = eq.broadcast_to(blend,mode='right') @ np.broadcast_to(np.array(vector),blend+(3,))
|
poles = eq.broadcast_to(blend,mode='right') @ np.broadcast_to(vector_,blend+(3,))
|
||||||
ok = self.in_SST(poles,proper=proper)
|
ok = self.in_SST(poles,proper=proper)
|
||||||
ok &= np.cumsum(ok,axis=0) == 1
|
ok &= np.cumsum(ok,axis=0) == 1
|
||||||
loc = np.where(ok)
|
loc = np.where(ok)
|
||||||
|
@ -637,13 +664,15 @@ class Orientation(Rotation,Crystal):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def in_SST(self,vector,proper=False):
|
def in_SST(self,
|
||||||
|
vector: FloatSequence,
|
||||||
|
proper: bool = False) -> Union[np.bool_, np.ndarray]:
|
||||||
"""
|
"""
|
||||||
Check whether given crystal frame vector falls into standard stereographic triangle of own symmetry.
|
Check whether given crystal frame vector falls into standard stereographic triangle of own symmetry.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
vector : numpy.ndarray of shape (...,3)
|
vector : numpy.ndarray, shape (...,3)
|
||||||
Vector to check.
|
Vector to check.
|
||||||
proper : bool, optional
|
proper : bool, optional
|
||||||
Consider only vectors with z >= 0, hence combine two neighboring SSTs.
|
Consider only vectors with z >= 0, hence combine two neighboring SSTs.
|
||||||
|
@ -655,39 +684,43 @@ class Orientation(Rotation,Crystal):
|
||||||
Whether vector falls into SST.
|
Whether vector falls into SST.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if not isinstance(vector,np.ndarray) or vector.shape[-1] != 3:
|
vector_ = np.array(vector,float)
|
||||||
|
if vector_.shape[-1] != 3:
|
||||||
raise ValueError('input is not a field of three-dimensional vectors')
|
raise ValueError('input is not a field of three-dimensional vectors')
|
||||||
|
|
||||||
if self.standard_triangle is None: # direct exit for no symmetry
|
if self.standard_triangle is None: # direct exit for no symmetry
|
||||||
return np.ones_like(vector[...,0],bool)
|
return np.ones_like(vector_[...,0],bool)
|
||||||
|
|
||||||
if proper:
|
if proper:
|
||||||
components_proper = np.around(np.einsum('...ji,...i',
|
components_proper = np.around(np.einsum('...ji,...i',
|
||||||
np.broadcast_to(self.standard_triangle['proper'], vector.shape+(3,)),
|
np.broadcast_to(self.standard_triangle['proper'], vector_.shape+(3,)),
|
||||||
vector), 12)
|
vector_), 12)
|
||||||
components_improper = np.around(np.einsum('...ji,...i',
|
components_improper = np.around(np.einsum('...ji,...i',
|
||||||
np.broadcast_to(self.standard_triangle['improper'], vector.shape+(3,)),
|
np.broadcast_to(self.standard_triangle['improper'], vector_.shape+(3,)),
|
||||||
vector), 12)
|
vector_), 12)
|
||||||
return np.all(components_proper >= 0.0,axis=-1) \
|
return np.all(components_proper >= 0.0,axis=-1) \
|
||||||
| np.all(components_improper >= 0.0,axis=-1)
|
| np.all(components_improper >= 0.0,axis=-1)
|
||||||
else:
|
else:
|
||||||
components = np.around(np.einsum('...ji,...i',
|
components = np.around(np.einsum('...ji,...i',
|
||||||
np.broadcast_to(self.standard_triangle['improper'], vector.shape+(3,)),
|
np.broadcast_to(self.standard_triangle['improper'], vector_.shape+(3,)),
|
||||||
np.block([vector[...,:2],np.abs(vector[...,2:3])])), 12)
|
np.block([vector_[...,:2],np.abs(vector_[...,2:3])])), 12)
|
||||||
|
|
||||||
return np.all(components >= 0.0,axis=-1)
|
return np.all(components >= 0.0,axis=-1)
|
||||||
|
|
||||||
|
|
||||||
def IPF_color(self,vector,in_SST=True,proper=False):
|
def IPF_color(self,
|
||||||
|
vector: FloatSequence,
|
||||||
|
in_SST: bool = True,
|
||||||
|
proper: bool = False) -> np.ndarray:
|
||||||
"""
|
"""
|
||||||
Map vector to RGB color within standard stereographic triangle of own symmetry.
|
Map vector to RGB color within standard stereographic triangle of own symmetry.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
vector : numpy.ndarray of shape (...,3)
|
vector : numpy.ndarray, shape (...,3)
|
||||||
Vector to colorize.
|
Vector to colorize.
|
||||||
Shape of vector blends with shape of own rotation array.
|
Shape of vector blends with shape of own rotation array.
|
||||||
For example, a rotation array of shape (3,2) and a (2,4) vector array result in (3,2,4) outputs.
|
For example, a rotation array of shape (3,2) and a vector array of shape (2,4) result in (3,2,4) outputs.
|
||||||
in_SST : bool, optional
|
in_SST : bool, optional
|
||||||
Consider symmetrically equivalent orientations such that poles are located in SST.
|
Consider symmetrically equivalent orientations such that poles are located in SST.
|
||||||
Defaults to True.
|
Defaults to True.
|
||||||
|
@ -697,7 +730,7 @@ class Orientation(Rotation,Crystal):
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
rgb : numpy.ndarray of shape (...,3)
|
rgb : numpy.ndarray, shape (...,3)
|
||||||
RGB array of IPF colors.
|
RGB array of IPF colors.
|
||||||
|
|
||||||
Examples
|
Examples
|
||||||
|
@ -726,30 +759,30 @@ class Orientation(Rotation,Crystal):
|
||||||
components_improper = np.around(np.einsum('...ji,...i',
|
components_improper = np.around(np.einsum('...ji,...i',
|
||||||
np.broadcast_to(self.standard_triangle['improper'], vector_.shape+(3,)),
|
np.broadcast_to(self.standard_triangle['improper'], vector_.shape+(3,)),
|
||||||
vector_), 12)
|
vector_), 12)
|
||||||
in_SST = np.all(components_proper >= 0.0,axis=-1) \
|
in_SST_ = np.all(components_proper >= 0.0,axis=-1) \
|
||||||
| np.all(components_improper >= 0.0,axis=-1)
|
| np.all(components_improper >= 0.0,axis=-1)
|
||||||
components = np.where((in_SST & np.all(components_proper >= 0.0,axis=-1))[...,np.newaxis],
|
components = np.where((in_SST_ & np.all(components_proper >= 0.0,axis=-1))[...,np.newaxis],
|
||||||
components_proper,components_improper)
|
components_proper,components_improper)
|
||||||
else:
|
else:
|
||||||
components = np.around(np.einsum('...ji,...i',
|
components = np.around(np.einsum('...ji,...i',
|
||||||
np.broadcast_to(self .standard_triangle['improper'], vector_.shape+(3,)),
|
np.broadcast_to(self .standard_triangle['improper'], vector_.shape+(3,)),
|
||||||
np.block([vector_[...,:2],np.abs(vector_[...,2:3])])), 12)
|
np.block([vector_[...,:2],np.abs(vector_[...,2:3])])), 12)
|
||||||
|
|
||||||
in_SST = np.all(components >= 0.0,axis=-1)
|
in_SST_ = np.all(components >= 0.0,axis=-1)
|
||||||
|
|
||||||
with np.errstate(invalid='ignore',divide='ignore'):
|
with np.errstate(invalid='ignore',divide='ignore'):
|
||||||
rgb = (components/np.linalg.norm(components,axis=-1,keepdims=True))**0.5 # smoothen color ramps
|
rgb = (components/np.linalg.norm(components,axis=-1,keepdims=True))**0.5 # smoothen color ramps
|
||||||
rgb = np.clip(rgb,0.,1.) # clip intensity
|
rgb = np.clip(rgb,0.,1.) # clip intensity
|
||||||
rgb /= np.max(rgb,axis=-1,keepdims=True) # normalize to (HS)V = 1
|
rgb /= np.max(rgb,axis=-1,keepdims=True) # normalize to (HS)V = 1
|
||||||
rgb[np.broadcast_to(~in_SST[...,np.newaxis],rgb.shape)] = 0.0
|
rgb[np.broadcast_to(~in_SST_[...,np.newaxis],rgb.shape)] = 0.0
|
||||||
|
|
||||||
return rgb
|
return rgb
|
||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def symmetry_operations(self):
|
def symmetry_operations(self) -> Rotation:
|
||||||
"""Symmetry operations as Rotations."""
|
"""Symmetry operations as Rotations."""
|
||||||
_symmetry_operations = {
|
_symmetry_operations: Dict[CrystalFamily, List] = {
|
||||||
'cubic': [
|
'cubic': [
|
||||||
[ 1.0, 0.0, 0.0, 0.0 ],
|
[ 1.0, 0.0, 0.0, 0.0 ],
|
||||||
[ 0.0, 1.0, 0.0, 0.0 ],
|
[ 0.0, 1.0, 0.0, 0.0 ],
|
||||||
|
@ -819,22 +852,25 @@ class Orientation(Rotation,Crystal):
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
# functions that require lattice, not just family
|
# functions that require lattice, not just family
|
||||||
|
|
||||||
def to_pole(self,*,uvw=None,hkl=None,with_symmetry=False):
|
def to_pole(self, *,
|
||||||
|
uvw: FloatSequence = None,
|
||||||
|
hkl: FloatSequence = None,
|
||||||
|
with_symmetry: bool = False) -> np.ndarray:
|
||||||
"""
|
"""
|
||||||
Calculate lab frame vector along lattice direction [uvw] or plane normal (hkl).
|
Calculate lab frame vector along lattice direction [uvw] or plane normal (hkl).
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
uvw|hkl : numpy.ndarray of shape (...,3)
|
uvw|hkl : numpy.ndarray, shape (...,3)
|
||||||
Miller indices of crystallographic direction or plane normal.
|
Miller indices of crystallographic direction or plane normal.
|
||||||
Shape of vector blends with shape of own rotation array.
|
Shape of vector blends with shape of own rotation array.
|
||||||
For example, a rotation array of shape (3,2) and a (2,4) vector array result in (3,2,4) outputs.
|
For example, a rotation array, shape (3,2) and a vector array of shape (2,4) result in (3,2,4) outputs.
|
||||||
with_symmetry : bool, optional
|
with_symmetry : bool, optional
|
||||||
Calculate all N symmetrically equivalent vectors.
|
Calculate all N symmetrically equivalent vectors.
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
vector : numpy.ndarray of shape (...,3) or (...,N,3)
|
vector : numpy.ndarray, shape (...,3) or (...,N,3)
|
||||||
Lab frame vector (or vectors if with_symmetry) along [uvw] direction or (hkl) plane normal.
|
Lab frame vector (or vectors if with_symmetry) along [uvw] direction or (hkl) plane normal.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
@ -846,23 +882,24 @@ class Orientation(Rotation,Crystal):
|
||||||
blend += sym_ops.shape
|
blend += sym_ops.shape
|
||||||
v = sym_ops.broadcast_to(shape) \
|
v = sym_ops.broadcast_to(shape) \
|
||||||
@ np.broadcast_to(v.reshape(util.shapeshifter(v.shape,shape+(3,))),shape+(3,))
|
@ np.broadcast_to(v.reshape(util.shapeshifter(v.shape,shape+(3,))),shape+(3,))
|
||||||
return ~(self.broadcast_to(blend)) \
|
return ~(self.broadcast_to(blend))@ np.broadcast_to(v,blend+(3,))
|
||||||
@ np.broadcast_to(v,blend+(3,))
|
|
||||||
|
|
||||||
|
|
||||||
def Schmid(self,*,N_slip=None,N_twin=None):
|
def Schmid(self, *,
|
||||||
|
N_slip: IntSequence = None,
|
||||||
|
N_twin: IntSequence = None) -> np.ndarray:
|
||||||
u"""
|
u"""
|
||||||
Calculate Schmid matrix P = d ⨂ n in the lab frame for selected deformation systems.
|
Calculate Schmid matrix P = d ⨂ n in the lab frame for selected deformation systems.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
N_slip|N_twin : iterable of int
|
N_slip|N_twin : '*' or iterable of int
|
||||||
Number of deformation systems per family of the deformation system.
|
Number of deformation systems per family of the deformation system.
|
||||||
Use '*' to select all.
|
Use '*' to select all.
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
P : numpy.ndarray of shape (N,...,3,3)
|
P : numpy.ndarray, shape (N,...,3,3)
|
||||||
Schmid matrix for each of the N deformation systems.
|
Schmid matrix for each of the N deformation systems.
|
||||||
|
|
||||||
Examples
|
Examples
|
||||||
|
@ -887,6 +924,8 @@ class Orientation(Rotation,Crystal):
|
||||||
(self.kinematics('twin'),N_twin)
|
(self.kinematics('twin'),N_twin)
|
||||||
if active == '*': active = [len(a) for a in kinematics['direction']]
|
if active == '*': active = [len(a) for a in kinematics['direction']]
|
||||||
|
|
||||||
|
if not active:
|
||||||
|
raise ValueError('Schmid matrix not defined')
|
||||||
d = self.to_frame(uvw=np.vstack([kinematics['direction'][i][:n] for i,n in enumerate(active)]))
|
d = self.to_frame(uvw=np.vstack([kinematics['direction'][i][:n] for i,n in enumerate(active)]))
|
||||||
p = self.to_frame(hkl=np.vstack([kinematics['plane'][i][:n] for i,n in enumerate(active)]))
|
p = self.to_frame(hkl=np.vstack([kinematics['plane'][i][:n] for i,n in enumerate(active)]))
|
||||||
P = np.einsum('...i,...j',d/np.linalg.norm(d,axis=1,keepdims=True),
|
P = np.einsum('...i,...j',d/np.linalg.norm(d,axis=1,keepdims=True),
|
||||||
|
@ -897,7 +936,8 @@ class Orientation(Rotation,Crystal):
|
||||||
@ np.broadcast_to(P.reshape(util.shapeshifter(P.shape,shape)),shape)
|
@ np.broadcast_to(P.reshape(util.shapeshifter(P.shape,shape)),shape)
|
||||||
|
|
||||||
|
|
||||||
def related(self,model):
|
def related(self: MyType,
|
||||||
|
model: str) -> MyType:
|
||||||
"""
|
"""
|
||||||
Orientations derived from the given relationship.
|
Orientations derived from the given relationship.
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -194,7 +194,7 @@ class Table:
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
mask : numpy.ndarray bool
|
mask : numpy.ndarray of bool
|
||||||
Mask indicating where corresponding table values are close.
|
Mask indicating where corresponding table values are close.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
"""Functionality for typehints."""
|
"""Functionality for typehints."""
|
||||||
|
|
||||||
from typing import Sequence, Union, TextIO
|
from typing import Sequence, Union, Literal, TextIO
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
@ -9,6 +9,9 @@ import numpy as np
|
||||||
FloatSequence = Union[np.ndarray,Sequence[float]]
|
FloatSequence = Union[np.ndarray,Sequence[float]]
|
||||||
IntSequence = Union[np.ndarray,Sequence[int]]
|
IntSequence = Union[np.ndarray,Sequence[int]]
|
||||||
FileHandle = Union[TextIO, str, Path]
|
FileHandle = Union[TextIO, str, Path]
|
||||||
|
CrystalFamily = Union[None,Literal['triclinic', 'monoclinic', 'orthorhombic', 'tetragonal', 'hexagonal', 'cubic']]
|
||||||
|
CrystalLattice = Union[None,Literal['aP', 'mP', 'mS', 'oP', 'oS', 'oI', 'oF', 'tP', 'tI', 'hP', 'cP', 'cI', 'cF']]
|
||||||
|
CrystalKinematics = Literal['slip', 'twin']
|
||||||
NumpyRngSeed = Union[int, IntSequence, np.random.SeedSequence, np.random.Generator]
|
NumpyRngSeed = Union[int, IntSequence, np.random.SeedSequence, np.random.Generator]
|
||||||
# BitGenerator does not exists in older numpy versions
|
# BitGenerator does not exists in older numpy versions
|
||||||
#NumpyRngSeed = Union[int, IntSequence, np.random.SeedSequence, np.random.BitGenerator, np.random.Generator]
|
#NumpyRngSeed = Union[int, IntSequence, np.random.SeedSequence, np.random.BitGenerator, np.random.Generator]
|
||||||
|
|
|
@ -9,7 +9,7 @@ import re
|
||||||
import fractions
|
import fractions
|
||||||
from collections import abc
|
from collections import abc
|
||||||
from functools import reduce
|
from functools import reduce
|
||||||
from typing import Union, Tuple, Iterable, Callable, Dict, List, Any, Literal, SupportsIndex, Sequence
|
from typing import Union, Tuple, Iterable, Callable, Dict, List, Any, Literal
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
@ -427,7 +427,7 @@ def hybrid_IA(dist: np.ndarray,
|
||||||
def shapeshifter(fro: Tuple[int, ...],
|
def shapeshifter(fro: Tuple[int, ...],
|
||||||
to: Tuple[int, ...],
|
to: Tuple[int, ...],
|
||||||
mode: Literal['left','right'] = 'left',
|
mode: Literal['left','right'] = 'left',
|
||||||
keep_ones: bool = False) -> Sequence[SupportsIndex]:
|
keep_ones: bool = False) -> Tuple[int, ...]:
|
||||||
"""
|
"""
|
||||||
Return dimensions that reshape 'fro' to become broadcastable to 'to'.
|
Return dimensions that reshape 'fro' to become broadcastable to 'to'.
|
||||||
|
|
||||||
|
@ -490,7 +490,7 @@ def shapeshifter(fro: Tuple[int, ...],
|
||||||
|
|
||||||
|
|
||||||
def shapeblender(a: Tuple[int, ...],
|
def shapeblender(a: Tuple[int, ...],
|
||||||
b: Tuple[int, ...]) -> Sequence[SupportsIndex]:
|
b: Tuple[int, ...]) -> Tuple[int, ...]:
|
||||||
"""
|
"""
|
||||||
Return a shape that overlaps the rightmost entries of 'a' with the leftmost of 'b'.
|
Return a shape that overlaps the rightmost entries of 'a' with the leftmost of 'b'.
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue