adopted more intutitive alternative of P=-1 from Rowenhorst_etal2015
This commit is contained in:
parent
7bb862f84e
commit
1d7172c971
|
@ -33,7 +33,7 @@ class Quaternion:
|
||||||
All methods and naming conventions based on Rowenhorst_etal2015
|
All methods and naming conventions based on Rowenhorst_etal2015
|
||||||
Convention 1: coordinate frames are right-handed
|
Convention 1: coordinate frames are right-handed
|
||||||
Convention 2: a rotation angle ω is taken to be positive for a counterclockwise rotation
|
Convention 2: a rotation angle ω is taken to be positive for a counterclockwise rotation
|
||||||
when viewing from the end point of the rotation axis unit vector towards the origin
|
when viewing from the end point of the rotation axis towards the origin
|
||||||
Convention 3: rotations will be interpreted in the passive sense
|
Convention 3: rotations will be interpreted in the passive sense
|
||||||
Convention 4: Euler angle triplets are implemented using the Bunge convention,
|
Convention 4: Euler angle triplets are implemented using the Bunge convention,
|
||||||
with the angular ranges as [0, 2π],[0, π],[0, 2π]
|
with the angular ranges as [0, 2π],[0, π],[0, 2π]
|
||||||
|
@ -49,11 +49,11 @@ class Quaternion:
|
||||||
|
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
quatArray = [1.0,0.0,0.0,0.0]):
|
quatArray = [1.0,0.0,0.0,0.0]):
|
||||||
"""Initializes to identity if not given"""
|
"""Initializes to identity unless specified"""
|
||||||
self.w, \
|
(self.w,
|
||||||
self.x, \
|
self.x,
|
||||||
self.y, \
|
self.y,
|
||||||
self.z = quatArray
|
self.z ) = quatArray
|
||||||
self.homomorph()
|
self.homomorph()
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
|
@ -61,16 +61,15 @@ class Quaternion:
|
||||||
return iter([self.w,self.x,self.y,self.z])
|
return iter([self.w,self.x,self.y,self.z])
|
||||||
|
|
||||||
def __copy__(self):
|
def __copy__(self):
|
||||||
"""Create copy"""
|
"""Copy"""
|
||||||
Q = Quaternion([self.w,self.x,self.y,self.z])
|
Q = Quaternion([self.w,self.x,self.y,self.z])
|
||||||
return Q
|
return Q
|
||||||
|
|
||||||
copy = __copy__
|
copy = __copy__
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
"""Readbable string"""
|
"""Readable string"""
|
||||||
return 'Quaternion(real=%+.6f, imag=<%+.6f, %+.6f, %+.6f>)' % \
|
return 'Quaternion(real=%+.6f, imag=<%+.6f, %+.6f, %+.6f>)' % (self.w, self.x, self.y, self.z)
|
||||||
(self.w, self.x, self.y, self.z)
|
|
||||||
|
|
||||||
def __pow__(self, exponent):
|
def __pow__(self, exponent):
|
||||||
"""Power"""
|
"""Power"""
|
||||||
|
@ -95,6 +94,8 @@ class Quaternion:
|
||||||
|
|
||||||
def __mul__(self, other):
|
def __mul__(self, other):
|
||||||
"""Multiplication"""
|
"""Multiplication"""
|
||||||
|
# Rowenhorst_etal2015 MSMSE: value of P is selected as -1
|
||||||
|
P = -1.0
|
||||||
try: # quaternion
|
try: # quaternion
|
||||||
Aw = self.w
|
Aw = self.w
|
||||||
Ax = self.x
|
Ax = self.x
|
||||||
|
@ -106,9 +107,9 @@ class Quaternion:
|
||||||
Bz = other.z
|
Bz = other.z
|
||||||
Q = Quaternion()
|
Q = Quaternion()
|
||||||
Q.w = - Ax * Bx - Ay * By - Az * Bz + Aw * Bw
|
Q.w = - Ax * Bx - Ay * By - Az * Bz + Aw * Bw
|
||||||
Q.x = + Ax * Bw + Ay * Bz - Az * By + Aw * Bx
|
Q.x = + Ax * Bw + Aw * Bx + P * (Ay * Bz - Az * By)
|
||||||
Q.y = - Ax * Bz + Ay * Bw + Az * Bx + Aw * By
|
Q.y = + Ay * Bw + Aw * By + P * (Az * Bx - Ax * Bz)
|
||||||
Q.z = + Ax * By - Ay * Bx + Az * Bw + Aw * Bz
|
Q.z = + Az * Bw + Aw * Bz + P * (Ax * By - Ay * Bx)
|
||||||
return Q
|
return Q
|
||||||
except: pass
|
except: pass
|
||||||
try: # vector (perform active rotation, i.e. q*v*q.conjugated)
|
try: # vector (perform active rotation, i.e. q*v*q.conjugated)
|
||||||
|
@ -120,16 +121,14 @@ class Quaternion:
|
||||||
Vy = other[1]
|
Vy = other[1]
|
||||||
Vz = other[2]
|
Vz = other[2]
|
||||||
|
|
||||||
return np.array([\
|
A = w**2 - x**2 - y**2 - z**2
|
||||||
w * w * Vx + 2 * y * w * Vz - 2 * z * w * Vy + \
|
B = 2.0*(x*Vx + y*Vy + z*Vz)
|
||||||
x * x * Vx + 2 * y * x * Vy + 2 * z * x * Vz - \
|
|
||||||
z * z * Vx - y * y * Vx,
|
return np.array([
|
||||||
2 * x * y * Vx + y * y * Vy + 2 * z * y * Vz + \
|
A*Vx + B*x + 2*P*w * (y*Vz - z*Vy),
|
||||||
2 * w * z * Vx - z * z * Vy + w * w * Vy - \
|
A*Vy + B*y + 2*P*w * (z*Vx - x*Vz),
|
||||||
2 * x * w * Vz - x * x * Vy,
|
A*Vz + B*z + 2*P*w * (x*Vy - y*Vx),
|
||||||
2 * x * z * Vx + 2 * y * z * Vy + \
|
])
|
||||||
z * z * Vz - 2 * w * y * Vx - y * y * Vz + \
|
|
||||||
2 * w * x * Vy - x * x * Vz + w * w * Vz ])
|
|
||||||
except: pass
|
except: pass
|
||||||
try: # scalar
|
try: # scalar
|
||||||
Q = self.copy()
|
Q = self.copy()
|
||||||
|
@ -143,6 +142,8 @@ class Quaternion:
|
||||||
|
|
||||||
def __imul__(self, other):
|
def __imul__(self, other):
|
||||||
"""In-place multiplication"""
|
"""In-place multiplication"""
|
||||||
|
# Rowenhorst_etal2015 MSMSE: value of P is selected as -1
|
||||||
|
P = -1.0
|
||||||
try: # Quaternion
|
try: # Quaternion
|
||||||
Aw = self.w
|
Aw = self.w
|
||||||
Ax = self.x
|
Ax = self.x
|
||||||
|
@ -153,9 +154,9 @@ class Quaternion:
|
||||||
By = other.y
|
By = other.y
|
||||||
Bz = other.z
|
Bz = other.z
|
||||||
self.w = - Ax * Bx - Ay * By - Az * Bz + Aw * Bw
|
self.w = - Ax * Bx - Ay * By - Az * Bz + Aw * Bw
|
||||||
self.x = + Ax * Bw + Ay * Bz - Az * By + Aw * Bx
|
self.x = + Ax * Bw + Aw * Bx + P * (Ay * Bz - Az * By)
|
||||||
self.y = - Ax * Bz + Ay * Bw + Az * Bx + Aw * By
|
self.y = + Ay * Bw + Aw * By + P * (Az * Bx - Ax * Bz)
|
||||||
self.z = + Ax * By - Ay * Bx + Az * Bw + Aw * Bz
|
self.z = + Az * Bw + Aw * Bz + P * (Ax * By - Ay * Bx)
|
||||||
except: pass
|
except: pass
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
@ -314,13 +315,15 @@ class Quaternion:
|
||||||
|
|
||||||
def asM(self): # to find Averaging Quaternions (see F. Landis Markley et al.)
|
def asM(self): # to find Averaging Quaternions (see F. Landis Markley et al.)
|
||||||
return np.outer([i for i in self],[i for i in self])
|
return np.outer([i for i in self],[i for i in self])
|
||||||
|
|
||||||
def asMatrix(self):
|
def asMatrix(self):
|
||||||
|
# Rowenhorst_etal2015 MSMSE: value of P is selected as -1
|
||||||
|
P = -1.0
|
||||||
qbarhalf = 0.5*(self.w**2 - self.x**2 - self.y**2 - self.z**2)
|
qbarhalf = 0.5*(self.w**2 - self.x**2 - self.y**2 - self.z**2)
|
||||||
return 2.0*np.array(
|
return 2.0*np.array(
|
||||||
[[ qbarhalf + self.x**2 , self.x*self.y - self.w*self.z, self.x*self.z + self.w*self.y],
|
[[ qbarhalf + self.x**2 , self.x*self.y -P* self.w*self.z, self.x*self.z +P* self.w*self.y],
|
||||||
[ self.x*self.y + self.w*self.z, qbarhalf + self.y**2 , self.y*self.z - self.w*self.x],
|
[ self.x*self.y +P* self.w*self.z, qbarhalf + self.y**2 , self.y*self.z -P* self.w*self.x],
|
||||||
[ self.x*self.z - self.w*self.y, self.y*self.z + self.w*self.x, qbarhalf + self.z**2 ],
|
[ self.x*self.z -P* self.w*self.y, self.y*self.z +P* self.w*self.x, qbarhalf + self.z**2 ],
|
||||||
])
|
])
|
||||||
|
|
||||||
def asAngleAxis(self,
|
def asAngleAxis(self,
|
||||||
|
@ -346,18 +349,20 @@ class Quaternion:
|
||||||
def asEulers(self,
|
def asEulers(self,
|
||||||
degrees = False):
|
degrees = False):
|
||||||
"""Orientation as Bunge-Euler angles."""
|
"""Orientation as Bunge-Euler angles."""
|
||||||
q03 = self.w**2+self.z**2
|
# Rowenhorst_etal2015 MSMSE: value of P is selected as -1
|
||||||
q12 = self.x**2+self.y**2
|
P = -1.0
|
||||||
|
q03 = self.w**2 + self.z**2
|
||||||
|
q12 = self.x**2 + self.y**2
|
||||||
chi = np.sqrt(q03*q12)
|
chi = np.sqrt(q03*q12)
|
||||||
|
|
||||||
if abs(chi) < 1e-10 and abs(q12) < 1e-10:
|
if abs(chi) < 1e-10 and abs(q12) < 1e-10:
|
||||||
eulers = np.array([math.atan2(-2*self.w*self.z,self.w**2-self.z**2),0,0])
|
eulers = np.array([math.atan2(-2*P*self.w*self.z,self.w**2-self.z**2),0,0])
|
||||||
elif abs(chi) < 1e-10 and abs(q03) < 1e-10:
|
elif abs(chi) < 1e-10 and abs(q03) < 1e-10:
|
||||||
eulers = np.array([math.atan2( 2*self.x*self.y,self.x**2-self.y**2),np.pi,0])
|
eulers = np.array([math.atan2( 2 *self.x*self.y,self.x**2-self.y**2),np.pi,0])
|
||||||
else:
|
else:
|
||||||
eulers = np.array([math.atan2((self.x*self.z-self.w*self.y)/chi,(-self.w*self.x-self.y*self.z)/chi),
|
eulers = np.array([math.atan2((self.x*self.z-P*self.w*self.y)/chi,(-P*self.w*self.x-self.y*self.z)/chi),
|
||||||
math.atan2(2*chi,q03-q12),
|
math.atan2(2*chi,q03-q12),
|
||||||
math.atan2((self.w*self.y+self.x*self.z)/chi,( self.y*self.z-self.w*self.x)/chi),
|
math.atan2((P*self.w*self.y+self.x*self.z)/chi,( self.y*self.z-P*self.w*self.x)/chi),
|
||||||
])
|
])
|
||||||
|
|
||||||
return np.degrees(eulers) if degrees else eulers
|
return np.degrees(eulers) if degrees else eulers
|
||||||
|
@ -371,8 +376,9 @@ class Quaternion:
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def fromRandom(cls,randomSeed = None):
|
def fromRandom(cls,randomSeed = None):
|
||||||
|
import binascii
|
||||||
if randomSeed is None:
|
if randomSeed is None:
|
||||||
randomSeed = int(os.urandom(4).encode('hex'), 16)
|
randomSeed = int(binascii.hexlify(os.urandom(4)),16)
|
||||||
np.random.seed(randomSeed)
|
np.random.seed(randomSeed)
|
||||||
r = np.random.random(3)
|
r = np.random.random(3)
|
||||||
w = math.cos(2.0*math.pi*r[0])*math.sqrt(r[2])
|
w = math.cos(2.0*math.pi*r[0])*math.sqrt(r[2])
|
||||||
|
@ -420,10 +426,12 @@ class Quaternion:
|
||||||
c = np.cos(0.5*eulers[1])
|
c = np.cos(0.5*eulers[1])
|
||||||
s = np.sin(0.5*eulers[1])
|
s = np.sin(0.5*eulers[1])
|
||||||
|
|
||||||
w = c * np.cos(sigma)
|
# Rowenhorst_etal2015 MSMSE: value of P is selected as -1
|
||||||
x = -s * np.cos(delta)
|
P = -1.0
|
||||||
y = -s * np.sin(delta)
|
w = c * np.cos(sigma)
|
||||||
z = -c * np.sin(sigma)
|
x = -P * s * np.cos(delta)
|
||||||
|
y = -P * s * np.sin(delta)
|
||||||
|
z = -P * c * np.sin(sigma)
|
||||||
return cls([w,x,y,z])
|
return cls([w,x,y,z])
|
||||||
|
|
||||||
|
|
||||||
|
@ -435,16 +443,18 @@ class Quaternion:
|
||||||
if m.shape != (3,3) and np.prod(m.shape) == 9:
|
if m.shape != (3,3) and np.prod(m.shape) == 9:
|
||||||
m = m.reshape(3,3)
|
m = m.reshape(3,3)
|
||||||
|
|
||||||
w = 0.5*math.sqrt(1.+m[0,0]+m[1,1]+m[2,2])
|
# Rowenhorst_etal2015 MSMSE: value of P is selected as -1
|
||||||
x = 0.5*math.sqrt(1.+m[0,0]-m[1,1]-m[2,2])
|
P = -1.0
|
||||||
y = 0.5*math.sqrt(1.-m[0,0]+m[1,1]-m[2,2])
|
w = 0.5*math.sqrt(1.+m[0,0]+m[1,1]+m[2,2])
|
||||||
z = 0.5*math.sqrt(1.-m[0,0]-m[1,1]+m[2,2])
|
x = P*0.5*math.sqrt(1.+m[0,0]-m[1,1]-m[2,2])
|
||||||
|
y = P*0.5*math.sqrt(1.-m[0,0]+m[1,1]-m[2,2])
|
||||||
|
z = P*0.5*math.sqrt(1.-m[0,0]-m[1,1]+m[2,2])
|
||||||
|
|
||||||
x *= -1 if m[2,1] < m[1,2] else 1
|
x *= -1 if m[2,1] < m[1,2] else 1
|
||||||
y *= -1 if m[0,2] < m[2,0] else 1
|
y *= -1 if m[0,2] < m[2,0] else 1
|
||||||
z *= -1 if m[1,0] < m[0,1] else 1
|
z *= -1 if m[1,0] < m[0,1] else 1
|
||||||
|
|
||||||
return cls( np.array([w,x,y,z])/math.sqrt(w**2 + x**2 + y**2 + z**2))
|
return cls(np.array([w,x,y,z])/math.sqrt(w**2 + x**2 + y**2 + z**2))
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
|
@ -43,7 +43,7 @@ parser.add_option('-e', '--eulers',
|
||||||
parser.add_option('-d', '--degrees',
|
parser.add_option('-d', '--degrees',
|
||||||
dest = 'degrees',
|
dest = 'degrees',
|
||||||
action = 'store_true',
|
action = 'store_true',
|
||||||
help = 'angles are given in degrees [%default]')
|
help = 'all angles are in degrees')
|
||||||
parser.add_option('-m', '--matrix',
|
parser.add_option('-m', '--matrix',
|
||||||
dest = 'matrix',
|
dest = 'matrix',
|
||||||
type = 'string', metavar = 'string',
|
type = 'string', metavar = 'string',
|
||||||
|
@ -71,7 +71,7 @@ parser.add_option('--axes',
|
||||||
parser.add_option('-s', '--symmetry',
|
parser.add_option('-s', '--symmetry',
|
||||||
dest = 'symmetry',
|
dest = 'symmetry',
|
||||||
action = 'extend', metavar = '<string LIST>',
|
action = 'extend', metavar = '<string LIST>',
|
||||||
help = 'crystal symmetry %default {{{}}} '.format(', '.join(damask.Symmetry.lattices[1:])))
|
help = 'crystal symmetry of each phase %default {{{}}} '.format(', '.join(damask.Symmetry.lattices[1:])))
|
||||||
parser.add_option('--homogenization',
|
parser.add_option('--homogenization',
|
||||||
dest = 'homogenization',
|
dest = 'homogenization',
|
||||||
type = 'int', metavar = 'int',
|
type = 'int', metavar = 'int',
|
||||||
|
@ -234,7 +234,7 @@ for name in filenames:
|
||||||
o = damask.Orientation(Eulers = myData[colOri:colOri+3]*toRadians,
|
o = damask.Orientation(Eulers = myData[colOri:colOri+3]*toRadians,
|
||||||
symmetry = mySym)
|
symmetry = mySym)
|
||||||
elif inputtype == 'matrix':
|
elif inputtype == 'matrix':
|
||||||
o = damask.Orientation(matrix = myData[colOri:colOri+9].reshape(3,3).transpose(),
|
o = damask.Orientation(matrix = myData[colOri:colOri+9].reshape(3,3),
|
||||||
symmetry = mySym)
|
symmetry = mySym)
|
||||||
elif inputtype == 'frame':
|
elif inputtype == 'frame':
|
||||||
o = damask.Orientation(matrix = np.hstack((myData[colOri[0]:colOri[0]+3],
|
o = damask.Orientation(matrix = np.hstack((myData[colOri[0]:colOri[0]+3],
|
||||||
|
@ -246,7 +246,7 @@ for name in filenames:
|
||||||
o = damask.Orientation(quaternion = myData[colOri:colOri+4],
|
o = damask.Orientation(quaternion = myData[colOri:colOri+4],
|
||||||
symmetry = mySym)
|
symmetry = mySym)
|
||||||
|
|
||||||
cos_disorientations = -np.ones(1,dtype='f') # largest possible disorientation
|
cos_disorientations = -np.ones(1,dtype=float) # largest possible disorientation
|
||||||
closest_grain = -1 # invalid neighbor
|
closest_grain = -1 # invalid neighbor
|
||||||
|
|
||||||
if options.tolerance > 0.0: # only try to compress orientations if asked to
|
if options.tolerance > 0.0: # only try to compress orientations if asked to
|
||||||
|
|
Loading…
Reference in New Issue