separating functionality

This commit is contained in:
Martin Diehl 2021-06-01 23:23:04 +02:00
parent e281d8384f
commit b55d51491d
5 changed files with 220 additions and 299 deletions

View File

@ -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

View File

@ -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.] ]),
}}

View File

@ -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

View File

@ -1,62 +1,5 @@
import numpy as _np import numpy as _np
def Bravais_to_Miller(*,uvtw=None,hkil=None):
"""
Transform 4 MillerBravais indices to 3 Miller indices of crystal direction [uvw] or plane normal (hkl).
Parameters
----------
uvtw|hkil : numpy.ndarray of shape (...,4)
MillerBravais 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 MillerBravais 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)
MillerBravais 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([

View File

@ -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):