diff --git a/python/damask/__init__.py b/python/damask/__init__.py index de87444b3..761fd5621 100644 --- a/python/damask/__init__.py +++ b/python/damask/__init__.py @@ -17,7 +17,6 @@ from . import grid_filters # 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 ._crystal import Crystal # noqa from ._orientation import Orientation # noqa from ._table import Table # noqa diff --git a/python/damask/_crystal.py b/python/damask/_crystal.py index 3df338a9e..525ce9682 100644 --- a/python/damask/_crystal.py +++ b/python/damask/_crystal.py @@ -1,7 +1,6 @@ import numpy as np from . import util -from . import LatticeFamily from . import Rotation lattice_symmetries = { @@ -26,7 +25,7 @@ lattice_symmetries = { } -class Crystal(LatticeFamily): +class Crystal(): """Lattice.""" def __init__(self,*, @@ -42,6 +41,8 @@ class Crystal(LatticeFamily): ---------- lattice : {'aP', 'mP', 'mS', 'oP', 'oS', 'oI', 'oF', 'tP', 'tI', 'hP', 'cP', 'cI', 'cF'}. Name of the Bravais lattice in Pearson notation. + family : {'triclinic', 'monoclinic', 'orthorhombic', 'tetragonal', 'hexagonal', 'cubic'} + Name of the crystal family. a : float, optional Length of lattice parameter 'a'. b : float, optional @@ -58,8 +59,10 @@ class Crystal(LatticeFamily): Angles are given in degrees. Defaults to False. """ - super().__init__(family = lattice_symmetries[lattice] if family is None else family) + if family not in [None] + list(self._immutable.keys()): + raise KeyError(f'invalid crystal family "{family}"') + self.family = lattice_symmetries[lattice] if family is None else family self.lattice = lattice if self.lattice is not None: @@ -114,7 +117,9 @@ class Crystal(LatticeFamily): Lattice to check for equality. """ - return self.lattice == other.lattice and self.parameters == other.parameters + return self.lattice == other.lattice and \ + self.parameters == other.parameters and \ + self.family == other.family @property def parameters(self): @@ -122,6 +127,75 @@ class Crystal(LatticeFamily): return (self.a,self.b,self.c,self.alpha,self.beta,self.gamma) + @property + def immutable(self): + """Return immutable 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 + ... } + + """ + _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.] ]), + }} + return _basis.get(self.family,None) + + @property def ratio(self): """Return axes ratios of own lattice.""" @@ -717,3 +791,35 @@ class Crystal(LatticeFamily): ],dtype=float), }, } + + _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': {} + } diff --git a/python/damask/_lattice_family.py b/python/damask/_lattice_family.py deleted file mode 100644 index e34c02e10..000000000 --- a/python/damask/_lattice_family.py +++ /dev/null @@ -1,208 +0,0 @@ -import numpy as np - -from . import Rotation - -class LatticeFamily(): - - def __init__(self,family): - """ - Symmetry-related operations for crystal family. - - Parameters - ---------- - family : {'triclinic', 'monoclinic', 'orthorhombic', 'tetragonal', 'hexagonal', 'cubic'} - Name of the crystal family. - - """ - if family not in self._immutable.keys(): - raise KeyError(f'invalid crystal family "{family}"') - self.family = family - - - def __eq__(self,other): - """ - Equal to other. - - Parameters - ---------- - other : LatticeFamily - Lattice family to check for equality. - - """ - return self.family == other.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 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.] ]), - }} diff --git a/python/damask/_orientation.py b/python/damask/_orientation.py index d1a5e9c7a..f6f489b44 100644 --- a/python/damask/_orientation.py +++ b/python/damask/_orientation.py @@ -753,6 +753,77 @@ class Orientation(Rotation,Crystal): return rgb + @property + def symmetry_operations(self): + """Symmetry operations as Rotations.""" + _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 ], + ]} + return Rotation.from_quaternion(_symmetry_operations[self.family],accept_homomorph=True) + + +#################################################################################################### # functions that require lattice, not just family def to_pole(self,*,uvw=None,hkl=None,with_symmetry=False):