diff --git a/PRIVATE b/PRIVATE index 25006bc97..406d482f8 160000 --- a/PRIVATE +++ b/PRIVATE @@ -1 +1 @@ -Subproject commit 25006bc974b752faf3464b082511590d50093c37 +Subproject commit 406d482f8059b4459634af729ce85491a9a3245c diff --git a/processing/post/addOrientations.py b/processing/post/addOrientations.py index ec824c88c..65444bcb9 100755 --- a/processing/post/addOrientations.py +++ b/processing/post/addOrientations.py @@ -79,8 +79,8 @@ parser.add_option('-z', help = 'label of lab z vector (expressed in crystal coords)') parser.set_defaults(output = [], - labrotation = (0.,1.,1.,1.), # no rotation about 1,1,1 - crystalrotation = (0.,1.,1.,1.), # no rotation about 1,1,1 + labrotation = (0.,1.,0.,0.), # no rotation about 1,0,0 + crystalrotation = (0.,1.,0.,0.), # no rotation about 1,0,0 degrees = False, ) @@ -108,8 +108,8 @@ if np.sum(input) != 1: parser.error('needs exactly one input format.') (options.quaternion,4,'quaternion'), ][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(np.array(options.labrotation),options.degrees) # lab frame rotation +r = damask.Rotation.fromAngleAxis(options.crystalrotation,options.degrees) # crystal frame rotation +R = damask.Rotation.fromAngleAxis(options.labrotation,options.degrees) # lab frame rotation # --- loop over input files ------------------------------------------------------------------------ @@ -153,13 +153,13 @@ for name in filenames: outputAlive = True while outputAlive and table.data_read(): # read next data line of ASCII table 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': - 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': - 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': 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)) 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 diff --git a/processing/post/rotateData.py b/processing/post/rotateData.py index 3712b8c73..293f0f0b8 100755 --- a/processing/post/rotateData.py +++ b/processing/post/rotateData.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # -*- coding: UTF-8 no BOM -*- -import os,sys,math +import os,sys import numpy as np from optparse import OptionParser import damask @@ -29,9 +29,9 @@ parser.add_option('-r', '--rotation', parser.add_option('--degrees', dest = 'degrees', 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, ) @@ -40,7 +40,7 @@ parser.set_defaults(rotation = (0.,1.,1.,1.), if options.data is None: 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 ------------------------------------------------------------------------- diff --git a/processing/pre/geom_addPrimitive.py b/processing/pre/geom_addPrimitive.py index f93cdd54f..e0d1094cf 100755 --- a/processing/pre/geom_addPrimitive.py +++ b/processing/pre/geom_addPrimitive.py @@ -63,9 +63,9 @@ parser.set_defaults(center = (.0,.0,.0), if options.dimension is None: parser.error('no dimension specified.') 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: - rotation = damask.Rotation.fromQuaternion(np.array(options.quaternion)) + rotation = damask.Rotation.fromQuaternion(options.quaternion) else: rotation = damask.Rotation() diff --git a/python/damask/orientation.py b/python/damask/orientation.py index 54482332b..e53915b13 100644 --- a/python/damask/orientation.py +++ b/python/damask/orientation.py @@ -236,7 +236,12 @@ class Rotation: __slots__ = ['quaternion'] 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.homomorph() # ToDo: Needed? @@ -247,7 +252,9 @@ class Rotation: 'Matrix:\n{}'.format( '\n'.join(['\t'.join(list(map(str,self.asMatrix()[i,:]))) for i in range(3)]) ), 'Bunge Eulers / deg: {}'.format('\t'.join(list(map(str,self.asEulers(degrees=True)))) ), ]) - + + ################################################################################################ + # convert to different orientation representations (numpy arrays) def asQuaternion(self): return self.quaternion.asArray() @@ -276,14 +283,16 @@ class Rotation: def asCubochoric(self): return qu2cu(self.quaternion.asArray()) - - + + ################################################################################################ + # static constructors. The input data needs to follow the convention, options allow to + # relax these convections @classmethod def fromQuaternion(cls, - quaternion, - P = -1): + quaternion, + 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 qu[0] < 0.0: raise ValueError('Quaternion has negative first component.\n{}'.format(qu[0])) @@ -296,8 +305,9 @@ class Rotation: def fromEulers(cls, eulers, 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: raise ValueError('Euler angles outside of [0..2π],[0..π],[0..2π].\n{} {} {}.'.format(*eu)) @@ -309,8 +319,8 @@ class Rotation: degrees = False, normalise = False, 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 degrees: ax[0] = np.degrees(ax[0]) if normalise: ax[1:4] /=np.linalg.norm(ax[1:4]) @@ -323,9 +333,13 @@ class Rotation: @classmethod 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): raise ValueError('matrix is not a proper rotation.\n{}'.format(om)) if not np.isclose(np.dot(om[0],om[1]), 0.0) \ @@ -341,7 +355,7 @@ class Rotation: normalise = False, 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 normalise: ro[1:4] /=np.linalg.norm(ro[1:4]) if not np.isclose(np.linalg.norm(ro[1:4]), 1.0): @@ -356,7 +370,7 @@ class Rotation: """ 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 return self.__class__((self.quaternion * other.quaternion).asArray())