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)')
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

View File

@ -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 -------------------------------------------------------------------------

View File

@ -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()

View File

@ -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())