Merge remote-tracking branch 'origin/development' into modularize-mech

This commit is contained in:
Martin Diehl 2020-11-11 07:04:23 +01:00
commit 8429a9d695
42 changed files with 3062 additions and 1316 deletions

@ -1 +1 @@
Subproject commit 624218a2b8224b57c3e5d2338517d3a41a3c4f0b Subproject commit 281e7eb84f76a2974a50eb54faf35ea25ec89b20

View File

@ -1 +1 @@
v3.0.0-alpha-658-g14209f3a1 v3.0.0-alpha-681-g946418561

View File

@ -15,12 +15,12 @@ from . import seeds # noqa
from . import mechanics # noqa from . import mechanics # noqa
from . import solver # noqa from . import solver # noqa
from . import grid_filters # noqa from . import grid_filters # noqa
from ._lattice import Symmetry, Lattice# noqa from . import lattice # noqa
from ._table import Table # noqa
from ._rotation import Rotation # noqa from ._rotation import Rotation # noqa
from ._orientation import Orientation # noqa
from ._table import Table # noqa
from ._vtk import VTK # noqa from ._vtk import VTK # noqa
from ._colormap import Colormap # noqa from ._colormap import Colormap # noqa
from ._orientation import Orientation # noqa
from ._config import Config # noqa from ._config import Config # noqa
from ._configmaterial import ConfigMaterial # noqa from ._configmaterial import ConfigMaterial # noqa
from ._geom import Geom # noqa from ._geom import Geom # noqa

View File

@ -3,8 +3,8 @@ import copy
import numpy as np import numpy as np
from . import Config from . import Config
from . import Lattice
from . import Rotation from . import Rotation
from . import Orientation
class ConfigMaterial(Config): class ConfigMaterial(Config):
"""Material configuration.""" """Material configuration."""
@ -152,7 +152,7 @@ class ConfigMaterial(Config):
for k,v in self['phase'].items(): for k,v in self['phase'].items():
if 'lattice' in v: if 'lattice' in v:
try: try:
Lattice(v['lattice']) Orientation(lattice=v['lattice'])
except KeyError: except KeyError:
s = v['lattice'] s = v['lattice']
print(f"Invalid lattice: '{s}' in phase '{k}'") print(f"Invalid lattice: '{s}' in phase '{k}'")

View File

@ -20,7 +20,7 @@ class Geom:
def __init__(self,material,size,origin=[0.0,0.0,0.0],comments=[]): def __init__(self,material,size,origin=[0.0,0.0,0.0],comments=[]):
""" """
New geometry definition from array of material, size, and origin. New geometry definition from array of materials, size, and origin.
Parameters Parameters
---------- ----------
@ -34,28 +34,10 @@ class Geom:
Comment lines. Comment lines.
""" """
if len(material.shape) != 3: self.material = material
raise ValueError(f'Invalid material shape {material.shape}.') self.size = size
elif material.dtype not in np.sctypes['float'] + np.sctypes['int']: self.origin = origin
raise TypeError(f'Invalid material data type {material.dtype}.') self.comments = comments
else:
self.material = np.copy(material)
if self.material.dtype in np.sctypes['float'] and \
np.all(self.material == self.material.astype(int).astype(float)):
self.material = self.material.astype(int)
if len(size) != 3 or any(np.array(size) <= 0):
raise ValueError(f'Invalid size {size}.')
else:
self.size = np.array(size)
if len(origin) != 3:
raise ValueError(f'Invalid origin {origin}.')
else:
self.origin = np.array(origin)
self.comments = [str(c) for c in comments] if isinstance(comments,list) else [str(comments)]
def __repr__(self): def __repr__(self):
@ -113,13 +95,68 @@ class Geom:
return util.return_message(message) return util.return_message(message)
@property
def material(self):
"""Material indices."""
return self._material
@material.setter
def material(self,material):
if len(material.shape) != 3:
raise ValueError(f'Invalid material shape {material.shape}.')
elif material.dtype not in np.sctypes['float'] + np.sctypes['int']:
raise TypeError(f'Invalid material data type {material.dtype}.')
else:
self._material = np.copy(material)
if self.material.dtype in np.sctypes['float'] and \
np.all(self.material == self.material.astype(int).astype(float)):
self._material = self.material.astype(int)
@property
def size(self):
"""Physical size of geometry in meter."""
return self._size
@size.setter
def size(self,size):
if len(size) != 3 or any(np.array(size) <= 0):
raise ValueError(f'Invalid size {size}.')
else:
self._size = np.array(size)
@property
def origin(self):
"""Coordinates of geometry origin in meter."""
return self._origin
@origin.setter
def origin(self,origin):
if len(origin) != 3:
raise ValueError(f'Invalid origin {origin}.')
else:
self._origin = np.array(origin)
@property
def comments(self):
"""Comments/history of geometry."""
return self._comments
@comments.setter
def comments(self,comments):
self._comments = [str(c) for c in comments] if isinstance(comments,list) else [str(comments)]
@property @property
def grid(self): def grid(self):
"""Grid dimension of geometry."""
return np.asarray(self.material.shape) return np.asarray(self.material.shape)
@property @property
def N_materials(self): def N_materials(self):
"""Number of (unique) material indices within geometry."""
return np.unique(self.material).size return np.unique(self.material).size
@ -132,7 +169,7 @@ class Geom:
---------- ----------
fname : str or or pathlib.Path fname : str or or pathlib.Path
Geometry file to read. Geometry file to read.
Valid extension is .vtr, it will be appended if not given. Valid extension is .vtr, which will be appended if not given.
""" """
v = VTK.load(fname if str(fname).endswith('.vtr') else str(fname)+'.vtr') v = VTK.load(fname if str(fname).endswith('.vtr') else str(fname)+'.vtr')
@ -153,7 +190,7 @@ class Geom:
Parameters Parameters
---------- ----------
fname : str or file handle fname : str, pathlib.Path, or file handle
Geometry file to read. Geometry file to read.
""" """
@ -221,26 +258,26 @@ class Geom:
fname : str fname : str
Filename of the DREAM.3D file Filename of the DREAM.3D file
base_group : str base_group : str
Name of the group (folder) below 'DataContainers'. For example Name of the group (folder) below 'DataContainers',
'SyntheticVolumeDataContainer'. for example 'SyntheticVolumeDataContainer'.
point_data : str, optional point_data : str, optional
Name of the group (folder) containing the pointwise material data, Name of the group (folder) containing the pointwise material data,
for example 'CellData'. Defaults to None, in which case points consecutively numbered. for example 'CellData'. Defaults to None, in which case points are consecutively numbered.
material : str, optional material : str, optional
Name of the dataset containing the material ID. Defaults to Name of the dataset containing the material ID.
'FeatureIds'. Defaults to 'FeatureIds'.
""" """
root_dir ='DataContainers' root_dir ='DataContainers'
f = h5py.File(fname, 'r') f = h5py.File(fname, 'r')
g = path.join(root_dir,base_group,'_SIMPL_GEOMETRY') g = path.join(root_dir,base_group,'_SIMPL_GEOMETRY')
size = f[path.join(g,'DIMENSIONS')][()] * f[path.join(g,'SPACING')][()]
grid = f[path.join(g,'DIMENSIONS')][()] grid = f[path.join(g,'DIMENSIONS')][()]
size = f[path.join(g,'SPACING')][()] * grid
origin = f[path.join(g,'ORIGIN')][()] origin = f[path.join(g,'ORIGIN')][()]
group_pointwise = path.join(root_dir,base_group,point_data)
ma = np.arange(1,np.product(grid)+1,dtype=int) if point_data is None else \ ma = np.arange(grid.prod(),dtype=int) \
np.reshape(f[path.join(group_pointwise,material)],grid.prod()) if point_data is None else \
np.reshape(f[path.join(root_dir,base_group,point_data,material)],grid.prod())
return Geom(ma.reshape(grid,order='F'),size,origin,util.execution_stamp('Geom','load_DREAM3D')) return Geom(ma.reshape(grid,order='F'),size,origin,util.execution_stamp('Geom','load_DREAM3D'))
@ -248,18 +285,18 @@ class Geom:
@staticmethod @staticmethod
def from_table(table,coordinates,labels): def from_table(table,coordinates,labels):
""" """
Load an ASCII table. Derive geometry from an ASCII table.
Parameters Parameters
---------- ----------
table : damask.Table table : damask.Table
Table that contains material information. Table that contains material information.
coordinates : str coordinates : str
Label of the column containing the vector of spatial coordinates. Label of the vector column containing the spatial coordinates.
Need to be ordered (1./x fast, 3./z slow). Need to be ordered (1./x fast, 3./z slow).
labels : str or list of str labels : str or list of str
Label(s) of the columns containing the material definition. Label(s) of the columns containing the material definition.
Each unique combintation of values results in a material. Each unique combintation of values results in one material ID.
""" """
grid,size,origin = grid_filters.cell_coord0_gridSizeOrigin(table.get(coordinates)) grid,size,origin = grid_filters.cell_coord0_gridSizeOrigin(table.get(coordinates))
@ -293,8 +330,8 @@ class Geom:
weights : numpy.ndarray of shape (seeds.shape[0]) weights : numpy.ndarray of shape (seeds.shape[0])
Weights of the seeds. Setting all weights to 1.0 gives a standard Voronoi tessellation. Weights of the seeds. Setting all weights to 1.0 gives a standard Voronoi tessellation.
material : numpy.ndarray of shape (seeds.shape[0]), optional material : numpy.ndarray of shape (seeds.shape[0]), optional
Material ID of the seeds. Defaults to None, in which case materials are Material ID of the seeds.
consecutively numbered. Defaults to None, in which case materials are consecutively numbered.
periodic : Boolean, optional periodic : Boolean, optional
Perform a periodic tessellation. Defaults to True. Perform a periodic tessellation. Defaults to True.
@ -342,8 +379,8 @@ class Geom:
seeds : numpy.ndarray of shape (:,3) seeds : numpy.ndarray of shape (:,3)
Position of the seed points in meter. All points need to lay within the box. Position of the seed points in meter. All points need to lay within the box.
material : numpy.ndarray of shape (seeds.shape[0]), optional material : numpy.ndarray of shape (seeds.shape[0]), optional
Material ID of the seeds. Defaults to None, in which case materials are Material ID of the seeds.
consecutively numbered. Defaults to None, in which case materials are consecutively numbered.
periodic : Boolean, optional periodic : Boolean, optional
Perform a periodic tessellation. Defaults to True. Perform a periodic tessellation. Defaults to True.
@ -438,19 +475,19 @@ class Geom:
References References
---------- ----------
Surface curvature in triply-periodic minimal surface architectures as
a distinct design parameter in preparing advanced tissue engineering scaffolds
Sébastien B G Blanquer, Maike Werner, Markus Hannula, Shahriar Sharifi, Sébastien B G Blanquer, Maike Werner, Markus Hannula, Shahriar Sharifi,
Guillaume P R Lajoinie, David Eglin, Jari Hyttinen, André A Poot, and Dirk W Grijpma Guillaume P R Lajoinie, David Eglin, Jari Hyttinen, André A Poot, and Dirk W Grijpma
10.1088/1758-5090/aa6553 Surface curvature in triply-periodic minimal surface architectures as
a distinct design parameter in preparing advanced tissue engineering scaffolds
https://doi.org/10.1088/1758-5090/aa6553
Triply Periodic Bicontinuous Cubic Microdomain Morphologies by Symmetries
Meinhard Wohlgemuth, Nataliya Yufa, James Hoffman, and Edwin L. Thomas Meinhard Wohlgemuth, Nataliya Yufa, James Hoffman, and Edwin L. Thomas
10.1021/ma0019499 Triply Periodic Bicontinuous Cubic Microdomain Morphologies by Symmetries
https://doi.org/10.1021/ma0019499
Minisurf A minimal surface generator for finite element modeling and additive manufacturing
Meng-Ting Hsieh, Lorenzo Valdevit Meng-Ting Hsieh, Lorenzo Valdevit
10.1016/j.simpa.2020.100026 Minisurf A minimal surface generator for finite element modeling and additive manufacturing
https://doi.org/10.1016/j.simpa.2020.100026
""" """
x,y,z = np.meshgrid(periods*2.0*np.pi*(np.arange(grid[0])+0.5)/grid[0], x,y,z = np.meshgrid(periods*2.0*np.pi*(np.arange(grid[0])+0.5)/grid[0],
@ -465,7 +502,7 @@ class Geom:
def save(self,fname,compress=True): def save(self,fname,compress=True):
""" """
Store as vtk rectilinear grid. Store as VTK rectilinear grid.
Parameters Parameters
---------- ----------
@ -689,7 +726,7 @@ class Geom:
def renumber(self): def renumber(self):
"""Renumber sorted material indices to 0,...,N-1.""" """Renumber sorted material indices as 0,...,N-1."""
_,renumbered = np.unique(self.material,return_inverse=True) _,renumbered = np.unique(self.material,return_inverse=True)
return Geom(material = renumbered.reshape(self.grid), return Geom(material = renumbered.reshape(self.grid),

View File

@ -1,646 +0,0 @@
import numpy as np
class Symmetry:
"""
Symmetry-related operations for crystal systems.
References
----------
https://en.wikipedia.org/wiki/Crystal_system
"""
crystal_systems = [None,'orthorhombic','tetragonal','hexagonal','cubic']
def __init__(self, system = None):
"""
Symmetry Definition.
Parameters
----------
system : {None,'orthorhombic','tetragonal','hexagonal','cubic'}, optional
Name of the crystal system. Defaults to 'None'.
"""
if system is not None and system.lower() not in self.crystal_systems:
raise KeyError(f'Crystal system "{system}" is unknown')
self.system = system.lower() if isinstance(system,str) else system
def __copy__(self):
"""Copy."""
return self.__class__(self.system)
copy = __copy__
def __repr__(self):
"""Readable string."""
return f'{self.system}'
def __eq__(self, other):
"""
Equal to other.
Parameters
----------
other : Symmetry
Symmetry to check for equality.
"""
return self.system == other.system
def __neq__(self, other):
"""
Not Equal to other.
Parameters
----------
other : Symmetry
Symmetry to check for inequality.
"""
return not self.__eq__(other)
def __cmp__(self,other):
"""
Linear ordering.
Parameters
----------
other : Symmetry
Symmetry to check for for order.
"""
myOrder = self.crystal_systems.index(self.system)
otherOrder = self.crystal_systems.index(other.system)
return (myOrder > otherOrder) - (myOrder < otherOrder)
@property
def symmetry_operations(self):
"""Symmetry operations as quaternions."""
if self.system == '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.system == '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.system == '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.system == '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 ],
]
else:
sym_quats = [
[ 1.0,0.0,0.0,0.0 ],
]
return np.array(sym_quats)
def in_FZ(self,rho):
"""
Check whether given Rodrigues-Frank vector falls into fundamental zone.
Fundamental zone in Rodrigues space is point symmetric around origin.
"""
if(rho.shape[-1] != 3):
raise ValueError('Input is not a Rodrigues-Frank vector field.')
rho_abs = np.abs(rho)
with np.errstate(invalid='ignore'):
# using '*'/prod for 'and'
if self.system == 'cubic':
return np.where(np.prod(np.sqrt(2)-1. >= rho_abs,axis=-1) *
(1. >= np.sum(rho_abs,axis=-1)),True,False)
elif self.system == 'hexagonal':
return np.where(np.prod(1. >= rho_abs,axis=-1) *
(2. >= np.sqrt(3)*rho_abs[...,0] + rho_abs[...,1]) *
(2. >= np.sqrt(3)*rho_abs[...,1] + rho_abs[...,0]) *
(2. >= np.sqrt(3) + rho_abs[...,2]),True,False)
elif self.system == 'tetragonal':
return np.where(np.prod(1. >= rho_abs[...,:2],axis=-1) *
(np.sqrt(2) >= rho_abs[...,0] + rho_abs[...,1]) *
(np.sqrt(2) >= rho_abs[...,2] + 1.),True,False)
elif self.system == 'orthorhombic':
return np.where(np.prod(1. >= rho_abs,axis=-1),True,False)
else:
return np.where(np.all(np.isfinite(rho_abs),axis=-1),True,False)
def in_disorientation_SST(self,rho):
"""
Check whether given Rodrigues-Frank vector (of misorientation) falls into standard stereographic triangle.
References
----------
A. Heinz and P. Neumann, Acta Crystallographica Section A 47:780-789, 1991
https://doi.org/10.1107/S0108767391006864
"""
if(rho.shape[-1] != 3):
raise ValueError('Input is not a Rodrigues-Frank vector field.')
with np.errstate(invalid='ignore'):
# using '*' for 'and'
if self.system == 'cubic':
return np.where((rho[...,0] >= rho[...,1]) * \
(rho[...,1] >= rho[...,2]) * \
(rho[...,2] >= 0),True,False)
elif self.system == 'hexagonal':
return np.where((rho[...,0] >= rho[...,1]*np.sqrt(3)) * \
(rho[...,1] >= 0) * \
(rho[...,2] >= 0),True,False)
elif self.system == 'tetragonal':
return np.where((rho[...,0] >= rho[...,1]) * \
(rho[...,1] >= 0) * \
(rho[...,2] >= 0),True,False)
elif self.system == 'orthorhombic':
return np.where((rho[...,0] >= 0) * \
(rho[...,1] >= 0) * \
(rho[...,2] >= 0),True,False)
else:
return np.ones_like(rho[...,0],dtype=bool)
#ToDo: IPF color in separate function
def in_SST(self,vector,proper=False,color=False):
"""
Check whether given vector falls into standard stereographic triangle of own symmetry.
proper considers only vectors with z >= 0, hence uses two neighboring SSTs.
Return inverse pole figure color if requested.
Bases are computed from
>>> basis = {'cubic' : np.linalg.inv(np.array([[0.,0.,1.], # direction of red
... [1.,0.,1.]/np.sqrt(2.), # direction of green
... [1.,1.,1.]/np.sqrt(3.)]).T), # direction of blue
... 'hexagonal' : np.linalg.inv(np.array([[0.,0.,1.], # direction of red
... [1.,0.,0.], # direction of green
... [np.sqrt(3.),1.,0.]/np.sqrt(4.)]).T), # direction of blue
... 'tetragonal' : np.linalg.inv(np.array([[0.,0.,1.], # direction of red
... [1.,0.,0.], # direction of green
... [1.,1.,0.]/np.sqrt(2.)]).T), # direction of blue
... 'orthorhombic': np.linalg.inv(np.array([[0.,0.,1.], # direction of red
... [1.,0.,0.], # direction of green
... [0.,1.,0.]]).T), # direction of blue
... }
"""
if(vector.shape[-1] != 3):
raise ValueError('Input is not a 3D vector field.')
if self.system == '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.system == '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.system == '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.system == '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 color:
return (np.ones_like(vector[...,0],bool),np.zeros_like(vector))
else:
return np.ones_like(vector[...,0],bool)
b_i = np.broadcast_to(basis['improper'],vector.shape+(3,))
if proper:
b_p = np.broadcast_to(basis['proper'], vector.shape+(3,))
improper = np.all(np.around(np.einsum('...ji,...i',b_i,vector),12)>=0.0,axis=-1,keepdims=True)
theComponents = np.where(np.broadcast_to(improper,vector.shape),
np.around(np.einsum('...ji,...i',b_i,vector),12),
np.around(np.einsum('...ji,...i',b_p,vector),12))
else:
vector_ = np.block([vector[...,0:2],np.abs(vector[...,2:3])]) # z component projects identical
theComponents = np.around(np.einsum('...ji,...i',b_i,vector_),12)
in_SST = np.all(theComponents >= 0.0,axis=-1)
if color: # have to return color array
with np.errstate(invalid='ignore',divide='ignore'):
rgb = (theComponents/np.linalg.norm(theComponents,axis=-1,keepdims=True))**0.5 # smoothen color ramps
rgb = np.minimum(1.,rgb) # limit to maximum intensity
rgb /= np.max(rgb,axis=-1,keepdims=True) # normalize to (HS)V = 1
rgb[np.broadcast_to(~in_SST.reshape(vector[...,0].shape+(1,)),vector.shape)] = 0.0
return (in_SST,rgb)
else:
return in_SST
# ******************************************************************************************
class Lattice: # ToDo: Make a subclass of Symmetry!
"""
Bravais lattice.
This contains only a mapping from Bravais lattice to symmetry
and orientation relationships. It could include twin and slip systems.
References
----------
https://en.wikipedia.org/wiki/Bravais_lattice
"""
lattices = {
'iso': {'system':None},
'triclinic':{'system':None},
'bct': {'system':'tetragonal'},
'hex': {'system':'hexagonal'},
'fcc': {'system':'cubic','c/a':1.0},
'bcc': {'system':'cubic','c/a':1.0},
}
def __init__(self,lattice,c_over_a=None):
"""
New lattice of given type.
Parameters
----------
lattice : str
Bravais lattice.
"""
self.lattice = lattice
self.symmetry = Symmetry(self.lattices[lattice]['system'])
# transition to subclass
self.system = self.symmetry.system
self.in_SST = self.symmetry.in_SST
self.in_FZ = self.symmetry.in_FZ
self.in_disorientation_SST = self.symmetry.in_disorientation_SST
def __repr__(self):
"""Report basic lattice information."""
return f'Bravais lattice {self.lattice} ({self.symmetry} crystal system)'
# Kurdjomov--Sachs orientation relationship for fcc <-> bcc transformation
# from S. Morito et al., Journal of Alloys and Compounds 577:s587-s592, 2013
# also see K. Kitahara et al., Acta Materialia 54:1279-1288, 2006
_KS = {'mapping':{'fcc':0,'bcc':1},
'planes': np.array([
[[ 1, 1, 1],[ 0, 1, 1]],
[[ 1, 1, 1],[ 0, 1, 1]],
[[ 1, 1, 1],[ 0, 1, 1]],
[[ 1, 1, 1],[ 0, 1, 1]],
[[ 1, 1, 1],[ 0, 1, 1]],
[[ 1, 1, 1],[ 0, 1, 1]],
[[ 1, -1, 1],[ 0, 1, 1]],
[[ 1, -1, 1],[ 0, 1, 1]],
[[ 1, -1, 1],[ 0, 1, 1]],
[[ 1, -1, 1],[ 0, 1, 1]],
[[ 1, -1, 1],[ 0, 1, 1]],
[[ 1, -1, 1],[ 0, 1, 1]],
[[ -1, 1, 1],[ 0, 1, 1]],
[[ -1, 1, 1],[ 0, 1, 1]],
[[ -1, 1, 1],[ 0, 1, 1]],
[[ -1, 1, 1],[ 0, 1, 1]],
[[ -1, 1, 1],[ 0, 1, 1]],
[[ -1, 1, 1],[ 0, 1, 1]],
[[ 1, 1, -1],[ 0, 1, 1]],
[[ 1, 1, -1],[ 0, 1, 1]],
[[ 1, 1, -1],[ 0, 1, 1]],
[[ 1, 1, -1],[ 0, 1, 1]],
[[ 1, 1, -1],[ 0, 1, 1]],
[[ 1, 1, -1],[ 0, 1, 1]]],dtype='float'),
'directions': np.array([
[[ -1, 0, 1],[ -1, -1, 1]],
[[ -1, 0, 1],[ -1, 1, -1]],
[[ 0, 1, -1],[ -1, -1, 1]],
[[ 0, 1, -1],[ -1, 1, -1]],
[[ 1, -1, 0],[ -1, -1, 1]],
[[ 1, -1, 0],[ -1, 1, -1]],
[[ 1, 0, -1],[ -1, -1, 1]],
[[ 1, 0, -1],[ -1, 1, -1]],
[[ -1, -1, 0],[ -1, -1, 1]],
[[ -1, -1, 0],[ -1, 1, -1]],
[[ 0, 1, 1],[ -1, -1, 1]],
[[ 0, 1, 1],[ -1, 1, -1]],
[[ 0, -1, 1],[ -1, -1, 1]],
[[ 0, -1, 1],[ -1, 1, -1]],
[[ -1, 0, -1],[ -1, -1, 1]],
[[ -1, 0, -1],[ -1, 1, -1]],
[[ 1, 1, 0],[ -1, -1, 1]],
[[ 1, 1, 0],[ -1, 1, -1]],
[[ -1, 1, 0],[ -1, -1, 1]],
[[ -1, 1, 0],[ -1, 1, -1]],
[[ 0, -1, -1],[ -1, -1, 1]],
[[ 0, -1, -1],[ -1, 1, -1]],
[[ 1, 0, 1],[ -1, -1, 1]],
[[ 1, 0, 1],[ -1, 1, -1]]],dtype='float')}
# Greninger--Troiano orientation relationship for fcc <-> bcc transformation
# from Y. He et al., Journal of Applied Crystallography 39:72-81, 2006
_GT = {'mapping':{'fcc':0,'bcc':1},
'planes': np.array([
[[ 1, 1, 1],[ 1, 0, 1]],
[[ 1, 1, 1],[ 1, 1, 0]],
[[ 1, 1, 1],[ 0, 1, 1]],
[[ -1, -1, 1],[ -1, 0, 1]],
[[ -1, -1, 1],[ -1, -1, 0]],
[[ -1, -1, 1],[ 0, -1, 1]],
[[ -1, 1, 1],[ -1, 0, 1]],
[[ -1, 1, 1],[ -1, 1, 0]],
[[ -1, 1, 1],[ 0, 1, 1]],
[[ 1, -1, 1],[ 1, 0, 1]],
[[ 1, -1, 1],[ 1, -1, 0]],
[[ 1, -1, 1],[ 0, -1, 1]],
[[ 1, 1, 1],[ 1, 1, 0]],
[[ 1, 1, 1],[ 0, 1, 1]],
[[ 1, 1, 1],[ 1, 0, 1]],
[[ -1, -1, 1],[ -1, -1, 0]],
[[ -1, -1, 1],[ 0, -1, 1]],
[[ -1, -1, 1],[ -1, 0, 1]],
[[ -1, 1, 1],[ -1, 1, 0]],
[[ -1, 1, 1],[ 0, 1, 1]],
[[ -1, 1, 1],[ -1, 0, 1]],
[[ 1, -1, 1],[ 1, -1, 0]],
[[ 1, -1, 1],[ 0, -1, 1]],
[[ 1, -1, 1],[ 1, 0, 1]]],dtype='float'),
'directions': np.array([
[[ -5,-12, 17],[-17, -7, 17]],
[[ 17, -5,-12],[ 17,-17, -7]],
[[-12, 17, -5],[ -7, 17,-17]],
[[ 5, 12, 17],[ 17, 7, 17]],
[[-17, 5,-12],[-17, 17, -7]],
[[ 12,-17, -5],[ 7,-17,-17]],
[[ -5, 12,-17],[-17, 7,-17]],
[[ 17, 5, 12],[ 17, 17, 7]],
[[-12,-17, 5],[ -7,-17, 17]],
[[ 5,-12,-17],[ 17, -7,-17]],
[[-17, -5, 12],[-17,-17, 7]],
[[ 12, 17, 5],[ 7, 17, 17]],
[[ -5, 17,-12],[-17, 17, -7]],
[[-12, -5, 17],[ -7,-17, 17]],
[[ 17,-12, -5],[ 17, -7,-17]],
[[ 5,-17,-12],[ 17,-17, -7]],
[[ 12, 5, 17],[ 7, 17, 17]],
[[-17, 12, -5],[-17, 7,-17]],
[[ -5,-17, 12],[-17,-17, 7]],
[[-12, 5,-17],[ -7, 17,-17]],
[[ 17, 12, 5],[ 17, 7, 17]],
[[ 5, 17, 12],[ 17, 17, 7]],
[[ 12, -5,-17],[ 7,-17,-17]],
[[-17,-12, 5],[-17,-7, 17]]],dtype='float')}
# Greninger--Troiano' orientation relationship for fcc <-> bcc transformation
# from Y. He et al., Journal of Applied Crystallography 39:72-81, 2006
_GTprime = {'mapping':{'fcc':0,'bcc':1},
'planes': np.array([
[[ 7, 17, 17],[ 12, 5, 17]],
[[ 17, 7, 17],[ 17, 12, 5]],
[[ 17, 17, 7],[ 5, 17, 12]],
[[ -7,-17, 17],[-12, -5, 17]],
[[-17, -7, 17],[-17,-12, 5]],
[[-17,-17, 7],[ -5,-17, 12]],
[[ 7,-17,-17],[ 12, -5,-17]],
[[ 17, -7,-17],[ 17,-12, -5]],
[[ 17,-17, -7],[ 5,-17,-12]],
[[ -7, 17,-17],[-12, 5,-17]],
[[-17, 7,-17],[-17, 12, -5]],
[[-17, 17, -7],[ -5, 17,-12]],
[[ 7, 17, 17],[ 12, 17, 5]],
[[ 17, 7, 17],[ 5, 12, 17]],
[[ 17, 17, 7],[ 17, 5, 12]],
[[ -7,-17, 17],[-12,-17, 5]],
[[-17, -7, 17],[ -5,-12, 17]],
[[-17,-17, 7],[-17, -5, 12]],
[[ 7,-17,-17],[ 12,-17, -5]],
[[ 17, -7,-17],[ 5, -12,-17]],
[[ 17,-17, -7],[ 17, -5,-12]],
[[ -7, 17,-17],[-12, 17, -5]],
[[-17, 7,-17],[ -5, 12,-17]],
[[-17, 17, -7],[-17, 5,-12]]],dtype='float'),
'directions': np.array([
[[ 0, 1, -1],[ 1, 1, -1]],
[[ -1, 0, 1],[ -1, 1, 1]],
[[ 1, -1, 0],[ 1, -1, 1]],
[[ 0, -1, -1],[ -1, -1, -1]],
[[ 1, 0, 1],[ 1, -1, 1]],
[[ 1, -1, 0],[ 1, -1, -1]],
[[ 0, 1, -1],[ -1, 1, -1]],
[[ 1, 0, 1],[ 1, 1, 1]],
[[ -1, -1, 0],[ -1, -1, 1]],
[[ 0, -1, -1],[ 1, -1, -1]],
[[ -1, 0, 1],[ -1, -1, 1]],
[[ -1, -1, 0],[ -1, -1, -1]],
[[ 0, -1, 1],[ 1, -1, 1]],
[[ 1, 0, -1],[ 1, 1, -1]],
[[ -1, 1, 0],[ -1, 1, 1]],
[[ 0, 1, 1],[ -1, 1, 1]],
[[ -1, 0, -1],[ -1, -1, -1]],
[[ -1, 1, 0],[ -1, 1, -1]],
[[ 0, -1, 1],[ -1, -1, 1]],
[[ -1, 0, -1],[ -1, 1, -1]],
[[ 1, 1, 0],[ 1, 1, 1]],
[[ 0, 1, 1],[ 1, 1, 1]],
[[ 1, 0, -1],[ 1, -1, -1]],
[[ 1, 1, 0],[ 1, 1, -1]]],dtype='float')}
# Nishiyama--Wassermann orientation relationship for fcc <-> bcc transformation
# from H. Kitahara et al., Materials Characterization 54:378-386, 2005
_NW = {'mapping':{'fcc':0,'bcc':1},
'planes': np.array([
[[ 1, 1, 1],[ 0, 1, 1]],
[[ 1, 1, 1],[ 0, 1, 1]],
[[ 1, 1, 1],[ 0, 1, 1]],
[[ -1, 1, 1],[ 0, 1, 1]],
[[ -1, 1, 1],[ 0, 1, 1]],
[[ -1, 1, 1],[ 0, 1, 1]],
[[ 1, -1, 1],[ 0, 1, 1]],
[[ 1, -1, 1],[ 0, 1, 1]],
[[ 1, -1, 1],[ 0, 1, 1]],
[[ -1, -1, 1],[ 0, 1, 1]],
[[ -1, -1, 1],[ 0, 1, 1]],
[[ -1, -1, 1],[ 0, 1, 1]]],dtype='float'),
'directions': np.array([
[[ 2, -1, -1],[ 0, -1, 1]],
[[ -1, 2, -1],[ 0, -1, 1]],
[[ -1, -1, 2],[ 0, -1, 1]],
[[ -2, -1, -1],[ 0, -1, 1]],
[[ 1, 2, -1],[ 0, -1, 1]],
[[ 1, -1, 2],[ 0, -1, 1]],
[[ 2, 1, -1],[ 0, -1, 1]],
[[ -1, -2, -1],[ 0, -1, 1]],
[[ -1, 1, 2],[ 0, -1, 1]],
[[ 2, -1, 1],[ 0, -1, 1]], #It is wrong in the paper, but matrix is correct
[[ -1, 2, 1],[ 0, -1, 1]],
[[ -1, -1, -2],[ 0, -1, 1]]],dtype='float')}
# Pitsch orientation relationship for fcc <-> bcc transformation
# from Y. He et al., Acta Materialia 53:1179-1190, 2005
_Pitsch = {'mapping':{'fcc':0,'bcc':1},
'planes': np.array([
[[ 0, 1, 0],[ -1, 0, 1]],
[[ 0, 0, 1],[ 1, -1, 0]],
[[ 1, 0, 0],[ 0, 1, -1]],
[[ 1, 0, 0],[ 0, -1, -1]],
[[ 0, 1, 0],[ -1, 0, -1]],
[[ 0, 0, 1],[ -1, -1, 0]],
[[ 0, 1, 0],[ -1, 0, -1]],
[[ 0, 0, 1],[ -1, -1, 0]],
[[ 1, 0, 0],[ 0, -1, -1]],
[[ 1, 0, 0],[ 0, -1, 1]],
[[ 0, 1, 0],[ 1, 0, -1]],
[[ 0, 0, 1],[ -1, 1, 0]]],dtype='float'),
'directions': np.array([
[[ 1, 0, 1],[ 1, -1, 1]],
[[ 1, 1, 0],[ 1, 1, -1]],
[[ 0, 1, 1],[ -1, 1, 1]],
[[ 0, 1, -1],[ -1, 1, -1]],
[[ -1, 0, 1],[ -1, -1, 1]],
[[ 1, -1, 0],[ 1, -1, -1]],
[[ 1, 0, -1],[ 1, -1, -1]],
[[ -1, 1, 0],[ -1, 1, -1]],
[[ 0, -1, 1],[ -1, -1, 1]],
[[ 0, 1, 1],[ -1, 1, 1]],
[[ 1, 0, 1],[ 1, -1, 1]],
[[ 1, 1, 0],[ 1, 1, -1]]],dtype='float')}
# Bain orientation relationship for fcc <-> bcc transformation
# from Y. He et al., Journal of Applied Crystallography 39:72-81, 2006
_Bain = {'mapping':{'fcc':0,'bcc':1},
'planes': np.array([
[[ 1, 0, 0],[ 1, 0, 0]],
[[ 0, 1, 0],[ 0, 1, 0]],
[[ 0, 0, 1],[ 0, 0, 1]]],dtype='float'),
'directions': np.array([
[[ 0, 1, 0],[ 0, 1, 1]],
[[ 0, 0, 1],[ 1, 0, 1]],
[[ 1, 0, 0],[ 1, 1, 0]]],dtype='float')}
def relation_operations(self,model):
"""
Crystallographic orientation relationships for phase transformations.
References
----------
S. Morito et al., Journal of Alloys and Compounds 577:s587-s592, 2013
https://doi.org/10.1016/j.jallcom.2012.02.004
K. Kitahara et al., Acta Materialia 54(5):1279-1288, 2006
https://doi.org/10.1016/j.actamat.2005.11.001
Y. He et al., Journal of Applied Crystallography 39:72-81, 2006
https://doi.org/10.1107/S0021889805038276
H. Kitahara et al., Materials Characterization 54(4-5):378-386, 2005
https://doi.org/10.1016/j.matchar.2004.12.015
Y. He et al., Acta Materialia 53(4):1179-1190, 2005
https://doi.org/10.1016/j.actamat.2004.11.021
"""
models={'KS':self._KS, 'GT':self._GT, 'GT_prime':self._GTprime,
'NW':self._NW, 'Pitsch': self._Pitsch, 'Bain':self._Bain}
try:
relationship = models[model]
except KeyError :
raise KeyError(f'Orientation relationship "{model}" is unknown')
if self.lattice not in relationship['mapping']:
raise ValueError(f'Relationship "{model}" not supported for lattice "{self.lattice}"')
r = {'lattice':Lattice((set(relationship['mapping'])-{self.lattice}).pop()), # target lattice
'rotations':[] }
myPlane_id = relationship['mapping'][self.lattice]
otherPlane_id = (myPlane_id+1)%2
myDir_id = myPlane_id +2
otherDir_id = otherPlane_id +2
for miller in np.hstack((relationship['planes'],relationship['directions'])):
myPlane = miller[myPlane_id]/ np.linalg.norm(miller[myPlane_id])
myDir = miller[myDir_id]/ np.linalg.norm(miller[myDir_id])
myMatrix = np.array([myDir,np.cross(myPlane,myDir),myPlane])
otherPlane = miller[otherPlane_id]/ np.linalg.norm(miller[otherPlane_id])
otherDir = miller[otherDir_id]/ np.linalg.norm(miller[otherDir_id])
otherMatrix = np.array([otherDir,np.cross(otherPlane,otherDir),otherPlane])
r['rotations'].append(np.dot(otherMatrix.T,myMatrix))
r['rotations'] = np.array(r['rotations'])
return r

File diff suppressed because it is too large Load Diff

View File

@ -15,7 +15,6 @@ from numpy.lib import recfunctions as rfn
import damask import damask
from . import VTK from . import VTK
from . import Table from . import Table
from . import Rotation
from . import Orientation from . import Orientation
from . import grid_filters from . import grid_filters
from . import mechanics from . import mechanics
@ -743,11 +742,13 @@ class Result:
def _add_IPF_color(q,l): def _add_IPF_color(q,l):
m = util.scale_to_coprime(np.array(l)) m = util.scale_to_coprime(np.array(l))
o = Orientation(Rotation(rfn.structured_to_unstructured(q['data'])), o = Orientation(rotation = (rfn.structured_to_unstructured(q['data'])),
lattice = q['meta']['Lattice']) lattice = {'fcc':'cF',
'bcc':'cI',
'hex':'hP'}[q['meta']['Lattice']])
return { return {
'data': np.uint8(o.IPF_color(l)*255), 'data': np.uint8(o.IPF_color(o.to_SST(l))*255),
'label': 'IPFcolor_[{} {} {}]'.format(*m), 'label': 'IPFcolor_[{} {} {}]'.format(*m),
'meta' : { 'meta' : {
'Unit': '8-bit RGB', 'Unit': '8-bit RGB',
@ -897,42 +898,47 @@ class Result:
self._add_generic_pointwise(self._add_PK2,{'P':P,'F':F}) self._add_generic_pointwise(self._add_PK2,{'P':P,'F':F})
@staticmethod # The add_pole functionality needs discussion.
def _add_pole(q,p,polar): # The new Crystal object can perform such a calculation but the outcome depends on the lattice parameters
pole = np.array(p) # as well as on whether a direction or plane is concerned (see the DAMASK_examples/pole_figure notebook).
unit_pole = pole/np.linalg.norm(pole) # Below code appears to be too simplistic.
m = util.scale_to_coprime(pole)
rot = Rotation(q['data'].view(np.double).reshape(-1,4))
rotatedPole = rot @ np.broadcast_to(unit_pole,rot.shape+(3,)) # rotate pole according to crystal orientation # @staticmethod
xy = rotatedPole[:,0:2]/(1.+abs(unit_pole[2])) # stereographic projection # def _add_pole(q,p,polar):
coords = xy if not polar else \ # pole = np.array(p)
np.block([np.sqrt(xy[:,0:1]*xy[:,0:1]+xy[:,1:2]*xy[:,1:2]),np.arctan2(xy[:,1:2],xy[:,0:1])]) # unit_pole = pole/np.linalg.norm(pole)
return { # m = util.scale_to_coprime(pole)
'data': coords, # rot = Rotation(q['data'].view(np.double).reshape(-1,4))
'label': 'p^{}_[{} {} {})'.format(u'' if polar else 'xy',*m), #
'meta' : { # rotatedPole = rot @ np.broadcast_to(unit_pole,rot.shape+(3,)) # rotate pole according to crystal orientation
'Unit': '1', # xy = rotatedPole[:,0:2]/(1.+abs(unit_pole[2])) # stereographic projection
'Description': '{} coordinates of stereographic projection of pole (direction/plane) in crystal frame'\ # coords = xy if not polar else \
.format('Polar' if polar else 'Cartesian'), # np.block([np.sqrt(xy[:,0:1]*xy[:,0:1]+xy[:,1:2]*xy[:,1:2]),np.arctan2(xy[:,1:2],xy[:,0:1])])
'Creator': 'add_pole' # return {
} # 'data': coords,
} # 'label': 'p^{}_[{} {} {})'.format(u'rφ' if polar else 'xy',*m),
def add_pole(self,q,p,polar=False): # 'meta' : {
""" # 'Unit': '1',
Add coordinates of stereographic projection of given pole in crystal frame. # 'Description': '{} coordinates of stereographic projection of pole (direction/plane) in crystal frame'\
# .format('Polar' if polar else 'Cartesian'),
Parameters # 'Creator': 'add_pole'
---------- # }
q : str # }
Label of the dataset containing the crystallographic orientation as quaternions. # def add_pole(self,q,p,polar=False):
p : numpy.array of shape (3) # """
Crystallographic direction or plane. # Add coordinates of stereographic projection of given pole in crystal frame.
polar : bool, optional #
Give pole in polar coordinates. Defaults to False. # Parameters
# ----------
""" # q : str
self._add_generic_pointwise(self._add_pole,{'q':q},{'p':p,'polar':polar}) # Label of the dataset containing the crystallographic orientation as quaternions.
# p : numpy.array of shape (3)
# Crystallographic direction or plane.
# polar : bool, optional
# Give pole in polar coordinates. Defaults to False.
#
# """
# self._add_generic_pointwise(self._add_pole,{'q':q},{'p':p,'polar':polar})
@staticmethod @staticmethod

View File

@ -13,18 +13,18 @@ _R1 = (3.*np.pi/4.)**(1./3.)
class Rotation: class Rotation:
u""" u"""
Orientation stored with functionality for conversion to different representations. Rotation with functionality for conversion between different representations.
The following conventions apply: The following conventions apply:
- coordinate frames are right-handed. - Coordinate frames are right-handed.
- a rotation angle ω is taken to be positive for a counterclockwise rotation - A rotation angle ω is taken to be positive for a counterclockwise rotation
when viewing from the end point of the rotation axis towards the origin. when viewing from the end point of the rotation axis towards the origin.
- rotations will be interpreted in the passive sense. - Rotations will be interpreted in the passive sense.
- Euler angle triplets are implemented using the Bunge convention, - Euler angle triplets are implemented using the Bunge convention,
with the angular ranges as [0,2π], [0,π], [0,2π]. with angular ranges of [0,2π], [0,π], [0,2π].
- the rotation angle ω is limited to the interval [0,π]. - The rotation angle ω is limited to the interval [0,π].
- the real part of a quaternion is positive, Re(q) > 0 - The real part of a quaternion is positive, Re(q) > 0
- P = -1 (as default). - P = -1 (as default).
Examples Examples
@ -33,7 +33,7 @@ class Rotation:
coordinates "b" expressed in system "B": coordinates "b" expressed in system "B":
- b = Q @ a - b = Q @ a
- b = np.dot(Q.asMatrix(),a) - b = np.dot(Q.as_matrix(),a)
References References
---------- ----------
@ -44,20 +44,83 @@ class Rotation:
__slots__ = ['quaternion'] __slots__ = ['quaternion']
def __init__(self,quaternion = np.array([1.0,0.0,0.0,0.0])): def __init__(self,rotation = np.array([1.0,0.0,0.0,0.0])):
""" """
Initializes to identity unless specified. Initialize rotation object.
Parameters Parameters
---------- ----------
quaternion : numpy.ndarray, optional rotation : list, numpy.ndarray, Rotation, optional
Unit quaternion in positive real hemisphere. Unit quaternion in positive real hemisphere.
Use .from_quaternion to perform a sanity check. Use .from_quaternion to perform a sanity check.
Defaults to no rotation.
""" """
if quaternion.shape[-1] != 4: if isinstance(rotation,Rotation):
raise ValueError('Not a quaternion') self.quaternion = rotation.quaternion.copy()
self.quaternion = quaternion.copy() elif np.array(rotation).shape[-1] == 4:
self.quaternion = np.array(rotation)
else:
raise ValueError('"rotation" is neither a Rotation nor a quaternion')
def __repr__(self):
"""Represent rotation as unit quaternion, rotation matrix, and Bunge-Euler angles."""
return 'Quaternions:\n'+str(self.quaternion) \
if self.quaternion.shape != (4,) else \
'\n'.join([
'Quaternion: (real={:.3f}, imag=<{:+.3f}, {:+.3f}, {:+.3f}>)'.format(*(self.quaternion)),
'Matrix:\n{}'.format(np.round(self.as_matrix(),8)),
'Bunge Eulers / deg: ({:3.2f}, {:3.2f}, {:3.2f})'.format(*self.as_Eulers(degrees=True)),
])
# ToDo: Check difference __copy__ vs __deepcopy__
def __copy__(self,**kwargs):
"""Copy."""
return self.__class__(rotation=kwargs['rotation'] if 'rotation' in kwargs else self.quaternion)
copy = __copy__
def __getitem__(self,item):
"""Return slice according to item."""
return self.copy() \
if self.shape == () else \
self.copy(rotation=self.quaternion[item+(slice(None),)] if isinstance(item,tuple) else self.quaternion[item])
def __eq__(self,other):
"""
Equal to other.
Equality is determined taking limited floating point precision into
account. See numpy.allclose for details.
Parameters
----------
other : Rotation
Rotation to check for equality.
"""
return np.prod(self.shape,dtype=int) == np.prod(other.shape,dtype=int) \
and np.allclose(self.quaternion,other.quaternion)
def __neq__(self,other):
"""
Not Equal to other.
Equality is determined taking limited floating point precision into
account. See numpy.allclose for details.
Parameters
----------
other : Rotation
Rotation to check for inequality.
"""
return not self.__eq__(other)
@property @property
@ -65,38 +128,35 @@ class Rotation:
return self.quaternion.shape[:-1] return self.quaternion.shape[:-1]
# ToDo: Check difference __copy__ vs __deepcopy__
def __copy__(self):
"""Copy."""
return self.__class__(self.quaternion)
copy = __copy__
def __repr__(self):
"""Orientation displayed as unit quaternion, rotation matrix, and Bunge-Euler angles."""
if self.quaternion.shape != (4,):
return 'Quaternions:\n'+str(self.quaternion) # ToDo: could be nicer ...
return '\n'.join([
'Quaternion: (real={:.3f}, imag=<{:+.3f}, {:+.3f}, {:+.3f}>)'.format(*(self.quaternion)),
'Matrix:\n{}'.format(np.round(self.as_matrix(),8)),
'Bunge Eulers / deg: ({:3.2f}, {:3.2f}, {:3.2f})'.format(*self.as_Eulers(degrees=True)),
])
def __getitem__(self,item):
"""Iterate over leading/leftmost dimension of Rotation array."""
if self.shape == (): return self.copy()
if isinstance(item,tuple) and len(item) >= len(self):
raise IndexError('Too many indices')
return self.__class__(self.quaternion[item])
def __len__(self): def __len__(self):
"""Length of leading/leftmost dimension of Rotation array.""" """Length of leading/leftmost dimension of Rotation array."""
return 0 if self.shape == () else self.shape[0] return 0 if self.shape == () else self.shape[0]
def __invert__(self):
"""Inverse rotation (backward rotation)."""
dup = self.copy()
dup.quaternion[...,1:] *= -1
return dup
def __pow__(self,pwr):
"""
Raise quaternion to power.
Equivalent to performing the rotation 'pwr' times.
Parameters
----------
pwr : float
Power to raise quaternion to.
"""
phi = np.arccos(self.quaternion[...,0:1])
p = self.quaternion[...,1:]/np.linalg.norm(self.quaternion[...,1:],axis=-1,keepdims=True)
return self.copy(rotation=Rotation(np.block([np.cos(pwr*phi),np.sin(pwr*phi)*p]))._standardize())
def __matmul__(self,other): def __matmul__(self,other):
""" """
Rotation of vector, second or fourth order tensor, or rotation object. Rotation of vector, second or fourth order tensor, or rotation object.
@ -119,7 +179,7 @@ class Rotation:
p_o = other.quaternion[...,1:] p_o = other.quaternion[...,1:]
q = (q_m*q_o - np.einsum('...i,...i',p_m,p_o).reshape(self.shape+(1,))) q = (q_m*q_o - np.einsum('...i,...i',p_m,p_o).reshape(self.shape+(1,)))
p = q_m*p_o + q_o*p_m + _P * np.cross(p_m,p_o) p = q_m*p_o + q_o*p_m + _P * np.cross(p_m,p_o)
return self.__class__(np.block([q,p]))._standardize() return Rotation(np.block([q,p]))._standardize()
elif isinstance(other,np.ndarray): elif isinstance(other,np.ndarray):
if self.shape + (3,) == other.shape: if self.shape + (3,) == other.shape:
@ -146,27 +206,89 @@ class Rotation:
def _standardize(self): def _standardize(self):
"""Standardize (ensure positive real hemisphere).""" """Standardize quaternion (ensure positive real hemisphere)."""
self.quaternion[self.quaternion[...,0] < 0.0] *= -1 self.quaternion[self.quaternion[...,0] < 0.0] *= -1
return self return self
def inverse(self):
"""In-place inverse rotation (backward rotation)."""
self.quaternion[...,1:] *= -1
return self
def __invert__(self): def append(self,other):
"""Inverse rotation (backward rotation).""" """Extend rotation array along first dimension with other array."""
return self.copy().inverse() return self.copy(rotation=np.vstack((self.quaternion,other.quaternion)))
def inversed(self):
"""Inverse rotation (backward rotation).""" def flatten(self,order = 'C'):
return ~ self """Flatten quaternion array."""
return self.copy(rotation=self.quaternion.reshape((-1,4),order=order))
def reshape(self,shape,order = 'C'):
"""Reshape quaternion array."""
if isinstance(shape,(int,np.integer)): shape = (shape,)
return self.copy(rotation=self.quaternion.reshape(tuple(shape)+(4,),order=order))
def broadcast_to(self,shape,mode = 'right'):
"""
Broadcast quaternion array to shape.
Parameters
----------
shape : tuple
Shape of broadcasted array.
mode : str, optional
Where to preferentially locate missing dimensions.
Either 'left' or 'right' (default).
"""
if isinstance(shape,(int,np.integer)): shape = (shape,)
return self.copy(rotation=np.broadcast_to(self.quaternion.reshape(util.shapeshifter(self.shape,shape,mode)+(4,)),
shape+(4,)))
def average(self,weights = None):
"""
Average rotations along last dimension.
Parameters
----------
weights : list of floats, optional
Relative weight of each rotation.
Returns
-------
average : Rotation
Weighted average of original Rotation field.
References
----------
Quaternion averaging
F. Landis Markley, Yang Cheng, John L. Crassidis, Yaakov Oshman
Journal of Guidance, Control, and Dynamics 30(4):1193-1197, 2007
10.2514/1.28949
"""
def _M(quat):
"""Intermediate representation supporting quaternion averaging."""
return np.einsum('...i,...j',quat,quat)
if not weights:
weights = np.ones(self.shape,dtype=float)
eig, vec = np.linalg.eig(np.sum(_M(self.quaternion) * weights[...,np.newaxis,np.newaxis],axis=-3) \
/np.sum( weights[...,np.newaxis,np.newaxis],axis=-3))
return Rotation.from_quaternion(np.real(
np.squeeze(
np.take_along_axis(vec,
eig.argmax(axis=-1)[...,np.newaxis,np.newaxis],
axis=-1),
axis=-1)),
accept_homomorph = True)
def misorientation(self,other): def misorientation(self,other):
""" """
Get Misorientation. Calculate misorientation from self to other Rotation.
Parameters Parameters
---------- ----------
@ -177,33 +299,6 @@ class Rotation:
return other@~self return other@~self
def broadcast_to(self,shape):
if isinstance(shape,(int,np.integer)): shape = (shape,)
if self.shape == ():
q = np.broadcast_to(self.quaternion,shape+(4,))
else:
q = np.block([np.broadcast_to(self.quaternion[...,0:1],shape).reshape(shape+(1,)),
np.broadcast_to(self.quaternion[...,1:2],shape).reshape(shape+(1,)),
np.broadcast_to(self.quaternion[...,2:3],shape).reshape(shape+(1,)),
np.broadcast_to(self.quaternion[...,3:4],shape).reshape(shape+(1,))])
return self.__class__(q)
def average(self,other): #ToDo: discuss calling for vectors
"""
Calculate the average rotation.
Parameters
----------
other : Rotation
Rotation from which the average is rotated.
"""
if self.quaternion.shape != (4,) or other.quaternion.shape != (4,):
raise NotImplementedError('Support for multiple rotations missing')
return Rotation.from_average([self,other])
################################################################################################ ################################################################################################
# convert to different orientation representations (numpy arrays) # convert to different orientation representations (numpy arrays)
@ -326,20 +421,6 @@ class Rotation:
""" """
return Rotation._qu2cu(self.quaternion) return Rotation._qu2cu(self.quaternion)
@property
def M(self): # ToDo not sure about the name: as_M or M? we do not have a from_M
"""
Intermediate representation supporting quaternion averaging.
References
----------
F. Landis Markley et al., Journal of Guidance, Control, and Dynamics 30(4):1193-1197, 2007
https://doi.org/10.2514/1.28949
"""
return np.einsum('...i,...j',self.quaternion,self.quaternion)
################################################################################################ ################################################################################################
# Static constructors. The input data needs to follow the conventions, options allow to # Static constructors. The input data needs to follow the conventions, options allow to
# relax the conventions. # relax the conventions.
@ -347,7 +428,7 @@ class Rotation:
def from_quaternion(q, def from_quaternion(q,
accept_homomorph = False, accept_homomorph = False,
P = -1, P = -1,
acceptHomomorph = None): # old name (for compatibility) **kwargs):
""" """
Initialize from quaternion. Initialize from quaternion.
@ -363,15 +444,13 @@ class Rotation:
Convention used. Defaults to -1. Convention used. Defaults to -1.
""" """
if acceptHomomorph is not None:
accept_homomorph = acceptHomomorph # for compatibility
qu = np.array(q,dtype=float) qu = np.array(q,dtype=float)
if qu.shape[:-2:-1] != (4,): if qu.shape[:-2:-1] != (4,):
raise ValueError('Invalid shape.') raise ValueError('Invalid shape.')
if abs(P) != 1: if abs(P) != 1:
raise ValueError('P ∉ {-1,1}') raise ValueError('P ∉ {-1,1}')
if P == 1: qu[...,1:4] *= -1 qu[...,1:4] *= -P
if accept_homomorph: if accept_homomorph:
qu[qu[...,0] < 0.0] *= -1 qu[qu[...,0] < 0.0] *= -1
else: else:
@ -384,7 +463,8 @@ class Rotation:
@staticmethod @staticmethod
def from_Eulers(phi, def from_Eulers(phi,
degrees = False): degrees = False,
**kwargs):
""" """
Initialize from Bunge-Euler angles. Initialize from Bunge-Euler angles.
@ -411,7 +491,8 @@ class Rotation:
def from_axis_angle(axis_angle, def from_axis_angle(axis_angle,
degrees = False, degrees = False,
normalize = False, normalize = False,
P = -1): P = -1,
**kwargs):
""" """
Initialize from Axis angle pair. Initialize from Axis angle pair.
@ -434,7 +515,7 @@ class Rotation:
if abs(P) != 1: if abs(P) != 1:
raise ValueError('P ∉ {-1,1}') raise ValueError('P ∉ {-1,1}')
if P == 1: ax[...,0:3] *= -1 ax[...,0:3] *= -P
if degrees: ax[..., 3] = np.radians(ax[...,3]) if degrees: ax[..., 3] = np.radians(ax[...,3])
if normalize: ax[...,0:3] /= np.linalg.norm(ax[...,0:3],axis=-1,keepdims=True) if normalize: ax[...,0:3] /= np.linalg.norm(ax[...,0:3],axis=-1,keepdims=True)
if np.any(ax[...,3] < 0.0) or np.any(ax[...,3] > np.pi): if np.any(ax[...,3] < 0.0) or np.any(ax[...,3] > np.pi):
@ -448,14 +529,15 @@ class Rotation:
@staticmethod @staticmethod
def from_basis(basis, def from_basis(basis,
orthonormal = True, orthonormal = True,
reciprocal = False): reciprocal = False,
**kwargs):
""" """
Initialize from lattice basis vectors. Initialize from lattice basis vectors.
Parameters Parameters
---------- ----------
basis : numpy.ndarray of shape (...,3,3) basis : numpy.ndarray of shape (...,3,3)
Three lattice basis vectors in three dimensions. Three three-dimensional lattice basis vectors.
orthonormal : boolean, optional orthonormal : boolean, optional
Basis is strictly orthonormal, i.e. is free of stretch components. Defaults to True. Basis is strictly orthonormal, i.e. is free of stretch components. Defaults to True.
reciprocal : boolean, optional reciprocal : boolean, optional
@ -463,7 +545,7 @@ class Rotation:
""" """
om = np.array(basis,dtype=float) om = np.array(basis,dtype=float)
if om.shape[:-3:-1] != (3,3): if om.shape[-2:] != (3,3):
raise ValueError('Invalid shape.') raise ValueError('Invalid shape.')
if reciprocal: if reciprocal:
@ -482,7 +564,7 @@ class Rotation:
return Rotation(Rotation._om2qu(om)) return Rotation(Rotation._om2qu(om))
@staticmethod @staticmethod
def from_matrix(R): def from_matrix(R,**kwargs):
""" """
Initialize from rotation matrix. Initialize from rotation matrix.
@ -494,10 +576,40 @@ class Rotation:
""" """
return Rotation.from_basis(R) return Rotation.from_basis(R)
@staticmethod
def from_parallel(a,b,
**kwargs):
"""
Initialize from pairs of two orthogonal lattice basis vectors.
Parameters
----------
a : numpy.ndarray of shape (...,2,3)
Two three-dimensional lattice vectors of first orthogonal basis.
b : numpy.ndarray of shape (...,2,3)
Corresponding three-dimensional lattice vectors of second basis.
"""
a_ = np.array(a)
b_ = np.array(b)
if a_.shape[-2:] != (2,3) or b_.shape[-2:] != (2,3) or a_.shape != b_.shape:
raise ValueError('Invalid shape.')
am = np.stack([ a_[...,0,:],
a_[...,1,:],
np.cross(a_[...,0,:],a_[...,1,:]) ],axis=-2)
bm = np.stack([ b_[...,0,:],
b_[...,1,:],
np.cross(b_[...,0,:],b_[...,1,:]) ],axis=-2)
return Rotation.from_basis(np.swapaxes(am/np.linalg.norm(am,axis=-1,keepdims=True),-1,-2))\
.misorientation(Rotation.from_basis(np.swapaxes(bm/np.linalg.norm(bm,axis=-1,keepdims=True),-1,-2)))
@staticmethod @staticmethod
def from_Rodrigues(rho, def from_Rodrigues(rho,
normalize = False, normalize = False,
P = -1): P = -1,
**kwargs):
""" """
Initialize from Rodrigues-Frank vector. Initialize from Rodrigues-Frank vector.
@ -518,7 +630,7 @@ class Rotation:
if abs(P) != 1: if abs(P) != 1:
raise ValueError('P ∉ {-1,1}') raise ValueError('P ∉ {-1,1}')
if P == 1: ro[...,0:3] *= -1 ro[...,0:3] *= -P
if normalize: ro[...,0:3] /= np.linalg.norm(ro[...,0:3],axis=-1,keepdims=True) if normalize: ro[...,0:3] /= np.linalg.norm(ro[...,0:3],axis=-1,keepdims=True)
if np.any(ro[...,3] < 0.0): if np.any(ro[...,3] < 0.0):
raise ValueError('Rodrigues vector rotation angle not positive.') raise ValueError('Rodrigues vector rotation angle not positive.')
@ -529,7 +641,8 @@ class Rotation:
@staticmethod @staticmethod
def from_homochoric(h, def from_homochoric(h,
P = -1): P = -1,
**kwargs):
""" """
Initialize from homochoric vector. Initialize from homochoric vector.
@ -547,7 +660,7 @@ class Rotation:
if abs(P) != 1: if abs(P) != 1:
raise ValueError('P ∉ {-1,1}') raise ValueError('P ∉ {-1,1}')
if P == 1: ho *= -1 ho *= -P
if np.any(np.linalg.norm(ho,axis=-1) >_R1+1e-9): if np.any(np.linalg.norm(ho,axis=-1) >_R1+1e-9):
raise ValueError('Homochoric coordinate outside of the sphere.') raise ValueError('Homochoric coordinate outside of the sphere.')
@ -556,7 +669,8 @@ class Rotation:
@staticmethod @staticmethod
def from_cubochoric(c, def from_cubochoric(c,
P = -1): P = -1,
**kwargs):
""" """
Initialize from cubochoric vector. Initialize from cubochoric vector.
@ -577,46 +691,15 @@ class Rotation:
if np.abs(np.max(cu)) > np.pi**(2./3.) * 0.5+1e-9: if np.abs(np.max(cu)) > np.pi**(2./3.) * 0.5+1e-9:
raise ValueError('Cubochoric coordinate outside of the cube.') raise ValueError('Cubochoric coordinate outside of the cube.')
ho = Rotation._cu2ho(cu) ho = -P * Rotation._cu2ho(cu)
if P == 1: ho *= -1
return Rotation(Rotation._ho2qu(ho)) return Rotation(Rotation._ho2qu(ho))
@staticmethod @staticmethod
def from_average(rotations,weights = None): def from_random(shape = None,
""" seed = None,
Average rotation. **kwargs):
References
----------
F. Landis Markley et al., Journal of Guidance, Control, and Dynamics 30(4):1193-1197, 2007
https://doi.org/10.2514/1.28949
Parameters
----------
rotations : list of Rotations
Rotations to average from
weights : list of floats, optional
Weights for each rotation used for averaging
"""
if not all(isinstance(item, Rotation) for item in rotations):
raise TypeError('Only instances of Rotation can be averaged.')
N = len(rotations)
if not weights:
weights = np.ones(N,dtype='i')
for i,(r,n) in enumerate(zip(rotations,weights)):
M = r.M * n if i == 0 \
else M + r.M * n # noqa add (multiples) of this rotation to average noqa
eig, vec = np.linalg.eig(M/N)
return Rotation.from_quaternion(np.real(vec.T[eig.argmax()]),accept_homomorph = True)
@staticmethod
def from_random(shape=None,seed=None):
""" """
Draw random rotation. Draw random rotation.
@ -633,12 +716,7 @@ class Rotation:
""" """
rng = np.random.default_rng(seed) rng = np.random.default_rng(seed)
if shape is None: r = rng.random(3 if shape is None else tuple(shape)+(3,) if hasattr(shape, '__iter__') else (shape,3))
r = rng.random(3)
elif hasattr(shape, '__iter__'):
r = rng.random(tuple(shape)+(3,))
else:
r = rng.random((shape,3))
A = np.sqrt(r[...,2]) A = np.sqrt(r[...,2])
B = np.sqrt(1.0-r[...,2]) B = np.sqrt(1.0-r[...,2])
@ -647,14 +725,17 @@ class Rotation:
np.cos(2.0*np.pi*r[...,1])*B, np.cos(2.0*np.pi*r[...,1])*B,
np.sin(2.0*np.pi*r[...,0])*A],axis=-1) np.sin(2.0*np.pi*r[...,0])*A],axis=-1)
return Rotation(q.reshape(r.shape[:-1]+(4,)) if shape is not None else q)._standardize() return Rotation(q if shape is None else q.reshape(r.shape[:-1]+(4,)))._standardize()
# for compatibility
__mul__ = __matmul__
@staticmethod @staticmethod
def from_ODF(weights,Eulers,N=500,degrees=True,fractions=True,seed=None): def from_ODF(weights,
Eulers,
N = 500,
degrees = True,
fractions = True,
seed = None,
**kwargs):
""" """
Sample discrete values from a binned ODF. Sample discrete values from a binned ODF.
@ -707,7 +788,12 @@ class Rotation:
@staticmethod @staticmethod
def from_spherical_component(center,sigma,N=500,degrees=True,seed=None): def from_spherical_component(center,
sigma,
N = 500,
degrees = True,
seed = None,
**kwargs):
""" """
Calculate set of rotations with Gaussian distribution around center. Calculate set of rotations with Gaussian distribution around center.
@ -738,7 +824,13 @@ class Rotation:
@staticmethod @staticmethod
def from_fiber_component(alpha,beta,sigma=0.0,N=500,degrees=True,seed=None): def from_fiber_component(alpha,
beta,
sigma = 0.0,
N = 500,
degrees = True,
seed = None,
**kwargs):
""" """
Calculate set of rotations with Gaussian distribution around direction. Calculate set of rotations with Gaussian distribution around direction.

View File

@ -175,7 +175,7 @@ class Table:
@property @property
def labels(self): def labels(self):
return list(self.shapes.keys()) return list(self.shapes)
def get(self,label): def get(self,label):

420
python/damask/lattice.py Normal file
View File

@ -0,0 +1,420 @@
import numpy as _np
kinematics = {
'cF': {
'slip' : _np.array([
[+0,+1,-1 , +1,+1,+1],
[-1,+0,+1 , +1,+1,+1],
[+1,-1,+0 , +1,+1,+1],
[+0,-1,-1 , -1,-1,+1],
[+1,+0,+1 , -1,-1,+1],
[-1,+1,+0 , -1,-1,+1],
[+0,-1,+1 , +1,-1,-1],
[-1,+0,-1 , +1,-1,-1],
[+1,+1,+0 , +1,-1,-1],
[+0,+1,+1 , -1,+1,-1],
[+1,+0,-1 , -1,+1,-1],
[-1,-1,+0 , -1,+1,-1],
[+1,+1,+0 , +1,-1,+0],
[+1,-1,+0 , +1,+1,+0],
[+1,+0,+1 , +1,+0,-1],
[+1,+0,-1 , +1,+0,+1],
[+0,+1,+1 , +0,+1,-1],
[+0,+1,-1 , +0,+1,+1],
],'d'),
'twin' : _np.array([
[-2, 1, 1, 1, 1, 1],
[ 1,-2, 1, 1, 1, 1],
[ 1, 1,-2, 1, 1, 1],
[ 2,-1, 1, -1,-1, 1],
[-1, 2, 1, -1,-1, 1],
[-1,-1,-2, -1,-1, 1],
[-2,-1,-1, 1,-1,-1],
[ 1, 2,-1, 1,-1,-1],
[ 1,-1, 2, 1,-1,-1],
[ 2, 1,-1, -1, 1,-1],
[-1,-2,-1, -1, 1,-1],
[-1, 1, 2, -1, 1,-1],
],dtype=float),
},
'cI': {
'slip' : _np.array([
[+1,-1,+1 , +0,+1,+1],
[-1,-1,+1 , +0,+1,+1],
[+1,+1,+1 , +0,-1,+1],
[-1,+1,+1 , +0,-1,+1],
[-1,+1,+1 , +1,+0,+1],
[-1,-1,+1 , +1,+0,+1],
[+1,+1,+1 , -1,+0,+1],
[+1,-1,+1 , -1,+0,+1],
[-1,+1,+1 , +1,+1,+0],
[-1,+1,-1 , +1,+1,+0],
[+1,+1,+1 , -1,+1,+0],
[+1,+1,-1 , -1,+1,+0],
[-1,+1,+1 , +2,+1,+1],
[+1,+1,+1 , -2,+1,+1],
[+1,+1,-1 , +2,-1,+1],
[+1,-1,+1 , +2,+1,-1],
[+1,-1,+1 , +1,+2,+1],
[+1,+1,-1 , -1,+2,+1],
[+1,+1,+1 , +1,-2,+1],
[-1,+1,+1 , +1,+2,-1],
[+1,+1,-1 , +1,+1,+2],
[+1,-1,+1 , -1,+1,+2],
[-1,+1,+1 , +1,-1,+2],
[+1,+1,+1 , +1,+1,-2],
],'d'),
'twin' : _np.array([
[-1, 1, 1, 2, 1, 1],
[ 1, 1, 1, -2, 1, 1],
[ 1, 1,-1, 2,-1, 1],
[ 1,-1, 1, 2, 1,-1],
[ 1,-1, 1, 1, 2, 1],
[ 1, 1,-1, -1, 2, 1],
[ 1, 1, 1, 1,-2, 1],
[-1, 1, 1, 1, 2,-1],
[ 1, 1,-1, 1, 1, 2],
[ 1,-1, 1, -1, 1, 2],
[-1, 1, 1, 1,-1, 2],
[ 1, 1, 1, 1, 1,-2],
],dtype=float),
},
'hP': {
'slip' : _np.array([
[+2,-1,-1,+0 , +0,+0,+0,+1],
[-1,+2,-1,+0 , +0,+0,+0,+1],
[-1,-1,+2,+0 , +0,+0,+0,+1],
[+2,-1,-1,+0 , +0,+1,-1,+0],
[-1,+2,-1,+0 , -1,+0,+1,+0],
[-1,-1,+2,+0 , +1,-1,+0,+0],
[-1,+1,+0,+0 , +1,+1,-2,+0],
[+0,-1,+1,+0 , -2,+1,+1,+0],
[+1,+0,-1,+0 , +1,-2,+1,+0],
[-1,+2,-1,+0 , +1,+0,-1,+1],
[-2,+1,+1,+0 , +0,+1,-1,+1],
[-1,-1,+2,+0 , -1,+1,+0,+1],
[+1,-2,+1,+0 , -1,+0,+1,+1],
[+2,-1,-1,+0 , +0,-1,+1,+1],
[+1,+1,-2,+0 , +1,-1,+0,+1],
[-2,+1,+1,+3 , +1,+0,-1,+1],
[-1,-1,+2,+3 , +1,+0,-1,+1],
[-1,-1,+2,+3 , +0,+1,-1,+1],
[+1,-2,+1,+3 , +0,+1,-1,+1],
[+1,-2,+1,+3 , -1,+1,+0,+1],
[+2,-1,-1,+3 , -1,+1,+0,+1],
[+2,-1,-1,+3 , -1,+0,+1,+1],
[+1,+1,-2,+3 , -1,+0,+1,+1],
[+1,+1,-2,+3 , +0,-1,+1,+1],
[-1,+2,-1,+3 , +0,-1,+1,+1],
[-1,+2,-1,+3 , +1,-1,+0,+1],
[-2,+1,+1,+3 , +1,-1,+0,+1],
[-1,-1,+2,+3 , +1,+1,-2,+2],
[+1,-2,+1,+3 , -1,+2,-1,+2],
[+2,-1,-1,+3 , -2,+1,+1,+2],
[+1,+1,-2,+3 , -1,-1,+2,+2],
[-1,+2,-1,+3 , +1,-2,+1,+2],
[-2,+1,+1,+3 , +2,-1,-1,+2],
],'d'),
'twin' : _np.array([
[-1, 0, 1, 1, 1, 0, -1, 2], # shear = (3-(c/a)^2)/(sqrt(3) c/a) <-10.1>{10.2}
[ 0, -1, 1, 1, 0, 1, -1, 2],
[ 1, -1, 0, 1, -1, 1, 0, 2],
[ 1, 0, -1, 1, -1, 0, 1, 2],
[ 0, 1, -1, 1, 0, -1, 1, 2],
[-1, 1, 0, 1, 1, -1, 0, 2],
[-1, -1, 2, 6, 1, 1, -2, 1], # shear = 1/(c/a) <11.6>{-1-1.1}
[ 1, -2, 1, 6, -1, 2, -1, 1],
[ 2, -1, -1, 6, -2, 1, 1, 1],
[ 1, 1, -2, 6, -1, -1, 2, 1],
[-1, 2, -1, 6, 1, -2, 1, 1],
[-2, 1, 1, 6, 2, -1, -1, 1],
[ 1, 0, -1, -2, 1, 0, -1, 1], # shear = (4(c/a)^2-9)/(4 sqrt(3) c/a) <10.-2>{10.1}
[ 0, 1, -1, -2, 0, 1, -1, 1],
[-1, 1, 0, -2, -1, 1, 0, 1],
[-1, 0, 1, -2, -1, 0, 1, 1],
[ 0, -1, 1, -2, 0, -1, 1, 1],
[ 1, -1, 0, -2, 1, -1, 0, 1],
[ 1, 1, -2, -3, 1, 1, -2, 2], # shear = 2((c/a)^2-2)/(3 c/a) <11.-3>{11.2}
[-1, 2, -1, -3, -1, 2, -1, 2],
[-2, 1, 1, -3, -2, 1, 1, 2],
[-1, -1, 2, -3, -1, -1, 2, 2],
[ 1, -2, 1, -3, 1, -2, 1, 2],
[ 2, -1, -1, -3, 2, -1, -1, 2],
],dtype=float),
},
}
# Kurdjomov--Sachs orientation relationship for fcc <-> bcc transformation
# from S. Morito et al., Journal of Alloys and Compounds 577:s587-s592, 2013
# also see K. Kitahara et al., Acta Materialia 54:1279-1288, 2006
relations = {
'KS': {
'cF' : _np.array([
[[ -1, 0, 1],[ 1, 1, 1]],
[[ -1, 0, 1],[ 1, 1, 1]],
[[ 0, 1, -1],[ 1, 1, 1]],
[[ 0, 1, -1],[ 1, 1, 1]],
[[ 1, -1, 0],[ 1, 1, 1]],
[[ 1, -1, 0],[ 1, 1, 1]],
[[ 1, 0, -1],[ 1, -1, 1]],
[[ 1, 0, -1],[ 1, -1, 1]],
[[ -1, -1, 0],[ 1, -1, 1]],
[[ -1, -1, 0],[ 1, -1, 1]],
[[ 0, 1, 1],[ 1, -1, 1]],
[[ 0, 1, 1],[ 1, -1, 1]],
[[ 0, -1, 1],[ -1, 1, 1]],
[[ 0, -1, 1],[ -1, 1, 1]],
[[ -1, 0, -1],[ -1, 1, 1]],
[[ -1, 0, -1],[ -1, 1, 1]],
[[ 1, 1, 0],[ -1, 1, 1]],
[[ 1, 1, 0],[ -1, 1, 1]],
[[ -1, 1, 0],[ 1, 1, -1]],
[[ -1, 1, 0],[ 1, 1, -1]],
[[ 0, -1, -1],[ 1, 1, -1]],
[[ 0, -1, -1],[ 1, 1, -1]],
[[ 1, 0, 1],[ 1, 1, -1]],
[[ 1, 0, 1],[ 1, 1, -1]],
],dtype=float),
'cI' : _np.array([
[[ -1, -1, 1],[ 0, 1, 1]],
[[ -1, 1, -1],[ 0, 1, 1]],
[[ -1, -1, 1],[ 0, 1, 1]],
[[ -1, 1, -1],[ 0, 1, 1]],
[[ -1, -1, 1],[ 0, 1, 1]],
[[ -1, 1, -1],[ 0, 1, 1]],
[[ -1, -1, 1],[ 0, 1, 1]],
[[ -1, 1, -1],[ 0, 1, 1]],
[[ -1, -1, 1],[ 0, 1, 1]],
[[ -1, 1, -1],[ 0, 1, 1]],
[[ -1, -1, 1],[ 0, 1, 1]],
[[ -1, 1, -1],[ 0, 1, 1]],
[[ -1, -1, 1],[ 0, 1, 1]],
[[ -1, 1, -1],[ 0, 1, 1]],
[[ -1, -1, 1],[ 0, 1, 1]],
[[ -1, 1, -1],[ 0, 1, 1]],
[[ -1, -1, 1],[ 0, 1, 1]],
[[ -1, 1, -1],[ 0, 1, 1]],
[[ -1, -1, 1],[ 0, 1, 1]],
[[ -1, 1, -1],[ 0, 1, 1]],
[[ -1, -1, 1],[ 0, 1, 1]],
[[ -1, 1, -1],[ 0, 1, 1]],
[[ -1, -1, 1],[ 0, 1, 1]],
[[ -1, 1, -1],[ 0, 1, 1]],
],dtype=float),
},
'GT': {
'cF' : _np.array([
[[ -5,-12, 17],[ 1, 1, 1]],
[[ 17, -5,-12],[ 1, 1, 1]],
[[-12, 17, -5],[ 1, 1, 1]],
[[ 5, 12, 17],[ -1, -1, 1]],
[[-17, 5,-12],[ -1, -1, 1]],
[[ 12,-17, -5],[ -1, -1, 1]],
[[ -5, 12,-17],[ -1, 1, 1]],
[[ 17, 5, 12],[ -1, 1, 1]],
[[-12,-17, 5],[ -1, 1, 1]],
[[ 5,-12,-17],[ 1, -1, 1]],
[[-17, -5, 12],[ 1, -1, 1]],
[[ 12, 17, 5],[ 1, -1, 1]],
[[ -5, 17,-12],[ 1, 1, 1]],
[[-12, -5, 17],[ 1, 1, 1]],
[[ 17,-12, -5],[ 1, 1, 1]],
[[ 5,-17,-12],[ -1, -1, 1]],
[[ 12, 5, 17],[ -1, -1, 1]],
[[-17, 12, -5],[ -1, -1, 1]],
[[ -5,-17, 12],[ -1, 1, 1]],
[[-12, 5,-17],[ -1, 1, 1]],
[[ 17, 12, 5],[ -1, 1, 1]],
[[ 5, 17, 12],[ 1, -1, 1]],
[[ 12, -5,-17],[ 1, -1, 1]],
[[-17,-12, 5],[ 1, -1, 1]],
],dtype=float),
'cI' : _np.array([
[[-17, -7, 17],[ 1, 0, 1]],
[[ 17,-17, -7],[ 1, 1, 0]],
[[ -7, 17,-17],[ 0, 1, 1]],
[[ 17, 7, 17],[ -1, 0, 1]],
[[-17, 17, -7],[ -1, -1, 0]],
[[ 7,-17,-17],[ 0, -1, 1]],
[[-17, 7,-17],[ -1, 0, 1]],
[[ 17, 17, 7],[ -1, 1, 0]],
[[ -7,-17, 17],[ 0, 1, 1]],
[[ 17, -7,-17],[ 1, 0, 1]],
[[-17,-17, 7],[ 1, -1, 0]],
[[ 7, 17, 17],[ 0, -1, 1]],
[[-17, 17, -7],[ 1, 1, 0]],
[[ -7,-17, 17],[ 0, 1, 1]],
[[ 17, -7,-17],[ 1, 0, 1]],
[[ 17,-17, -7],[ -1, -1, 0]],
[[ 7, 17, 17],[ 0, -1, 1]],
[[-17, 7,-17],[ -1, 0, 1]],
[[-17,-17, 7],[ -1, 1, 0]],
[[ -7, 17,-17],[ 0, 1, 1]],
[[ 17, 7, 17],[ -1, 0, 1]],
[[ 17, 17, 7],[ 1, -1, 0]],
[[ 7,-17,-17],[ 0, -1, 1]],
[[-17, -7, 17],[ 1, 0, 1]],
],dtype=float),
},
'GT_prime': {
'cF' : _np.array([
[[ 0, 1, -1],[ 7, 17, 17]],
[[ -1, 0, 1],[ 17, 7, 17]],
[[ 1, -1, 0],[ 17, 17, 7]],
[[ 0, -1, -1],[ -7,-17, 17]],
[[ 1, 0, 1],[-17, -7, 17]],
[[ 1, -1, 0],[-17,-17, 7]],
[[ 0, 1, -1],[ 7,-17,-17]],
[[ 1, 0, 1],[ 17, -7,-17]],
[[ -1, -1, 0],[ 17,-17, -7]],
[[ 0, -1, -1],[ -7, 17,-17]],
[[ -1, 0, 1],[-17, 7,-17]],
[[ -1, -1, 0],[-17, 17, -7]],
[[ 0, -1, 1],[ 7, 17, 17]],
[[ 1, 0, -1],[ 17, 7, 17]],
[[ -1, 1, 0],[ 17, 17, 7]],
[[ 0, 1, 1],[ -7,-17, 17]],
[[ -1, 0, -1],[-17, -7, 17]],
[[ -1, 1, 0],[-17,-17, 7]],
[[ 0, -1, 1],[ 7,-17,-17]],
[[ -1, 0, -1],[ 17, -7,-17]],
[[ 1, 1, 0],[ 17,-17, -7]],
[[ 0, 1, 1],[ -7, 17,-17]],
[[ 1, 0, -1],[-17, 7,-17]],
[[ 1, 1, 0],[-17, 17, -7]],
],dtype=float),
'cI' : _np.array([
[[ 1, 1, -1],[ 12, 5, 17]],
[[ -1, 1, 1],[ 17, 12, 5]],
[[ 1, -1, 1],[ 5, 17, 12]],
[[ -1, -1, -1],[-12, -5, 17]],
[[ 1, -1, 1],[-17,-12, 5]],
[[ 1, -1, -1],[ -5,-17, 12]],
[[ -1, 1, -1],[ 12, -5,-17]],
[[ 1, 1, 1],[ 17,-12, -5]],
[[ -1, -1, 1],[ 5,-17,-12]],
[[ 1, -1, -1],[-12, 5,-17]],
[[ -1, -1, 1],[-17, 12, -5]],
[[ -1, -1, -1],[ -5, 17,-12]],
[[ 1, -1, 1],[ 12, 17, 5]],
[[ 1, 1, -1],[ 5, 12, 17]],
[[ -1, 1, 1],[ 17, 5, 12]],
[[ -1, 1, 1],[-12,-17, 5]],
[[ -1, -1, -1],[ -5,-12, 17]],
[[ -1, 1, -1],[-17, -5, 12]],
[[ -1, -1, 1],[ 12,-17, -5]],
[[ -1, 1, -1],[ 5,-12,-17]],
[[ 1, 1, 1],[ 17, -5,-12]],
[[ 1, 1, 1],[-12, 17, -5]],
[[ 1, -1, -1],[ -5, 12,-17]],
[[ 1, 1, -1],[-17, 5,-12]],
],dtype=float),
},
'NW': {
'cF' : _np.array([
[[ 2, -1, -1],[ 1, 1, 1]],
[[ -1, 2, -1],[ 1, 1, 1]],
[[ -1, -1, 2],[ 1, 1, 1]],
[[ -2, -1, -1],[ -1, 1, 1]],
[[ 1, 2, -1],[ -1, 1, 1]],
[[ 1, -1, 2],[ -1, 1, 1]],
[[ 2, 1, -1],[ 1, -1, 1]],
[[ -1, -2, -1],[ 1, -1, 1]],
[[ -1, 1, 2],[ 1, -1, 1]],
[[ 2, -1, 1],[ -1, -1, 1]],
[[ -1, 2, 1],[ -1, -1, 1]],
[[ -1, -1, -2],[ -1, -1, 1]],
],dtype=float),
'cI' : _np.array([
[[ 0, -1, 1],[ 0, 1, 1]],
[[ 0, -1, 1],[ 0, 1, 1]],
[[ 0, -1, 1],[ 0, 1, 1]],
[[ 0, -1, 1],[ 0, 1, 1]],
[[ 0, -1, 1],[ 0, 1, 1]],
[[ 0, -1, 1],[ 0, 1, 1]],
[[ 0, -1, 1],[ 0, 1, 1]],
[[ 0, -1, 1],[ 0, 1, 1]],
[[ 0, -1, 1],[ 0, 1, 1]],
[[ 0, -1, 1],[ 0, 1, 1]],
[[ 0, -1, 1],[ 0, 1, 1]],
[[ 0, -1, 1],[ 0, 1, 1]],
],dtype=float),
},
'Pitsch': {
'cF' : _np.array([
[[ 1, 0, 1],[ 0, 1, 0]],
[[ 1, 1, 0],[ 0, 0, 1]],
[[ 0, 1, 1],[ 1, 0, 0]],
[[ 0, 1, -1],[ 1, 0, 0]],
[[ -1, 0, 1],[ 0, 1, 0]],
[[ 1, -1, 0],[ 0, 0, 1]],
[[ 1, 0, -1],[ 0, 1, 0]],
[[ -1, 1, 0],[ 0, 0, 1]],
[[ 0, -1, 1],[ 1, 0, 0]],
[[ 0, 1, 1],[ 1, 0, 0]],
[[ 1, 0, 1],[ 0, 1, 0]],
[[ 1, 1, 0],[ 0, 0, 1]],
],dtype=float),
'cI' : _np.array([
[[ 1, -1, 1],[ -1, 0, 1]],
[[ 1, 1, -1],[ 1, -1, 0]],
[[ -1, 1, 1],[ 0, 1, -1]],
[[ -1, 1, -1],[ 0, -1, -1]],
[[ -1, -1, 1],[ -1, 0, -1]],
[[ 1, -1, -1],[ -1, -1, 0]],
[[ 1, -1, -1],[ -1, 0, -1]],
[[ -1, 1, -1],[ -1, -1, 0]],
[[ -1, -1, 1],[ 0, -1, -1]],
[[ -1, 1, 1],[ 0, -1, 1]],
[[ 1, -1, 1],[ 1, 0, -1]],
[[ 1, 1, -1],[ -1, 1, 0]],
],dtype=float),
},
'Bain': {
'cF' : _np.array([
[[ 0, 1, 0],[ 1, 0, 0]],
[[ 0, 0, 1],[ 0, 1, 0]],
[[ 1, 0, 0],[ 0, 0, 1]],
],dtype=float),
'cI' : _np.array([
[[ 0, 1, 1],[ 1, 0, 0]],
[[ 1, 0, 1],[ 0, 1, 0]],
[[ 1, 1, 0],[ 0, 0, 1]],
],dtype=float),
},
'Burgers' : {
'cI' : _np.array([
[[ -1, 1, 1],[ 1, 1, 0]],
[[ -1, 1, -1],[ 1, 1, 0]],
[[ 1, 1, 1],[ 1, -1, 0]],
[[ 1, 1, -1],[ 1, -1, 0]],
[[ 1, 1, -1],[ 1, 0, 1]],
[[ -1, 1, 1],[ 1, 0, 1]],
[[ 1, 1, 1],[ -1, 0, 1]],
[[ 1, -1, 1],[ -1, 0, 1]],
[[ -1, 1, -1],[ 0, 1, 1]],
[[ 1, 1, -1],[ 0, 1, 1]],
[[ -1, 1, 1],[ 0, -1, 1]],
[[ 1, 1, 1],[ 0, -1, 1]],
],dtype=float),
'hP' : _np.array([
[[ -1, 2, -1, 0],[ 0, 0, 0, 1]],
[[ -1, -1, 2, 0],[ 0, 0, 0, 1]],
[[ -1, 2, -1, 0],[ 0, 0, 0, 1]],
[[ -1, -1, 2, 0],[ 0, 0, 0, 1]],
[[ -1, 2, -1, 0],[ 0, 0, 0, 1]],
[[ -1, -1, 2, 0],[ 0, 0, 0, 1]],
[[ -1, 2, -1, 0],[ 0, 0, 0, 1]],
[[ -1, -1, 2, 0],[ 0, 0, 0, 1]],
[[ -1, 2, -1, 0],[ 0, 0, 0, 1]],
[[ -1, -1, 2, 0],[ 0, 0, 0, 1]],
[[ -1, 2, -1, 0],[ 0, 0, 0, 1]],
[[ -1, -1, 2, 0],[ 0, 0, 0, 1]],
],dtype=float),
},
}

View File

@ -3,6 +3,7 @@ import datetime
import os import os
import subprocess import subprocess
import shlex import shlex
import re
import fractions import fractions
from functools import reduce from functools import reduce
from optparse import Option from optparse import Option
@ -20,10 +21,13 @@ __all__=[
'execute', 'execute',
'show_progress', 'show_progress',
'scale_to_coprime', 'scale_to_coprime',
'project_stereographic',
'hybrid_IA', 'hybrid_IA',
'return_message', 'return_message',
'extendableOption', 'extendableOption',
'execution_stamp' 'execution_stamp',
'shapeshifter',
'shapeblender',
] ]
#################################################################################################### ####################################################################################################
@ -182,6 +186,28 @@ def scale_to_coprime(v):
return m return m
def project_stereographic(vector,normalize=False):
"""
Apply stereographic projection to vector.
Parameters
----------
vector : numpy.ndarray of shape (...,3)
Vector coordinates to be projected.
normalize : bool
Ensure unit length for vector. Defaults to False.
Returns
-------
coordinates : numpy.ndarray of shape (...,2)
Projected coordinates.
"""
v_ = vector/np.linalg.norm(vector,axis=-1,keepdims=True) if normalize else vector
return np.block([v_[...,:2]/(1+np.abs(v_[...,2:3])),
np.zeros_like(v_[...,2:3])])
def execution_stamp(class_name,function_name=None): def execution_stamp(class_name,function_name=None):
"""Timestamp the execution of a (function within a) class.""" """Timestamp the execution of a (function within a) class."""
now = datetime.datetime.now().astimezone().strftime('%Y-%m-%d %H:%M:%S%z') now = datetime.datetime.now().astimezone().strftime('%Y-%m-%d %H:%M:%S%z')
@ -203,6 +229,77 @@ def hybrid_IA(dist,N,seed=None):
return np.repeat(np.arange(len(dist)),repeats)[np.random.default_rng(seed).permutation(N_inv_samples)[:N]] return np.repeat(np.arange(len(dist)),repeats)[np.random.default_rng(seed).permutation(N_inv_samples)[:N]]
def shapeshifter(fro,to,mode='left',keep_ones=False):
"""
Return a tuple that reshapes 'fro' to become broadcastable to 'to'.
Parameters
----------
fro : tuple
Original shape of array.
to : tuple
Target shape of array after broadcasting.
len(to) cannot be less than len(fro).
mode : str, optional
Indicates whether new axes are preferably added to
either 'left' or 'right' of the original shape.
Defaults to 'left'.
keep_ones : bool, optional
Treat '1' in fro as literal value instead of dimensional placeholder.
Defaults to False.
"""
beg = dict(left ='(^.*\\b)',
right='(^.*?\\b)')
sep = dict(left ='(.*\\b)',
right='(.*?\\b)')
end = dict(left ='(.*?$)',
right='(.*$)')
fro = (1,) if not len(fro) else fro
to = (1,) if not len(to) else to
try:
grp = re.match(beg[mode]
+f',{sep[mode]}'.join(map(lambda x: f'{x}'
if x>1 or (keep_ones and len(fro)>1) else
'\\d+',fro))
+f',{end[mode]}',
','.join(map(str,to))+',').groups()
except AttributeError:
raise ValueError(f'Shapes can not be shifted {fro} --> {to}')
fill = ()
for g,d in zip(grp,fro+(None,)):
fill += (1,)*g.count(',')+(d,)
return fill[:-1]
def shapeblender(a,b):
"""
Return a shape that overlaps the rightmost entries of 'a' with the leftmost of 'b'.
Parameters
----------
a : tuple
Shape of first array.
b : tuple
Shape of second array.
Examples
--------
>>> shapeblender((4,4,3),(3,2,1))
(4,4,3,2,1)
>>> shapeblender((1,2),(1,2,3))
(1,2,3)
>>> shapeblender((1,),(2,2,1))
(1,2,2,1)
>>> shapeblender((3,2),(3,2))
(3,2)
"""
i = min(len(a),len(b))
while i > 0 and a[-i:] != b[:i]: i -= 1
return a + b[i:]
#################################################################################################### ####################################################################################################
# Classes # Classes
#################################################################################################### ####################################################################################################

View File

@ -33,12 +33,12 @@ material:
phase: phase:
Aluminum: Aluminum:
lattice: fcc lattice: cF
mech: mech:
output: [F, P, F_e, F_p, L_p] output: [F, P, F_e, F_p, L_p]
elasticity: {C_11: 106.75e9, C_12: 60.41e9, C_44: 28.34e9, type: hooke} elasticity: {C_11: 106.75e9, C_12: 60.41e9, C_44: 28.34e9, type: hooke}
Steel: Steel:
lattice: bcc lattice: cI
mech: mech:
output: [F, P, F_e, F_p, L_p] output: [F, P, F_e, F_p, L_p]
elasticity: {C_11: 233.3e9, C_12: 135.5e9, C_44: 118.0e9, type: hooke} elasticity: {C_11: 233.3e9, C_12: 135.5e9, C_44: 118.0e9, type: hooke}

View File

@ -0,0 +1,5 @@
1 header
1_Eulers 2_Eulers 3_Eulers 1_pos 2_pos
180.0 45.00000000000001 180.0 1 1
270.0 45.00000000000001 90.0 1 2
315.0 0.0 0.0 1 3

View File

@ -0,0 +1,26 @@
1 header
1_Eulers 2_Eulers 3_Eulers 1_pos 2_pos
146.75362934444064 9.976439066337804 256.395594327347 1 1
356.59977719102034 43.39784965440254 12.173896584899929 1 2
75.92521636876346 43.82007387041961 277.8843642946069 1 3
326.75362934444064 9.976439066337806 76.39559432734703 1 4
176.59977719102034 43.397849654402556 192.17389658489986 1 5
255.92521636876344 43.82007387041961 97.88436429460687 1 6
213.24637065555936 9.976439066337804 103.604405672653 1 7
3.400222808979685 43.39784965440255 347.8261034151001 1 8
284.0747836312365 43.82007387041961 82.11563570539313 1 9
33.24637065555936 9.976439066337804 283.60440567265294 1 10
183.40022280897963 43.397849654402556 167.8261034151001 1 11
104.07478363123654 43.82007387041961 262.1156357053931 1 12
273.4002228089796 43.397849654402556 77.82610341510008 1 13
123.24637065555939 9.976439066337806 193.60440567265297 1 14
194.07478363123653 43.82007387041961 172.11563570539317 1 15
93.40022280897969 43.39784965440255 257.8261034151001 1 16
303.24637065555936 9.976439066337804 13.604405672652977 1 17
14.074783631236542 43.82007387041961 352.1156357053931 1 18
86.59977719102032 43.39784965440254 282.17389658489986 1 19
236.75362934444058 9.976439066337804 166.39559432734703 1 20
165.92521636876344 43.82007387041961 187.88436429460683 1 21
266.59977719102034 43.39784965440254 102.17389658489992 1 22
56.75362934444064 9.976439066337804 346.395594327347 1 23
345.9252163687635 43.82007387041961 7.884364294606862 1 24

View File

@ -0,0 +1,26 @@
1 header
1_Eulers 2_Eulers 3_Eulers 1_pos 2_pos
166.39559432734697 9.976439066337804 236.75362934444058 1 1
352.1156357053931 43.82007387041961 14.074783631236542 1 2
77.82610341510008 43.397849654402556 273.4002228089796 1 3
346.395594327347 9.976439066337804 56.75362934444064 1 4
172.11563570539317 43.82007387041961 194.07478363123653 1 5
257.8261034151001 43.39784965440255 93.40022280897969 1 6
193.604405672653 9.976439066337804 123.24637065555939 1 7
7.884364294606862 43.82007387041961 345.9252163687635 1 8
282.17389658489986 43.39784965440254 86.59977719102032 1 9
13.604405672652977 9.976439066337804 303.24637065555936 1 10
187.88436429460683 43.82007387041961 165.92521636876344 1 11
102.17389658489992 43.39784965440254 266.59977719102034 1 12
277.8843642946069 43.82007387041961 75.92521636876346 1 13
103.604405672653 9.976439066337804 213.24637065555936 1 14
192.17389658489986 43.397849654402556 176.59977719102034 1 15
97.88436429460687 43.82007387041961 255.92521636876344 1 16
283.60440567265294 9.976439066337804 33.24637065555936 1 17
12.173896584899929 43.39784965440254 356.59977719102034 1 18
82.11563570539313 43.82007387041961 284.0747836312365 1 19
256.395594327347 9.976439066337804 146.75362934444064 1 20
167.8261034151001 43.397849654402556 183.40022280897963 1 21
262.1156357053931 43.82007387041961 104.07478363123654 1 22
76.39559432734703 9.976439066337806 326.75362934444064 1 23
347.8261034151001 43.39784965440255 3.400222808979685 1 24

View File

@ -0,0 +1,26 @@
1 header
1_Eulers 2_Eulers 3_Eulers 1_pos 2_pos
114.20342833932975 10.52877936550932 204.20342833932972 1 1
94.3573968784815 80.40593177313954 311.22729452432543 1 2
175.6426031215185 80.40593177313954 48.77270547567447 1 3
155.79657166067025 10.52877936550932 155.79657166067025 1 4
99.62136089109411 85.70366403943004 318.04510841542015 1 5
170.37863910890587 85.70366403943002 41.954891584579855 1 6
85.64260312151852 80.40593177313954 48.77270547567448 1 7
65.79657166067024 10.52877936550932 155.79657166067025 1 8
9.621360891094124 85.70366403943004 318.04510841542015 1 9
80.37863910890587 85.70366403943004 41.95489158457987 1 10
24.203428339329758 10.52877936550932 204.20342833932975 1 11
4.357396878481486 80.40593177313954 311.2272945243255 1 12
204.20342833932972 10.52877936550932 204.20342833932972 1 13
184.35739687848147 80.40593177313954 311.2272945243255 1 14
265.64260312151845 80.40593177313953 48.77270547567449 1 15
245.79657166067025 10.528779365509317 155.79657166067025 1 16
189.62136089109413 85.70366403943004 318.04510841542015 1 17
260.3786391089059 85.70366403943002 41.954891584579855 1 18
170.37863910890587 94.29633596056996 138.04510841542015 1 19
99.62136089109411 94.29633596056998 221.95489158457983 1 20
155.79657166067025 169.4712206344907 24.203428339329754 1 21
175.64260312151848 99.59406822686046 131.22729452432552 1 22
94.35739687848151 99.59406822686046 228.77270547567446 1 23
114.20342833932975 169.4712206344907 335.7965716606702 1 24

View File

@ -0,0 +1,14 @@
1 header
1_Eulers 2_Eulers 3_Eulers 1_pos 2_pos
96.91733794010702 83.13253115922213 314.5844440567886 1 1
173.082662059893 83.13253115922211 45.41555594321143 1 2
135.0 9.735610317245317 180.0 1 3
263.082662059893 83.13253115922213 45.415555943211444 1 4
186.91733794010702 83.13253115922211 314.5844440567886 1 5
224.99999999999997 9.735610317245317 180.0 1 6
83.082662059893 83.13253115922213 45.415555943211444 1 7
6.917337940106983 83.13253115922211 314.5844440567886 1 8
45.0 9.73561031724532 180.0 1 9
13.638707279476469 45.81931182053557 80.40196970123216 1 10
256.36129272052347 45.81931182053556 279.59803029876775 1 11
315.0 99.73561031724536 0.0 1 12

View File

@ -0,0 +1,14 @@
1 header
1_Eulers 2_Eulers 3_Eulers 1_pos 2_pos
135.41555594321144 83.13253115922213 173.082662059893 1 1
260.26438968275465 90.0 135.0 1 2
260.40196970123213 45.81931182053557 13.638707279476478 1 3
314.5844440567886 83.13253115922213 96.91733794010702 1 4
350.40196970123213 45.81931182053557 283.6387072794765 1 5
170.26438968275465 90.0 224.99999999999997 1 6
315.4155559432114 83.13253115922213 353.08266205989304 1 7
99.73561031724536 90.0 225.0 1 8
279.59803029876787 45.819311820535574 166.36129272052352 1 9
134.58444405678856 83.13253115922213 276.91733794010696 1 10
9.598030298767851 45.819311820535574 76.36129272052355 1 11
9.735610317245369 90.0 315.0 1 12

View File

@ -0,0 +1,19 @@
3x3:1_Schmid 3x3:2_Schmid 3x3:3_Schmid 3x3:4_Schmid 3x3:5_Schmid 3x3:6_Schmid 3x3:7_Schmid 3x3:8_Schmid 3x3:9_Schmid
0.0 0.0 0.0 0.408248290463863 0.408248290463863 0.40824829046386296 -0.4082482904638631 -0.4082482904638631 -0.408248290463863
-0.408248290463863 -0.408248290463863 -0.40824829046386296 2.4997998108697446e-17 2.4997998108697446e-17 2.499799810869744e-17 0.4082482904638631 0.4082482904638631 0.408248290463863
0.408248290463863 0.408248290463863 0.40824829046386296 -0.4082482904638631 -0.4082482904638631 -0.408248290463863 0.0 0.0 0.0
4.999599621739488e-17 4.9995996217394874e-17 -4.999599621739488e-17 0.408248290463863 0.40824829046386296 -0.408248290463863 0.408248290463863 0.40824829046386296 -0.408248290463863
-0.408248290463863 -0.40824829046386296 0.408248290463863 -2.499799810869744e-17 -2.4997998108697437e-17 2.499799810869744e-17 -0.408248290463863 -0.40824829046386296 0.408248290463863
0.408248290463863 0.40824829046386296 -0.408248290463863 -0.4082482904638631 -0.408248290463863 0.4082482904638631 0.0 0.0 0.0
0.0 0.0 0.0 -0.408248290463863 0.408248290463863 0.408248290463863 0.4082482904638631 -0.4082482904638631 -0.4082482904638631
-0.408248290463863 0.408248290463863 0.408248290463863 -2.499799810869744e-17 2.499799810869744e-17 2.499799810869744e-17 -0.408248290463863 0.408248290463863 0.408248290463863
0.408248290463863 -0.408248290463863 -0.408248290463863 0.408248290463863 -0.408248290463863 -0.408248290463863 0.0 0.0 0.0
-4.999599621739488e-17 4.999599621739488e-17 -4.9995996217394874e-17 -0.408248290463863 0.408248290463863 -0.40824829046386296 -0.408248290463863 0.408248290463863 -0.40824829046386296
-0.408248290463863 0.408248290463863 -0.40824829046386296 2.4997998108697446e-17 -2.4997998108697446e-17 2.499799810869744e-17 0.4082482904638631 -0.4082482904638631 0.408248290463863
0.408248290463863 -0.408248290463863 0.40824829046386296 0.408248290463863 -0.408248290463863 0.40824829046386296 0.0 0.0 0.0
0.4999999999999999 -0.4999999999999999 0.0 0.4999999999999999 -0.4999999999999999 0.0 0.0 0.0 0.0
0.5 0.4999999999999999 -6.123233995736766e-17 -0.5000000000000001 -0.5 6.123233995736767e-17 0.0 0.0 0.0
0.4999999999999999 -3.0616169978683824e-17 -0.4999999999999999 3.0616169978683824e-17 -1.874699728327322e-33 -3.0616169978683824e-17 0.4999999999999999 -3.0616169978683824e-17 -0.4999999999999999
0.5 -3.061616997868383e-17 0.4999999999999999 -3.0616169978683836e-17 1.8746997283273227e-33 -3.061616997868383e-17 -0.5000000000000001 3.0616169978683836e-17 -0.5
0.0 6.123233995736765e-17 -6.123233995736765e-17 0.0 0.4999999999999999 -0.4999999999999999 0.0 0.4999999999999999 -0.4999999999999999
0.0 0.0 0.0 0.0 0.5 0.4999999999999999 0.0 -0.5000000000000001 -0.5

View File

@ -0,0 +1,13 @@
3x3:1_Schmid 3x3:2_Schmid 3x3:3_Schmid 3x3:4_Schmid 3x3:5_Schmid 3x3:6_Schmid 3x3:7_Schmid 3x3:8_Schmid 3x3:9_Schmid
-0.4714045207910318 -0.4714045207910318 -0.47140452079103173 0.2357022603955159 0.2357022603955159 0.23570226039551587 0.2357022603955159 0.2357022603955159 0.23570226039551587
0.2357022603955159 0.2357022603955159 0.23570226039551587 -0.4714045207910318 -0.4714045207910318 -0.47140452079103173 0.2357022603955159 0.2357022603955159 0.23570226039551587
0.23570226039551587 0.23570226039551587 0.23570226039551584 0.23570226039551587 0.23570226039551587 0.23570226039551584 -0.4714045207910318 -0.4714045207910318 -0.47140452079103173
-0.4714045207910318 -0.47140452079103173 0.4714045207910318 0.23570226039551587 0.23570226039551584 -0.23570226039551587 -0.2357022603955159 -0.23570226039551587 0.2357022603955159
0.23570226039551584 0.23570226039551578 -0.23570226039551584 -0.4714045207910318 -0.47140452079103173 0.4714045207910318 -0.2357022603955159 -0.23570226039551587 0.2357022603955159
0.2357022603955159 0.23570226039551587 -0.2357022603955159 0.2357022603955159 0.23570226039551587 -0.2357022603955159 0.4714045207910317 0.47140452079103157 -0.4714045207910317
-0.4714045207910318 0.4714045207910318 0.4714045207910318 -0.2357022603955159 0.2357022603955159 0.2357022603955159 -0.2357022603955159 0.2357022603955159 0.2357022603955159
0.23570226039551595 -0.23570226039551595 -0.23570226039551595 0.4714045207910318 -0.4714045207910318 -0.4714045207910318 -0.2357022603955159 0.2357022603955159 0.2357022603955159
0.2357022603955159 -0.2357022603955159 -0.2357022603955159 -0.23570226039551587 0.23570226039551587 0.23570226039551587 0.4714045207910318 -0.4714045207910318 -0.4714045207910318
-0.4714045207910318 0.4714045207910318 -0.47140452079103173 -0.23570226039551587 0.23570226039551587 -0.23570226039551584 0.2357022603955159 -0.2357022603955159 0.23570226039551587
0.23570226039551595 -0.23570226039551595 0.2357022603955159 0.4714045207910318 -0.4714045207910318 0.47140452079103173 0.2357022603955159 -0.2357022603955159 0.23570226039551587
0.23570226039551584 -0.23570226039551584 0.23570226039551578 -0.23570226039551595 0.23570226039551595 -0.2357022603955159 -0.4714045207910318 0.4714045207910318 -0.47140452079103173

View File

@ -0,0 +1,5 @@
1 header
1_Eulers 2_Eulers 3_Eulers 1_pos 2_pos
0.0 45.00000000000001 0.0 1 1
90.0 45.00000000000001 270.0 1 2
45.00000000000001 0.0 0.0 1 3

View File

@ -0,0 +1,26 @@
1 header
1_Eulers 2_Eulers 3_Eulers 1_pos 2_pos
283.60440567265294 9.976439066337804 33.24637065555936 1 1
167.8261034151001 43.397849654402556 183.40022280897963 1 2
262.1156357053931 43.82007387041961 104.07478363123654 1 3
103.604405672653 9.976439066337804 213.24637065555936 1 4
347.8261034151001 43.39784965440255 3.400222808979685 1 5
82.11563570539313 43.82007387041961 284.0747836312365 1 6
76.39559432734703 9.976439066337806 326.75362934444064 1 7
192.17389658489986 43.397849654402556 176.59977719102034 1 8
97.88436429460687 43.82007387041961 255.92521636876344 1 9
256.395594327347 9.976439066337804 146.75362934444064 1 10
12.173896584899929 43.39784965440254 356.59977719102034 1 11
277.8843642946069 43.82007387041961 75.92521636876346 1 12
102.17389658489992 43.39784965440254 266.59977719102034 1 13
346.395594327347 9.976439066337804 56.75362934444064 1 14
7.884364294606862 43.82007387041961 345.9252163687635 1 15
282.17389658489986 43.39784965440254 86.59977719102032 1 16
166.39559432734703 9.976439066337804 236.75362934444058 1 17
187.88436429460683 43.82007387041961 165.92521636876344 1 18
257.8261034151001 43.39784965440255 93.40022280897969 1 19
13.604405672652977 9.976439066337804 303.24637065555936 1 20
352.1156357053931 43.82007387041961 14.074783631236542 1 21
77.82610341510008 43.397849654402556 273.4002228089796 1 22
193.60440567265297 9.976439066337806 123.24637065555939 1 23
172.11563570539317 43.82007387041961 194.07478363123653 1 24

View File

@ -0,0 +1,26 @@
1 header
1_Eulers 2_Eulers 3_Eulers 1_pos 2_pos
303.24637065555936 9.976439066337804 13.604405672652977 1 1
165.92521636876344 43.82007387041961 187.88436429460683 1 2
266.59977719102034 43.39784965440254 102.17389658489992 1 3
123.24637065555939 9.976439066337804 193.604405672653 1 4
345.9252163687635 43.82007387041961 7.884364294606862 1 5
86.59977719102032 43.39784965440254 282.17389658489986 1 6
56.75362934444064 9.976439066337804 346.395594327347 1 7
194.07478363123653 43.82007387041961 172.11563570539317 1 8
93.40022280897969 43.39784965440255 257.8261034151001 1 9
236.75362934444058 9.976439066337804 166.39559432734697 1 10
14.074783631236542 43.82007387041961 352.1156357053931 1 11
273.4002228089796 43.397849654402556 77.82610341510008 1 12
104.07478363123654 43.82007387041961 262.1156357053931 1 13
326.75362934444064 9.976439066337806 76.39559432734703 1 14
3.400222808979685 43.39784965440255 347.8261034151001 1 15
284.0747836312365 43.82007387041961 82.11563570539313 1 16
146.75362934444064 9.976439066337804 256.395594327347 1 17
183.40022280897963 43.397849654402556 167.8261034151001 1 18
255.92521636876344 43.82007387041961 97.88436429460687 1 19
33.24637065555936 9.976439066337804 283.60440567265294 1 20
356.59977719102034 43.39784965440254 12.173896584899929 1 21
75.92521636876346 43.82007387041961 277.8843642946069 1 22
213.24637065555936 9.976439066337804 103.604405672653 1 23
176.59977719102034 43.397849654402556 192.17389658489986 1 24

View File

@ -0,0 +1,26 @@
1 header
1_Eulers 2_Eulers 3_Eulers 1_pos 2_pos
335.7965716606702 10.528779365509317 65.79657166067024 1 1
228.77270547567446 80.40593177313953 85.64260312151849 1 2
131.22729452432552 80.40593177313954 4.357396878481506 1 3
24.20342833932977 10.52877936550932 24.20342833932976 1 4
221.95489158457983 85.70366403943002 80.37863910890589 1 5
138.04510841542015 85.70366403943004 9.621360891094124 1 6
131.22729452432552 80.40593177313953 94.35739687848151 1 7
24.203428339329765 10.52877936550932 114.20342833932976 1 8
221.95489158457983 85.70366403943004 170.37863910890587 1 9
138.04510841542015 85.70366403943004 99.62136089109411 1 10
335.7965716606702 10.52877936550932 155.79657166067025 1 11
228.77270547567448 80.40593177313954 175.6426031215185 1 12
335.7965716606702 10.52877936550932 335.7965716606702 1 13
228.77270547567448 80.40593177313954 355.6426031215185 1 14
131.2272945243255 80.40593177313954 274.35739687848144 1 15
24.203428339329747 10.52877936550932 294.2034283393298 1 16
221.95489158457985 85.70366403943004 350.3786391089059 1 17
138.04510841542015 85.70366403943004 279.6213608910941 1 18
41.95489158457986 94.29633596056998 9.621360891094133 1 19
318.04510841542015 94.29633596056996 80.37863910890589 1 20
155.79657166067025 169.4712206344907 24.203428339329754 1 21
48.77270547567448 99.59406822686046 4.357396878481504 1 22
311.2272945243255 99.59406822686046 85.64260312151852 1 23
204.20342833932975 169.4712206344907 65.79657166067024 1 24

View File

@ -0,0 +1,14 @@
1 header
1_Eulers 2_Eulers 3_Eulers 1_pos 2_pos
225.41555594321144 83.13253115922213 83.08266205989301 1 1
134.58444405678856 83.13253115922211 6.917337940107012 1 2
4.702125169424418e-15 9.735610317245317 45.0 1 3
134.58444405678856 83.13253115922213 276.91733794010696 1 4
225.4155559432114 83.13253115922213 353.082662059893 1 5
0.0 9.735610317245317 315.0 1 6
134.58444405678858 83.13253115922213 96.91733794010702 1 7
225.41555594321142 83.13253115922213 173.082662059893 1 8
0.0 9.735610317245317 135.0 1 9
99.59803029876785 45.81931182053557 166.36129272052355 1 10
260.40196970123213 45.81931182053556 283.6387072794765 1 11
180.0 99.73561031724535 225.0 1 12

View File

@ -0,0 +1,14 @@
1 header
1_Eulers 2_Eulers 3_Eulers 1_pos 2_pos
6.9173379401070045 83.13253115922213 44.58444405678856 1 1
45.0 89.99999999999999 279.7356103172453 1 2
166.36129272052352 45.819311820535574 279.59803029876787 1 3
83.08266205989301 83.13253115922213 225.41555594321144 1 4
256.3612927205235 45.819311820535574 189.59803029876787 1 5
315.0 90.0 9.735610317245369 1 6
186.917337940107 83.13253115922213 224.58444405678856 1 7
315.0 90.0 80.26438968275463 1 8
13.638707279476478 45.81931182053557 260.40196970123213 1 9
263.082662059893 83.13253115922213 45.415555943211444 1 10
103.63870727947646 45.819311820535574 170.40196970123213 1 11
224.99999999999997 90.0 170.26438968275465 1 12

View File

@ -0,0 +1,25 @@
3x3:1_Schmid 3x3:2_Schmid 3x3:3_Schmid 3x3:4_Schmid 3x3:5_Schmid 3x3:6_Schmid 3x3:7_Schmid 3x3:8_Schmid 3x3:9_Schmid
0.0 0.4082482904638631 0.408248290463863 0.0 -0.408248290463863 -0.40824829046386296 0.0 0.4082482904638631 0.408248290463863
0.0 -0.408248290463863 -0.40824829046386296 0.0 -0.408248290463863 -0.40824829046386296 0.0 0.4082482904638631 0.408248290463863
0.0 -0.408248290463863 0.408248290463863 0.0 -0.408248290463863 0.408248290463863 0.0 -0.408248290463863 0.408248290463863
0.0 0.40824829046386285 -0.40824829046386285 0.0 -0.408248290463863 0.408248290463863 0.0 -0.408248290463863 0.408248290463863
-0.40824829046386296 2.4997998108697434e-17 -0.40824829046386285 0.4082482904638631 -2.4997998108697446e-17 0.408248290463863 0.4082482904638631 -2.4997998108697446e-17 0.408248290463863
-0.408248290463863 2.499799810869744e-17 -0.40824829046386296 -0.408248290463863 2.499799810869744e-17 -0.40824829046386296 0.4082482904638631 -2.4997998108697446e-17 0.408248290463863
-0.408248290463863 2.499799810869744e-17 0.408248290463863 -0.408248290463863 2.499799810869744e-17 0.408248290463863 -0.408248290463863 2.499799810869744e-17 0.408248290463863
-0.408248290463863 2.499799810869744e-17 0.408248290463863 0.40824829046386296 -2.4997998108697437e-17 -0.40824829046386296 -0.408248290463863 2.499799810869744e-17 0.408248290463863
-0.40824829046386296 -0.40824829046386285 4.999599621739487e-17 0.4082482904638631 0.408248290463863 -4.999599621739489e-17 0.4082482904638631 0.408248290463863 -4.999599621739489e-17
-0.4082482904638631 -0.408248290463863 4.999599621739489e-17 0.408248290463863 0.40824829046386296 -4.999599621739488e-17 -0.4082482904638631 -0.408248290463863 4.999599621739489e-17
-0.408248290463863 0.408248290463863 0.0 -0.408248290463863 0.408248290463863 0.0 -0.408248290463863 0.408248290463863 0.0
-0.40824829046386296 0.40824829046386296 0.0 -0.40824829046386296 0.40824829046386296 0.0 0.408248290463863 -0.408248290463863 0.0
-0.4714045207910316 -0.23570226039551578 -0.23570226039551576 0.4714045207910318 0.23570226039551587 0.23570226039551584 0.4714045207910318 0.23570226039551587 0.23570226039551584
-0.4714045207910318 0.23570226039551595 0.2357022603955159 -0.4714045207910318 0.23570226039551595 0.2357022603955159 -0.4714045207910318 0.23570226039551595 0.2357022603955159
0.47140452079103173 -0.2357022603955159 0.23570226039551584 0.47140452079103173 -0.2357022603955159 0.23570226039551584 -0.4714045207910318 0.23570226039551595 -0.23570226039551587
0.4714045207910318 0.23570226039551587 -0.23570226039551595 -0.47140452079103173 -0.23570226039551584 0.2357022603955159 0.4714045207910318 0.23570226039551587 -0.23570226039551595
0.2357022603955159 0.4714045207910318 0.23570226039551584 -0.23570226039551587 -0.47140452079103173 -0.23570226039551578 0.2357022603955159 0.4714045207910318 0.23570226039551584
-0.23570226039551587 0.47140452079103173 0.23570226039551587 -0.23570226039551587 0.47140452079103173 0.23570226039551587 0.2357022603955159 -0.4714045207910318 -0.2357022603955159
0.2357022603955159 -0.4714045207910318 0.23570226039551595 0.2357022603955159 -0.4714045207910318 0.23570226039551595 0.2357022603955159 -0.4714045207910318 0.23570226039551595
-0.2357022603955158 -0.4714045207910316 0.23570226039551584 0.2357022603955159 0.4714045207910318 -0.23570226039551595 0.2357022603955159 0.4714045207910318 -0.23570226039551595
0.23570226039551587 0.23570226039551584 0.47140452079103173 0.23570226039551587 0.23570226039551584 0.47140452079103173 -0.2357022603955159 -0.23570226039551587 -0.4714045207910318
-0.2357022603955159 0.2357022603955159 0.4714045207910318 0.23570226039551587 -0.23570226039551587 -0.47140452079103173 -0.2357022603955159 0.2357022603955159 0.4714045207910318
-0.2357022603955158 0.2357022603955158 -0.4714045207910316 0.2357022603955159 -0.2357022603955159 0.4714045207910318 0.2357022603955159 -0.2357022603955159 0.4714045207910318
0.2357022603955159 0.23570226039551587 -0.4714045207910318 0.2357022603955159 0.23570226039551587 -0.4714045207910318 0.2357022603955159 0.23570226039551587 -0.4714045207910318

View File

@ -0,0 +1,13 @@
3x3:1_Schmid 3x3:2_Schmid 3x3:3_Schmid 3x3:4_Schmid 3x3:5_Schmid 3x3:6_Schmid 3x3:7_Schmid 3x3:8_Schmid 3x3:9_Schmid
-0.4714045207910316 -0.23570226039551578 -0.23570226039551576 0.4714045207910318 0.23570226039551587 0.23570226039551584 0.4714045207910318 0.23570226039551587 0.23570226039551584
-0.4714045207910318 0.23570226039551595 0.2357022603955159 -0.4714045207910318 0.23570226039551595 0.2357022603955159 -0.4714045207910318 0.23570226039551595 0.2357022603955159
0.47140452079103173 -0.2357022603955159 0.23570226039551584 0.47140452079103173 -0.2357022603955159 0.23570226039551584 -0.4714045207910318 0.23570226039551595 -0.23570226039551587
0.4714045207910318 0.23570226039551587 -0.23570226039551595 -0.47140452079103173 -0.23570226039551584 0.2357022603955159 0.4714045207910318 0.23570226039551587 -0.23570226039551595
0.2357022603955159 0.4714045207910318 0.23570226039551584 -0.23570226039551587 -0.47140452079103173 -0.23570226039551578 0.2357022603955159 0.4714045207910318 0.23570226039551584
-0.23570226039551587 0.47140452079103173 0.23570226039551587 -0.23570226039551587 0.47140452079103173 0.23570226039551587 0.2357022603955159 -0.4714045207910318 -0.2357022603955159
0.2357022603955159 -0.4714045207910318 0.23570226039551595 0.2357022603955159 -0.4714045207910318 0.23570226039551595 0.2357022603955159 -0.4714045207910318 0.23570226039551595
-0.2357022603955158 -0.4714045207910316 0.23570226039551584 0.2357022603955159 0.4714045207910318 -0.23570226039551595 0.2357022603955159 0.4714045207910318 -0.23570226039551595
0.23570226039551587 0.23570226039551584 0.47140452079103173 0.23570226039551587 0.23570226039551584 0.47140452079103173 -0.2357022603955159 -0.23570226039551587 -0.4714045207910318
-0.2357022603955159 0.2357022603955159 0.4714045207910318 0.23570226039551587 -0.23570226039551587 -0.47140452079103173 -0.2357022603955159 0.2357022603955159 0.4714045207910318
-0.2357022603955158 0.2357022603955158 -0.4714045207910316 0.2357022603955159 -0.2357022603955159 0.4714045207910318 0.2357022603955159 -0.2357022603955159 0.4714045207910318
0.2357022603955159 0.23570226039551587 -0.4714045207910318 0.2357022603955159 0.23570226039551587 -0.4714045207910318 0.2357022603955159 0.23570226039551587 -0.4714045207910318

View File

@ -0,0 +1,34 @@
3x3:1_Schmid 3x3:2_Schmid 3x3:3_Schmid 3x3:4_Schmid 3x3:5_Schmid 3x3:6_Schmid 3x3:7_Schmid 3x3:8_Schmid 3x3:9_Schmid
0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0
0.0 0.0 -0.4999999999999998 0.0 0.0 0.8660254037844388 0.0 0.0 0.0
0.0 0.0 -0.5000000000000001 0.0 0.0 -0.8660254037844386 0.0 0.0 0.0
0.0 1.0 -5.914589856893347e-17 0.0 0.0 0.0 0.0 0.0 0.0
0.4330127018922192 0.24999999999999975 -3.102315069664884e-17 -0.7500000000000002 -0.4330127018922192 5.373367321746164e-17 0.0 0.0 0.0
-0.43301270189221935 0.25000000000000006 1.4502014121821253e-18 -0.7499999999999998 0.4330127018922193 2.5118225271075755e-18 0.0 0.0 0.0
-0.4330127018922194 -0.7499999999999999 6.059609998111558e-17 0.2500000000000001 0.4330127018922194 -3.498517463593857e-17 0.0 0.0 0.0
2.563950248511418e-16 -5.693113199781536e-32 -9.614043519462407e-33 1.0 -2.220446049250313e-16 -3.7496997163046135e-17 0.0 0.0 0.0
0.4330127018922194 -0.75 2.8122747872284606e-17 0.25000000000000006 -0.43301270189221935 1.6236676054415494e-17 0.0 0.0 0.0
-0.38254602783800284 -0.22086305214969287 -0.23426064283290896 0.6625891564490795 0.38254602783800284 0.40575133560034454 0.0 0.0 0.0
0.0 -0.8834522085987724 -0.4685212856658182 0.0 0.0 0.0 0.0 0.0 0.0
0.38254602783800307 -0.22086305214969315 -0.23426064283290912 0.6625891564490792 -0.38254602783800296 -0.40575133560034443 0.0 0.0 0.0
-0.38254602783800284 -0.22086305214969287 0.23426064283290904 0.6625891564490795 0.38254602783800284 -0.4057513356003447 0.0 0.0 0.0
0.0 -0.8834522085987724 0.46852128566581835 0.0 0.0 0.0 0.0 0.0 0.0
0.38254602783800307 -0.22086305214969315 0.23426064283290912 0.6625891564490792 -0.38254602783800296 0.40575133560034443 0.0 0.0 0.0
-0.39955629492721617 -0.23068393443263763 -0.24467726152216654 3.8591083978971935e-17 2.228057272357889e-17 2.3632116092343994e-17 0.6524726973924442 0.3767052874784087 0.39955629492721606
-0.19977814746360817 -0.11534196721631886 -0.12233863076108333 -0.34602590164895664 -0.19977814746360797 -0.21189672420660496 0.6524726973924442 0.3767052874784087 0.39955629492721606
0.0 -0.23068393443263785 -0.12233863076108334 0.0 -0.39955629492721617 -0.211896724206605 0.0 0.7534105749568177 0.3995562949272161
0.0 0.23068393443263768 0.12233863076108326 0.0 -0.3995562949272162 -0.21189672420660502 0.0 0.7534105749568178 0.39955629492721617
-0.199778147463608 0.11534196721631884 0.12233863076108324 0.34602590164895664 -0.1997781474636081 -0.211896724206605 -0.6524726973924442 0.3767052874784089 0.3995562949272161
-0.3995562949272161 0.23068393443263774 0.24467726152216654 -3.859108397897193e-17 2.22805727235789e-17 2.3632116092343994e-17 -0.6524726973924441 0.37670528747840887 0.39955629492721606
-0.39955629492721617 -0.23068393443263763 0.24467726152216662 -3.8591083978971935e-17 -2.228057272357889e-17 2.3632116092344003e-17 -0.6524726973924442 -0.3767052874784087 0.39955629492721617
-0.1997781474636082 -0.11534196721631888 0.1223386307610834 -0.3460259016489568 -0.19977814746360806 0.21189672420660513 -0.6524726973924442 -0.3767052874784087 0.39955629492721617
0.0 -0.23068393443263788 0.12233863076108341 0.0 -0.3995562949272163 0.21189672420660516 0.0 -0.7534105749568177 0.3995562949272162
0.0 0.2306839344326376 -0.12233863076108324 0.0 -0.3995562949272163 0.21189672420660516 0.0 -0.7534105749568177 0.3995562949272162
-0.19977814746360792 0.1153419672163188 -0.12233863076108319 0.34602590164895675 -0.19977814746360814 0.21189672420660505 0.6524726973924441 -0.37670528747840887 0.39955629492721606
-0.3995562949272161 0.23068393443263774 -0.24467726152216654 3.859108397897193e-17 -2.22805727235789e-17 2.3632116092343994e-17 0.6524726973924441 -0.37670528747840887 0.39955629492721606
-0.11134044285378089 -0.19284730395996755 -0.1363636363636364 -0.19284730395996755 -0.3340213285613424 -0.23618874648666507 0.36363636363636365 0.6298366572977734 0.44536177141512323
-0.11134044285378081 0.1928473039599675 0.13636363636363633 0.19284730395996758 -0.3340213285613426 -0.2361887464866651 -0.3636363636363637 0.6298366572977737 0.4453617714151233
-0.44536177141512323 9.889017858258314e-17 0.2727272727272727 -4.301519895922435e-17 9.551292858672588e-33 2.634132215859942e-17 -0.7272727272727272 1.6148698540002275e-16 0.44536177141512323
-0.1113404428537809 -0.1928473039599676 0.13636363636363644 -0.1928473039599676 -0.33402132856134253 0.23618874648666516 -0.36363636363636365 -0.6298366572977734 0.44536177141512323
-0.11134044285378074 0.19284730395996735 -0.13636363636363627 0.19284730395996758 -0.33402132856134253 0.23618874648666516 0.3636363636363636 -0.6298366572977734 0.44536177141512323
-0.44536177141512334 9.889017858258316e-17 -0.2727272727272727 4.3015198959224354e-17 -9.55129285867259e-33 2.634132215859942e-17 0.7272727272727273 -1.6148698540002277e-16 0.44536177141512323

View File

@ -0,0 +1,25 @@
3x3:1_Schmid 3x3:2_Schmid 3x3:3_Schmid 3x3:4_Schmid 3x3:5_Schmid 3x3:6_Schmid 3x3:7_Schmid 3x3:8_Schmid 3x3:9_Schmid
-0.3743506488634663 -0.21613144789263322 -0.4584840372976439 -0.21613144789263333 -0.12478354962115536 -0.2647058823529411 0.4075413664867946 0.23529411764705865 0.4991341984846217
0.0 -1.1032987950073291e-16 -1.1702250894369439e-16 0.0 -0.4991341984846217 -0.5294117647058824 0.0 0.47058823529411753 0.4991341984846218
-0.3743506488634663 0.2161314478926334 0.4584840372976439 0.2161314478926334 -0.1247835496211555 -0.2647058823529412 -0.4075413664867947 0.23529411764705888 0.4991341984846218
-0.3743506488634663 -0.21613144789263322 0.4584840372976439 -0.2161314478926334 -0.12478354962115538 0.2647058823529412 -0.4075413664867946 -0.23529411764705865 0.4991341984846217
0.0 -1.4562117094830577e-16 1.5445457619280876e-16 0.0 -0.4991341984846217 0.5294117647058824 0.0 -0.47058823529411753 0.4991341984846218
-0.3743506488634663 0.2161314478926334 -0.4584840372976439 0.21613144789263342 -0.12478354962115551 0.26470588235294124 0.4075413664867947 -0.23529411764705888 0.4991341984846218
-0.06998542122237655 -0.1212183053462653 -0.04285714285714286 -0.12121830534626532 -0.20995626366712955 -0.07423074889580901 0.45714285714285724 0.7917946548886295 0.279941684889506
-0.06998542122237653 0.12121830534626528 0.04285714285714286 0.12121830534626532 -0.20995626366712958 -0.07423074889580904 -0.45714285714285724 0.7917946548886297 0.2799416848895061
-0.27994168488950616 6.2159540823338e-17 0.08571428571428573 -5.407625012016776e-17 1.2007339593759827e-32 1.6557402499691063e-17 -0.9142857142857143 2.0301221021717148e-16 0.27994168488950605
-0.0699854212223766 -0.12121830534626538 0.04285714285714291 -0.12121830534626538 -0.20995626366712963 0.07423074889580909 -0.45714285714285724 -0.7917946548886295 0.2799416848895062
-0.06998542122237648 0.12121830534626521 -0.04285714285714283 0.12121830534626538 -0.20995626366712966 0.07423074889580906 0.45714285714285724 -0.7917946548886297 0.2799416848895061
-0.27994168488950605 6.215954082333798e-17 -0.08571428571428569 5.407625012016776e-17 -1.2007339593759827e-32 1.6557402499691063e-17 0.9142857142857143 -2.0301221021717148e-16 0.27994168488950605
0.3104371234477526 0.17923095678901296 0.19010313741609627 0.17923095678901305 0.1034790411492508 0.1097560975609756 -0.6759222663683424 -0.39024390243902424 -0.41391616459700337
0.0 7.68600963028337e-17 4.07612214737886e-17 0.0 0.4139161645970035 0.21951219512195125 0.0 -0.7804878048780488 -0.4139161645970034
0.31043712344775254 -0.17923095678901305 -0.19010313741609627 -0.17923095678901302 0.10347904114925086 0.1097560975609756 0.6759222663683423 -0.3902439024390244 -0.41391616459700337
0.3104371234477527 0.179230956789013 -0.19010313741609638 0.17923095678901313 0.10347904114925086 -0.10975609756097568 0.6759222663683424 0.3902439024390242 -0.4139161645970035
0.0 1.3539199431344235e-16 -7.180244797305419e-17 0.0 0.4139161645970036 -0.21951219512195136 0.0 0.7804878048780487 -0.41391616459700353
0.3104371234477525 -0.179230956789013 0.19010313741609622 -0.17923095678901313 0.10347904114925092 -0.10975609756097565 -0.6759222663683423 0.3902439024390244 -0.41391616459700337
0.11134044285378089 0.19284730395996755 0.1363636363636364 0.19284730395996755 0.3340213285613424 0.23618874648666507 -0.36363636363636365 -0.6298366572977734 -0.44536177141512323
0.11134044285378081 -0.1928473039599675 -0.13636363636363633 -0.19284730395996758 0.3340213285613426 0.2361887464866651 0.3636363636363637 -0.6298366572977737 -0.4453617714151233
0.44536177141512323 -9.889017858258314e-17 -0.2727272727272727 4.301519895922435e-17 -9.551292858672588e-33 -2.634132215859942e-17 0.7272727272727272 -1.6148698540002275e-16 -0.44536177141512323
0.1113404428537809 0.1928473039599676 -0.13636363636363644 0.1928473039599676 0.33402132856134253 -0.23618874648666516 0.36363636363636365 0.6298366572977734 -0.44536177141512323
0.11134044285378074 -0.19284730395996735 0.13636363636363627 -0.19284730395996758 0.33402132856134253 -0.23618874648666516 -0.3636363636363636 0.6298366572977734 -0.44536177141512323
0.44536177141512334 -9.889017858258316e-17 0.2727272727272727 -4.3015198959224354e-17 9.55129285867259e-33 -2.634132215859942e-17 -0.7272727272727273 1.6148698540002277e-16 -0.44536177141512323

View File

@ -1,157 +0,0 @@
import random
import pytest
import numpy as np
from damask import Rotation
from damask import Symmetry
def in_FZ(system,rho):
"""Non-vectorized version of 'in_FZ'."""
rho_abs = abs(rho)
if system == 'cubic':
return np.sqrt(2.0)-1.0 >= rho_abs[0] \
and np.sqrt(2.0)-1.0 >= rho_abs[1] \
and np.sqrt(2.0)-1.0 >= rho_abs[2] \
and 1.0 >= rho_abs[0] + rho_abs[1] + rho_abs[2]
elif system == 'hexagonal':
return 1.0 >= rho_abs[0] and 1.0 >= rho_abs[1] and 1.0 >= rho_abs[2] \
and 2.0 >= np.sqrt(3)*rho_abs[0] + rho_abs[1] \
and 2.0 >= np.sqrt(3)*rho_abs[1] + rho_abs[0] \
and 2.0 >= np.sqrt(3) + rho_abs[2]
elif system == 'tetragonal':
return 1.0 >= rho_abs[0] and 1.0 >= rho_abs[1] \
and np.sqrt(2.0) >= rho_abs[0] + rho_abs[1] \
and np.sqrt(2.0) >= rho_abs[2] + 1.0
elif system == 'orthorhombic':
return 1.0 >= rho_abs[0] and 1.0 >= rho_abs[1] and 1.0 >= rho_abs[2]
else:
return np.all(np.isfinite(rho_abs))
def in_disorientation_SST(system,rho):
"""Non-vectorized version of 'in_Disorientation_SST'."""
epsilon = 0.0
if system == 'cubic':
return rho[0] >= rho[1]+epsilon and rho[1] >= rho[2]+epsilon and rho[2] >= epsilon
elif system == 'hexagonal':
return rho[0] >= np.sqrt(3)*(rho[1]-epsilon) and rho[1] >= epsilon and rho[2] >= epsilon
elif system == 'tetragonal':
return rho[0] >= rho[1]-epsilon and rho[1] >= epsilon and rho[2] >= epsilon
elif system == 'orthorhombic':
return rho[0] >= epsilon and rho[1] >= epsilon and rho[2] >= epsilon
else:
return True
def in_SST(system,vector,proper = False):
"""Non-vectorized version of 'in_SST'."""
if system == '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 system == '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 system == '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 system == '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:
return True
v = np.array(vector,dtype=float)
if proper:
theComponents = np.around(np.dot(basis['improper'],v),12)
inSST = np.all(theComponents >= 0.0)
if not inSST:
theComponents = np.around(np.dot(basis['proper'],v),12)
inSST = np.all(theComponents >= 0.0)
else:
v[2] = abs(v[2])
theComponents = np.around(np.dot(basis['improper'],v),12)
inSST = np.all(theComponents >= 0.0)
return inSST
@pytest.fixture
def set_of_rodrigues(set_of_quaternions):
return Rotation(set_of_quaternions).as_Rodrigues(vector=True)[:200]
class TestSymmetry:
@pytest.mark.parametrize('system',Symmetry.crystal_systems)
def test_in_FZ_vectorize(self,set_of_rodrigues,system):
result = Symmetry(system).in_FZ(set_of_rodrigues.reshape(50,4,3)).reshape(200)
for i,r in enumerate(result):
assert r == in_FZ(system,set_of_rodrigues[i])
@pytest.mark.parametrize('system',Symmetry.crystal_systems)
def test_in_disorientation_SST_vectorize(self,set_of_rodrigues,system):
result = Symmetry(system).in_disorientation_SST(set_of_rodrigues.reshape(50,4,3)).reshape(200)
for i,r in enumerate(result):
assert r == in_disorientation_SST(system,set_of_rodrigues[i])
@pytest.mark.parametrize('proper',[True,False])
@pytest.mark.parametrize('system',Symmetry.crystal_systems)
def test_in_SST_vectorize(self,system,proper):
vecs = np.random.rand(20,4,3)
result = Symmetry(system).in_SST(vecs,proper).reshape(20*4)
for i,r in enumerate(result):
assert r == in_SST(system,vecs.reshape(20*4,3)[i],proper)
@pytest.mark.parametrize('invalid_symmetry',['fcc','bcc','hello'])
def test_invalid_symmetry(self,invalid_symmetry):
with pytest.raises(KeyError):
s = Symmetry(invalid_symmetry) # noqa
def test_equal(self):
symmetry = random.choice(Symmetry.crystal_systems)
print(symmetry)
assert Symmetry(symmetry) == Symmetry(symmetry)
def test_not_equal(self):
symmetries = random.sample(Symmetry.crystal_systems,k=2)
assert Symmetry(symmetries[0]) != Symmetry(symmetries[1])
@pytest.mark.parametrize('system',Symmetry.crystal_systems)
def test_in_FZ(self,system):
assert Symmetry(system).in_FZ(np.zeros(3))
@pytest.mark.parametrize('system',Symmetry.crystal_systems)
def test_in_disorientation_SST(self,system):
assert Symmetry(system).in_disorientation_SST(np.zeros(3))
@pytest.mark.parametrize('system',Symmetry.crystal_systems)
@pytest.mark.parametrize('proper',[True,False])
def test_in_SST(self,system,proper):
assert Symmetry(system).in_SST(np.zeros(3),proper)
@pytest.mark.parametrize('function',['in_FZ','in_disorientation_SST','in_SST'])
def test_invalid_argument(self,function):
s = Symmetry() # noqa
with pytest.raises(ValueError):
eval(f's.{function}(np.ones(4))')

View File

@ -1,131 +1,536 @@
import os
from itertools import permutations
import pytest import pytest
import numpy as np import numpy as np
from itertools import permutations
from damask import Table
from damask import Rotation from damask import Rotation
from damask import Orientation from damask import Orientation
from damask import Lattice from damask import Table
from damask import lattice
n = 1000 from damask import util
def IPF_color(orientation,direction):
"""TSL color of inverse pole figure for given axis (non-vectorized)."""
for o in orientation.equivalent:
pole = o.rotation@direction
inSST,color = orientation.lattice.in_SST(pole,color=True)
if inSST: break
return color
def inverse_pole(orientation,axis,proper=False,SST=True):
if SST:
for eq in orientation.equivalent:
pole = eq.rotation @ axis/np.linalg.norm(axis)
if orientation.lattice.in_SST(pole,proper=proper):
return pole
else:
return orientation.rotation @ axis/np.linalg.norm(axis)
@pytest.fixture @pytest.fixture
def reference_dir(reference_dir_base): def reference_dir(reference_dir_base):
"""Directory containing reference results.""" """Directory containing reference results."""
return reference_dir_base/'Rotation' return reference_dir_base/'Orientation'
@pytest.fixture
def set_of_rodrigues(set_of_quaternions):
return Rotation(set_of_quaternions).as_Rodrigues()[:200]
class TestOrientation: class TestOrientation:
@pytest.mark.parametrize('model',['Bain','KS','GT','GT_prime','NW','Pitsch']) @pytest.mark.parametrize('lattice',Orientation.crystal_families)
@pytest.mark.parametrize('lattice',['fcc','bcc']) @pytest.mark.parametrize('shape',[None,5,(4,6)])
def test_relationship_vectorize(self,set_of_quaternions,lattice,model): def test_equal(self,lattice,shape):
result = Orientation(set_of_quaternions[:200].reshape(50,4,4),lattice).related(model) R = Rotation.from_random(shape)
ref_qu = result.rotation.quaternion.reshape(-1,200,4) assert Orientation(R,lattice) == Orientation(R,lattice)
for i in range(200):
single = Orientation(set_of_quaternions[i],lattice).related(model).rotation.quaternion
assert np.allclose(ref_qu[:,i,:],single)
@pytest.mark.parametrize('lattice',Lattice.lattices) @pytest.mark.parametrize('lattice',Orientation.crystal_families)
def test_IPF_vectorize(self,set_of_quaternions,lattice): @pytest.mark.parametrize('shape',[None,5,(4,6)])
direction = np.random.random(3)*2.0-1 def test_unequal(self,lattice,shape):
oris = Orientation(Rotation(set_of_quaternions),lattice)[:200] R = Rotation.from_random(shape)
for i,color in enumerate(oris.IPF_color(direction)): assert not(Orientation(R,lattice) != Orientation(R,lattice))
assert np.allclose(color,IPF_color(oris[i],direction))
@pytest.mark.parametrize('SST',[False,True]) @pytest.mark.parametrize('a,b',[
(dict(rotation=[1,0,0,0]),
dict(rotation=[0.5,0.5,0.5,0.5])),
(dict(rotation=[1,0,0,0],lattice='cubic'),
dict(rotation=[1,0,0,0],lattice='hexagonal')),
(dict(rotation=[1,0,0,0],lattice='cF',a=1),
dict(rotation=[1,0,0,0],lattice='cF',a=2)),
])
def test_nonequal(self,a,b):
assert Orientation(**a) != Orientation(**b)
@pytest.mark.parametrize('kwargs',[
dict(lattice='aP', alpha=np.pi/4,beta=np.pi/3, ),
dict(lattice='mP', c=1.2,alpha=np.pi/4, gamma=np.pi/2),
dict(lattice='oP', c=1.2,alpha=np.pi/4, ),
dict(lattice='oS',a=1.0, c=2.0,alpha=np.pi/2,beta=np.pi/3, ),
dict(lattice='tP',a=1.0,b=1.2, ),
dict(lattice='tI', alpha=np.pi/3, ),
dict(lattice='hP', gamma=np.pi/2),
dict(lattice='cI',a=1.0, c=2.0,alpha=np.pi/2,beta=np.pi/2, ),
dict(lattice='cF', beta=np.pi/3, ),
])
def test_invalid_init(self,kwargs):
with pytest.raises(ValueError):
Orientation(**kwargs).parameters # noqa
@pytest.mark.parametrize('kwargs',[
dict(lattice='aP',a=1.0,b=1.1,c=1.2,alpha=np.pi/4,beta=np.pi/3,gamma=np.pi/2),
dict(lattice='mP',a=1.0,b=1.1,c=1.2, beta=np.pi/3 ),
dict(lattice='oS',a=1.0,b=1.1,c=1.2, ),
dict(lattice='tI',a=1.0, c=1.2, ),
dict(lattice='hP',a=1.0 ),
dict(lattice='cI',a=1.0, ),
])
def test_repr(self,kwargs):
o = Orientation.from_random(**kwargs)
assert isinstance(o.__repr__(),str)
@pytest.mark.parametrize('kwargs',[
dict(lattice='aP',a=1.0,b=1.1,c=1.2,alpha=np.pi/4,beta=np.pi/3,gamma=np.pi/2),
dict(lattice='mP',a=1.0,b=1.1,c=1.2, beta=np.pi/3 ),
dict(lattice='oS',a=1.0,b=1.1,c=1.2, ),
dict(lattice='tI',a=1.0, c=1.2, ),
dict(lattice='hP',a=1.0 ),
dict(lattice='cI',a=1.0, ),
])
def test_copy(self,kwargs):
o = Orientation.from_random(**kwargs)
p = o.copy(rotation=Rotation.from_random())
assert o != p
def test_from_quaternion(self):
assert np.all(Orientation.from_quaternion(q=np.array([1,0,0,0]),lattice='triclinic').as_matrix()
== np.eye(3))
def test_from_Eulers(self):
assert np.all(Orientation.from_Eulers(phi=np.zeros(3),lattice='triclinic').as_matrix()
== np.eye(3))
def test_from_axis_angle(self):
assert np.all(Orientation.from_axis_angle(axis_angle=[1,0,0,0],lattice='triclinic').as_matrix()
== np.eye(3))
def test_from_basis(self):
assert np.all(Orientation.from_basis(basis=np.eye(3),lattice='triclinic').as_matrix()
== np.eye(3))
def test_from_matrix(self):
assert np.all(Orientation.from_matrix(R=np.eye(3),lattice='triclinic').as_matrix()
== np.eye(3))
def test_from_Rodrigues(self):
assert np.all(Orientation.from_Rodrigues(rho=np.array([0,0,1,0]),lattice='triclinic').as_matrix()
== np.eye(3))
def test_from_homochoric(self):
assert np.all(Orientation.from_homochoric(h=np.zeros(3),lattice='triclinic').as_matrix()
== np.eye(3))
def test_from_cubochoric(self):
assert np.all(Orientation.from_cubochoric(c=np.zeros(3),lattice='triclinic').as_matrix()
== np.eye(3))
def test_from_spherical_component(self):
assert np.all(Orientation.from_spherical_component(center=Rotation(),
sigma=0.0,N=1,lattice='triclinic').as_matrix()
== np.eye(3))
def test_from_fiber_component(self):
r = Rotation.from_fiber_component(alpha=np.zeros(2),beta=np.zeros(2),
sigma=0.0,N=1,seed=0)
assert np.all(Orientation.from_fiber_component(alpha=np.zeros(2),beta=np.zeros(2),
sigma=0.0,N=1,seed=0,lattice='triclinic').quaternion
== r.quaternion)
@pytest.mark.parametrize('kwargs',[
dict(lattice='aP',a=1.0,b=1.1,c=1.2,alpha=np.pi/4.5,beta=np.pi/3.5,gamma=np.pi/2.5),
dict(lattice='mP',a=1.0,b=1.1,c=1.2, beta=np.pi/3.5),
dict(lattice='oS',a=1.0,b=1.1,c=1.2,),
dict(lattice='tI',a=1.0, c=1.2,),
dict(lattice='hP',a=1.0 ),
dict(lattice='cI',a=1.0, ),
])
def test_from_direction(self,kwargs):
for a,b in np.random.random((10,2,3)):
c = np.cross(b,a)
if np.all(np.isclose(c,0)): continue
o = Orientation.from_directions(uvw=a,hkl=c,**kwargs)
x = o.to_pole(uvw=a)
z = o.to_pole(hkl=c)
assert np.isclose(np.dot(x/np.linalg.norm(x),np.array([1,0,0])),1) \
and np.isclose(np.dot(z/np.linalg.norm(z),np.array([0,0,1])),1)
def test_negative_angle(self):
with pytest.raises(ValueError):
Orientation(lattice='aP',a=1,b=2,c=3,alpha=45,beta=45,gamma=-45,degrees=True) # noqa
def test_excess_angle(self):
with pytest.raises(ValueError):
Orientation(lattice='aP',a=1,b=2,c=3,alpha=45,beta=45,gamma=90.0001,degrees=True) # noqa
@pytest.mark.parametrize('lattice',Orientation.crystal_families)
@pytest.mark.parametrize('angle',[10,20,30,40])
def test_average(self,angle,lattice):
o = Orientation.from_axis_angle(lattice=lattice,axis_angle=[[0,0,1,10],[0,0,1,angle]],degrees=True)
avg_angle = o.average().as_axis_angle(degrees=True,pair=True)[1]
assert np.isclose(avg_angle,10+(angle-10)/2.)
@pytest.mark.parametrize('lattice',Orientation.crystal_families)
def test_reduced_equivalent(self,lattice):
i = Orientation(lattice=lattice)
o = Orientation.from_random(lattice=lattice)
eq = o.equivalent
FZ = np.argmin(abs(eq.misorientation(i.broadcast_to(len(eq))).as_axis_angle(pair=True)[1]))
assert o.reduced == eq[FZ]
@pytest.mark.parametrize('lattice',Orientation.crystal_families)
@pytest.mark.parametrize('N',[1,8,32])
def test_disorientation(self,lattice,N):
o = Orientation.from_random(lattice=lattice,shape=N,seed=0)
p = Orientation.from_random(lattice=lattice,shape=N,seed=1)
d,ops = o.disorientation(p,return_operators=True)
for n in range(N):
assert np.allclose(d[n].as_quaternion(),
o[n].equivalent[ops[n][0]]
.misorientation(p[n].equivalent[ops[n][1]])
.as_quaternion()) \
or np.allclose((~d)[n].as_quaternion(),
o[n].equivalent[ops[n][0]]
.misorientation(p[n].equivalent[ops[n][1]])
.as_quaternion())
@pytest.mark.parametrize('lattice',Orientation.crystal_families)
@pytest.mark.parametrize('a,b',[
((2,3,2),(2,3,2)),
((2,2),(4,4)),
((3,1),(1,3)),
(None,None),
])
def test_disorientation_blending(self,lattice,a,b):
o = Orientation.from_random(lattice=lattice,shape=a,seed=0)
p = Orientation.from_random(lattice=lattice,shape=b,seed=1)
blend = util.shapeblender(o.shape,p.shape)
for loc in np.random.randint(0,blend,(10,len(blend))):
assert o[tuple(loc[:len(o.shape)])].disorientation(p[tuple(loc[-len(p.shape):])]) \
== o.disorientation(p)[tuple(loc)]
@pytest.mark.parametrize('lattice',Orientation.crystal_families)
def test_disorientation360(self,lattice):
o_1 = Orientation(Rotation(),lattice)
o_2 = Orientation.from_Eulers(lattice=lattice,phi=[360,0,0],degrees=True)
assert np.allclose((o_1.disorientation(o_2)).as_matrix(),np.eye(3))
@pytest.mark.parametrize('lattice',Orientation.crystal_families)
@pytest.mark.parametrize('shape',[(1),(2,3),(4,3,2)])
def test_reduced_vectorization(self,lattice,shape):
o = Orientation.from_random(lattice=lattice,shape=shape,seed=0)
for r, theO in zip(o.reduced.flatten(),o.flatten()):
assert r == theO.reduced
@pytest.mark.parametrize('lattice',Orientation.crystal_families)
@pytest.mark.parametrize('shape',[(1),(2,3),(4,3,2)])
@pytest.mark.parametrize('vector',np.array([[1,0,0],[1,2,3],[-1,1,-1]]))
@pytest.mark.parametrize('proper',[True,False]) @pytest.mark.parametrize('proper',[True,False])
@pytest.mark.parametrize('lattice',Lattice.lattices) def test_to_SST_vectorization(self,lattice,shape,vector,proper):
def test_inverse_pole_vectorize(self,set_of_quaternions,lattice,SST,proper): o = Orientation.from_random(lattice=lattice,shape=shape,seed=0)
axis = np.random.random(3)*2.0-1 for r, theO in zip(o.to_SST(vector=vector,proper=proper).reshape((-1,3)),o.flatten()):
oris = Orientation(Rotation(set_of_quaternions),lattice)[:200] assert np.allclose(r,theO.to_SST(vector=vector,proper=proper))
for i,pole in enumerate(oris.inverse_pole(axis,SST=SST)):
assert np.allclose(pole,inverse_pole(oris[i],axis,SST=SST)) @pytest.mark.parametrize('lattice',Orientation.crystal_families)
@pytest.mark.parametrize('shape',[(1),(2,3),(4,3,2)])
@pytest.mark.parametrize('vector',np.array([[1,0,0],[1,2,3],[-1,1,-1]]))
@pytest.mark.parametrize('proper',[True,False])
def test_IPF_color_vectorization(self,lattice,shape,vector,proper):
o = Orientation.from_random(lattice=lattice,shape=shape,seed=0)
poles = o.to_SST(vector=vector,proper=proper)
for r, theO in zip(o.IPF_color(poles,proper=proper).reshape((-1,3)),o.flatten()):
assert np.allclose(r,theO.IPF_color(theO.to_SST(vector=vector,proper=proper),proper=proper))
@pytest.mark.parametrize('lattice',Orientation.crystal_families)
@pytest.mark.parametrize('a,b',[
((2,3,2),(2,3,2)),
((2,2),(4,4)),
((3,1),(1,3)),
(None,(3,)),
])
def test_to_SST_blending(self,lattice,a,b):
o = Orientation.from_random(lattice=lattice,shape=a,seed=0)
v = np.random.random(b+(3,))
blend = util.shapeblender(o.shape,b)
for loc in np.random.randint(0,blend,(10,len(blend))):
print(f'{a}/{b} @ {loc}')
print(o[tuple(loc[:len(o.shape)])].to_SST(v[tuple(loc[-len(b):])]))
print(o.to_SST(v)[tuple(loc)])
assert np.allclose(o[tuple(loc[:len(o.shape)])].to_SST(v[tuple(loc[-len(b):])]),
o.to_SST(v)[tuple(loc)])
@pytest.mark.parametrize('color',[{'label':'red', 'RGB':[1,0,0],'direction':[0,0,1]}, @pytest.mark.parametrize('color',[{'label':'red', 'RGB':[1,0,0],'direction':[0,0,1]},
{'label':'green','RGB':[0,1,0],'direction':[0,1,1]}, {'label':'green','RGB':[0,1,0],'direction':[0,1,1]},
{'label':'blue', 'RGB':[0,0,1],'direction':[1,1,1]}]) {'label':'blue', 'RGB':[0,0,1],'direction':[1,1,1]}])
@pytest.mark.parametrize('lattice',['fcc','bcc']) @pytest.mark.parametrize('proper',[True,False])
def test_IPF_cubic(self,color,lattice): def test_IPF_cubic(self,color,proper):
cube = Orientation(Rotation(),lattice) cube = Orientation(lattice='cubic')
for direction in set(permutations(np.array(color['direction']))): for direction in set(permutations(np.array(color['direction']))):
assert np.allclose(cube.IPF_color(np.array(direction)),np.array(color['RGB'])) assert np.allclose(np.array(color['RGB']),
cube.IPF_color(cube.to_SST(vector=np.array(direction),proper=proper),proper=proper))
@pytest.mark.parametrize('lattice',Lattice.lattices) @pytest.mark.parametrize('lattice',Orientation.crystal_families)
def test_IPF_equivalent(self,set_of_quaternions,lattice): @pytest.mark.parametrize('proper',[True,False])
direction = np.random.random(3)*2.0-1 def test_IPF_equivalent(self,set_of_quaternions,lattice,proper):
for ori in Orientation(Rotation(set_of_quaternions),lattice)[:200]: direction = np.random.random(3)*2.0-1.0
color = ori.IPF_color(direction) o = Orientation(rotation=set_of_quaternions,lattice=lattice).equivalent
for equivalent in ori.equivalent: color = o.IPF_color(o.to_SST(vector=direction,proper=proper),proper=proper)
assert np.allclose(color,equivalent.IPF_color(direction)) assert np.allclose(np.broadcast_to(color[0,...],color.shape),color)
@pytest.mark.parametrize('lattice',Lattice.lattices) @pytest.mark.parametrize('lattice',Orientation.crystal_families)
def test_reduced(self,set_of_quaternions,lattice): def test_in_FZ_vectorization(self,set_of_rodrigues,lattice):
oris = Orientation(Rotation(set_of_quaternions),lattice) result = Orientation.from_Rodrigues(rho=set_of_rodrigues.reshape((50,4,-1)),lattice=lattice).in_FZ.reshape(-1)
reduced = oris.reduced for r,rho in zip(result,set_of_rodrigues[:len(result)]):
assert np.all(reduced.in_FZ) and oris.rotation.shape == reduced.rotation.shape assert r == Orientation.from_Rodrigues(rho=rho,lattice=lattice).in_FZ
@pytest.mark.parametrize('lattice',Orientation.crystal_families)
def test_in_disorientation_FZ_vectorization(self,set_of_rodrigues,lattice):
result = Orientation.from_Rodrigues(rho=set_of_rodrigues.reshape((50,4,-1)),
lattice=lattice).in_disorientation_FZ.reshape(-1)
for r,rho in zip(result,set_of_rodrigues[:len(result)]):
assert r == Orientation.from_Rodrigues(rho=rho,lattice=lattice).in_disorientation_FZ
@pytest.mark.parametrize('proper',[True,False])
@pytest.mark.parametrize('lattice',Orientation.crystal_families)
def test_in_SST_vectorization(self,lattice,proper):
vecs = np.random.rand(20,4,3)
result = Orientation(lattice=lattice).in_SST(vecs,proper).flatten()
for r,v in zip(result,vecs.reshape((-1,3))):
assert np.all(r == Orientation(lattice=lattice).in_SST(v,proper))
@pytest.mark.parametrize('invalid_lattice',['fcc','bcc','hello'])
def test_invalid_lattice_init(self,invalid_lattice):
with pytest.raises(KeyError):
Orientation(lattice=invalid_lattice) # noqa
@pytest.mark.parametrize('invalid_family',[None,'fcc','bcc','hello'])
def test_invalid_symmetry_family(self,invalid_family):
with pytest.raises(KeyError):
o = Orientation(lattice='cubic')
o.family = invalid_family
o.symmetry_operations # noqa
def test_missing_symmetry_equivalent(self):
with pytest.raises(ValueError):
Orientation(lattice=None).equivalent # noqa
def test_missing_symmetry_reduced(self):
with pytest.raises(ValueError):
Orientation(lattice=None).reduced # noqa
def test_missing_symmetry_in_FZ(self):
with pytest.raises(ValueError):
Orientation(lattice=None).in_FZ # noqa
def test_missing_symmetry_in_disorientation_FZ(self):
with pytest.raises(ValueError):
Orientation(lattice=None).in_disorientation_FZ # noqa
def test_missing_symmetry_disorientation(self):
with pytest.raises(ValueError):
Orientation(lattice=None).disorientation(Orientation(lattice=None)) # noqa
def test_missing_symmetry_average(self):
with pytest.raises(ValueError):
Orientation(lattice=None).average() # noqa
def test_missing_symmetry_to_SST(self):
with pytest.raises(ValueError):
Orientation(lattice=None).to_SST(np.zeros(3)) # noqa
def test_missing_symmetry_immutable(self):
with pytest.raises(KeyError):
Orientation(lattice=None).immutable # noqa
def test_missing_symmetry_basis_real(self):
with pytest.raises(KeyError):
Orientation(lattice=None).basis_real # noqa
def test_missing_symmetry_basis_reciprocal(self):
with pytest.raises(KeyError):
Orientation(lattice=None).basis_reciprocal # noqa
def test_double_Bravais_to_Miller(self):
with pytest.raises(KeyError):
Orientation.Bravais_to_Miller(uvtw=np.ones(4),hkil=np.ones(4)) # noqa
def test_double_Miller_to_Bravais(self):
with pytest.raises(KeyError):
Orientation.Miller_to_Bravais(uvw=np.ones(4),hkl=np.ones(4)) # noqa
def test_double_to_lattice(self):
with pytest.raises(KeyError):
Orientation().to_lattice(direction=np.ones(3),plane=np.ones(3)) # noqa
def test_double_to_frame(self):
with pytest.raises(KeyError):
Orientation().to_frame(uvw=np.ones(3),hkl=np.ones(3)) # noqa
@pytest.mark.parametrize('relation',[None,'Peter','Paul'])
def test_unknown_relation(self,relation):
with pytest.raises(KeyError):
Orientation(lattice='cF').related(relation) # noqa
@pytest.mark.parametrize('relation,lattice,a,b,c,alpha,beta,gamma',
[
('Bain', 'aP',0.5,2.0,3.0,0.8,0.5,1.2),
('KS', 'mP',1.0,2.0,3.0,np.pi/2,0.5,np.pi/2),
('Pitsch', 'oI',0.5,1.5,3.0,np.pi/2,np.pi/2,np.pi/2),
('Burgers','tP',0.5,0.5,3.0,np.pi/2,np.pi/2,np.pi/2),
('GT', 'hP',1.0,None,1.6,np.pi/2,np.pi/2,2*np.pi/3),
('Burgers','cF',1.0,1.0,None,np.pi/2,np.pi/2,np.pi/2),
])
def test_unknown_relation_lattice(self,relation,lattice,a,b,c,alpha,beta,gamma):
with pytest.raises(KeyError):
Orientation(lattice=lattice,
a=a,b=b,c=c,
alpha=alpha,beta=beta,gamma=gamma).related(relation) # noqa
@pytest.mark.parametrize('lattice',Orientation.crystal_families)
@pytest.mark.parametrize('proper',[True,False])
def test_in_SST(self,lattice,proper):
assert Orientation(lattice=lattice).in_SST(np.zeros(3),proper)
@pytest.mark.parametrize('function',['in_SST','IPF_color'])
def test_invalid_argument(self,function):
o = Orientation(lattice='cubic') # noqa
with pytest.raises(ValueError):
eval(f'o.{function}(np.ones(4))')
@pytest.mark.parametrize('model',lattice.relations)
def test_relationship_definition(self,model):
m,o = list(lattice.relations[model])
assert lattice.relations[model][m].shape[:-1] == lattice.relations[model][o].shape[:-1]
@pytest.mark.parametrize('model',['Bain','KS','GT','GT_prime','NW','Pitsch']) @pytest.mark.parametrize('model',['Bain','KS','GT','GT_prime','NW','Pitsch'])
@pytest.mark.parametrize('lattice',['fcc','bcc']) @pytest.mark.parametrize('lattice',['cF','cI'])
def test_relationship_vectorize(self,set_of_quaternions,lattice,model):
r = Orientation(rotation=set_of_quaternions[:200].reshape((50,4,4)),lattice=lattice).related(model)
for i in range(200):
assert r.reshape((-1,200))[:,i] == Orientation(set_of_quaternions[i],lattice).related(model)
@pytest.mark.parametrize('model',['Bain','KS','GT','GT_prime','NW','Pitsch'])
@pytest.mark.parametrize('lattice',['cF','cI'])
def test_relationship_forward_backward(self,model,lattice): def test_relationship_forward_backward(self,model,lattice):
ori = Orientation(Rotation.from_random(),lattice) o = Orientation.from_random(lattice=lattice)
for i,r in enumerate(ori.related(model)): for i,r in enumerate(o.related(model)):
ori2 = r.related(model)[i] assert o.disorientation(r.related(model)[i]).as_axis_angle(degrees=True,pair=True)[1]<1.0e-5
misorientation = ori.rotation.misorientation(ori2.rotation)
assert misorientation.as_axis_angle(degrees=True)[3]<1.0e-5
@pytest.mark.parametrize('model',['Bain','KS','GT','GT_prime','NW','Pitsch']) @pytest.mark.parametrize('model',['Bain','KS','GT','GT_prime','NW','Pitsch'])
@pytest.mark.parametrize('lattice',['fcc','bcc']) @pytest.mark.parametrize('lattice',['cF','cI'])
def test_relationship_reference(self,update,reference_dir,model,lattice): def test_relationship_reference(self,update,reference_dir,model,lattice):
reference = os.path.join(reference_dir,f'{lattice}_{model}.txt') reference = reference_dir/f'{lattice}_{model}.txt'
ori = Orientation(Rotation(),lattice) o = Orientation(lattice=lattice)
eu = np.array([o.rotation.as_Eulers(degrees=True) for o in ori.related(model)]) eu = o.related(model).as_Eulers(degrees=True)
if update: if update:
coords = np.array([(1,i+1) for i,x in enumerate(eu)]) coords = np.array([(1,i+1) for i,x in enumerate(eu)])
table = Table(eu,{'Eulers':(3,)}) Table(eu,{'Eulers':(3,)})\
table = table.add('pos',coords) .add('pos',coords)\
table.save(reference) .save(reference)
assert np.allclose(eu,Table.load(reference).get('Eulers')) assert np.allclose(eu,Table.load(reference).get('Eulers'))
@pytest.mark.parametrize('lattice',Lattice.lattices) def test_basis_real(self):
def test_disorientation360(self,lattice): for gamma in np.random.random(2**8)*np.pi:
R_1 = Orientation(Rotation(),lattice) basis = np.tril(np.random.random((3,3))+1e-6)
R_2 = Orientation(Rotation.from_Eulers([360,0,0],degrees=True),lattice) basis[1,:2] = basis[1,1]*np.array([np.cos(gamma),np.sin(gamma)])
assert np.allclose(R_1.disorientation(R_2).as_matrix(),np.eye(3)) basis[2,:2] = basis[2,:2]*2-1
lengths = np.linalg.norm(basis,axis=-1)
cosines = np.roll(np.einsum('ij,ij->i',basis,np.roll(basis,1,axis=0))/lengths/np.roll(lengths,1),1)
o = Orientation.from_random(lattice='aP',
**dict(zip(['a','b','c'],lengths)),
**dict(zip(['alpha','beta','gamma'],np.arccos(cosines))),
)
assert np.allclose(o.to_frame(uvw=np.eye(3)),basis), 'Lattice basis disagrees with initialization'
@pytest.mark.parametrize('lattice',Lattice.lattices) @pytest.mark.parametrize('lattice,a,b,c,alpha,beta,gamma',
@pytest.mark.parametrize('angle',[10,20,30,40]) [
def test_average(self,angle,lattice): ('aP',0.5,2.0,3.0,0.8,0.5,1.2),
R_1 = Orientation(Rotation.from_axis_angle([0,0,1,10],degrees=True),lattice) ('mP',1.0,2.0,3.0,np.pi/2,0.5,np.pi/2),
R_2 = Orientation(Rotation.from_axis_angle([0,0,1,angle],degrees=True),lattice) ('oI',0.5,1.5,3.0,np.pi/2,np.pi/2,np.pi/2),
avg_angle = R_1.average(R_2).rotation.as_axis_angle(degrees=True,pair=True)[1] ('tP',0.5,0.5,3.0,np.pi/2,np.pi/2,np.pi/2),
assert np.isclose(avg_angle,10+(angle-10)/2.) ('hP',1.0,None,1.6,np.pi/2,np.pi/2,2*np.pi/3),
('cF',1.0,1.0,None,np.pi/2,np.pi/2,np.pi/2),
])
def test_bases_contraction(self,lattice,a,b,c,alpha,beta,gamma):
L = Orientation(lattice=lattice,
a=a,b=b,c=c,
alpha=alpha,beta=beta,gamma=gamma)
assert np.allclose(np.eye(3),np.einsum('ik,jk',L.basis_real,L.basis_reciprocal))
@pytest.mark.parametrize('lattice',Lattice.lattices) @pytest.mark.parametrize('keyFrame,keyLattice',[('uvw','direction'),('hkl','plane'),])
def test_from_average(self,lattice): @pytest.mark.parametrize('vector',np.array([
R_1 = Orientation(Rotation.from_random(),lattice) [1.,1.,1.],
eqs = [r for r in R_1.equivalent] [-2.,3.,0.5],
R_2 = Orientation.from_average(eqs) [0.,0.,1.],
assert np.allclose(R_1.rotation.quaternion,R_2.rotation.quaternion) [1.,1.,1.],
[2.,2.,2.],
[0.,1.,1.],
]))
@pytest.mark.parametrize('lattice,a,b,c,alpha,beta,gamma',
[
('aP',0.5,2.0,3.0,0.8,0.5,1.2),
('mP',1.0,2.0,3.0,np.pi/2,0.5,np.pi/2),
('oI',0.5,1.5,3.0,np.pi/2,np.pi/2,np.pi/2),
('tP',0.5,0.5,3.0,np.pi/2,np.pi/2,np.pi/2),
('hP',1.0,1.0,1.6,np.pi/2,np.pi/2,2*np.pi/3),
('cF',1.0,1.0,1.0,np.pi/2,np.pi/2,np.pi/2),
])
def test_to_frame_to_lattice(self,lattice,a,b,c,alpha,beta,gamma,vector,keyFrame,keyLattice):
L = Orientation(lattice=lattice,
a=a,b=b,c=c,
alpha=alpha,beta=beta,gamma=gamma)
assert np.allclose(vector,
L.to_frame(**{keyFrame:L.to_lattice(**{keyLattice:vector})}))
@pytest.mark.parametrize('vector',np.array([
[1,0,0],
[1,1,0],
[1,1,1],
[1,0,-2],
]))
@pytest.mark.parametrize('kw_Miller,kw_Bravais',[('uvw','uvtw'),('hkl','hkil')])
def test_Miller_Bravais_Miller(self,vector,kw_Miller,kw_Bravais):
assert np.all(vector == Orientation.Bravais_to_Miller(**{kw_Bravais:Orientation.Miller_to_Bravais(**{kw_Miller:vector})}))
@pytest.mark.parametrize('vector',np.array([
[1,0,-1,2],
[1,-1,0,3],
[1,1,-2,-3],
[0,0,0,1],
]))
@pytest.mark.parametrize('kw_Miller,kw_Bravais',[('uvw','uvtw'),('hkl','hkil')])
def test_Bravais_Miller_Bravais(self,vector,kw_Miller,kw_Bravais):
assert np.all(vector == Orientation.Miller_to_Bravais(**{kw_Miller:Orientation.Bravais_to_Miller(**{kw_Bravais:vector})}))
@pytest.mark.parametrize('lattice,a,b,c,alpha,beta,gamma',
[
('aP',0.5,2.0,3.0,0.8,0.5,1.2),
('mP',1.0,2.0,3.0,np.pi/2,0.5,np.pi/2),
('oI',0.5,1.5,3.0,np.pi/2,np.pi/2,np.pi/2),
('tP',0.5,0.5,3.0,np.pi/2,np.pi/2,np.pi/2),
('hP',1.0,1.0,1.6,np.pi/2,np.pi/2,2*np.pi/3),
('cF',1.0,1.0,1.0,np.pi/2,np.pi/2,np.pi/2),
])
@pytest.mark.parametrize('kw',['uvw','hkl'])
@pytest.mark.parametrize('with_symmetry',[False,True])
@pytest.mark.parametrize('shape',[None,1,(12,24)])
@pytest.mark.parametrize('vector',[
np.random.random( 3 ),
np.random.random( (4,3)),
np.random.random((4,8,3)),
])
def test_to_pole(self,shape,lattice,a,b,c,alpha,beta,gamma,vector,kw,with_symmetry):
o = Orientation.from_random(shape=shape,
lattice=lattice,
a=a,b=b,c=c,
alpha=alpha,beta=beta,gamma=gamma)
assert o.to_pole(**{kw:vector,'with_symmetry':with_symmetry}).shape \
== o.shape + (o.symmetry_operations.shape if with_symmetry else ()) + vector.shape
@pytest.mark.parametrize('lattice',['hP','cI','cF'])
def test_Schmid(self,update,reference_dir,lattice):
L = Orientation(lattice=lattice)
for mode in L.kinematics:
reference = reference_dir/f'{lattice}_{mode}.txt'
P = L.Schmid(mode)
if update:
table = Table(P.reshape(-1,9),{'Schmid':(3,3,)})
table.save(reference)
assert np.allclose(P,Table.load(reference).get('Schmid'))

View File

@ -168,15 +168,16 @@ class TestResult:
@pytest.mark.parametrize('d',[[1,0,0],[0,1,0],[0,0,1]]) @pytest.mark.parametrize('d',[[1,0,0],[0,1,0],[0,0,1]])
def test_add_IPF_color(self,default,d): def test_add_IPF_color(self,default,d):
default.add_IPF_color('O',d) default.add_IPF_color('O',np.array(d))
loc = {'orientation': default.get_dataset_location('O'), loc = {'O': default.get_dataset_location('O'),
'color': default.get_dataset_location('IPFcolor_[{} {} {}]'.format(*d))} 'color': default.get_dataset_location('IPFcolor_[{} {} {}]'.format(*d))}
qu = default.read_dataset(loc['orientation']).view(np.double).reshape(-1,4) qu = default.read_dataset(loc['O']).view(np.double).squeeze()
crystal_structure = default.get_crystal_structure() crystal_structure = default.get_crystal_structure()
in_memory = np.empty((qu.shape[0],3),np.uint8) c = Orientation(rotation=qu,
for i,q in enumerate(qu): lattice={'fcc':'cF',
o = Orientation(q,crystal_structure).reduced 'bcc':'cI',
in_memory[i] = np.uint8(o.IPF_color(np.array(d))*255) 'hex':'hP'}[crystal_structure])
in_memory = np.uint8(c.IPF_color(c.to_SST(np.array(d)))*255)
in_file = default.read_dataset(loc['color']) in_file = default.read_dataset(loc['color'])
assert np.allclose(in_memory,in_file) assert np.allclose(in_memory,in_file)
@ -244,13 +245,14 @@ class TestResult:
in_file = default.read_dataset(loc['S'],0) in_file = default.read_dataset(loc['S'],0)
assert np.allclose(in_memory,in_file) assert np.allclose(in_memory,in_file)
@pytest.mark.skip(reason='requires rework of lattice.f90')
@pytest.mark.parametrize('polar',[True,False]) @pytest.mark.parametrize('polar',[True,False])
def test_add_pole(self,default,polar): def test_add_pole(self,default,polar):
pole = np.array([1.,0.,0.]) pole = np.array([1.,0.,0.])
default.add_pole('O',pole,polar) default.add_pole('O',pole,polar)
loc = {'orientation': default.get_dataset_location('O'), loc = {'O': default.get_dataset_location('O'),
'pole': default.get_dataset_location('p^{}_[1 0 0)'.format(u'' if polar else 'xy'))} 'pole': default.get_dataset_location('p^{}_[1 0 0)'.format(u'' if polar else 'xy'))}
rot = Rotation(default.read_dataset(loc['orientation']).view(np.double)) rot = Rotation(default.read_dataset(loc['O']).view(np.double))
rotated_pole = rot * np.broadcast_to(pole,rot.shape+(3,)) rotated_pole = rot * np.broadcast_to(pole,rot.shape+(3,))
xy = rotated_pole[:,0:2]/(1.+abs(pole[2])) xy = rotated_pole[:,0:2]/(1.+abs(pole[2]))
in_memory = xy if not polar else \ in_memory = xy if not polar else \

View File

@ -771,6 +771,53 @@ class TestRotation:
def test_random(self,shape): def test_random(self,shape):
Rotation.from_random(shape) Rotation.from_random(shape)
def test_equal(self):
r = Rotation.from_random(seed=0)
assert r == r
def test_unequal(self):
r = Rotation.from_random(seed=0)
assert not (r != r)
def test_inversion(self):
r = Rotation.from_random(seed=0)
assert r == ~~r
@pytest.mark.parametrize('shape',[None,1,(1,),(4,2),(1,1,1)])
def test_shape(self,shape):
r = Rotation.from_random(shape=shape)
assert r.shape == (shape if isinstance(shape,tuple) else (shape,) if shape else ())
@pytest.mark.parametrize('shape',[None,1,(1,),(4,2),(3,3,2)])
def test_append(self,shape):
r = Rotation.from_random(shape=shape)
p = Rotation.from_random(shape=shape)
s = r.append(p)
print(f'append 2x {shape} --> {s.shape}')
assert s[0,...] == r[0,...] and s[-1,...] == p[-1,...]
@pytest.mark.parametrize('quat,standardized',[
([-1,0,0,0],[1,0,0,0]),
([-0.5,-0.5,-0.5,-0.5],[0.5,0.5,0.5,0.5]),
])
def test_standardization(self,quat,standardized):
assert Rotation(quat)._standardize() == Rotation(standardized)
@pytest.mark.parametrize('shape,length',[
((2,3,4),2),
(4,4),
((),0)
])
def test_len(self,shape,length):
r = Rotation.from_random(shape=shape)
assert len(r) == length
@pytest.mark.parametrize('shape',[(4,6),(2,3,4),(3,3,3)])
@pytest.mark.parametrize('order',['C','F'])
def test_flatten_reshape(self,shape,order):
r = Rotation.from_random(shape=shape)
assert r == r.flatten(order).reshape(shape,order)
@pytest.mark.parametrize('function',[Rotation.from_quaternion, @pytest.mark.parametrize('function',[Rotation.from_quaternion,
Rotation.from_Eulers, Rotation.from_Eulers,
Rotation.from_axis_angle, Rotation.from_axis_angle,
@ -848,7 +895,8 @@ class TestRotation:
np.random.rand(3,3,3,3)]) np.random.rand(3,3,3,3)])
def test_rotate_identity(self,data): def test_rotate_identity(self,data):
R = Rotation() R = Rotation()
assert np.allclose(data,R*data) print(R,data)
assert np.allclose(data,R@data)
@pytest.mark.parametrize('data',[np.random.rand(3), @pytest.mark.parametrize('data',[np.random.rand(3),
np.random.rand(3,3), np.random.rand(3,3),
@ -860,6 +908,16 @@ class TestRotation:
R_2 = Rotation.from_Eulers(np.array([0.,0.,phi_2])) R_2 = Rotation.from_Eulers(np.array([0.,0.,phi_2]))
assert np.allclose(data,R_2@(R_1@data)) assert np.allclose(data,R_2@(R_1@data))
@pytest.mark.parametrize('pwr',[-10,0,1,2.5,np.pi,np.random.random()])
def test_rotate_power(self,pwr):
R = Rotation.from_random()
axis_angle = R.as_axis_angle()
axis_angle[ 3] = (pwr*axis_angle[-1])%(2.*np.pi)
if axis_angle[3] > np.pi:
axis_angle[3] -= 2.*np.pi
axis_angle *= -1
assert R**pwr == Rotation.from_axis_angle(axis_angle)
def test_rotate_inverse(self): def test_rotate_inverse(self):
R = Rotation.from_random() R = Rotation.from_random()
assert np.allclose(np.eye(3),(~R@R).as_matrix()) assert np.allclose(np.eye(3),(~R@R).as_matrix())
@ -877,7 +935,7 @@ class TestRotation:
def test_rotate_invalid_shape(self,data): def test_rotate_invalid_shape(self,data):
R = Rotation.from_random() R = Rotation.from_random()
with pytest.raises(ValueError): with pytest.raises(ValueError):
R*data R@data
@pytest.mark.parametrize('data',['does_not_work', @pytest.mark.parametrize('data',['does_not_work',
(1,2), (1,2),
@ -885,7 +943,7 @@ class TestRotation:
def test_rotate_invalid_type(self,data): def test_rotate_invalid_type(self,data):
R = Rotation.from_random() R = Rotation.from_random()
with pytest.raises(TypeError): with pytest.raises(TypeError):
R*data R@data
def test_misorientation(self): def test_misorientation(self):
R = Rotation.from_random() R = Rotation.from_random()
@ -898,9 +956,8 @@ class TestRotation:
@pytest.mark.parametrize('angle',[10,20,30,40,50,60,70,80,90,100,120]) @pytest.mark.parametrize('angle',[10,20,30,40,50,60,70,80,90,100,120])
def test_average(self,angle): def test_average(self,angle):
R_1 = Rotation.from_axis_angle([0,0,1,10],degrees=True) R = Rotation.from_axis_angle([[0,0,1,10],[0,0,1,angle]],degrees=True)
R_2 = Rotation.from_axis_angle([0,0,1,angle],degrees=True) avg_angle = R.average().as_axis_angle(degrees=True,pair=True)[1]
avg_angle = R_1.average(R_2).as_axis_angle(degrees=True,pair=True)[1]
assert np.isclose(avg_angle,10+(angle-10)/2.) assert np.isclose(avg_angle,10+(angle-10)/2.)

View File

@ -44,3 +44,52 @@ class TestUtil:
selected = util.hybrid_IA(dist,N_samples) selected = util.hybrid_IA(dist,N_samples)
dist_sampled = np.histogram(centers[selected],bins)[0]/N_samples*np.sum(dist) dist_sampled = np.histogram(centers[selected],bins)[0]/N_samples*np.sum(dist)
assert np.sqrt(((dist - dist_sampled) ** 2).mean()) < .025 and selected.shape[0]==N_samples assert np.sqrt(((dist - dist_sampled) ** 2).mean()) < .025 and selected.shape[0]==N_samples
@pytest.mark.parametrize('point,normalize,answer',
[
([1,0,0],False,[1,0,0]),
([1,0,0],True, [1,0,0]),
([0,1,1],False,[0,0.5,0]),
([0,1,1],True, [0,0.41421356,0]),
([1,1,1],False,[0.5,0.5,0]),
([1,1,1],True, [0.3660254, 0.3660254, 0]),
])
def test_project_stereographic(self,point,normalize,answer):
assert np.allclose(util.project_stereographic(np.array(point),normalize=normalize),answer)
@pytest.mark.parametrize('fro,to,mode,answer',
[
((),(1,),'left',(1,)),
((1,),(7,),'right',(1,)),
((1,2),(1,1,2,2),'right',(1,1,2,1)),
((1,2),(1,1,2,2),'left',(1,1,1,2)),
((1,2,3),(1,1,2,3,4),'right',(1,1,2,3,1)),
((10,2),(10,3,2,2,),'right',(10,1,2,1)),
((10,2),(10,3,2,2,),'left',(10,1,1,2)),
((2,2,3),(2,2,2,3,4),'left',(1,2,2,3,1)),
((2,2,3),(2,2,2,3,4),'right',(2,2,1,3,1)),
])
def test_shapeshifter(self,fro,to,mode,answer):
assert util.shapeshifter(fro,to,mode) == answer
@pytest.mark.parametrize('fro,to,mode',
[
((10,3,4),(10,3,2,2),'left'),
((2,3),(10,3,2,2),'right'),
])
def test_invalid_shapeshifter(self,fro,to,mode):
with pytest.raises(ValueError):
util.shapeshifter(fro,to,mode)
@pytest.mark.parametrize('a,b,answer',
[
((),(1,),(1,)),
((1,),(),(1,)),
((1,),(7,),(1,7)),
((2,),(2,2),(2,2)),
((1,2),(2,2),(1,2,2)),
((1,2,3),(2,3,4),(1,2,3,4)),
((1,2,3),(1,2,3),(1,2,3)),
])
def test_shapeblender(self,a,b,answer):
assert util.shapeblender(a,b) == answer

View File

@ -93,7 +93,8 @@ subroutine prec_init
print'(a,i19)', ' Maximum value: ',huge(0) print'(a,i19)', ' Maximum value: ',huge(0)
print'(/,a,i3)', ' Size of float in bit: ',storage_size(0.0_pReal) print'(/,a,i3)', ' Size of float in bit: ',storage_size(0.0_pReal)
print'(a,e10.3)', ' Maximum value: ',huge(0.0_pReal) print'(a,e10.3)', ' Maximum value: ',huge(0.0_pReal)
print'(a,e10.3)', ' Minimum value: ',tiny(0.0_pReal) print'(a,e10.3)', ' Minimum value: ',PREAL_MIN
print'(a,e10.3)', ' Epsilon value: ',PREAL_EPSILON
print'(a,i3)', ' Decimal precision: ',precision(0.0_pReal) print'(a,i3)', ' Decimal precision: ',precision(0.0_pReal)
call selfTest call selfTest