fast reduced operation

This commit is contained in:
Martin Diehl 2020-07-01 00:37:02 +02:00
parent 23365660d8
commit de8e9b5fc1
4 changed files with 30 additions and 49 deletions

View File

@ -3,7 +3,7 @@ import numpy as np
from . import Lattice from . import Lattice
from . import Rotation from . import Rotation
class Orientation: # ToDo: make subclass of lattice and Rotation class Orientation: # ToDo: make subclass of lattice and Rotation?
""" """
Crystallographic orientation. Crystallographic orientation.
@ -44,6 +44,7 @@ class Orientation: # ToDo: make subclass of lattice and Rotation
return self.__class__(self.rotation[item],self.lattice) return self.__class__(self.rotation[item],self.lattice)
# ToDo: Discuss vectorization/calling signature
def disorientation(self, def disorientation(self,
other, other,
SST = True, SST = True,
@ -80,17 +81,19 @@ class Orientation: # ToDo: make subclass of lattice and Rotation
# ... own sym, other sym, # ... own sym, other sym,
# self-->other: True, self<--other: False # self-->other: True, self<--other: False
@property
def in_FZ(self): def in_FZ(self):
"""Check if orientations fall into Fundamental Zone.""" """Check if orientations fall into Fundamental Zone."""
return self.lattice.in_FZ(self.rotation.as_Rodrigues(vector=True)) return self.lattice.in_FZ(self.rotation.as_Rodrigues(vector=True))
@property @property
def equivalent(self): def equivalent(self):
""" """
Return orientations which are symmetrically equivalent. Return orientations which are symmetrically equivalent.
One dimension (length according to symmetrically equivalent orientations) One dimension (length according to symmetrically equivalent orientations)
is added to the left of the rotation array. is added to the left of the Rotation array.
""" """
s = self.lattice.symmetry.symmetry_operations s = self.lattice.symmetry.symmetry_operations
@ -98,9 +101,8 @@ class Orientation: # ToDo: make subclass of lattice and Rotation
s = Rotation(np.broadcast_to(s,s.shape[:1]+self.rotation.quaternion.shape)) s = Rotation(np.broadcast_to(s,s.shape[:1]+self.rotation.quaternion.shape))
r = np.broadcast_to(self.rotation.quaternion,s.shape[:1]+self.rotation.quaternion.shape) r = np.broadcast_to(self.rotation.quaternion,s.shape[:1]+self.rotation.quaternion.shape)
r = Rotation(r)
return self.__class__(s@r,self.lattice) return self.__class__(s@Rotation(r),self.lattice)
def relatedOrientations_vec(self,model): def relatedOrientations_vec(self,model):
@ -123,34 +125,24 @@ class Orientation: # ToDo: make subclass of lattice and Rotation
r = self.lattice.relationOperations(model) r = self.lattice.relationOperations(model)
return [self.__class__(o*self.rotation,r['lattice']) for o in r['rotations']] return [self.__class__(o*self.rotation,r['lattice']) for o in r['rotations']]
@property @property
def reduced_vec(self):
"""Transform orientation to fall into fundamental zone according to symmetry."""
equi= self.equivalent.rotation #equivalent orientations
r= 1 if not self.rotation.shape else equi.shape[1] #number of rotations
num_equi=equi.shape[0] #number of equivalente orientations
quat= np.reshape( equi.as_quaternion(), (r*num_equi,4) ,order='F') #equivalents are listed in intiuitive order
boolean=Orientation(quat, self.lattice).in_FZ() #check which ones are in FZ
if sum(boolean) == r:
return self.__class__(quat[boolean],self.lattice)
else:
print('More than 1 equivalent orientation has been found for an orientation')
index=np.empty(r, dtype=int)
for l,h in enumerate(range(0,r*num_equi, num_equi)):
index[l]=np.where(boolean[h:h+num_equi])[0][0] + (l*num_equi) #get first index that is true then go check to next orientation
return self.__class__(quat[index],self.lattice)
def reduced(self): def reduced(self):
"""Transform orientation to fall into fundamental zone according to symmetry.""" """Transform orientation to fall into fundamental zone according to symmetry."""
for me in self.equivalent: eq = self.equivalent
if self.lattice.in_FZ(me.rotation.as_Rodrigues(vector=True)): break in_FZ = eq.in_FZ
return self.__class__(me.rotation,self.lattice) # remove duplicates (occur for highly symmetric orientations)
found = np.zeros_like(in_FZ[0],dtype=bool)
q = self.rotation.quaternion[0]
for s in range(in_FZ.shape[0]):
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, def inversePole(self,
axis, axis,
proper = False, proper = False,
@ -159,7 +151,7 @@ class Orientation: # ToDo: make subclass of lattice and Rotation
if SST: # pole requested to be within SST if SST: # pole requested to be within SST
for i,o in enumerate(self.equivalent): # test all symmetric equivalent quaternions for i,o in enumerate(self.equivalent): # test all symmetric equivalent quaternions
pole = o.rotation@axis # align crystal direction to axis pole = o.rotation@axis # align crystal direction to axis
if self.lattice.in_SST(pole,proper): break # found SST version if self.lattice.in_SST(pole,proper): break # found SST version
else: else:
pole = self.rotation@axis # align crystal direction to axis pole = self.rotation@axis # align crystal direction to axis
@ -172,7 +164,7 @@ class Orientation: # ToDo: make subclass of lattice and Rotation
pole = eq.rotation @ np.broadcast_to(axis/np.linalg.norm(axis),eq.rotation.shape+(3,)) pole = eq.rotation @ np.broadcast_to(axis/np.linalg.norm(axis),eq.rotation.shape+(3,))
in_SST, color = self.lattice.in_SST(pole,color=True) in_SST, color = self.lattice.in_SST(pole,color=True)
# ignore duplicates (occur for highly symmetric orientations) # remove duplicates (occur for highly symmetric orientations)
found = np.zeros_like(in_SST[0],dtype=bool) found = np.zeros_like(in_SST[0],dtype=bool)
c = np.empty(color.shape[1:]) c = np.empty(color.shape[1:])
for s in range(in_SST.shape[0]): for s in range(in_SST.shape[0]):
@ -182,6 +174,7 @@ class Orientation: # ToDo: make subclass of lattice and Rotation
return c return c
# ToDo: Discuss vectorization/calling signature
@staticmethod @staticmethod
def fromAverage(orientations, def fromAverage(orientations,
weights = []): weights = []):
@ -202,6 +195,7 @@ class Orientation: # ToDo: make subclass of lattice and Rotation
return Orientation(Rotation.fromAverage(closest,weights),ref.lattice) return Orientation(Rotation.fromAverage(closest,weights),ref.lattice)
# ToDo: Discuss vectorization/calling signature
def average(self,other): def average(self,other):
"""Calculate the average rotation.""" """Calculate the average rotation."""
return Orientation.fromAverage([self,other]) return Orientation.fromAverage([self,other])

View File

@ -54,6 +54,13 @@ class TestOrientation:
assert np.allclose(color,IPF_color(oris[i],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)
reduced = oris.reduced
assert np.all(reduced.in_FZ) and oris.rotation.shape == reduced.rotation.shape
@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',['fcc','bcc'])
def test_relationship_forward_backward(self,model,lattice): def test_relationship_forward_backward(self,model,lattice):

View File

@ -161,7 +161,7 @@ class TestResult:
crystal_structure = default.get_crystal_structure() crystal_structure = default.get_crystal_structure()
in_memory = np.empty((qu.shape[0],3),np.uint8) in_memory = np.empty((qu.shape[0],3),np.uint8)
for i,q in enumerate(qu): for i,q in enumerate(qu):
o = damask.Orientation(q,crystal_structure).reduced() o = damask.Orientation(q,crystal_structure).reduced
in_memory[i] = np.uint8(o.IPF_color(np.array(d))*255) in_memory[i] = np.uint8(o.IPF_color(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)

View File

@ -39,23 +39,3 @@ class TestOrientation_vec:
ori2.relatedOrientations(model)[s].rotation.as_Rodrigues()) ori2.relatedOrientations(model)[s].rotation.as_Rodrigues())
assert all(ori_vec.relatedOrientations_vec(model).rotation.as_cubochoric()[s,3] == \ assert all(ori_vec.relatedOrientations_vec(model).rotation.as_cubochoric()[s,3] == \
ori3.relatedOrientations(model)[s].rotation.as_cubochoric()) ori3.relatedOrientations(model)[s].rotation.as_cubochoric())
@pytest.mark.parametrize('lattice',Lattice.lattices)
def test_reduced_vec(self,lattice):
ori0=Orientation(rot0,lattice)
ori1=Orientation(rot1,lattice)
ori2=Orientation(rot2,lattice)
ori3=Orientation(rot3,lattice)
#ensure 1 of them is in FZ
ori4=ori0.reduced()
rot4=ori4.rotation
quat=np.array([rot0.as_quaternion(),rot1.as_quaternion(),\
rot2.as_quaternion(),rot3.as_quaternion(), rot4.as_quaternion()])
ori_vec=Orientation(quat,lattice)
assert all(ori_vec.reduced_vec.rotation.as_Eulers()[0] == ori0.reduced().rotation.as_Eulers() )
assert all(ori_vec.reduced_vec.rotation.as_quaternion()[1] == ori1.reduced().rotation.as_quaternion() )
assert all(ori_vec.reduced_vec.rotation.as_Rodrigues()[2] == ori2.reduced().rotation.as_Rodrigues() )
assert all(ori_vec.reduced_vec.rotation.as_cubochoric()[3] == ori3.reduced().rotation.as_cubochoric() )
assert all(ori_vec.reduced_vec.rotation.as_axis_angle()[4] == ori4.reduced().rotation.as_axis_angle() )