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