I can share my algorithm. It is a symplectic algorithm, based on a splitting of the inertia matrix into a sum two inertia matrices, two equal axes each, which leads to splitting the original system of differential equations for the angular momentum (the so called Euler's equations) into two systems of differential equations of bodies with two equal inertial axes. Each system can be solved explicitly and then the phase flows of the two systems are combined in a leap-frog manner. As a result, this algorithm preserve angular momenutm just like the original system and it quasi-conserves energy, i.e. it almost conserves energy and the latter does not dissipate, so there is no angular velocity drift. This is due to the fact that the algorithm preserves the so called symplectic structure on the sphere of the angular momentum, which geometrically means the algorithm preserves the area on the said sphere.
import math
import numpy as np
def Rot_3(m):
cs = math.cos(m)
sn = math.sin(m)
return np.array([
[cs, -sn, 0],
[sn, cs, 0],
[ 0, 0, 1]])
def Rot_1(m):
cs = math.cos(m)
sn = math.sin(m)
return np.array([
[1, 0, 0],
[0, cs, -sn],
[0, sn, cs]])
def vector_to_matrix(Vector):
Matrix = np.array([ 0, - Vector[2], Vector[1]],
[ 0, 0, -Vector[0]],
[ 0, 0, 0 ])
return Matrix - Matrix.T
def Angular_Momentum_step(M_input, k_23, k_21, t_step):
M_step = Rot_3(t_step*k_23 * M_input[2]/2).dot(M_input)
M_step = Rot_1(t_step*k_21 * M_step[0]).dot(M_step)
M_step = Rot_3(t_step*k_23 * M_step[2]/2).dot(M_step)
return M_step
def Rotation_step(M, I_inv, t_step):
O = I_inv*M
angle = math.sqrt(O.dot(O))
O = O / angle
angle = t_step*angle
O = vector_to_matrix(O)
U = np.array([[1,0,0],[0,1,0],[0,0,1]])
U = U + math.sin(angle)*O + (1-math.cos(angle))*(O.dot(O))
return U
def Propagate_Angular_Momentum(M_initial, Inertia, n_iterations, t_step):
I_inv = np.array([1/Inertia[0], 1/Inertia[1], 1/Inertia[2]])
k_23 = I_inv[1]-I_inv[2]
k_21 = I_inv[1]-I_inv[0]
M_evolution = np.empty((3, n_iterations), dtype=float)
M_evolution[:,0] = M_initial.copy()
for i in range((n_iterations-1)):
M_evolution[:,i+1] = Angular_Momentum_step(M_evolution[:,i], k_23, k_21, t_step)
return M_evolution
def Propagate_Rotation(Body_initial, Moment_initial, Inertia, n_iterations, t_step):
I_inv = np.array([1/Inertia[0], 1/Inertia[1], 1/Inertia[2]])
k_23 = I_inv[1]-I_inv[2]
k_21 = I_inv[1]-I_inv[0]
Moment_evolution = np.empty((3, n_iterations), dtype=float)
Moment_evolution[:,0] = Moment_initial.copy()
Body_evolution = np.empty((3, n_iterations+1), dtype=float)
Body_evolution[:,0] = Body_initial.dopy()
for i in range((n_iterations-1)):
Moment_evolution[:,i+1] = Angular_Momentum_step(Moment_evolution[:,i], k_23, k_21, t_step)
Body_evolution[:,i+1] = Rotation_step(Moment_evolution[:,i], I_inv, t_step)
Body_evolution[:,i+1] = Body_evolution[:,i+1].dot(Body_evolution[:,i])
return Body_evolution, Moment_evolution
# a test example, set up the initial angular velocity and the
#
I1 = 2.35
I2 = 2.0
I3 = 1.0
I = np.array([I1, I2, I3])
O = np.array([0, 2, 0.95])
O = Rot_3(-math.pi*(30)/180).dot(O)
M = I*O
dt = 0.3
n_iter=500
# propagate the system
Momenta = Propagate_Angular_Momentum(M, I, n_iter, dt)
# plot
fig = plt.figure()
ax = fig.add_subplot(1,2,1,projection='3d')
ax.set_xlim((-4, 4))
ax.set_ylim((-4, 4))
ax.set_zlim((-4, 4))
ax.plot(Momenta[0,:], Momenta[1,:], Momenta[2,:], 'r-')
plt.show()
Observe that the angular momentum always traverses the red curve which is always on a 2D sphere. The curve looks closed, there is no spiraling ootwards any of the coordinate axes, which are aligned with the inertial axes in the body fixed frame.
