documenting (in accordance with new prospector rules)
This commit is contained in:
parent
a428a924eb
commit
3657f81c59
|
@ -10,35 +10,44 @@ from .quaternion import P
|
|||
####################################################################################################
|
||||
class Rotation:
|
||||
u"""
|
||||
Orientation stored as unit quaternion.
|
||||
|
||||
Following: D Rowenhorst et al. Consistent representations of and conversions between 3D rotations
|
||||
10.1088/0965-0393/23/8/083501
|
||||
Convention 1: coordinate frames are right-handed
|
||||
Convention 2: a rotation angle ω is taken to be positive for a counterclockwise rotation
|
||||
when viewing from the end point of the rotation axis towards the origin
|
||||
Convention 3: rotations will be interpreted in the passive sense
|
||||
Convention 4: Euler angle triplets are implemented using the Bunge convention,
|
||||
with the angular ranges as [0, 2π],[0, π],[0, 2π]
|
||||
Convention 5: the rotation angle ω is limited to the interval [0, π]
|
||||
Convention 6: P = -1 (as default)
|
||||
|
||||
q is the real part, p = (x, y, z) are the imaginary parts.
|
||||
Orientation stored with functionality for conversion to different representations.
|
||||
|
||||
References
|
||||
----------
|
||||
D. Rowenhorst et al., Modelling and Simulation in Materials Science and Engineering 23:083501, 2015
|
||||
https://doi.org/10.1088/0965-0393/23/8/083501
|
||||
|
||||
Conventions
|
||||
-----------
|
||||
Convention 1: Coordinate frames are right-handed.
|
||||
Convention 2: A rotation angle ω is taken to be positive for a counterclockwise rotation
|
||||
when viewing from the end point of the rotation axis towards the origin.
|
||||
Convention 3: Rotations will be interpreted in the passive sense.
|
||||
Convention 4: Euler angle triplets are implemented using the Bunge convention,
|
||||
with the angular ranges as [0, 2π],[0, π],[0, 2π].
|
||||
Convention 5: The rotation angle ω is limited to the interval [0, π].
|
||||
Convention 6: P = -1 (as default).
|
||||
|
||||
Usage
|
||||
-----
|
||||
Vector "a" (defined in coordinate system "A") is passively rotated
|
||||
resulting in new coordinates "b" when expressed in system "B".
|
||||
b = Q * a
|
||||
b = np.dot(Q.asMatrix(),a)
|
||||
|
||||
"""
|
||||
|
||||
__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.
|
||||
Parameters
|
||||
----------
|
||||
quaternion : numpy.ndarray, optional
|
||||
Unit quaternion that follows the conventions. Use .fromQuaternion to perform a sanity check.
|
||||
|
||||
"""
|
||||
if isinstance(quaternion,Quaternion):
|
||||
self.quaternion = quaternion.copy()
|
||||
|
@ -46,14 +55,14 @@ class Rotation:
|
|||
self.quaternion = Quaternion(q=quaternion[0],p=quaternion[1:4])
|
||||
|
||||
def __copy__(self):
|
||||
"""Copy"""
|
||||
"""Copy."""
|
||||
return self.__class__(self.quaternion)
|
||||
|
||||
copy = __copy__
|
||||
|
||||
|
||||
def __repr__(self):
|
||||
"""Value in selected representation"""
|
||||
"""Orientation displayed as unit quaternion, rotation matrix, and Bunge-Euler angles."""
|
||||
return '\n'.join([
|
||||
'{}'.format(self.quaternion),
|
||||
'Matrix:\n{}'.format( '\n'.join(['\t'.join(list(map(str,self.asMatrix()[i,:]))) for i in range(3)]) ),
|
||||
|
@ -62,9 +71,18 @@ class Rotation:
|
|||
|
||||
def __mul__(self, other):
|
||||
"""
|
||||
Multiplication
|
||||
Multiplication.
|
||||
|
||||
Rotation: Details needed (active/passive), rotation of (3,3,3,3)-matrix should be considered
|
||||
Parameters
|
||||
----------
|
||||
other : numpy.ndarray or Rotation
|
||||
Vector, second or fourth order tensor, or rotation object that is rotated.
|
||||
|
||||
Todo
|
||||
----
|
||||
Document details active/passive)
|
||||
considere rotation of (3,3,3,3)-matrix
|
||||
|
||||
"""
|
||||
if isinstance(other, Rotation): # rotate a rotation
|
||||
return self.__class__(self.quaternion * other.quaternion).standardize()
|
||||
|
@ -104,32 +122,48 @@ class Rotation:
|
|||
|
||||
|
||||
def inverse(self):
|
||||
"""In-place inverse rotation/backward rotation"""
|
||||
"""In-place inverse rotation/backward rotation."""
|
||||
self.quaternion.conjugate()
|
||||
return self
|
||||
|
||||
def inversed(self):
|
||||
"""Inverse rotation/backward rotation"""
|
||||
"""Inverse rotation/backward rotation."""
|
||||
return self.copy().inverse()
|
||||
|
||||
|
||||
def standardize(self):
|
||||
"""In-place quaternion representation with positive q"""
|
||||
"""In-place quaternion representation with positive q."""
|
||||
if self.quaternion.q < 0.0: self.quaternion.homomorph()
|
||||
return self
|
||||
|
||||
def standardized(self):
|
||||
"""Quaternion representation with positive q"""
|
||||
"""Quaternion representation with positive q."""
|
||||
return self.copy().standardize()
|
||||
|
||||
|
||||
def misorientation(self,other):
|
||||
"""Misorientation"""
|
||||
"""
|
||||
Get Misorientation.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
other : Rotation
|
||||
Rotation to which the misorientation is computed.
|
||||
|
||||
"""
|
||||
return other*self.inversed()
|
||||
|
||||
|
||||
def average(self,other):
|
||||
"""Calculate the average rotation"""
|
||||
"""
|
||||
Calculate the average rotation.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
other : Rotation
|
||||
Rotation from which the average is rotated.
|
||||
|
||||
"""
|
||||
return Rotation.fromAverage([self,other])
|
||||
|
||||
|
||||
|
@ -137,41 +171,75 @@ class Rotation:
|
|||
# convert to different orientation representations (numpy arrays)
|
||||
|
||||
def asQuaternion(self):
|
||||
"""Unit quaternion: (q, [p_1, p_2, p_3])"""
|
||||
"""Unit quaternion: (q, p_1, p_2, p_3)."""
|
||||
return self.quaternion.asArray()
|
||||
|
||||
def asEulers(self,
|
||||
degrees = False):
|
||||
"""Bunge-Euler angles: (φ_1, ϕ, φ_2)"""
|
||||
"""
|
||||
Bunge-Euler angles: (φ_1, ϕ, φ_2).
|
||||
|
||||
Parameters
|
||||
----------
|
||||
degrees : bool, optional
|
||||
return angles in degrees.
|
||||
|
||||
"""
|
||||
eu = qu2eu(self.quaternion.asArray())
|
||||
if degrees: eu = np.degrees(eu)
|
||||
return eu
|
||||
|
||||
def asAxisAngle(self,
|
||||
degrees = False):
|
||||
"""Axis-angle pair: ([n_1, n_2, n_3], ω)"""
|
||||
"""
|
||||
Axis angle pair: ([n_1, n_2, n_3], ω).
|
||||
|
||||
Parameters
|
||||
----------
|
||||
degrees : bool, optional
|
||||
return rotation angle in degrees.
|
||||
|
||||
"""
|
||||
ax = qu2ax(self.quaternion.asArray())
|
||||
if degrees: ax[3] = np.degrees(ax[3])
|
||||
return ax
|
||||
|
||||
def asMatrix(self):
|
||||
"""Rotation matrix"""
|
||||
"""Rotation matrix."""
|
||||
return qu2om(self.quaternion.asArray())
|
||||
|
||||
def asRodrigues(self,vector=False):
|
||||
"""Rodrigues-Frank vector: ([n_1, n_2, n_3], tan(ω/2))"""
|
||||
def asRodrigues(self,
|
||||
vector=False):
|
||||
"""
|
||||
Rodrigues-Frank vector: ([n_1, n_2, n_3], tan(ω/2)).
|
||||
|
||||
Parameters
|
||||
----------
|
||||
vector : bool, optional
|
||||
return as array of length 3, i.e. scale the unit vector giving the rotation axis.
|
||||
|
||||
"""
|
||||
ro = qu2ro(self.quaternion.asArray())
|
||||
return ro[:3]*ro[3] if vector else ro
|
||||
|
||||
def asHomochoric(self):
|
||||
"""Homochoric vector: (h_1, h_2, h_3)"""
|
||||
"""Homochoric vector: (h_1, h_2, h_3)."""
|
||||
return qu2ho(self.quaternion.asArray())
|
||||
|
||||
def asCubochoric(self):
|
||||
"""Cubochoric vector: (c_1, c_2, c_3)."""
|
||||
return qu2cu(self.quaternion.asArray())
|
||||
|
||||
def asM(self):
|
||||
"""Intermediate representation supporting quaternion averaging (see F. Landis Markley et al.)"""
|
||||
"""
|
||||
Intermediate representation supporting quaternion averaging.
|
||||
|
||||
References
|
||||
----------
|
||||
F. Landis Markley et al., Journal of Guidance, Control, and Dynamics 30(4):1193-1197, 2007
|
||||
https://doi.org/10.2514/1.28949
|
||||
|
||||
"""
|
||||
return self.quaternion.asM()
|
||||
|
||||
|
||||
|
@ -299,14 +367,20 @@ class Rotation:
|
|||
rotations,
|
||||
weights = []):
|
||||
"""
|
||||
Average rotation
|
||||
|
||||
ref: F. Landis Markley, Yang Cheng, John Lucas Crassidis, and Yaakov Oshman.
|
||||
Averaging Quaternions,
|
||||
Journal of Guidance, Control, and Dynamics, Vol. 30, No. 4 (2007), pp. 1193-1197.
|
||||
doi: 10.2514/1.28949
|
||||
Average rotation.
|
||||
|
||||
References
|
||||
----------
|
||||
F. Landis Markley et al., Journal of Guidance, Control, and Dynamics 30(4):1193-1197, 2007
|
||||
https://doi.org/10.2514/1.28949
|
||||
|
||||
usage: input a list of rotations and optional weights
|
||||
Parameters
|
||||
----------
|
||||
rotations : list of Rotations
|
||||
Rotations to average from
|
||||
weights : list of floats, optional
|
||||
Weights for each rotation used for averaging
|
||||
|
||||
"""
|
||||
if not all(isinstance(item, Rotation) for item in rotations):
|
||||
raise TypeError("Only instances of Rotation can be averaged.")
|
||||
|
@ -339,42 +413,78 @@ class Rotation:
|
|||
# ******************************************************************************************
|
||||
class Symmetry:
|
||||
"""
|
||||
Symmetry operations for lattice systems
|
||||
Symmetry operations for lattice systems.
|
||||
|
||||
References
|
||||
----------
|
||||
https://en.wikipedia.org/wiki/Crystal_system
|
||||
|
||||
"""
|
||||
|
||||
lattices = [None,'orthorhombic','tetragonal','hexagonal','cubic',]
|
||||
|
||||
def __init__(self, symmetry = None):
|
||||
if isinstance(symmetry, str) and symmetry.lower() in Symmetry.lattices:
|
||||
self.lattice = symmetry.lower()
|
||||
else:
|
||||
self.lattice = None
|
||||
"""
|
||||
Symmetry Definition.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
symmetry : str, optional
|
||||
label of the crystal system
|
||||
|
||||
"""
|
||||
if symmetry is not None and symmetry.lower() not in Symmetry.lattices:
|
||||
raise KeyError('Symmetry/crystal system "{}" is unknown'.format(symmetry))
|
||||
|
||||
self.lattice = symmetry.lower() if isinstance(symmetry,str) else symmetry
|
||||
|
||||
|
||||
def __copy__(self):
|
||||
"""Copy"""
|
||||
"""Copy."""
|
||||
return self.__class__(self.lattice)
|
||||
|
||||
copy = __copy__
|
||||
|
||||
|
||||
def __repr__(self):
|
||||
"""Readable string"""
|
||||
"""Readable string."""
|
||||
return '{}'.format(self.lattice)
|
||||
|
||||
|
||||
def __eq__(self, other):
|
||||
"""Equal to other"""
|
||||
"""
|
||||
Equal to other.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
other : Symmetry
|
||||
Symmetry to check for equality.
|
||||
|
||||
"""
|
||||
return self.lattice == other.lattice
|
||||
|
||||
def __neq__(self, other):
|
||||
"""Not equal to other"""
|
||||
"""
|
||||
Not Equal to other.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
other : Symmetry
|
||||
Symmetry to check for inequality.
|
||||
|
||||
"""
|
||||
return not self.__eq__(other)
|
||||
|
||||
def __cmp__(self,other):
|
||||
"""Linear ordering"""
|
||||
"""
|
||||
Linear ordering.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
other : Symmetry
|
||||
Symmetry to check for for order.
|
||||
|
||||
"""
|
||||
myOrder = Symmetry.lattices.index(self.lattice)
|
||||
otherOrder = Symmetry.lattices.index(other.lattice)
|
||||
return (myOrder > otherOrder) - (myOrder < otherOrder)
|
||||
|
@ -458,7 +568,7 @@ class Symmetry:
|
|||
|
||||
def inFZ(self,rodrigues):
|
||||
"""
|
||||
Check whether given Rodrigues vector falls into fundamental zone of own symmetry.
|
||||
Check whether given Rodriques-Frank vector falls into fundamental zone of own symmetry.
|
||||
|
||||
Fundamental zone in Rodrigues space is point symmetric around origin.
|
||||
"""
|
||||
|
@ -491,11 +601,13 @@ class Symmetry:
|
|||
|
||||
def inDisorientationSST(self,rodrigues):
|
||||
"""
|
||||
Check whether given Rodrigues vector (of misorientation) falls into standard stereographic triangle of own symmetry.
|
||||
Check whether given Rodriques-Frank vector (of misorientation) falls into standard stereographic triangle of own symmetry.
|
||||
|
||||
References
|
||||
----------
|
||||
A. Heinz and P. Neumann, Acta Crystallographica Section A 47:780-789, 1991
|
||||
https://doi.org/10.1107/S0108767391006864
|
||||
|
||||
Determination of disorientations follow the work of A. Heinz and P. Neumann:
|
||||
Representation of Orientation and Disorientation Data for Cubic, Hexagonal, Tetragonal and Orthorhombic Crystals
|
||||
Acta Cryst. (1991). A47, 780-789
|
||||
"""
|
||||
if (len(rodrigues) != 3):
|
||||
raise ValueError('Input is not a Rodriques-Frank vector.\n')
|
||||
|
@ -611,11 +723,15 @@ class Symmetry:
|
|||
# ******************************************************************************************
|
||||
class Lattice:
|
||||
"""
|
||||
Lattice system
|
||||
Lattice system.
|
||||
|
||||
Currently, this contains only a mapping from Bravais lattice to symmetry
|
||||
and orientation relationships. It could include twin and slip systems.
|
||||
|
||||
References
|
||||
----------
|
||||
https://en.wikipedia.org/wiki/Bravais_lattice
|
||||
|
||||
"""
|
||||
|
||||
lattices = {
|
||||
|
@ -628,12 +744,21 @@ class Lattice:
|
|||
|
||||
|
||||
def __init__(self, lattice):
|
||||
"""
|
||||
New lattice of given type.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
lattice : str
|
||||
Bravais lattice.
|
||||
|
||||
"""
|
||||
self.lattice = lattice
|
||||
self.symmetry = Symmetry(self.lattices[lattice]['symmetry'])
|
||||
|
||||
|
||||
def __repr__(self):
|
||||
"""Report basic lattice information"""
|
||||
"""Report basic lattice information."""
|
||||
return 'Bravais lattice {} ({} symmetry)'.format(self.lattice,self.symmetry)
|
||||
|
||||
|
||||
|
@ -878,7 +1003,7 @@ class Lattice:
|
|||
'NW':self.NW, 'Pitsch': self.Pitsch, 'Bain':self.Bain}
|
||||
try:
|
||||
relationship = models[model]
|
||||
except:
|
||||
except KeyError :
|
||||
raise KeyError('Orientation relationship "{}" is unknown'.format(model))
|
||||
|
||||
if self.lattice not in relationship['mapping']:
|
||||
|
@ -909,19 +1034,29 @@ class Lattice:
|
|||
|
||||
class Orientation:
|
||||
"""
|
||||
Crystallographic orientation
|
||||
Crystallographic orientation.
|
||||
|
||||
A crystallographic orientation contains a rotation and a lattice
|
||||
A crystallographic orientation contains a rotation and a lattice.
|
||||
"""
|
||||
|
||||
__slots__ = ['rotation','lattice']
|
||||
|
||||
def __repr__(self):
|
||||
"""Report lattice type and orientation"""
|
||||
"""Report lattice type and orientation."""
|
||||
return self.lattice.__repr__()+'\n'+self.rotation.__repr__()
|
||||
|
||||
def __init__(self, rotation, lattice):
|
||||
|
||||
"""
|
||||
New orientation from rotation and lattice.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
rotation : Rotation
|
||||
Rotation specifying the lattice orientation.
|
||||
lattice : Lattice
|
||||
Lattice type of the crystal.
|
||||
|
||||
"""
|
||||
if isinstance(lattice, Lattice):
|
||||
self.lattice = lattice
|
||||
else:
|
||||
|
@ -971,7 +1106,7 @@ class Orientation:
|
|||
return self.lattice.symmetry.inFZ(self.rotation.asRodrigues(vector=True))
|
||||
|
||||
def equivalentOrientations(self,members=[]):
|
||||
"""List of orientations which are symmetrically equivalent"""
|
||||
"""List of orientations which are symmetrically equivalent."""
|
||||
try:
|
||||
iter(members) # asking for (even empty) list of members?
|
||||
except TypeError:
|
||||
|
@ -981,12 +1116,12 @@ class Orientation:
|
|||
for q in self.lattice.symmetry.symmetryOperations(members)] # yes, return list of rotations
|
||||
|
||||
def relatedOrientations(self,model):
|
||||
"""List of orientations related by the given orientation relationship"""
|
||||
"""List of orientations related by the given orientation relationship."""
|
||||
r = self.lattice.relationOperations(model)
|
||||
return [self.__class__(self.rotation*o,r['lattice']) for o in r['rotations']]
|
||||
|
||||
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.equivalentOrientations():
|
||||
if self.lattice.symmetry.inFZ(me.rotation.asRodrigues(vector=True)): break
|
||||
|
||||
|
@ -996,7 +1131,7 @@ class Orientation:
|
|||
axis,
|
||||
proper = False,
|
||||
SST = True):
|
||||
"""Axis rotated according to orientation (using crystal symmetry to ensure location falls into SST)"""
|
||||
"""Axis rotated according to orientation (using crystal symmetry to ensure location falls into SST)."""
|
||||
if SST: # pole requested to be within SST
|
||||
for i,o in enumerate(self.equivalentOrientations()): # test all symmetric equivalent quaternions
|
||||
pole = o.rotation*axis # align crystal direction to axis
|
||||
|
@ -1008,7 +1143,7 @@ class Orientation:
|
|||
|
||||
|
||||
def IPFcolor(self,axis):
|
||||
"""TSL color of inverse pole figure for given axis"""
|
||||
"""TSL color of inverse pole figure for given axis."""
|
||||
color = np.zeros(3,'d')
|
||||
|
||||
for o in self.equivalentOrientations():
|
||||
|
@ -1023,7 +1158,7 @@ class Orientation:
|
|||
def fromAverage(cls,
|
||||
orientations,
|
||||
weights = []):
|
||||
"""Create orientation from average of list of orientations"""
|
||||
"""Create orientation from average of list of orientations."""
|
||||
if not all(isinstance(item, Orientation) for item in orientations):
|
||||
raise TypeError("Only instances of Orientation can be averaged.")
|
||||
|
||||
|
@ -1039,7 +1174,7 @@ class Orientation:
|
|||
|
||||
|
||||
def average(self,other):
|
||||
"""Calculate the average rotation"""
|
||||
"""Calculate the average rotation."""
|
||||
return Orientation.fromAverage([self,other])
|
||||
|
||||
|
||||
|
@ -1080,10 +1215,10 @@ def isone(a):
|
|||
def iszero(a):
|
||||
return np.isclose(a,0.0,atol=1.0e-12,rtol=0.0)
|
||||
|
||||
#---------- quaternion ----------
|
||||
#---------- Quaternion ----------
|
||||
|
||||
def qu2om(qu):
|
||||
"""Quaternion to orientation matrix"""
|
||||
"""Quaternion to rotation matrix."""
|
||||
qq = qu[0]**2-(qu[1]**2 + qu[2]**2 + qu[3]**2)
|
||||
om = np.diag(qq + 2.0*np.array([qu[1],qu[2],qu[3]])**2)
|
||||
|
||||
|
@ -1097,7 +1232,7 @@ def qu2om(qu):
|
|||
|
||||
|
||||
def qu2eu(qu):
|
||||
"""Quaternion to Euler angles"""
|
||||
"""Quaternion to Bunge-Euler angles."""
|
||||
q03 = qu[0]**2+qu[3]**2
|
||||
q12 = qu[1]**2+qu[2]**2
|
||||
chi = np.sqrt(q03*q12)
|
||||
|
@ -1117,7 +1252,7 @@ def qu2eu(qu):
|
|||
|
||||
def qu2ax(qu):
|
||||
"""
|
||||
Quaternion to axis angle
|
||||
Quaternion to axis angle pair.
|
||||
|
||||
Modified version of the original formulation, should be numerically more stable
|
||||
"""
|
||||
|
@ -1134,7 +1269,7 @@ def qu2ax(qu):
|
|||
|
||||
|
||||
def qu2ro(qu):
|
||||
"""Quaternion to Rodrigues vector"""
|
||||
"""Quaternion to Rodriques-Frank vector."""
|
||||
if iszero(qu[0]):
|
||||
ro = [qu[1], qu[2], qu[3], np.inf]
|
||||
else:
|
||||
|
@ -1146,7 +1281,7 @@ def qu2ro(qu):
|
|||
|
||||
|
||||
def qu2ho(qu):
|
||||
"""Quaternion to homochoric"""
|
||||
"""Quaternion to homochoric vector."""
|
||||
omega = 2.0 * np.arccos(np.clip(qu[0],-1.0,1.0)) # avoid numerical difficulties
|
||||
|
||||
if iszero(omega):
|
||||
|
@ -1160,15 +1295,15 @@ def qu2ho(qu):
|
|||
|
||||
|
||||
def qu2cu(qu):
|
||||
"""Quaternion to cubochoric"""
|
||||
"""Quaternion to cubochoric vector."""
|
||||
return ho2cu(qu2ho(qu))
|
||||
|
||||
|
||||
#---------- orientation matrix ----------
|
||||
#---------- Rotation matrix ----------
|
||||
|
||||
def om2qu(om):
|
||||
"""
|
||||
Orientation matrix to quaternion
|
||||
Rotation matrix to quaternion.
|
||||
|
||||
The original formulation (direct conversion) had (numerical?) issues
|
||||
"""
|
||||
|
@ -1176,7 +1311,7 @@ def om2qu(om):
|
|||
|
||||
|
||||
def om2eu(om):
|
||||
"""Orientation matrix to Euler angles"""
|
||||
"""Rotation matrix to Bunge-Euler angles."""
|
||||
if abs(om[2,2]) < 1.0:
|
||||
zeta = 1.0/np.sqrt(1.0-om[2,2]**2)
|
||||
eu = np.array([np.arctan2(om[2,0]*zeta,-om[2,1]*zeta),
|
||||
|
@ -1191,7 +1326,7 @@ def om2eu(om):
|
|||
|
||||
|
||||
def om2ax(om):
|
||||
"""Orientation matrix to axis angle"""
|
||||
"""Rotation matrix to axis angle pair."""
|
||||
ax=np.empty(4)
|
||||
|
||||
# first get the rotation angle
|
||||
|
@ -1212,24 +1347,24 @@ def om2ax(om):
|
|||
|
||||
|
||||
def om2ro(om):
|
||||
"""Orientation matrix to Rodriques vector"""
|
||||
"""Rotation matrix to Rodriques-Frank vector."""
|
||||
return eu2ro(om2eu(om))
|
||||
|
||||
|
||||
def om2ho(om):
|
||||
"""Orientation matrix to homochoric"""
|
||||
"""Rotation matrix to homochoric vector."""
|
||||
return ax2ho(om2ax(om))
|
||||
|
||||
|
||||
def om2cu(om):
|
||||
"""Orientation matrix to cubochoric"""
|
||||
"""Rotation matrix to cubochoric vector."""
|
||||
return ho2cu(om2ho(om))
|
||||
|
||||
|
||||
#---------- Euler angles ----------
|
||||
#---------- Bunge-Euler angles ----------
|
||||
|
||||
def eu2qu(eu):
|
||||
"""Euler angles to quaternion"""
|
||||
"""Bunge-Euler angles to quaternion."""
|
||||
ee = 0.5*eu
|
||||
cPhi = np.cos(ee[1])
|
||||
sPhi = np.sin(ee[1])
|
||||
|
@ -1242,7 +1377,7 @@ def eu2qu(eu):
|
|||
|
||||
|
||||
def eu2om(eu):
|
||||
"""Euler angles to orientation matrix"""
|
||||
"""Bunge-Euler angles to rotation matrix."""
|
||||
c = np.cos(eu)
|
||||
s = np.sin(eu)
|
||||
|
||||
|
@ -1255,7 +1390,7 @@ def eu2om(eu):
|
|||
|
||||
|
||||
def eu2ax(eu):
|
||||
"""Euler angles to axis angle"""
|
||||
"""Bunge-Euler angles to axis angle pair."""
|
||||
t = np.tan(eu[1]*0.5)
|
||||
sigma = 0.5*(eu[0]+eu[2])
|
||||
delta = 0.5*(eu[0]-eu[2])
|
||||
|
@ -1266,7 +1401,7 @@ def eu2ax(eu):
|
|||
if iszero(alpha):
|
||||
ax = np.array([ 0.0, 0.0, 1.0, 0.0 ])
|
||||
else:
|
||||
ax = -P/tau * np.array([ t*np.cos(delta), t*np.sin(delta), np.sin(sigma) ]) # passive axis-angle pair so a minus sign in front
|
||||
ax = -P/tau * np.array([ t*np.cos(delta), t*np.sin(delta), np.sin(sigma) ]) # passive axis angle pair so a minus sign in front
|
||||
ax = np.append(ax,alpha)
|
||||
if alpha < 0.0: ax *= -1.0 # ensure alpha is positive
|
||||
|
||||
|
@ -1274,8 +1409,8 @@ def eu2ax(eu):
|
|||
|
||||
|
||||
def eu2ro(eu):
|
||||
"""Euler angles to Rodrigues vector"""
|
||||
ro = eu2ax(eu) # convert to axis angle representation
|
||||
"""Bunge-Euler angles to Rodriques-Frank vector."""
|
||||
ro = eu2ax(eu) # convert to axis angle pair representation
|
||||
if ro[3] >= np.pi: # Differs from original implementation. check convention 5
|
||||
ro[3] = np.inf
|
||||
elif iszero(ro[3]):
|
||||
|
@ -1287,19 +1422,19 @@ def eu2ro(eu):
|
|||
|
||||
|
||||
def eu2ho(eu):
|
||||
"""Euler angles to homochoric"""
|
||||
"""Bunge-Euler angles to homochoric vector."""
|
||||
return ax2ho(eu2ax(eu))
|
||||
|
||||
|
||||
def eu2cu(eu):
|
||||
"""Euler angles to cubochoric"""
|
||||
"""Bunge-Euler angles to cubochoric vector."""
|
||||
return ho2cu(eu2ho(eu))
|
||||
|
||||
|
||||
#---------- axis angle ----------
|
||||
#---------- Axis angle pair ----------
|
||||
|
||||
def ax2qu(ax):
|
||||
"""Axis angle to quaternion"""
|
||||
"""Axis angle pair to quaternion."""
|
||||
if iszero(ax[3]):
|
||||
qu = np.array([ 1.0, 0.0, 0.0, 0.0 ])
|
||||
else:
|
||||
|
@ -1311,7 +1446,7 @@ def ax2qu(ax):
|
|||
|
||||
|
||||
def ax2om(ax):
|
||||
"""Axis angle to orientation matrix"""
|
||||
"""Axis angle pair to rotation matrix."""
|
||||
c = np.cos(ax[3])
|
||||
s = np.sin(ax[3])
|
||||
omc = 1.0-c
|
||||
|
@ -1326,12 +1461,12 @@ def ax2om(ax):
|
|||
|
||||
|
||||
def ax2eu(ax):
|
||||
"""Orientation matrix to Euler angles"""
|
||||
"""Rotation matrix to Bunge Euler angles."""
|
||||
return om2eu(ax2om(ax))
|
||||
|
||||
|
||||
def ax2ro(ax):
|
||||
"""Axis angle to Rodrigues vector"""
|
||||
"""Axis angle pair to Rodriques-Frank vector."""
|
||||
if iszero(ax[3]):
|
||||
ro = [ 0.0, 0.0, P, 0.0 ]
|
||||
else:
|
||||
|
@ -1344,36 +1479,36 @@ def ax2ro(ax):
|
|||
|
||||
|
||||
def ax2ho(ax):
|
||||
"""Axis angle to homochoric"""
|
||||
"""Axis angle pair to homochoric vector."""
|
||||
f = (0.75 * ( ax[3] - np.sin(ax[3]) ))**(1.0/3.0)
|
||||
ho = ax[0:3] * f
|
||||
return ho
|
||||
|
||||
|
||||
def ax2cu(ax):
|
||||
"""Axis angle to cubochoric"""
|
||||
"""Axis angle pair to cubochoric vector."""
|
||||
return ho2cu(ax2ho(ax))
|
||||
|
||||
|
||||
#---------- Rodrigues--Frank ----------
|
||||
#---------- Rodrigues-Frank vector ----------
|
||||
|
||||
def ro2qu(ro):
|
||||
"""Rodrigues vector to quaternion"""
|
||||
"""Rodriques-Frank vector to quaternion."""
|
||||
return ax2qu(ro2ax(ro))
|
||||
|
||||
|
||||
def ro2om(ro):
|
||||
"""Rodgrigues vector to orientation matrix"""
|
||||
"""Rodgrigues-Frank vector to rotation matrix."""
|
||||
return ax2om(ro2ax(ro))
|
||||
|
||||
|
||||
def ro2eu(ro):
|
||||
"""Rodrigues vector to orientation matrix"""
|
||||
"""Rodriques-Frank vector to Bunge-Euler angles."""
|
||||
return om2eu(ro2om(ro))
|
||||
|
||||
|
||||
def ro2ax(ro):
|
||||
"""Rodrigues vector to axis angle"""
|
||||
"""Rodriques-Frank vector to axis angle pair."""
|
||||
ta = ro[3]
|
||||
|
||||
if iszero(ta):
|
||||
|
@ -1389,7 +1524,7 @@ def ro2ax(ro):
|
|||
|
||||
|
||||
def ro2ho(ro):
|
||||
"""Rodrigues vector to homochoric"""
|
||||
"""Rodriques-Frank vector to homochoric vector."""
|
||||
if iszero(np.sum(ro[0:3]**2.0)):
|
||||
ho = [ 0.0, 0.0, 0.0 ]
|
||||
else:
|
||||
|
@ -1400,29 +1535,29 @@ def ro2ho(ro):
|
|||
|
||||
|
||||
def ro2cu(ro):
|
||||
"""Rodrigues vector to cubochoric"""
|
||||
"""Rodriques-Frank vector to cubochoric vector."""
|
||||
return ho2cu(ro2ho(ro))
|
||||
|
||||
|
||||
#---------- homochoric ----------
|
||||
#---------- Homochoric vector----------
|
||||
|
||||
def ho2qu(ho):
|
||||
"""Homochoric to quaternion"""
|
||||
"""Homochoric vector to quaternion."""
|
||||
return ax2qu(ho2ax(ho))
|
||||
|
||||
|
||||
def ho2om(ho):
|
||||
"""Homochoric to orientation matrix"""
|
||||
"""Homochoric vector to rotation matrix."""
|
||||
return ax2om(ho2ax(ho))
|
||||
|
||||
|
||||
def ho2eu(ho):
|
||||
"""Homochoric to Euler angles"""
|
||||
"""Homochoric vector to Bunge-Euler angles."""
|
||||
return ax2eu(ho2ax(ho))
|
||||
|
||||
|
||||
def ho2ax(ho):
|
||||
"""Homochoric to axis angle"""
|
||||
"""Homochoric vector to axis angle pair."""
|
||||
tfit = np.array([+1.0000000000018852, -0.5000000002194847,
|
||||
-0.024999992127593126, -0.003928701544781374,
|
||||
-0.0008152701535450438, -0.0002009500426119712,
|
||||
|
@ -1448,42 +1583,42 @@ def ho2ax(ho):
|
|||
|
||||
|
||||
def ho2ro(ho):
|
||||
"""Axis angle to Rodriques vector"""
|
||||
"""Axis angle pair to Rodriques-Frank vector."""
|
||||
return ax2ro(ho2ax(ho))
|
||||
|
||||
|
||||
def ho2cu(ho):
|
||||
"""Homochoric to cubochoric"""
|
||||
"""Homochoric vector to cubochoric vector."""
|
||||
return Lambert.BallToCube(ho)
|
||||
|
||||
|
||||
#---------- cubochoric ----------
|
||||
#---------- Cubochoric ----------
|
||||
|
||||
def cu2qu(cu):
|
||||
"""Cubochoric to quaternion"""
|
||||
"""Cubochoric vector to quaternion."""
|
||||
return ho2qu(cu2ho(cu))
|
||||
|
||||
|
||||
def cu2om(cu):
|
||||
"""Cubochoric to orientation matrix"""
|
||||
"""Cubochoric vector to rotation matrix."""
|
||||
return ho2om(cu2ho(cu))
|
||||
|
||||
|
||||
def cu2eu(cu):
|
||||
"""Cubochoric to Euler angles"""
|
||||
"""Cubochoric vector to Bunge-Euler angles."""
|
||||
return ho2eu(cu2ho(cu))
|
||||
|
||||
|
||||
def cu2ax(cu):
|
||||
"""Cubochoric to axis angle"""
|
||||
"""Cubochoric vector to axis angle pair."""
|
||||
return ho2ax(cu2ho(cu))
|
||||
|
||||
|
||||
def cu2ro(cu):
|
||||
"""Cubochoric to Rodrigues vector"""
|
||||
"""Cubochoric vector to Rodriques-Frank vector."""
|
||||
return ho2ro(cu2ho(cu))
|
||||
|
||||
|
||||
def cu2ho(cu):
|
||||
"""Cubochoric to homochoric"""
|
||||
"""Cubochoric vector to homochoric vector."""
|
||||
return Lambert.CubeToBall(cu)
|
||||
|
|
Loading…
Reference in New Issue