1   
2   
3   
4   
5   
6   
7   
8   
9   
10  
11  
12  
13  
14  
15  
16  
17  package org.hipparchus.ode;
18  
19  import java.util.Arrays;
20  
21  import org.hipparchus.geometry.euclidean.threed.Rotation;
22  import org.hipparchus.geometry.euclidean.threed.RotationConvention;
23  import org.hipparchus.geometry.euclidean.threed.RotationOrder;
24  import org.hipparchus.geometry.euclidean.threed.Vector3D;
25  import org.hipparchus.linear.DecompositionSolver;
26  import org.hipparchus.linear.MatrixUtils;
27  import org.hipparchus.linear.QRDecomposer;
28  import org.hipparchus.linear.RealMatrix;
29  import org.hipparchus.linear.RealVector;
30  import org.hipparchus.ode.nonstiff.DormandPrince853Integrator;
31  import org.hipparchus.special.elliptic.jacobi.CopolarN;
32  import org.hipparchus.special.elliptic.jacobi.JacobiElliptic;
33  import org.hipparchus.special.elliptic.jacobi.JacobiEllipticBuilder;
34  import org.hipparchus.special.elliptic.legendre.LegendreEllipticIntegral;
35  import org.hipparchus.util.FastMath;
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  public class TestProblem8 extends TestProblemAbstract {
59  
60      
61      final RealMatrix inertiaTensor;
62  
63      
64      final DecompositionSolver inertiaSolver;
65  
66      
67      final Inertia sortedInertia;
68  
69      
70      final double o1Scale;
71  
72      
73      final double o2Scale;
74  
75      
76      final double o3Scale;
77  
78      
79      final JacobiElliptic jacobi;
80  
81      
82      public final double tScale;
83  
84      
85      final double tRef;
86  
87      
88      Rotation inertToAligned;
89  
90      
91      final Rotation sortedToBody;
92  
93      
94      final double period;
95  
96      
97      final double phiSlope;
98  
99      
100     final DenseOutputModel phiQuadratureModel;
101 
102     
103     final double integOnePeriod;
104 
105     
106 
107 
108 
109 
110 
111 
112 
113 
114 
115 
116 
117 
118     public TestProblem8(final double t0, final double t1, final Vector3D omega0, final Rotation r0,
119                         final double i1, final Vector3D a1,
120                         final double i2, final Vector3D a2,
121                         final double i3, final Vector3D a3) {
122         
123         
124         super(t0,
125               new double[] {
126                   omega0.getX(), omega0.getY(), omega0.getZ(),
127                   r0.getQ0(), r0.getQ1(), r0.getQ2(), r0.getQ3()
128               },
129               t1,
130               new double[] { 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 });
131 
132         
133         final Vector3D n1  = a1.normalize();
134         final Vector3D n2  = a2.normalize();
135         final Vector3D n3  = (Vector3D.dotProduct(Vector3D.crossProduct(a1, a2), a3) > 0 ?
136                               a3.normalize() : a3.normalize().negate());
137         final RealMatrix q = MatrixUtils.createRealMatrix(3, 3);
138         q.setEntry(0, 0, n1.getX());
139         q.setEntry(0, 1, n1.getY());
140         q.setEntry(0, 2, n1.getZ());
141         q.setEntry(1, 0, n2.getX());
142         q.setEntry(1, 1, n2.getY());
143         q.setEntry(1, 2, n2.getZ());
144         q.setEntry(2, 0, n3.getX());
145         q.setEntry(2, 1, n3.getY());
146         q.setEntry(2, 2, n3.getZ());
147         final RealMatrix d = MatrixUtils.createRealDiagonalMatrix(new double[] { i1, i2, i3});
148         this.inertiaTensor = q.multiply(d.multiplyTransposed(q));
149         this.inertiaSolver = new QRDecomposer(1.0e-10).decompose(inertiaTensor);
150 
151         final Vector3D m0 = new Vector3D(i1 * Vector3D.dotProduct(omega0, n1), n1,
152                                          i2 * Vector3D.dotProduct(omega0, n2), n2,
153                                          i3 * Vector3D.dotProduct(omega0, n3), n3);
154 
155         
156         Inertia inertia = new Inertia(new InertiaAxis(i1, n1), new InertiaAxis(i2, n2), new InertiaAxis(i3, n3));
157         if (inertia.getInertiaAxis1().getI() > inertia.getInertiaAxis2().getI()) {
158             inertia = inertia.swap12();
159         }
160         if (inertia.getInertiaAxis2().getI() > inertia.getInertiaAxis3().getI()) {
161             inertia = inertia.swap23();
162         }
163         if (inertia.getInertiaAxis1().getI() > inertia.getInertiaAxis2().getI()) {
164             inertia = inertia.swap12();
165         }
166 
167         
168         
169         final double  o1                = Vector3D.dotProduct(omega0, n1);
170         final double  o2                = Vector3D.dotProduct(omega0, n2);
171         final double  o3                = Vector3D.dotProduct(omega0, n3);
172         final double  o12               = o1 * o1;
173         final double  o22               = o2 * o2;
174         final double  o32               = o3 * o3;
175         final double  twoE              = i1 * o12 + i2 * o22 + i3 * o32;
176         final double  m2                = i1 * i1 * o12 + i2 * i2 * o22 + i3 * i3 * o32;
177         final double  separatrixInertia = (twoE == 0) ? 0.0 : m2 / twoE;
178         final boolean clockwise;
179         if (separatrixInertia < inertia.getInertiaAxis2().getI()) {
180             
181             
182             
183             clockwise = true;
184             inertia   = inertia.swap13();
185         } else {
186             
187             
188             
189             clockwise = false;
190         }
191         sortedInertia = inertia;
192 
193         final double i1C = inertia.getInertiaAxis1().getI();
194         final double i2C = inertia.getInertiaAxis2().getI();
195         final double i3C = inertia.getInertiaAxis3().getI();
196         final double i32 = i3C - i2C;
197         final double i31 = i3C - i1C;
198         final double i21 = i2C - i1C;
199 
200         
201         sortedToBody   = new Rotation(Vector3D.PLUS_I, Vector3D.PLUS_J, inertia.getInertiaAxis1().getA(), inertia.getInertiaAxis2().getA());
202         final Vector3D omega0Sorted = sortedToBody.applyInverseTo(omega0);
203         final Vector3D m0Sorted     = sortedToBody.applyInverseTo(m0);
204         final double   phi0         = 0; 
205         final double   theta0       = FastMath.acos(m0Sorted.getZ() / m0Sorted.getNorm());
206         final double   psi0         = FastMath.atan2(m0Sorted.getX(), m0Sorted.getY()); 
207 
208         
209         final Rotation alignedToSorted0 = new Rotation(RotationOrder.ZXZ, RotationConvention.FRAME_TRANSFORM,
210                                                        phi0, theta0, psi0);
211         inertToAligned = alignedToSorted0.applyInverseTo(sortedToBody.applyInverseTo(r0));
212 
213         
214         tScale  = FastMath.copySign(FastMath.sqrt(i32 * (m2 - twoE * i1C) / (i1C * i2C * i3C)),
215                                     clockwise ? -omega0Sorted.getZ() : omega0Sorted.getZ());
216         o1Scale = FastMath.sqrt((twoE * i3C - m2) / (i1C * i31));
217         o2Scale = FastMath.sqrt((twoE * i3C - m2) / (i2C * i32));
218         o3Scale = FastMath.copySign(FastMath.sqrt((m2 - twoE * i1C) / (i3C * i31)), omega0Sorted.getZ());
219 
220         final double k2 = (twoE == 0) ? 0.0 : i21 * (twoE * i3C - m2) / (i32 * (m2 - twoE * i1C));
221         jacobi = JacobiEllipticBuilder.build(k2);
222         period = 4 * LegendreEllipticIntegral.bigK(k2) / tScale;
223 
224         if (o1Scale == 0) {
225             
226             
227             tRef = t0;
228         } else {
229             if (FastMath.abs(omega0Sorted.getX()) >= FastMath.abs(omega0Sorted.getY())) {
230                 if (omega0Sorted.getX() >= 0) {
231                     
232                     tRef = t0 - jacobi.arcsn(omega0Sorted.getY() / o2Scale) / tScale;
233                 } else {
234                     
235                     tRef = t0 + jacobi.arcsn(omega0Sorted.getY() / o2Scale) / tScale - 0.5 * period;
236                 }
237             } else {
238                 if (omega0Sorted.getY() >= 0) {
239                     
240                     tRef = t0 - jacobi.arccn(omega0Sorted.getX() / o1Scale) / tScale;
241                 } else {
242                     
243                     tRef = t0 + jacobi.arccn(omega0Sorted.getX() / o1Scale) / tScale;
244                 }
245             }
246         }
247 
248         phiSlope           = FastMath.sqrt(m2) / i3C;
249         phiQuadratureModel = computePhiQuadratureModel(t0);
250         integOnePeriod     = phiQuadratureModel.getInterpolatedState(phiQuadratureModel.getFinalTime()).getPrimaryState()[0];
251 
252     }
253 
254     private DenseOutputModel computePhiQuadratureModel(final double t0) {
255 
256         final double i1C = sortedInertia.getInertiaAxis1().getI();
257         final double i2C = sortedInertia.getInertiaAxis2().getI();
258         final double i3C = sortedInertia.getInertiaAxis3().getI();
259 
260         final double i32 = i3C - i2C;
261         final double i31 = i3C - i1C;
262         final double i21 = i2C - i1C;
263 
264         
265         final double b = phiSlope * i32 * i31;
266         final double c = i1C * i32;
267         final double d = i3C * i21;
268 
269         
270         final DormandPrince853Integrator integ = new DormandPrince853Integrator(1.0e-6 * period, 1.0e-2 * period,
271                                                                                 phiSlope * period * 1.0e-13, 1.0e-13);
272         final DenseOutputModel model = new DenseOutputModel();
273         integ.addStepHandler(model);
274 
275         integ.integrate(new OrdinaryDifferentialEquation() {
276 
277             
278             @Override
279             public int getDimension() {
280                 return 1;
281             }
282 
283             
284            @Override
285             public double[] computeDerivatives(final double t, final double[] y) {
286                 final double sn = jacobi.valuesN((t - tRef) * tScale).sn();
287                 return new double[] {
288                         b / (c + d * sn * sn)
289                 };
290             }
291 
292         }, new ODEState(t0, new double[1]), t0 + period);
293 
294         return model;
295 
296     }
297 
298     public double[] computeTheoreticalState(double t) {
299 
300         final double t0            = getInitialTime();
301 
302         
303         final CopolarN valuesN     = jacobi.valuesN((t - tRef) * tScale);
304         final Vector3D omegaSorted = new Vector3D(o1Scale * valuesN.cn(), o2Scale * valuesN.sn(), o3Scale * valuesN.dn());
305         final Vector3D omegaBody   = sortedToBody.applyTo(omegaSorted);
306 
307         
308         final double   psi         = FastMath.atan2(sortedInertia.getInertiaAxis1().getI() * omegaSorted.getX(),
309                                                     sortedInertia.getInertiaAxis2().getI() * omegaSorted.getY());
310         final double   theta       = FastMath.acos(omegaSorted.getZ() / phiSlope);
311         final double   phiLinear   = phiSlope * (t - t0);
312 
313         
314         final int    nbPeriods     = (int) FastMath.floor((t - t0) / period);
315         final double tStartInteg   = t0 + nbPeriods * period;
316         final double integPartial  = phiQuadratureModel.getInterpolatedState(t - tStartInteg).getPrimaryState()[0];
317         final double phiQuadrature = nbPeriods * integOnePeriod + integPartial;
318         final double phi           = phiLinear + phiQuadrature;
319 
320         
321         
322         final Rotation alignedToSorted = new Rotation(RotationOrder.ZXZ, RotationConvention.FRAME_TRANSFORM,
323                                                       phi, theta, psi);
324 
325         
326         Rotation inertToBody = sortedToBody.applyTo(alignedToSorted.applyTo(inertToAligned));
327 
328         return new double[] {
329             omegaBody.getX(), omegaBody.getY(), omegaBody.getZ(),
330             inertToBody.getQ0(), inertToBody.getQ1(), inertToBody.getQ2(), inertToBody.getQ3()
331         };
332 
333     }
334 
335     public double[] doComputeDerivatives(double t, double[] y) {
336 
337         final  double[] yDot = new double[getDimension()];
338 
339         
340         final double[]   omega    = Arrays.copyOfRange(y, 0, 3);
341         final double[]   minusOiO = Vector3D.crossProduct(new Vector3D(omega),
342                                                           new Vector3D(inertiaTensor.operate(omega))).negate().toArray();
343         final RealVector omegaDot = inertiaSolver.solve(MatrixUtils.createRealVector(minusOiO));
344         yDot[0] = omegaDot.getEntry(0);
345         yDot[1] = omegaDot.getEntry(1);
346         yDot[2] = omegaDot.getEntry(2);
347 
348         
349         yDot[3] = 0.5 * (-y[0] * y[4] - y[1] * y[5] - y[2] * y[6]);
350         yDot[4] = 0.5 * ( y[0] * y[3] + y[2] * y[5] - y[1] * y[6]);
351         yDot[5] = 0.5 * ( y[1] * y[3] - y[2] * y[4] + y[0] * y[6]);
352         yDot[6] = 0.5 * ( y[2] * y[3] + y[1] * y[4] - y[0] * y[5]);
353 
354         return yDot;
355 
356     }
357 
358     
359 
360 
361 
362 
363    public static class Inertia {
364 
365        
366        private final InertiaAxis iA1;
367 
368        
369        private final InertiaAxis iA2;
370 
371        
372        private final InertiaAxis iA3;
373 
374        
375 
376 
377 
378 
379        public Inertia(final InertiaAxis iA1, final InertiaAxis iA2, final InertiaAxis iA3) {
380            this.iA1 = iA1;
381            this.iA2 = iA2;
382            this.iA3 = iA3;
383        }
384 
385        
386 
387 
388        public Inertia swap12() {
389            return new Inertia(iA2, iA1, iA3.negate());
390        }
391 
392        
393 
394 
395        public Inertia swap13() {
396            return new Inertia(iA3, iA2.negate(), iA1);
397        }
398 
399        
400 
401 
402        public Inertia swap23() {
403            return new Inertia(iA1.negate(), iA3, iA2);
404        }
405 
406        
407 
408 
409        public InertiaAxis getInertiaAxis1() {
410            return iA1;
411        }
412 
413        
414 
415 
416        public InertiaAxis getInertiaAxis2() {
417            return iA2;
418        }
419 
420        
421 
422 
423        public InertiaAxis getInertiaAxis3() {
424            return iA3;
425        }
426 
427    }
428 
429    
430 
431 
432 
433 
434     public static class InertiaAxis {
435 
436         
437         private final double i;
438 
439         
440         private final Vector3D a;
441 
442         
443 
444 
445 
446         public InertiaAxis(final double i, final Vector3D a) {
447             this.i = i;
448             this.a = a;
449         }
450 
451         
452 
453 
454         public InertiaAxis negate() {
455             return new InertiaAxis(i, a.negate());
456         }
457 
458         
459 
460 
461         public double getI() {
462             return i;
463         }
464 
465         
466 
467 
468         public Vector3D getA() {
469             return a;
470         }
471 
472     }
473 
474 }