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 }