more user friendly constructors

This commit is contained in:
Martin Diehl 2019-02-12 07:55:54 +01:00
parent ef3fc0b58a
commit 97ac437686
5 changed files with 44 additions and 30 deletions

@ -1 +1 @@
Subproject commit 25006bc974b752faf3464b082511590d50093c37 Subproject commit 406d482f8059b4459634af729ce85491a9a3245c

View File

@ -79,8 +79,8 @@ parser.add_option('-z',
help = 'label of lab z vector (expressed in crystal coords)') help = 'label of lab z vector (expressed in crystal coords)')
parser.set_defaults(output = [], parser.set_defaults(output = [],
labrotation = (0.,1.,1.,1.), # no rotation about 1,1,1 labrotation = (0.,1.,0.,0.), # no rotation about 1,0,0
crystalrotation = (0.,1.,1.,1.), # no rotation about 1,1,1 crystalrotation = (0.,1.,0.,0.), # no rotation about 1,0,0
degrees = False, degrees = False,
) )
@ -108,8 +108,8 @@ if np.sum(input) != 1: parser.error('needs exactly one input format.')
(options.quaternion,4,'quaternion'), (options.quaternion,4,'quaternion'),
][np.where(input)[0][0]] # select input label that was requested ][np.where(input)[0][0]] # select input label that was requested
r = damask.Rotation.fromAngleAxis(np.array(options.crystalrotation),options.degrees) # crystal frame rotation r = damask.Rotation.fromAngleAxis(options.crystalrotation,options.degrees) # crystal frame rotation
R = damask.Rotation.fromAngleAxis(np.array(options.labrotation),options.degrees) # lab frame rotation R = damask.Rotation.fromAngleAxis(options.labrotation,options.degrees) # lab frame rotation
# --- loop over input files ------------------------------------------------------------------------ # --- loop over input files ------------------------------------------------------------------------
@ -153,13 +153,13 @@ for name in filenames:
outputAlive = True outputAlive = True
while outputAlive and table.data_read(): # read next data line of ASCII table while outputAlive and table.data_read(): # read next data line of ASCII table
if inputtype == 'eulers': if inputtype == 'eulers':
o = damask.Rotation.fromEulers(np.array(list(map(float,table.data[column:column+3]))),options.degrees) o = damask.Rotation.fromEulers(list(map(float,table.data[column:column+3])),options.degrees)
elif inputtype == 'rodrigues': elif inputtype == 'rodrigues':
o = damask.Rotation.fromRodrigues(np.array(list(map(float,table.data[column:column+3])))) o = damask.Rotation.fromRodrigues(list(map(float,table.data[column:column+3])))
elif inputtype == 'matrix': elif inputtype == 'matrix':
o = damask.Rotation.fromMatrix(np.array(list(map(float,table.data[column:column+9]))).reshape(3,3)) o = damask.Rotation.fromMatrix(list(map(float,table.data[column:column+9])).reshape(3,3))
elif inputtype == 'frame': elif inputtype == 'frame':
M = np.array(list(map(float,table.data[column[0]:column[0]+3] + \ M = np.array(list(map(float,table.data[column[0]:column[0]+3] + \
@ -168,7 +168,7 @@ for name in filenames:
o = damask.Rotation.fromMatrix(M/np.linalg.norm(M,axis=0)) o = damask.Rotation.fromMatrix(M/np.linalg.norm(M,axis=0))
elif inputtype == 'quaternion': elif inputtype == 'quaternion':
o = damask.Rotation.fromQuaternion(np.array(list(map(float,table.data[column:column+4])))) o = damask.Rotation.fromQuaternion(list(map(float,table.data[column:column+4])))
o= r*o*R # apply additional lab and crystal frame rotations o= r*o*R # apply additional lab and crystal frame rotations

View File

@ -1,7 +1,7 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# -*- coding: UTF-8 no BOM -*- # -*- coding: UTF-8 no BOM -*-
import os,sys,math import os,sys
import numpy as np import numpy as np
from optparse import OptionParser from optparse import OptionParser
import damask import damask
@ -29,9 +29,9 @@ parser.add_option('-r', '--rotation',
parser.add_option('--degrees', parser.add_option('--degrees',
dest = 'degrees', dest = 'degrees',
action = 'store_true', action = 'store_true',
help = 'angles are given in degrees [%default]') help = 'angle is given in degrees [%default]')
parser.set_defaults(rotation = (0.,1.,1.,1.), # no rotation about 1,1,1 parser.set_defaults(rotation = (0.,1.,0.,0.), # no rotation about 1,0,0
degrees = False, degrees = False,
) )
@ -40,7 +40,7 @@ parser.set_defaults(rotation = (0.,1.,1.,1.),
if options.data is None: if options.data is None:
parser.error('no data column specified.') parser.error('no data column specified.')
r = damask.Rotation.fromAngleAxis(options.rotation,degrees) r = damask.Rotation.fromAngleAxis(options.rotation,options.degrees,normalise=True)
# --- loop over input files ------------------------------------------------------------------------- # --- loop over input files -------------------------------------------------------------------------

View File

@ -63,9 +63,9 @@ parser.set_defaults(center = (.0,.0,.0),
if options.dimension is None: if options.dimension is None:
parser.error('no dimension specified.') parser.error('no dimension specified.')
if options.angleaxis is not None: if options.angleaxis is not None:
rotation = damask.Rotation.fromAngleAxis(np.array(options.angleaxis),options.degrees,normalise=True) rotation = damask.Rotation.fromAngleAxis(options.angleaxis,options.degrees,normalise=True)
elif options.quaternion is not None: elif options.quaternion is not None:
rotation = damask.Rotation.fromQuaternion(np.array(options.quaternion)) rotation = damask.Rotation.fromQuaternion(options.quaternion)
else: else:
rotation = damask.Rotation() rotation = damask.Rotation()

View File

@ -236,7 +236,12 @@ class Rotation:
__slots__ = ['quaternion'] __slots__ = ['quaternion']
def __init__(self,quaternion = np.array([1.0,0.0,0.0,0.0])): def __init__(self,quaternion = np.array([1.0,0.0,0.0,0.0])):
"""Initializes to identity unless specified""" """
Initializes to identity unless specified
If a quaternion is given, it needs to comply with the convection. Use .fromQuaternion
to check the input.
"""
self.quaternion = Quaternion2(q=quaternion[0],p=quaternion[1:4]) self.quaternion = Quaternion2(q=quaternion[0],p=quaternion[1:4])
self.quaternion.homomorph() # ToDo: Needed? self.quaternion.homomorph() # ToDo: Needed?
@ -248,6 +253,8 @@ class Rotation:
'Bunge Eulers / deg: {}'.format('\t'.join(list(map(str,self.asEulers(degrees=True)))) ), 'Bunge Eulers / deg: {}'.format('\t'.join(list(map(str,self.asEulers(degrees=True)))) ),
]) ])
################################################################################################
# convert to different orientation representations (numpy arrays)
def asQuaternion(self): def asQuaternion(self):
return self.quaternion.asArray() return self.quaternion.asArray()
@ -277,13 +284,15 @@ class Rotation:
return qu2cu(self.quaternion.asArray()) return qu2cu(self.quaternion.asArray())
################################################################################################
# static constructors. The input data needs to follow the convention, options allow to
# relax these convections
@classmethod @classmethod
def fromQuaternion(cls, def fromQuaternion(cls,
quaternion, quaternion,
P = -1): P = -1):
qu = quaternion qu = quaternion if isinstance(quaternion, np.ndarray) else np.array(quaternion)
if P > 0: qu[1:4] *= -1 # convert from P=1 to P=-1 if P > 0: qu[1:4] *= -1 # convert from P=1 to P=-1
if qu[0] < 0.0: if qu[0] < 0.0:
raise ValueError('Quaternion has negative first component.\n{}'.format(qu[0])) raise ValueError('Quaternion has negative first component.\n{}'.format(qu[0]))
@ -297,7 +306,8 @@ class Rotation:
eulers, eulers,
degrees = False): degrees = False):
eu = np.radians(eulers) if degrees else eulers eu = eulers if isinstance(eulers, np.ndarray) else np.array(eulers)
eu = np.radians(eu) if degrees else eu
if np.any(eu < 0.0) or np.any(eu > 2.0*np.pi) or eu[1] > np.pi: if np.any(eu < 0.0) or np.any(eu > 2.0*np.pi) or eu[1] > np.pi:
raise ValueError('Euler angles outside of [0..2π],[0..π],[0..2π].\n{} {} {}.'.format(*eu)) raise ValueError('Euler angles outside of [0..2π],[0..π],[0..2π].\n{} {} {}.'.format(*eu))
@ -310,7 +320,7 @@ class Rotation:
normalise = False, normalise = False,
P = -1): P = -1):
ax = angleAxis ax = angleAxis if isinstance(angleAxis, np.ndarray) else np.array(angleAxis)
if P > 0: ax[1:4] *= -1 # convert from P=1 to P=-1 if P > 0: ax[1:4] *= -1 # convert from P=1 to P=-1
if degrees: ax[0] = np.degrees(ax[0]) if degrees: ax[0] = np.degrees(ax[0])
if normalise: ax[1:4] /=np.linalg.norm(ax[1:4]) if normalise: ax[1:4] /=np.linalg.norm(ax[1:4])
@ -323,9 +333,13 @@ class Rotation:
@classmethod @classmethod
def fromMatrix(cls, def fromMatrix(cls,
matrix): matrix,
containsStretch = False): #ToDo: better name?
om = matrix om = matrix if isinstance(matrix, np.ndarray) else np.array(matrix)
if containsStretch:
(U,S,Vh) = np.linalg.svd(om) # singular value decomposition
om = np.dot(U,Vh)
if not np.isclose(np.linalg.det(om),1.0): if not np.isclose(np.linalg.det(om),1.0):
raise ValueError('matrix is not a proper rotation.\n{}'.format(om)) raise ValueError('matrix is not a proper rotation.\n{}'.format(om))
if not np.isclose(np.dot(om[0],om[1]), 0.0) \ if not np.isclose(np.dot(om[0],om[1]), 0.0) \
@ -341,7 +355,7 @@ class Rotation:
normalise = False, normalise = False,
P = -1): P = -1):
ro = rodrigues ro = rodrigues if isinstance(rodrigues, np.ndarray) else np.array(rodrigues)
if P > 0: ro[1:4] *= -1 # convert from P=1 to P=-1 if P > 0: ro[1:4] *= -1 # convert from P=1 to P=-1
if normalise: ro[1:4] /=np.linalg.norm(ro[1:4]) if normalise: ro[1:4] /=np.linalg.norm(ro[1:4])
if not np.isclose(np.linalg.norm(ro[1:4]), 1.0): if not np.isclose(np.linalg.norm(ro[1:4]), 1.0):
@ -356,7 +370,7 @@ class Rotation:
""" """
Multiplication Multiplication
Rotation: Details needed (active/passive), more cases (3,3), (3,3,3,3) need to be considered Rotation: Details needed (active/passive), more rotation of (3,3,3,3) should be considered
""" """
if isinstance(other, Rotation): # rotate a rotation if isinstance(other, Rotation): # rotate a rotation
return self.__class__((self.quaternion * other.quaternion).asArray()) return self.__class__((self.quaternion * other.quaternion).asArray())