1 /* 2 * Licensed to the Hipparchus project under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The Hipparchus project licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * https://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package org.hipparchus.ode; 19 20 import org.hipparchus.special.elliptic.jacobi.CopolarN; 21 import org.hipparchus.special.elliptic.jacobi.JacobiElliptic; 22 import org.hipparchus.special.elliptic.jacobi.JacobiEllipticBuilder; 23 import org.hipparchus.util.FastMath; 24 25 /** 26 * This class is used in the junit tests for the ODE integrators. 27 28 * <p>This specific problem correspond to torque-free motion of a solid body 29 * with moments of inertia I₁, I₂, and I₃ with respect to body axes x, y, and z. 30 * We use here the notations from Landau and Lifchitz Course of Theoretical Physics, 31 * Mechanics vol 1. 32 * </p> 33 * <p> 34 * The equations of torque-free motion are given in the solid body frame by 35 * equation 36.5: 36 * <pre> 37 * I₁ dΩ₁/dt + (I₃ - I₂) Ω₂ Ω₃ = 0 38 * I₂ dΩ₂/dt + (I₁ - I₃) Ω₃ Ω₁ = 0 39 * I₃ dΩ₃/dt + (I₂ - I₁) Ω₁ Ω₂ = 0 40 * </pre> 41 * <p> 42 * The moments of inertia and initial conditions are: I₁ = 3/8, I₂ = 1/2, I₃ = 5/8, 43 * Ω₁ = 5, Ω₂ = 0, Ω₃ = 4. This corresponds to a motion with angular velocity 44 * describing a large polhode around Z axis in solid body frame. The motion is almost 45 * unstable as M² is only slightly greater than 2EI₂. Increasing Ω₁ to √(80/3) ≈ 5.16398 46 * would imply M²=2EI₂, and the polhode would degenerate to two intersecting ellipses. 47 * </p> 48 * <p> 49 * The torque-free motion can be solved analytically using Jacobi elliptic functions 50 * (in the rotating body frame) 51 * <pre> 52 * τ = t √([I₃-I₂][M²-2EI₁]/[I₁I₂I₃]) 53 * Ω₁ (τ) = √([2EI₃-M²]/[I₁(I₃-I₁)]) cn(τ) 54 * Ω₂ (τ) = √([2EI₃-M²]/[I₂(I₃-I₂)]) sn(τ) 55 * Ω₃ (τ) = √([M²-2EI₁]/[I₃(I₃-I₁)]) dn(τ) 56 * </pre> 57 * </p> 58 * <p> 59 * This problem solves only solves the rotation rate part, whereas {@code TestProblem8} 60 * solves the full motion (rotation rate and rotation). 61 * </p> 62 63 */ 64 public class TestProblem7 extends TestProblemAbstract { 65 66 /** Moments of inertia. */ 67 final double i1; 68 final double i2; 69 final double i3; 70 71 /** Twice the angular kinetic energy. */ 72 final double twoE; 73 74 final double m2; 75 76 /** Time scaling factor. */ 77 final double tScale; 78 79 /** State scaling factors. */ 80 final double o1Scale; 81 final double o2Scale; 82 final double o3Scale; 83 84 /** Jacobi elliptic function. */ 85 final JacobiElliptic jacobi; 86 87 /** 88 * Simple constructor. 89 */ 90 public TestProblem7() { 91 92 super(0.0, new double[] { 5.0, 0.0, 4.0 }, 4.0, new double[] { 1.0, 1.0, 1.0 }); 93 i1 = 3.0 / 8.0; 94 i2 = 1.0 / 2.0; 95 i3 = 5.0 / 8.0; 96 97 final double[] s0 = getInitialState().getPrimaryState(); 98 final double o12 = s0[0] * s0[0]; 99 final double o22 = s0[1] * s0[1]; 100 final double o32 = s0[2] * s0[2]; 101 twoE = i1 * o12 + i2 * o22 + i3 * o32; 102 m2 = i1 * i1 * o12 + i2 * i2 * o22 + i3 * i3 * o32; 103 tScale = FastMath.sqrt((i3 - i2) * (m2 - twoE * i1) / (i1 * i2 * i3)); 104 o1Scale = FastMath.sqrt((twoE * i3 - m2) / (i1 * (i3 - i1))); 105 o2Scale = FastMath.sqrt((twoE * i3 - m2) / (i2 * (i3 - i2))); 106 o3Scale = FastMath.sqrt((m2 - twoE * i1) / (i3 * (i3 - i1))); 107 108 final double k2 = (i2 - i1) * (twoE * i3 - m2) / ((i3 - i2) * (m2 - twoE * i1)); 109 jacobi = JacobiEllipticBuilder.build(k2); 110 111 } 112 113 @Override 114 public double[] doComputeDerivatives(double t, double[] y) { 115 116 final double[] yDot = new double[getDimension()]; 117 118 // compute the derivatives using Euler equations 119 yDot[0] = y[1] * y[2] * (i2 - i3) / i1; 120 yDot[1] = y[2] * y[0] * (i3 - i1) / i2; 121 yDot[2] = y[0] * y[1] * (i1 - i2) / i3; 122 123 return yDot; 124 125 } 126 127 @Override 128 public double[] computeTheoreticalState(double t) { 129 final CopolarN valuesN = jacobi.valuesN(t * tScale); 130 return new double[] { 131 o1Scale * valuesN.cn(), 132 o2Scale * valuesN.sn(), 133 o3Scale * valuesN.dn() 134 }; 135 } 136 137 }