simplifications + more tests
This commit is contained in:
parent
de8e9b5fc1
commit
e18a5b8a1b
|
@ -1,7 +1,5 @@
|
|||
import numpy as np
|
||||
|
||||
from . import Rotation
|
||||
|
||||
|
||||
class Symmetry:
|
||||
"""
|
||||
|
@ -29,7 +27,6 @@ class Symmetry:
|
|||
raise KeyError(f'Crystal system "{system}" is unknown')
|
||||
|
||||
self.system = system.lower() if isinstance(system,str) else system
|
||||
self.lattice = self.system # ToDo: for compatibility
|
||||
|
||||
|
||||
def __copy__(self):
|
||||
|
@ -219,6 +216,7 @@ class Symmetry:
|
|||
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.
|
||||
|
@ -311,9 +309,9 @@ class Symmetry:
|
|||
# ******************************************************************************************
|
||||
class Lattice: # ToDo: Make a subclass of Symmetry!
|
||||
"""
|
||||
Lattice system.
|
||||
Bravais lattice.
|
||||
|
||||
Currently, this contains only a mapping from Bravais lattice to symmetry
|
||||
This contains only a mapping from Bravais lattice to symmetry
|
||||
and orientation relationships. It could include twin and slip systems.
|
||||
|
||||
References
|
||||
|
@ -331,7 +329,7 @@ class Lattice: # ToDo: Make a subclass of Symmetry!
|
|||
}
|
||||
|
||||
|
||||
def __init__(self, lattice):
|
||||
def __init__(self,lattice,c_over_a=None):
|
||||
"""
|
||||
New lattice of given type.
|
||||
|
||||
|
@ -348,17 +346,17 @@ class Lattice: # ToDo: Make a subclass of Symmetry!
|
|||
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 'Bravais lattice {} ({} symmetry)'.format(self.lattice,self.symmetry)
|
||||
return 'Bravais lattice {} ({} crystal system)'.format(self.lattice,self.symmetry)
|
||||
|
||||
|
||||
# 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},
|
||||
_KS = {'mapping':{'fcc':0,'bcc':1},
|
||||
'planes': np.array([
|
||||
[[ 1, 1, 1],[ 0, 1, 1]],
|
||||
[[ 1, 1, 1],[ 0, 1, 1]],
|
||||
|
@ -412,7 +410,7 @@ class Lattice: # ToDo: Make a subclass of Symmetry!
|
|||
|
||||
# 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},
|
||||
_GT = {'mapping':{'fcc':0,'bcc':1},
|
||||
'planes': np.array([
|
||||
[[ 1, 1, 1],[ 1, 0, 1]],
|
||||
[[ 1, 1, 1],[ 1, 1, 0]],
|
||||
|
@ -466,7 +464,7 @@ class Lattice: # ToDo: Make a subclass of Symmetry!
|
|||
|
||||
# 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},
|
||||
_GTprime = {'mapping':{'fcc':0,'bcc':1},
|
||||
'planes': np.array([
|
||||
[[ 7, 17, 17],[ 12, 5, 17]],
|
||||
[[ 17, 7, 17],[ 17, 12, 5]],
|
||||
|
@ -520,7 +518,7 @@ class Lattice: # ToDo: Make a subclass of Symmetry!
|
|||
|
||||
# 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},
|
||||
_NW = {'mapping':{'fcc':0,'bcc':1},
|
||||
'planes': np.array([
|
||||
[[ 1, 1, 1],[ 0, 1, 1]],
|
||||
[[ 1, 1, 1],[ 0, 1, 1]],
|
||||
|
@ -550,7 +548,7 @@ class Lattice: # ToDo: Make a subclass of Symmetry!
|
|||
|
||||
# Pitsch orientation relationship for fcc <-> bcc transformation
|
||||
# from Y. He et al., Acta Materialia 53:1179-1190, 2005
|
||||
Pitsch = {'mapping':{'fcc':0,'bcc':1},
|
||||
_Pitsch = {'mapping':{'fcc':0,'bcc':1},
|
||||
'planes': np.array([
|
||||
[[ 0, 1, 0],[ -1, 0, 1]],
|
||||
[[ 0, 0, 1],[ 1, -1, 0]],
|
||||
|
@ -580,7 +578,7 @@ class Lattice: # ToDo: Make a subclass of Symmetry!
|
|||
|
||||
# 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},
|
||||
_Bain = {'mapping':{'fcc':0,'bcc':1},
|
||||
'planes': np.array([
|
||||
[[ 1, 0, 0],[ 1, 0, 0]],
|
||||
[[ 0, 1, 0],[ 0, 1, 0]],
|
||||
|
@ -590,7 +588,8 @@ class Lattice: # ToDo: Make a subclass of Symmetry!
|
|||
[[ 0, 0, 1],[ 1, 0, 1]],
|
||||
[[ 1, 0, 0],[ 1, 1, 0]]],dtype='float')}
|
||||
|
||||
def relationOperations(self,model):
|
||||
|
||||
def relation_operations(self,model):
|
||||
"""
|
||||
Crystallographic orientation relationships for phase transformations.
|
||||
|
||||
|
@ -612,8 +611,8 @@ class Lattice: # ToDo: Make a subclass of Symmetry!
|
|||
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}
|
||||
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 :
|
||||
|
@ -639,6 +638,8 @@ class Lattice: # ToDo: Make a subclass of Symmetry!
|
|||
otherDir = miller[otherDir_id]/ np.linalg.norm(miller[otherDir_id])
|
||||
otherMatrix = np.array([otherDir,np.cross(otherPlane,otherDir),otherPlane])
|
||||
|
||||
r['rotations'].append(Rotation.from_matrix(np.dot(otherMatrix.T,myMatrix)))
|
||||
r['rotations'].append(np.dot(otherMatrix.T,myMatrix))
|
||||
|
||||
r['rotations'] = np.array(r['rotations'])
|
||||
|
||||
return r
|
||||
|
|
|
@ -71,8 +71,8 @@ class Orientation: # ToDo: make subclass of lattice and Rotation?
|
|||
r = b*aInv
|
||||
for k in range(2):
|
||||
r.inverse()
|
||||
breaker = self.in_FZ \
|
||||
and (not SST or other.lattice.symmetry.inDisorientationSST(r.as_Rodrigues(vector=True)))
|
||||
breaker = self.lattice.in_FZ(r.as_Rodrigues(vector=True)) \
|
||||
and (not SST or other.lattice.in_disorientation_SST(r.as_Rodrigues(vector=True)))
|
||||
if breaker: break
|
||||
if breaker: break
|
||||
if breaker: break
|
||||
|
@ -90,40 +90,36 @@ class Orientation: # ToDo: make subclass of lattice and Rotation?
|
|||
@property
|
||||
def equivalent(self):
|
||||
"""
|
||||
Return orientations which are symmetrically equivalent.
|
||||
Orientations which are symmetrically equivalent.
|
||||
|
||||
One dimension (length according to symmetrically equivalent orientations)
|
||||
One dimension (length according to number of symmetrically equivalent orientations)
|
||||
is added to the left of the Rotation array.
|
||||
|
||||
"""
|
||||
s = self.lattice.symmetry.symmetry_operations
|
||||
s = s.reshape(s.shape[:1]+(1,)*len(self.rotation.shape)+(4,))
|
||||
s = Rotation(np.broadcast_to(s,s.shape[:1]+self.rotation.quaternion.shape))
|
||||
o = self.lattice.symmetry.symmetry_operations
|
||||
o = o.reshape(o.shape[:1]+(1,)*len(self.rotation.shape)+(4,))
|
||||
o = Rotation(np.broadcast_to(o,o.shape[:1]+self.rotation.quaternion.shape))
|
||||
|
||||
r = np.broadcast_to(self.rotation.quaternion,s.shape[:1]+self.rotation.quaternion.shape)
|
||||
s = np.broadcast_to(self.rotation.quaternion,o.shape[:1]+self.rotation.quaternion.shape)
|
||||
|
||||
return self.__class__(s@Rotation(r),self.lattice)
|
||||
return self.__class__(o@Rotation(s),self.lattice)
|
||||
|
||||
|
||||
def relatedOrientations_vec(self,model):
|
||||
"""List of orientations related by the given orientation relationship."""
|
||||
h = self.lattice.relationOperations(model)
|
||||
rot= h['rotations']
|
||||
op=np.array([o.as_quaternion() for o in rot])
|
||||
def related(self,model):
|
||||
"""
|
||||
Orientations related by the given orientation relationship.
|
||||
|
||||
s = op.reshape(op.shape[:1]+(1,)*len(self.rotation.shape)+(4,))
|
||||
s = Rotation(np.broadcast_to(s,s.shape[:1]+self.rotation.quaternion.shape))
|
||||
One dimension (length according to number of related orientations)
|
||||
is added to the left of the Rotation array.
|
||||
|
||||
r = np.broadcast_to(self.rotation.quaternion,s.shape[:1]+self.rotation.quaternion.shape)
|
||||
r = Rotation(r)
|
||||
"""
|
||||
o = Rotation.from_matrix(self.lattice.relation_operations(model)['rotations']).as_quaternion()
|
||||
o = o.reshape(o.shape[:1]+(1,)*len(self.rotation.shape)+(4,))
|
||||
o = Rotation(np.broadcast_to(o,o.shape[:1]+self.rotation.quaternion.shape))
|
||||
|
||||
return self.__class__(s@r,h['lattice'])
|
||||
s = np.broadcast_to(self.rotation.quaternion,o.shape[:1]+self.rotation.quaternion.shape)
|
||||
|
||||
|
||||
def relatedOrientations(self,model):
|
||||
"""List of orientations related by the given orientation relationship."""
|
||||
r = self.lattice.relationOperations(model)
|
||||
return [self.__class__(o*self.rotation,r['lattice']) for o in r['rotations']]
|
||||
return self.__class__(o@Rotation(s),self.lattice.relation_operations(model)['lattice'])
|
||||
|
||||
|
||||
@property
|
||||
|
@ -136,26 +132,31 @@ class Orientation: # ToDo: make subclass of lattice and Rotation?
|
|||
found = np.zeros_like(in_FZ[0],dtype=bool)
|
||||
q = self.rotation.quaternion[0]
|
||||
for s in range(in_FZ.shape[0]):
|
||||
#something fishy... why does q needs to be initialized?
|
||||
q = np.where(np.expand_dims(np.logical_and(in_FZ[s],~found),-1),eq.rotation.quaternion[s],q)
|
||||
found = np.logical_or(in_FZ[s],found)
|
||||
|
||||
return self.__class__(q,self.lattice)
|
||||
|
||||
|
||||
# ToDo: vectorize
|
||||
def inversePole(self,
|
||||
axis,
|
||||
proper = False,
|
||||
SST = True):
|
||||
def inverse_pole(self,axis,proper=False,SST=True):
|
||||
"""Axis rotated according to orientation (using crystal symmetry to ensure location falls into SST)."""
|
||||
if SST: # pole requested to be within SST
|
||||
for i,o in enumerate(self.equivalent): # test all symmetric equivalent quaternions
|
||||
pole = o.rotation@axis # align crystal direction to axis
|
||||
if self.lattice.in_SST(pole,proper): break # found SST version
|
||||
else:
|
||||
pole = self.rotation@axis # align crystal direction to axis
|
||||
if SST:
|
||||
eq = self.equivalent
|
||||
pole = eq.rotation @ np.broadcast_to(axis/np.linalg.norm(axis),eq.rotation.shape+(3,))
|
||||
in_SST = self.lattice.in_SST(pole,proper=proper)
|
||||
|
||||
# remove duplicates (occur for highly symmetric orientations)
|
||||
found = np.zeros_like(in_SST[0],dtype=bool)
|
||||
p = pole[0]
|
||||
for s in range(in_SST.shape[0]):
|
||||
p = np.where(np.expand_dims(np.logical_and(in_SST[s],~found),-1),pole[s],p)
|
||||
found = np.logical_or(in_SST[s],found)
|
||||
|
||||
return p
|
||||
else:
|
||||
return self.rotation @ np.broadcast_to(axis/np.linalg.norm(axis),self.rotation.shape+(3,))
|
||||
|
||||
return (pole,i if SST else 0)
|
||||
|
||||
|
||||
def IPF_color(self,axis): #ToDo axis or direction?
|
||||
|
@ -166,7 +167,7 @@ class Orientation: # ToDo: make subclass of lattice and Rotation?
|
|||
|
||||
# remove duplicates (occur for highly symmetric orientations)
|
||||
found = np.zeros_like(in_SST[0],dtype=bool)
|
||||
c = np.empty(color.shape[1:])
|
||||
c = color[0]
|
||||
for s in range(in_SST.shape[0]):
|
||||
c = np.where(np.expand_dims(np.logical_and(in_SST[s],~found),-1),color[s],c)
|
||||
found = np.logical_or(in_SST[s],found)
|
||||
|
|
|
@ -28,6 +28,22 @@ def reference_dir(reference_dir_base):
|
|||
|
||||
class TestOrientation:
|
||||
|
||||
@pytest.mark.parametrize('model',['Bain','KS','GT','GT_prime','NW','Pitsch'])
|
||||
@pytest.mark.parametrize('lattice',['fcc','bcc'])
|
||||
def test_relationship_vectorize(self,set_of_quaternions,lattice,model):
|
||||
result = Orientation(set_of_quaternions[:200].reshape(50,4,4),lattice).related(model)
|
||||
ref_qu = result.rotation.quaternion.reshape(-1,200,4)
|
||||
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)
|
||||
def test_IPF_vectorize(self,set_of_quaternions,lattice):
|
||||
direction = np.random.random(3)*2.0-1
|
||||
oris = Orientation(Rotation(set_of_quaternions),lattice)[:200]
|
||||
for i,color in enumerate(oris.IPF_color(direction)):
|
||||
assert np.allclose(color,IPF_color(oris[i],direction))
|
||||
|
||||
@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':'blue', 'RGB':[0,0,1],'direction':[1,1,1]}])
|
||||
|
@ -45,15 +61,6 @@ class TestOrientation:
|
|||
for equivalent in ori.equivalent:
|
||||
assert np.allclose(color,equivalent.IPF_color(direction))
|
||||
|
||||
|
||||
@pytest.mark.parametrize('lattice',Lattice.lattices)
|
||||
def test_IPF_vectorize(self,set_of_quaternions,lattice):
|
||||
direction = np.random.random(3)*2.0-1
|
||||
oris = Orientation(Rotation(set_of_quaternions),lattice)[:200]
|
||||
for i,color in enumerate(oris.IPF_color(direction)):
|
||||
assert np.allclose(color,IPF_color(oris[i],direction))
|
||||
|
||||
|
||||
@pytest.mark.parametrize('lattice',Lattice.lattices)
|
||||
def test_reduced(self,set_of_quaternions,lattice):
|
||||
oris = Orientation(Rotation(set_of_quaternions),lattice)
|
||||
|
@ -65,8 +72,8 @@ class TestOrientation:
|
|||
@pytest.mark.parametrize('lattice',['fcc','bcc'])
|
||||
def test_relationship_forward_backward(self,model,lattice):
|
||||
ori = Orientation(Rotation.from_random(),lattice)
|
||||
for i,r in enumerate(ori.relatedOrientations(model)):
|
||||
ori2 = r.relatedOrientations(model)[i]
|
||||
for i,r in enumerate(ori.related(model)):
|
||||
ori2 = r.related(model)[i]
|
||||
misorientation = ori.rotation.misorientation(ori2.rotation)
|
||||
assert misorientation.asAxisAngle(degrees=True)[3]<1.0e-5
|
||||
|
||||
|
@ -75,7 +82,7 @@ class TestOrientation:
|
|||
def test_relationship_reference(self,update,reference_dir,model,lattice):
|
||||
reference = os.path.join(reference_dir,'{}_{}.txt'.format(lattice,model))
|
||||
ori = Orientation(Rotation(),lattice)
|
||||
eu = np.array([o.rotation.as_Eulers(degrees=True) for o in ori.relatedOrientations(model)])
|
||||
eu = np.array([o.rotation.as_Eulers(degrees=True) for o in ori.related(model)])
|
||||
if update:
|
||||
coords = np.array([(1,i+1) for i,x in enumerate(eu)])
|
||||
table = damask.Table(eu,{'Eulers':(3,)})
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
import pytest
|
||||
import numpy as np
|
||||
|
||||
from damask import Rotation
|
||||
from damask import Orientation
|
||||
from damask import Lattice
|
||||
|
||||
rot0= Rotation.from_random()
|
||||
rot1= Rotation.from_random()
|
||||
rot2= Rotation.from_random()
|
||||
rot3= Rotation.from_random()
|
||||
|
||||
#disorientation
|
||||
|
||||
#fromaverage
|
||||
#average
|
||||
|
||||
class TestOrientation_vec:
|
||||
|
||||
|
||||
@pytest.mark.parametrize('model',['Bain','KS','GT','GT_prime','NW','Pitsch'])
|
||||
@pytest.mark.parametrize('lattice',['fcc','bcc'])
|
||||
def test_relatedOrientations_vec(self,model,lattice):
|
||||
ori0=Orientation(rot0,lattice)
|
||||
ori1=Orientation(rot1,lattice)
|
||||
ori2=Orientation(rot2,lattice)
|
||||
ori3=Orientation(rot3,lattice)
|
||||
|
||||
quat=np.array([rot0.as_quaternion(),rot1.as_quaternion(),rot2.as_quaternion(),rot3.as_quaternion()])
|
||||
ori_vec=Orientation(quat,lattice)
|
||||
|
||||
|
||||
for s in range(len(ori1.lattice.relationOperations(model)['rotations'])):
|
||||
assert all(ori_vec.relatedOrientations_vec(model).rotation.as_Eulers()[s,0] == \
|
||||
ori0.relatedOrientations(model)[s].rotation.as_Eulers())
|
||||
assert all(ori_vec.relatedOrientations_vec(model).rotation.as_quaternion()[s,1] == \
|
||||
ori1.relatedOrientations(model)[s].rotation.as_quaternion())
|
||||
assert all(ori_vec.relatedOrientations_vec(model).rotation.as_Rodrigues()[s,2] == \
|
||||
ori2.relatedOrientations(model)[s].rotation.as_Rodrigues())
|
||||
assert all(ori_vec.relatedOrientations_vec(model).rotation.as_cubochoric()[s,3] == \
|
||||
ori3.relatedOrientations(model)[s].rotation.as_cubochoric())
|
Loading…
Reference in New Issue