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 '_'.
|
#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'.
|
#For example, '_colormap' containsa class called 'Colormap' which is imported as 'damask.Colormap'.
|
||||||
from ._rotation import Rotation # noqa
|
from ._rotation import Rotation # noqa
|
||||||
|
from ._lattice_family import LatticeFamily # noqa
|
||||||
from ._orientation import Orientation # noqa
|
from ._orientation import Orientation # noqa
|
||||||
from ._table import Table # noqa
|
from ._table import Table # noqa
|
||||||
from ._vtk import VTK # 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
|
import numpy as np
|
||||||
|
|
||||||
from . import Rotation
|
from . import Rotation
|
||||||
|
from . import LatticeFamily
|
||||||
from . import util
|
from . import util
|
||||||
from . import tensor
|
from . import tensor
|
||||||
from . import lattice as lattice_
|
from . import lattice as lattice_
|
||||||
|
@ -132,16 +133,27 @@ class Orientation(Rotation):
|
||||||
Rotation.__init__(self,rotation=rotation)
|
Rotation.__init__(self,rotation=rotation)
|
||||||
|
|
||||||
if family in set(lattice_symmetries.values()) and lattice is None:
|
if family in set(lattice_symmetries.values()) and lattice is None:
|
||||||
self.family = family
|
self.family = family
|
||||||
self.lattice = None
|
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.a = self.b = self.c = None
|
||||||
self.alpha = self.beta = self.gamma = None
|
self.alpha = self.beta = self.gamma = None
|
||||||
|
|
||||||
elif lattice in lattice_symmetries:
|
elif lattice in lattice_symmetries:
|
||||||
|
|
||||||
self.family = lattice_symmetries[lattice]
|
self.family = lattice_symmetries[lattice]
|
||||||
self.lattice = 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.a = 1 if a is None else a
|
||||||
self.b = b
|
self.b = b
|
||||||
self.c = c
|
self.c = c
|
||||||
|
@ -201,7 +213,6 @@ class Orientation(Rotation):
|
||||||
alpha = kwargs['alpha'] if 'alpha' in kwargs else self.alpha,
|
alpha = kwargs['alpha'] if 'alpha' in kwargs else self.alpha,
|
||||||
beta = kwargs['beta'] if 'beta' in kwargs else self.beta,
|
beta = kwargs['beta'] if 'beta' in kwargs else self.beta,
|
||||||
gamma = kwargs['gamma'] if 'gamma' in kwargs else self.gamma,
|
gamma = kwargs['gamma'] if 'gamma' in kwargs else self.gamma,
|
||||||
degrees = kwargs['degrees'] if 'degrees' in kwargs else None,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
copy = __copy__
|
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))))
|
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
|
@property
|
||||||
def equivalent(self):
|
def equivalent(self):
|
||||||
"""
|
"""
|
||||||
|
@ -707,46 +640,6 @@ class Orientation(Rotation):
|
||||||
return (self.a,self.b,self.c,self.alpha,self.beta,self.gamma)
|
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
|
@property
|
||||||
def ratio(self):
|
def ratio(self):
|
||||||
"""Return axes ratios of own lattice."""
|
"""Return axes ratios of own lattice."""
|
||||||
|
@ -808,76 +701,25 @@ class Orientation(Rotation):
|
||||||
in : numpy.ndarray of shape (...)
|
in : numpy.ndarray of shape (...)
|
||||||
Boolean array indicating whether vector falls into SST.
|
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:
|
if not isinstance(vector,np.ndarray) or 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.family == 'cubic':
|
if self.basis is None: # direct exit for no symmetry
|
||||||
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
|
|
||||||
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(basis['proper'], vector.shape+(3,)),
|
np.broadcast_to(self.basis['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(basis['improper'], vector.shape+(3,)),
|
np.broadcast_to(self.basis['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(basis['improper'], vector.shape+(3,)),
|
np.broadcast_to(self.basis['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)
|
||||||
|
@ -911,25 +753,6 @@ class Orientation(Rotation):
|
||||||
>>> o.IPF_color([0,0,1])
|
>>> o.IPF_color([0,0,1])
|
||||||
array([1., 0., 0.])
|
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:
|
if np.array(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')
|
||||||
|
@ -937,47 +760,15 @@ class Orientation(Rotation):
|
||||||
vector_ = self.to_SST(vector,proper) if in_SST else \
|
vector_ = self.to_SST(vector,proper) if in_SST else \
|
||||||
self @ np.broadcast_to(vector,self.shape+(3,))
|
self @ np.broadcast_to(vector,self.shape+(3,))
|
||||||
|
|
||||||
if self.family == 'cubic':
|
if self.basis is None: # direct exit for no symmetry
|
||||||
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
|
|
||||||
return np.zeros_like(vector_)
|
return np.zeros_like(vector_)
|
||||||
|
|
||||||
if proper:
|
if proper:
|
||||||
components_proper = np.around(np.einsum('...ji,...i',
|
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)
|
vector_), 12)
|
||||||
components_improper = np.around(np.einsum('...ji,...i',
|
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)
|
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)
|
||||||
|
@ -985,16 +776,17 @@ class Orientation(Rotation):
|
||||||
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(basis['improper'], vector_.shape+(3,)),
|
np.broadcast_to(self.basis['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
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,62 +1,5 @@
|
||||||
import numpy as _np
|
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 = {
|
kinematics = {
|
||||||
'cF': {
|
'cF': {
|
||||||
'slip' : _np.array([
|
'slip' : _np.array([
|
||||||
|
|
|
@ -336,17 +336,10 @@ class TestOrientation:
|
||||||
for r,v in zip(result,vecs.reshape((-1,3))):
|
for r,v in zip(result,vecs.reshape((-1,3))):
|
||||||
assert np.all(r == Orientation(family=family).in_SST(v,proper))
|
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):
|
def test_invalid_lattice_init(self,invalid_family):
|
||||||
with pytest.raises(KeyError):
|
with pytest.raises(KeyError):
|
||||||
Orientation(family=invalid_family) # noqa
|
Orientation(family=invalid_family)
|
||||||
|
|
||||||
@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
|
|
||||||
|
|
||||||
def test_invalid_rot(self):
|
def test_invalid_rot(self):
|
||||||
with pytest.raises(TypeError):
|
with pytest.raises(TypeError):
|
||||||
|
|
Loading…
Reference in New Issue