Module beef.rotation

Quaternion and tensor rotations.

Expand source code
'''
Quaternion  and tensor rotations.
'''

import numpy as np

def rodrot(theta, rotaxis=[0, 0, 1], style='row'):
    """
    Establishes 3D rotation matrix based on Euler-Rodrigues formula.
    See https://en.wikipedia.org/wiki/Euler-Rodrigues_formula.


    Arguments
    -------------
        theta : float
            the rotation angle (in radians)
        rotaxis : [0, 0, 1]
            vector defining rotation axis
        style : 'row'
            unit vectors as stacked as 'row' or 'column'

    Returns
    -----------
        T : float
            transformation matrix in NumPy format

    """

    axis = np.asarray(rotaxis)
    axis = rotaxis/np.sqrt(np.dot(rotaxis, rotaxis))    # Normalize
    a = np.cos(theta/2.0)
    b, c, d = axis*np.sin(theta/2.0)
    a2, b2, c2, d2 = a*a, b*b, c*c, d*d
    bc, ad, ac, ab, bd, cd = b*c, a*d, a*c, a*b, b*d, c*d
    T = np.array([[a2+b2-c2-d2, 2*(bc-ad), 2*(bd+ac)],
                  [2*(bc+ad), a2+c2-b2-d2, 2*(cd-ab)],
                  [2*(bd-ac), 2*(cd+ab), a2+d2-b2-c2]])
    
    if style=='row':
        T = T.T
    
    return T


def rot_from_R(R):
    '''
    Calculate rotation vector from (deformation part of) rotation tensor.

    Arguments
    -------------
    R : float
        numpy 3x3 matrix describing to rotation transformation
        matrix equivalent to the incremental rotation vector

    Returns
    -----------
    rot : float
        3-by-1 numpy array with three rotations of a node    

    '''

    rot = 0.5*np.array([R[2,1] - R[1,2],
                        R[0,2] - R[2,0],
                        R[1,0] - R[0,1]])
    
    theta = np.linalg.norm(rot)

    if theta<1e-12:
        scale = 1.0
    else:
        scale = theta/np.sin(theta)
    
    return scale * rot


def R_from_drot(drot):
    '''
    Calculate the rotation tensor from an incremental rotation vector.

    Arguments
    -----------
    drot : float
        3-by-1 numpy array with three rotation increments of a node

    Returns
    -------------
    R : float
        numpy 3x3 matrix describing to rotation transformation
        matrix equivalent to the incremental rotation vector

    '''

    theta = np.linalg.norm(drot)
    if theta<1e-12:
        return np.eye(3)
    else:
        n = drot/theta
        N = np.zeros([3,3])
        N[1-1,2-1]=-n[3-1]
        N[1-1,3-1]=n[2-1]
        N[2-1,3-1]=-n[1-1]  
        I = np.eye(3)

        N = N - N.T
        R = I + N * np.sin(theta) + (np.outer(n,n) - I) * (1 - np.cos(theta))

        return R


def quat_from_R(R):
    '''
    Calculate quaternions from matrix [R]

    Arguments
    ----------
    R : float
        numpy 3x3 matrix describing to rotation transformation
        matrix equivalent to the input quaternion representation
        
    Returns 
    ---------
     r0 : float
         scalar defining the trace of the rotation tensor
     r : float
         numpy array with three quaternions r1,r2,r3

    '''
    

    e = lambda i,j,k:(i-j)*(j-k)*(k-i)/2

    r0 = (np.trace(R)+1)/4
    r = np.zeros(3)
    for l in range(3):
        e_mat = np.zeros([3,3])
        for i in range(3):
            for j in range(3):
                e_mat[i,j] = e(l, i, j)
        
        r[l] = -np.sum(e_mat*R)/4/r0
    
    return r0, r


def R_from_quat(r0, r, row_wise=True): 
    '''
    Calculate transformation matrix [R] according to
    Eq 3.50 in Krenk [1].

    Arguments
    ----------
    r0 : float
        scalar defining the trace of the rotation tensor
    r : float
        numpy array with three quaternions r1,r2,r3
    row_wise : {True, False}
        if converting the result such that basis vectors are
        stacked column-wise (instead of the column-wise used in [1])

    Returns 
    ---------
    R : float
        numpy 3x3 matrix describing to rotation transformation
        matrix equivalent to the input quaternion representation
        
    References
    ------------
    [[1]](../#1) Krenk, 2009.
              
    '''

    r_hat = np.array([[0, -r[2], r[1]], 
                     [r[2], 0, -r[0]], 
                     [-r[1], r[0], 0]]) # Equation 3.7 in Krenk [1]
    R = (r0**2 - np.dot(r,r)) * np.eye(3) + 2*r0*r_hat + 2 * np.outer(r,r)
    
    # Convert such that unit vectors are stacked row-wise
    if row_wise:    
        R = R.T

    return R

def increment_from_drot(drot):
    '''
    Establish linearized quaternion increments from 
    given rotation increments, as in Eq. 5.123 in Krenk [1].

    Arguments
    -----------
    drot : float
        3-by-1 or 1-by-3 numpy array with three rotation increments of a node

    Returns
    -----------
    dr0 : float
        scalar defining the trace of the rotation tensor
        of the incremental rotation
    dr : float
        numpy array with three quaternions r1,r2,r3, corresponding
        to the incremental rotation
        
    References
    ------------
    [[1]](../#1) Krenk, 2009.
          
    '''

    dr = 0.5 * drot
    dr0 = np.sqrt(1 - np.dot(dr,dr))

    return dr0, dr

def add_increment_from_quat(r0, r, dr0, dr):
    '''
    Add incremental rotation quaternions to initial rotation using Eq. 5.124
    in Krenk [1].

    Arguments
    ----------
    r0 : float
        scalar defining the trace of the initial rotation tensor
    r : float
        numpy array with three quaternions r1,r2,r3 representing
        the inital rotation
    dr0 : float
        scalar defining the trace of the rotation tensor
        of the incremental rotation
    dr : float
        numpy array with three quaternions r1,r2,r3, corresponding
        to the incremental rotation

    Returns 
    ---------
    r0 : float
        scalar defining the trace of the final rotation tensor
    r : float
        numpy array with three quaternions r1,r2,r3 representing
        the final rotation

        
    References
    ------------
    [[1]](../#1) Krenk, 2009.
    '''

    r0 = dr0*r0 - np.dot(dr, r)
    r = dr0*r + r0*dr + np.cross(dr, r)

    return r0, r

def add_increment_from_rot(r0, r, drot):
    '''
    Add incremental rotation to initial rotation using Eq. 5.124
    in Krenk [1]. Combining functions inc_from_rot and increment_from_quat.
    
    Arguments
    -----------
    r0 : float
        scalar defining the trace of the initial rotation tensor
    r : float
        numpy array with three quaternions r1,r2,r3 representing
        the inital rotation
    drot : float
        3-by-1 or 1-by-3 numpy array with three rotation increments of a node

    Returns 
    ---------
    r0 : float
        scalar defining the trace of the final rotation tensor
    r : float
        numpy array with three quaternions r1,r2,r3 representing
        the final rotation
        
        
    References
    ------------
    [[1]](../#1) Krenk, 2009.
    '''

    dr0, dr = increment_from_drot(drot)     
    r0, r = add_increment_from_quat(r0, r, dr0, dr)

    return r0,r


def quat_mean_and_diff(ra0, ra, rb0, rb):
    '''
    Calculate the mean and difference quaternions from two sets of quaternions
    {ra0, ra} and {rb0, rb}.

    Arguments
    ----------
    ra0 : float
        scalar defining the trace of the rotation tensor
        of the first point
    ra : float
        numpy array with three quaternions r1,r2,r3 representing
        the rotation of the first point
    rb0 : float
        scalar defining the trace of the rotation tensor
        of the second point
    rb : float
        numpy array with three quaternions r1,r2,r3 representing
        the rotation of the second point

    Returns 
    ---------
    r0 : float
        scalar defining the trace of the final rotation tensor
    r : float
        numpy array with three quaternions r1,r2,r3 representing
        the final rotation
    s0 : float
        scalar part of difference quaternion
    s : float
        numpy array with three quaternions s1,s2,s3 representing
        the difference in rotation
        
    References
    ------------
    [[1]](../#1) Krenk, 2009.
    '''    

    s0 = 0.5 * np.sqrt((ra0+rb0)**2 + np.linalg.norm(ra+rb)**2)
    r0 = 0.5 * (ra0+rb0)/s0
    r = 0.5 * (ra+rb)/s0
    s = 0.5 * (ra0*rb - rb0*ra + np.cross(ra,rb))/s0

    return r0, r, s0, s

Functions

def R_from_drot(drot)

Calculate the rotation tensor from an incremental rotation vector.

Arguments

drot : float
3-by-1 numpy array with three rotation increments of a node

Returns

R : float
numpy 3x3 matrix describing to rotation transformation matrix equivalent to the incremental rotation vector
def R_from_quat(r0, r, row_wise=True)

Calculate transformation matrix [R] according to Eq 3.50 in Krenk [1].

Arguments

r0 : float
scalar defining the trace of the rotation tensor
r : float
numpy array with three quaternions r1,r2,r3
row_wise : {True, False}
if converting the result such that basis vectors are stacked column-wise (instead of the column-wise used in [1])

Returns

R : float numpy 3x3 matrix describing to rotation transformation matrix equivalent to the input quaternion representation

References

[1] Krenk, 2009.

def add_increment_from_quat(r0, r, dr0, dr)

Add incremental rotation quaternions to initial rotation using Eq. 5.124 in Krenk [1].

Arguments

r0 : float
scalar defining the trace of the initial rotation tensor
r : float
numpy array with three quaternions r1,r2,r3 representing the inital rotation
dr0 : float
scalar defining the trace of the rotation tensor of the incremental rotation
dr : float
numpy array with three quaternions r1,r2,r3, corresponding to the incremental rotation

Returns

r0 : float scalar defining the trace of the final rotation tensor r : float numpy array with three quaternions r1,r2,r3 representing the final rotation

References

[1] Krenk, 2009.

def add_increment_from_rot(r0, r, drot)

Add incremental rotation to initial rotation using Eq. 5.124 in Krenk [1]. Combining functions inc_from_rot and increment_from_quat.

Arguments

r0 : float
scalar defining the trace of the initial rotation tensor
r : float
numpy array with three quaternions r1,r2,r3 representing the inital rotation
drot : float
3-by-1 or 1-by-3 numpy array with three rotation increments of a node

Returns

r0 : float scalar defining the trace of the final rotation tensor r : float numpy array with three quaternions r1,r2,r3 representing the final rotation

References

[1] Krenk, 2009.

def increment_from_drot(drot)

Establish linearized quaternion increments from given rotation increments, as in Eq. 5.123 in Krenk [1].

Arguments

drot : float
3-by-1 or 1-by-3 numpy array with three rotation increments of a node

Returns

dr0 : float
scalar defining the trace of the rotation tensor of the incremental rotation
dr : float
numpy array with three quaternions r1,r2,r3, corresponding to the incremental rotation

References

[1] Krenk, 2009.

def quat_from_R(R)

Calculate quaternions from matrix [R]

Arguments

R : float
numpy 3x3 matrix describing to rotation transformation matrix equivalent to the input quaternion representation

Returns

r0 : float scalar defining the trace of the rotation tensor r : float numpy array with three quaternions r1,r2,r3

def quat_mean_and_diff(ra0, ra, rb0, rb)

Calculate the mean and difference quaternions from two sets of quaternions {ra0, ra} and {rb0, rb}.

Arguments

ra0 : float
scalar defining the trace of the rotation tensor of the first point
ra : float
numpy array with three quaternions r1,r2,r3 representing the rotation of the first point
rb0 : float
scalar defining the trace of the rotation tensor of the second point
rb : float
numpy array with three quaternions r1,r2,r3 representing the rotation of the second point

Returns

r0 : float scalar defining the trace of the final rotation tensor r : float numpy array with three quaternions r1,r2,r3 representing the final rotation s0 : float scalar part of difference quaternion s : float numpy array with three quaternions s1,s2,s3 representing the difference in rotation

References

[1] Krenk, 2009.

def rodrot(theta, rotaxis=[0, 0, 1], style='row')

Establishes 3D rotation matrix based on Euler-Rodrigues formula. See https://en.wikipedia.org/wiki/Euler-Rodrigues_formula.

Arguments

theta : float
    the rotation angle (in radians)
rotaxis : [0, 0, 1]
    vector defining rotation axis
style : 'row'
    unit vectors as stacked as 'row' or 'column'

Returns

T : float
    transformation matrix in NumPy format
def rot_from_R(R)

Calculate rotation vector from (deformation part of) rotation tensor.

Arguments

R : float
numpy 3x3 matrix describing to rotation transformation matrix equivalent to the incremental rotation vector

Returns

rot : float
3-by-1 numpy array with three rotations of a node