separating functionality
This commit is contained in:
parent
e281d8384f
commit
b55d51491d
|
@ -18,6 +18,7 @@ from . import lattice # noqa
|
|||
#Modules that contain only one class (of the same name), are prefixed by a '_'.
|
||||
#For example, '_colormap' containsa class called 'Colormap' which is imported as 'damask.Colormap'.
|
||||
from ._rotation import Rotation # noqa
|
||||
from ._lattice_family import LatticeFamily # noqa
|
||||
from ._orientation import Orientation # noqa
|
||||
from ._table import Table # noqa
|
||||
from ._vtk import VTK # noqa
|
||||
|
|
|
@ -0,0 +1,192 @@
|
|||
import numpy as np
|
||||
|
||||
from . import Rotation
|
||||
|
||||
class LatticeFamily():
|
||||
|
||||
def __init__(self,family):
|
||||
"""Symmetry-related operations for crystal families."""
|
||||
if family not in self._immutable.keys():
|
||||
raise KeyError(f'invalid lattice family "{family}"')
|
||||
self.family = family
|
||||
|
||||
|
||||
@property
|
||||
def symmetry_operations(self):
|
||||
"""Symmetry operations as Rotations."""
|
||||
return Rotation.from_quaternion(self._symmetry_operations[self.family],accept_homomorph=True)
|
||||
|
||||
|
||||
@property
|
||||
def immutable(self):
|
||||
"""Return immutable parameters lattice parameters."""
|
||||
return self._immutable[self.family]
|
||||
|
||||
|
||||
@property
|
||||
def basis(self):
|
||||
"""
|
||||
Corners of the standard triangle.
|
||||
|
||||
Not yet defined for monoclinic.
|
||||
|
||||
|
||||
References
|
||||
----------
|
||||
Bases are computed from
|
||||
|
||||
>>> basis = {
|
||||
... 'cubic' : np.linalg.inv(np.array([[0.,0.,1.], # direction of red
|
||||
... [1.,0.,1.]/np.sqrt(2.), # green
|
||||
... [1.,1.,1.]/np.sqrt(3.)]).T), # blue
|
||||
... 'hexagonal' : np.linalg.inv(np.array([[0.,0.,1.], # direction of red
|
||||
... [1.,0.,0.], # green
|
||||
... [np.sqrt(3.),1.,0.]/np.sqrt(4.)]).T), # blue
|
||||
... 'tetragonal' : np.linalg.inv(np.array([[0.,0.,1.], # direction of red
|
||||
... [1.,0.,0.], # green
|
||||
... [1.,1.,0.]/np.sqrt(2.)]).T), # blue
|
||||
... 'orthorhombic': np.linalg.inv(np.array([[0.,0.,1.], # direction of red
|
||||
... [1.,0.,0.], # green
|
||||
... [0.,1.,0.]]).T), # blue
|
||||
... }
|
||||
|
||||
"""
|
||||
return self._basis.get(self.family,None)
|
||||
|
||||
|
||||
_symmetry_operations = {
|
||||
'cubic': [
|
||||
[ 1.0, 0.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, 0.0, 1.0 ],
|
||||
[ 0.0, 0.0, 0.5*np.sqrt(2), 0.5*np.sqrt(2) ],
|
||||
[ 0.0, 0.0, 0.5*np.sqrt(2),-0.5*np.sqrt(2) ],
|
||||
[ 0.0, 0.5*np.sqrt(2), 0.0, 0.5*np.sqrt(2) ],
|
||||
[ 0.0, 0.5*np.sqrt(2), 0.0, -0.5*np.sqrt(2) ],
|
||||
[ 0.0, 0.5*np.sqrt(2),-0.5*np.sqrt(2), 0.0 ],
|
||||
[ 0.0, -0.5*np.sqrt(2),-0.5*np.sqrt(2), 0.0 ],
|
||||
[ 0.5, 0.5, 0.5, 0.5 ],
|
||||
[-0.5, 0.5, 0.5, 0.5 ],
|
||||
[-0.5, 0.5, 0.5, -0.5 ],
|
||||
[-0.5, 0.5, -0.5, 0.5 ],
|
||||
[-0.5, -0.5, 0.5, 0.5 ],
|
||||
[-0.5, -0.5, 0.5, -0.5 ],
|
||||
[-0.5, -0.5, -0.5, 0.5 ],
|
||||
[-0.5, 0.5, -0.5, -0.5 ],
|
||||
[-0.5*np.sqrt(2), 0.0, 0.0, 0.5*np.sqrt(2) ],
|
||||
[ 0.5*np.sqrt(2), 0.0, 0.0, 0.5*np.sqrt(2) ],
|
||||
[-0.5*np.sqrt(2), 0.0, 0.5*np.sqrt(2), 0.0 ],
|
||||
[-0.5*np.sqrt(2), 0.0, -0.5*np.sqrt(2), 0.0 ],
|
||||
[-0.5*np.sqrt(2), 0.5*np.sqrt(2), 0.0, 0.0 ],
|
||||
[-0.5*np.sqrt(2),-0.5*np.sqrt(2), 0.0, 0.0 ],
|
||||
],
|
||||
'hexagonal': [
|
||||
[ 1.0, 0.0, 0.0, 0.0 ],
|
||||
[-0.5*np.sqrt(3), 0.0, 0.0, -0.5 ],
|
||||
[ 0.5, 0.0, 0.0, 0.5*np.sqrt(3) ],
|
||||
[ 0.0, 0.0, 0.0, 1.0 ],
|
||||
[-0.5, 0.0, 0.0, 0.5*np.sqrt(3) ],
|
||||
[-0.5*np.sqrt(3), 0.0, 0.0, 0.5 ],
|
||||
[ 0.0, 1.0, 0.0, 0.0 ],
|
||||
[ 0.0, -0.5*np.sqrt(3), 0.5, 0.0 ],
|
||||
[ 0.0, 0.5, -0.5*np.sqrt(3), 0.0 ],
|
||||
[ 0.0, 0.0, 1.0, 0.0 ],
|
||||
[ 0.0, -0.5, -0.5*np.sqrt(3), 0.0 ],
|
||||
[ 0.0, 0.5*np.sqrt(3), 0.5, 0.0 ],
|
||||
],
|
||||
'tetragonal': [
|
||||
[ 1.0, 0.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, 0.0, 1.0 ],
|
||||
[ 0.0, 0.5*np.sqrt(2), 0.5*np.sqrt(2), 0.0 ],
|
||||
[ 0.0, -0.5*np.sqrt(2), 0.5*np.sqrt(2), 0.0 ],
|
||||
[ 0.5*np.sqrt(2), 0.0, 0.0, 0.5*np.sqrt(2) ],
|
||||
[-0.5*np.sqrt(2), 0.0, 0.0, 0.5*np.sqrt(2) ],
|
||||
],
|
||||
'orthorhombic': [
|
||||
[ 1.0,0.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,0.0,1.0 ],
|
||||
],
|
||||
'monoclinic': [
|
||||
[ 1.0,0.0,0.0,0.0 ],
|
||||
[ 0.0,0.0,1.0,0.0 ],
|
||||
],
|
||||
'triclinic': [
|
||||
[ 1.0,0.0,0.0,0.0 ],
|
||||
]}
|
||||
|
||||
|
||||
_immutable = {
|
||||
'cubic':
|
||||
{
|
||||
'b': 1.0,
|
||||
'c': 1.0,
|
||||
'alpha': np.pi/2.,
|
||||
'beta': np.pi/2.,
|
||||
'gamma': np.pi/2.,
|
||||
},
|
||||
'hexagonal':
|
||||
{
|
||||
'b': 1.0,
|
||||
'alpha': np.pi/2.,
|
||||
'beta': np.pi/2.,
|
||||
'gamma': 2.*np.pi/3.,
|
||||
},
|
||||
'tetragonal':
|
||||
{
|
||||
'b': 1.0,
|
||||
'alpha': np.pi/2.,
|
||||
'beta': np.pi/2.,
|
||||
'gamma': np.pi/2.,
|
||||
},
|
||||
'orthorhombic':
|
||||
{
|
||||
'alpha': np.pi/2.,
|
||||
'beta': np.pi/2.,
|
||||
'gamma': np.pi/2.,
|
||||
},
|
||||
'monoclinic':
|
||||
{
|
||||
'alpha': np.pi/2.,
|
||||
'gamma': np.pi/2.,
|
||||
},
|
||||
'triclinic': {}
|
||||
}
|
||||
|
||||
|
||||
_basis = {
|
||||
'cubic': {'improper':np.array([ [-1. , 0. , 1. ],
|
||||
[ np.sqrt(2.) , -np.sqrt(2.) , 0. ],
|
||||
[ 0. , np.sqrt(3.) , 0. ] ]),
|
||||
'proper':np.array([ [ 0. , -1. , 1. ],
|
||||
[-np.sqrt(2.) , np.sqrt(2.) , 0. ],
|
||||
[ np.sqrt(3.) , 0. , 0. ] ]),
|
||||
},
|
||||
'hexagonal':
|
||||
{'improper':np.array([ [ 0. , 0. , 1. ],
|
||||
[ 1. , -np.sqrt(3.) , 0. ],
|
||||
[ 0. , 2. , 0. ] ]),
|
||||
'proper':np.array([ [ 0. , 0. , 1. ],
|
||||
[-1. , np.sqrt(3.) , 0. ],
|
||||
[ np.sqrt(3.) , -1. , 0. ] ]),
|
||||
},
|
||||
'tetragonal':
|
||||
{'improper':np.array([ [ 0. , 0. , 1. ],
|
||||
[ 1. , -1. , 0. ],
|
||||
[ 0. , np.sqrt(2.) , 0. ] ]),
|
||||
'proper':np.array([ [ 0. , 0. , 1. ],
|
||||
[-1. , 1. , 0. ],
|
||||
[ np.sqrt(2.) , 0. , 0. ] ]),
|
||||
},
|
||||
'orthorhombic':
|
||||
{'improper':np.array([ [ 0., 0., 1.],
|
||||
[ 1., 0., 0.],
|
||||
[ 0., 1., 0.] ]),
|
||||
'proper':np.array([ [ 0., 0., 1.],
|
||||
[-1., 0., 0.],
|
||||
[ 0., 1., 0.] ]),
|
||||
}}
|
|
@ -3,6 +3,7 @@ import inspect
|
|||
import numpy as np
|
||||
|
||||
from . import Rotation
|
||||
from . import LatticeFamily
|
||||
from . import util
|
||||
from . import tensor
|
||||
from . import lattice as lattice_
|
||||
|
@ -132,16 +133,27 @@ class Orientation(Rotation):
|
|||
Rotation.__init__(self,rotation=rotation)
|
||||
|
||||
if family in set(lattice_symmetries.values()) and lattice is None:
|
||||
self.family = family
|
||||
self.family = family
|
||||
self.lattice = None
|
||||
|
||||
l = LatticeFamily(self.family)
|
||||
self.immutable = l.immutable
|
||||
self.basis = l.basis
|
||||
self.symmetry_operations = l.symmetry_operations
|
||||
|
||||
self.a = self.b = self.c = None
|
||||
self.alpha = self.beta = self.gamma = None
|
||||
|
||||
elif lattice in lattice_symmetries:
|
||||
|
||||
self.family = lattice_symmetries[lattice]
|
||||
self.lattice = lattice
|
||||
|
||||
l = LatticeFamily(self.family)
|
||||
self.immutable = l.immutable
|
||||
self.basis = l.basis
|
||||
self.symmetry_operations = l.symmetry_operations
|
||||
|
||||
self.a = 1 if a is None else a
|
||||
self.b = b
|
||||
self.c = c
|
||||
|
@ -201,7 +213,6 @@ class Orientation(Rotation):
|
|||
alpha = kwargs['alpha'] if 'alpha' in kwargs else self.alpha,
|
||||
beta = kwargs['beta'] if 'beta' in kwargs else self.beta,
|
||||
gamma = kwargs['gamma'] if 'gamma' in kwargs else self.gamma,
|
||||
degrees = kwargs['degrees'] if 'degrees' in kwargs else None,
|
||||
)
|
||||
|
||||
copy = __copy__
|
||||
|
@ -435,84 +446,6 @@ class Orientation(Rotation):
|
|||
return o.copy(rotation=Rotation.from_matrix(tensor.transpose(om/np.linalg.norm(om,axis=-1,keepdims=True))))
|
||||
|
||||
|
||||
@property
|
||||
def symmetry_operations(self):
|
||||
"""Symmetry operations as Rotations."""
|
||||
if self.family == 'cubic':
|
||||
sym_quats = [
|
||||
[ 1.0, 0.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, 0.0, 1.0 ],
|
||||
[ 0.0, 0.0, 0.5*np.sqrt(2), 0.5*np.sqrt(2) ],
|
||||
[ 0.0, 0.0, 0.5*np.sqrt(2),-0.5*np.sqrt(2) ],
|
||||
[ 0.0, 0.5*np.sqrt(2), 0.0, 0.5*np.sqrt(2) ],
|
||||
[ 0.0, 0.5*np.sqrt(2), 0.0, -0.5*np.sqrt(2) ],
|
||||
[ 0.0, 0.5*np.sqrt(2),-0.5*np.sqrt(2), 0.0 ],
|
||||
[ 0.0, -0.5*np.sqrt(2),-0.5*np.sqrt(2), 0.0 ],
|
||||
[ 0.5, 0.5, 0.5, 0.5 ],
|
||||
[-0.5, 0.5, 0.5, 0.5 ],
|
||||
[-0.5, 0.5, 0.5, -0.5 ],
|
||||
[-0.5, 0.5, -0.5, 0.5 ],
|
||||
[-0.5, -0.5, 0.5, 0.5 ],
|
||||
[-0.5, -0.5, 0.5, -0.5 ],
|
||||
[-0.5, -0.5, -0.5, 0.5 ],
|
||||
[-0.5, 0.5, -0.5, -0.5 ],
|
||||
[-0.5*np.sqrt(2), 0.0, 0.0, 0.5*np.sqrt(2) ],
|
||||
[ 0.5*np.sqrt(2), 0.0, 0.0, 0.5*np.sqrt(2) ],
|
||||
[-0.5*np.sqrt(2), 0.0, 0.5*np.sqrt(2), 0.0 ],
|
||||
[-0.5*np.sqrt(2), 0.0, -0.5*np.sqrt(2), 0.0 ],
|
||||
[-0.5*np.sqrt(2), 0.5*np.sqrt(2), 0.0, 0.0 ],
|
||||
[-0.5*np.sqrt(2),-0.5*np.sqrt(2), 0.0, 0.0 ],
|
||||
]
|
||||
elif self.family == 'hexagonal':
|
||||
sym_quats = [
|
||||
[ 1.0, 0.0, 0.0, 0.0 ],
|
||||
[-0.5*np.sqrt(3), 0.0, 0.0, -0.5 ],
|
||||
[ 0.5, 0.0, 0.0, 0.5*np.sqrt(3) ],
|
||||
[ 0.0, 0.0, 0.0, 1.0 ],
|
||||
[-0.5, 0.0, 0.0, 0.5*np.sqrt(3) ],
|
||||
[-0.5*np.sqrt(3), 0.0, 0.0, 0.5 ],
|
||||
[ 0.0, 1.0, 0.0, 0.0 ],
|
||||
[ 0.0, -0.5*np.sqrt(3), 0.5, 0.0 ],
|
||||
[ 0.0, 0.5, -0.5*np.sqrt(3), 0.0 ],
|
||||
[ 0.0, 0.0, 1.0, 0.0 ],
|
||||
[ 0.0, -0.5, -0.5*np.sqrt(3), 0.0 ],
|
||||
[ 0.0, 0.5*np.sqrt(3), 0.5, 0.0 ],
|
||||
]
|
||||
elif self.family == 'tetragonal':
|
||||
sym_quats = [
|
||||
[ 1.0, 0.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, 0.0, 1.0 ],
|
||||
[ 0.0, 0.5*np.sqrt(2), 0.5*np.sqrt(2), 0.0 ],
|
||||
[ 0.0, -0.5*np.sqrt(2), 0.5*np.sqrt(2), 0.0 ],
|
||||
[ 0.5*np.sqrt(2), 0.0, 0.0, 0.5*np.sqrt(2) ],
|
||||
[-0.5*np.sqrt(2), 0.0, 0.0, 0.5*np.sqrt(2) ],
|
||||
]
|
||||
elif self.family == 'orthorhombic':
|
||||
sym_quats = [
|
||||
[ 1.0,0.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,0.0,1.0 ],
|
||||
]
|
||||
elif self.family == 'monoclinic':
|
||||
sym_quats = [
|
||||
[ 1.0,0.0,0.0,0.0 ],
|
||||
[ 0.0,0.0,1.0,0.0 ],
|
||||
]
|
||||
elif self.family == 'triclinic':
|
||||
sym_quats = [
|
||||
[ 1.0,0.0,0.0,0.0 ],
|
||||
]
|
||||
else:
|
||||
raise KeyError(f'unknown crystal family "{self.family}"')
|
||||
|
||||
return Rotation.from_quaternion(sym_quats,accept_homomorph=True)
|
||||
|
||||
|
||||
@property
|
||||
def equivalent(self):
|
||||
"""
|
||||
|
@ -707,46 +640,6 @@ class Orientation(Rotation):
|
|||
return (self.a,self.b,self.c,self.alpha,self.beta,self.gamma)
|
||||
|
||||
|
||||
@property
|
||||
def immutable(self):
|
||||
"""Return immutable parameters of own lattice."""
|
||||
if self.family == 'triclinic':
|
||||
return {}
|
||||
if self.family == 'monoclinic':
|
||||
return {
|
||||
'alpha': np.pi/2.,
|
||||
'gamma': np.pi/2.,
|
||||
}
|
||||
if self.family == 'orthorhombic':
|
||||
return {
|
||||
'alpha': np.pi/2.,
|
||||
'beta': np.pi/2.,
|
||||
'gamma': np.pi/2.,
|
||||
}
|
||||
if self.family == 'tetragonal':
|
||||
return {
|
||||
'b': 1.0,
|
||||
'alpha': np.pi/2.,
|
||||
'beta': np.pi/2.,
|
||||
'gamma': np.pi/2.,
|
||||
}
|
||||
if self.family == 'hexagonal':
|
||||
return {
|
||||
'b': 1.0,
|
||||
'alpha': np.pi/2.,
|
||||
'beta': np.pi/2.,
|
||||
'gamma': 2.*np.pi/3.,
|
||||
}
|
||||
if self.family == 'cubic':
|
||||
return {
|
||||
'b': 1.0,
|
||||
'c': 1.0,
|
||||
'alpha': np.pi/2.,
|
||||
'beta': np.pi/2.,
|
||||
'gamma': np.pi/2.,
|
||||
}
|
||||
|
||||
|
||||
@property
|
||||
def ratio(self):
|
||||
"""Return axes ratios of own lattice."""
|
||||
|
@ -808,76 +701,25 @@ class Orientation(Rotation):
|
|||
in : numpy.ndarray of shape (...)
|
||||
Boolean array indicating whether vector falls into SST.
|
||||
|
||||
References
|
||||
----------
|
||||
Bases are computed from
|
||||
|
||||
>>> basis = {
|
||||
... 'cubic' : np.linalg.inv(np.array([[0.,0.,1.], # direction of red
|
||||
... [1.,0.,1.]/np.sqrt(2.), # green
|
||||
... [1.,1.,1.]/np.sqrt(3.)]).T), # blue
|
||||
... 'hexagonal' : np.linalg.inv(np.array([[0.,0.,1.], # direction of red
|
||||
... [1.,0.,0.], # green
|
||||
... [np.sqrt(3.),1.,0.]/np.sqrt(4.)]).T), # blue
|
||||
... 'tetragonal' : np.linalg.inv(np.array([[0.,0.,1.], # direction of red
|
||||
... [1.,0.,0.], # green
|
||||
... [1.,1.,0.]/np.sqrt(2.)]).T), # blue
|
||||
... 'orthorhombic': np.linalg.inv(np.array([[0.,0.,1.], # direction of red
|
||||
... [1.,0.,0.], # green
|
||||
... [0.,1.,0.]]).T), # blue
|
||||
... }
|
||||
|
||||
"""
|
||||
if not isinstance(vector,np.ndarray) or vector.shape[-1] != 3:
|
||||
raise ValueError('input is not a field of three-dimensional vectors')
|
||||
|
||||
if self.family == 'cubic':
|
||||
basis = {'improper':np.array([ [-1. , 0. , 1. ],
|
||||
[ np.sqrt(2.) , -np.sqrt(2.) , 0. ],
|
||||
[ 0. , np.sqrt(3.) , 0. ] ]),
|
||||
'proper':np.array([ [ 0. , -1. , 1. ],
|
||||
[-np.sqrt(2.) , np.sqrt(2.) , 0. ],
|
||||
[ np.sqrt(3.) , 0. , 0. ] ]),
|
||||
}
|
||||
elif self.family == 'hexagonal':
|
||||
basis = {'improper':np.array([ [ 0. , 0. , 1. ],
|
||||
[ 1. , -np.sqrt(3.) , 0. ],
|
||||
[ 0. , 2. , 0. ] ]),
|
||||
'proper':np.array([ [ 0. , 0. , 1. ],
|
||||
[-1. , np.sqrt(3.) , 0. ],
|
||||
[ np.sqrt(3.) , -1. , 0. ] ]),
|
||||
}
|
||||
elif self.family == 'tetragonal':
|
||||
basis = {'improper':np.array([ [ 0. , 0. , 1. ],
|
||||
[ 1. , -1. , 0. ],
|
||||
[ 0. , np.sqrt(2.) , 0. ] ]),
|
||||
'proper':np.array([ [ 0. , 0. , 1. ],
|
||||
[-1. , 1. , 0. ],
|
||||
[ np.sqrt(2.) , 0. , 0. ] ]),
|
||||
}
|
||||
elif self.family == 'orthorhombic':
|
||||
basis = {'improper':np.array([ [ 0., 0., 1.],
|
||||
[ 1., 0., 0.],
|
||||
[ 0., 1., 0.] ]),
|
||||
'proper':np.array([ [ 0., 0., 1.],
|
||||
[-1., 0., 0.],
|
||||
[ 0., 1., 0.] ]),
|
||||
}
|
||||
else: # direct exit for unspecified symmetry
|
||||
if self.basis is None: # direct exit for no symmetry
|
||||
return np.ones_like(vector[...,0],bool)
|
||||
|
||||
if proper:
|
||||
components_proper = np.around(np.einsum('...ji,...i',
|
||||
np.broadcast_to(basis['proper'], vector.shape+(3,)),
|
||||
np.broadcast_to(self.basis['proper'], vector.shape+(3,)),
|
||||
vector), 12)
|
||||
components_improper = np.around(np.einsum('...ji,...i',
|
||||
np.broadcast_to(basis['improper'], vector.shape+(3,)),
|
||||
np.broadcast_to(self.basis['improper'], vector.shape+(3,)),
|
||||
vector), 12)
|
||||
return np.all(components_proper >= 0.0,axis=-1) \
|
||||
| np.all(components_improper >= 0.0,axis=-1)
|
||||
else:
|
||||
components = np.around(np.einsum('...ji,...i',
|
||||
np.broadcast_to(basis['improper'], vector.shape+(3,)),
|
||||
np.broadcast_to(self.basis['improper'], vector.shape+(3,)),
|
||||
np.block([vector[...,:2],np.abs(vector[...,2:3])])), 12)
|
||||
|
||||
return np.all(components >= 0.0,axis=-1)
|
||||
|
@ -911,25 +753,6 @@ class Orientation(Rotation):
|
|||
>>> o.IPF_color([0,0,1])
|
||||
array([1., 0., 0.])
|
||||
|
||||
References
|
||||
----------
|
||||
Bases are computed from
|
||||
|
||||
>>> basis = {
|
||||
... 'cubic' : np.linalg.inv(np.array([[0.,0.,1.], # direction of red
|
||||
... [1.,0.,1.]/np.sqrt(2.), # green
|
||||
... [1.,1.,1.]/np.sqrt(3.)]).T), # blue
|
||||
... 'hexagonal' : np.linalg.inv(np.array([[0.,0.,1.], # direction of red
|
||||
... [1.,0.,0.], # green
|
||||
... [np.sqrt(3.),1.,0.]/np.sqrt(4.)]).T), # blue
|
||||
... 'tetragonal' : np.linalg.inv(np.array([[0.,0.,1.], # direction of red
|
||||
... [1.,0.,0.], # green
|
||||
... [1.,1.,0.]/np.sqrt(2.)]).T), # blue
|
||||
... 'orthorhombic': np.linalg.inv(np.array([[0.,0.,1.], # direction of red
|
||||
... [1.,0.,0.], # green
|
||||
... [0.,1.,0.]]).T), # blue
|
||||
... }
|
||||
|
||||
"""
|
||||
if np.array(vector).shape[-1] != 3:
|
||||
raise ValueError('input is not a field of three-dimensional vectors')
|
||||
|
@ -937,47 +760,15 @@ class Orientation(Rotation):
|
|||
vector_ = self.to_SST(vector,proper) if in_SST else \
|
||||
self @ np.broadcast_to(vector,self.shape+(3,))
|
||||
|
||||
if self.family == 'cubic':
|
||||
basis = {'improper':np.array([ [-1. , 0. , 1. ],
|
||||
[ np.sqrt(2.) , -np.sqrt(2.) , 0. ],
|
||||
[ 0. , np.sqrt(3.) , 0. ] ]),
|
||||
'proper':np.array([ [ 0. , -1. , 1. ],
|
||||
[-np.sqrt(2.) , np.sqrt(2.) , 0. ],
|
||||
[ np.sqrt(3.) , 0. , 0. ] ]),
|
||||
}
|
||||
elif self.family == 'hexagonal':
|
||||
basis = {'improper':np.array([ [ 0. , 0. , 1. ],
|
||||
[ 1. , -np.sqrt(3.) , 0. ],
|
||||
[ 0. , 2. , 0. ] ]),
|
||||
'proper':np.array([ [ 0. , 0. , 1. ],
|
||||
[-1. , np.sqrt(3.) , 0. ],
|
||||
[ np.sqrt(3.) , -1. , 0. ] ]),
|
||||
}
|
||||
elif self.family == 'tetragonal':
|
||||
basis = {'improper':np.array([ [ 0. , 0. , 1. ],
|
||||
[ 1. , -1. , 0. ],
|
||||
[ 0. , np.sqrt(2.) , 0. ] ]),
|
||||
'proper':np.array([ [ 0. , 0. , 1. ],
|
||||
[-1. , 1. , 0. ],
|
||||
[ np.sqrt(2.) , 0. , 0. ] ]),
|
||||
}
|
||||
elif self.family == 'orthorhombic':
|
||||
basis = {'improper':np.array([ [ 0., 0., 1.],
|
||||
[ 1., 0., 0.],
|
||||
[ 0., 1., 0.] ]),
|
||||
'proper':np.array([ [ 0., 0., 1.],
|
||||
[-1., 0., 0.],
|
||||
[ 0., 1., 0.] ]),
|
||||
}
|
||||
else: # direct exit for unspecified symmetry
|
||||
if self.basis is None: # direct exit for no symmetry
|
||||
return np.zeros_like(vector_)
|
||||
|
||||
if proper:
|
||||
components_proper = np.around(np.einsum('...ji,...i',
|
||||
np.broadcast_to(basis['proper'], vector_.shape+(3,)),
|
||||
np.broadcast_to(self.basis['proper'], vector_.shape+(3,)),
|
||||
vector_), 12)
|
||||
components_improper = np.around(np.einsum('...ji,...i',
|
||||
np.broadcast_to(basis['improper'], vector_.shape+(3,)),
|
||||
np.broadcast_to(self.basis['improper'], vector_.shape+(3,)),
|
||||
vector_), 12)
|
||||
in_SST = np.all(components_proper >= 0.0,axis=-1) \
|
||||
| np.all(components_improper >= 0.0,axis=-1)
|
||||
|
@ -985,16 +776,17 @@ class Orientation(Rotation):
|
|||
components_proper,components_improper)
|
||||
else:
|
||||
components = np.around(np.einsum('...ji,...i',
|
||||
np.broadcast_to(basis['improper'], vector_.shape+(3,)),
|
||||
np.broadcast_to(self.basis['improper'], vector_.shape+(3,)),
|
||||
np.block([vector_[...,:2],np.abs(vector_[...,2:3])])), 12)
|
||||
|
||||
in_SST = np.all(components >= 0.0,axis=-1)
|
||||
|
||||
with np.errstate(invalid='ignore',divide='ignore'):
|
||||
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.max(rgb,axis=-1,keepdims=True) # normalize to (HS)V = 1
|
||||
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.max(rgb,axis=-1,keepdims=True) # normalize to (HS)V = 1
|
||||
rgb[np.broadcast_to(~in_SST[...,np.newaxis],rgb.shape)] = 0.0
|
||||
|
||||
return rgb
|
||||
|
||||
|
||||
|
|
|
@ -1,62 +1,5 @@
|
|||
import numpy as _np
|
||||
|
||||
|
||||
def Bravais_to_Miller(*,uvtw=None,hkil=None):
|
||||
"""
|
||||
Transform 4 Miller–Bravais indices to 3 Miller indices of crystal direction [uvw] or plane normal (hkl).
|
||||
|
||||
Parameters
|
||||
----------
|
||||
uvtw|hkil : numpy.ndarray of shape (...,4)
|
||||
Miller–Bravais indices of crystallographic direction [uvtw] or plane normal (hkil).
|
||||
|
||||
Returns
|
||||
-------
|
||||
uvw|hkl : numpy.ndarray of shape (...,3)
|
||||
Miller indices of [uvw] direction or (hkl) plane normal.
|
||||
|
||||
"""
|
||||
if (uvtw is not None) ^ (hkil is None):
|
||||
raise KeyError('Specify either "uvtw" or "hkil"')
|
||||
axis,basis = (_np.array(uvtw),_np.array([[1,0,-1,0],
|
||||
[0,1,-1,0],
|
||||
[0,0, 0,1]])) \
|
||||
if hkil is None else \
|
||||
(_np.array(hkil),_np.array([[1,0,0,0],
|
||||
[0,1,0,0],
|
||||
[0,0,0,1]]))
|
||||
return _np.einsum('il,...l',basis,axis)
|
||||
|
||||
|
||||
def Miller_to_Bravais(*,uvw=None,hkl=None):
|
||||
"""
|
||||
Transform 3 Miller indices to 4 Miller–Bravais indices of crystal direction [uvtw] or plane normal (hkil).
|
||||
|
||||
Parameters
|
||||
----------
|
||||
uvw|hkl : numpy.ndarray of shape (...,3)
|
||||
Miller indices of crystallographic direction [uvw] or plane normal (hkl).
|
||||
|
||||
Returns
|
||||
-------
|
||||
uvtw|hkil : numpy.ndarray of shape (...,4)
|
||||
Miller–Bravais indices of [uvtw] direction or (hkil) plane normal.
|
||||
|
||||
"""
|
||||
if (uvw is not None) ^ (hkl is None):
|
||||
raise KeyError('Specify either "uvw" or "hkl"')
|
||||
axis,basis = (_np.array(uvw),_np.array([[ 2,-1, 0],
|
||||
[-1, 2, 0],
|
||||
[-1,-1, 0],
|
||||
[ 0, 0, 3]])/3) \
|
||||
if hkl is None else \
|
||||
(_np.array(hkl),_np.array([[ 1, 0, 0],
|
||||
[ 0, 1, 0],
|
||||
[-1,-1, 0],
|
||||
[ 0, 0, 1]]))
|
||||
return _np.einsum('il,...l',basis,axis)
|
||||
|
||||
|
||||
kinematics = {
|
||||
'cF': {
|
||||
'slip' : _np.array([
|
||||
|
|
|
@ -336,17 +336,10 @@ class TestOrientation:
|
|||
for r,v in zip(result,vecs.reshape((-1,3))):
|
||||
assert np.all(r == Orientation(family=family).in_SST(v,proper))
|
||||
|
||||
@pytest.mark.parametrize('invalid_family',['fcc','bcc','hello'])
|
||||
@pytest.mark.parametrize('invalid_family',[None,'fcc','bcc','hello'])
|
||||
def test_invalid_lattice_init(self,invalid_family):
|
||||
with pytest.raises(KeyError):
|
||||
Orientation(family=invalid_family) # noqa
|
||||
|
||||
@pytest.mark.parametrize('invalid_family',[None,'fcc','bcc','hello'])
|
||||
def test_invalid_symmetry_family(self,invalid_family):
|
||||
with pytest.raises(KeyError):
|
||||
o = Orientation(family='cubic')
|
||||
o.family = invalid_family
|
||||
o.symmetry_operations # noqa
|
||||
Orientation(family=invalid_family)
|
||||
|
||||
def test_invalid_rot(self):
|
||||
with pytest.raises(TypeError):
|
||||
|
|
Loading…
Reference in New Issue