1 /* Copyright 2002-2017 CS Systèmes d'Information
2 * Licensed to CS Systèmes d'Information (CS) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * CS 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 * http://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 package org.orekit.propagation.semianalytical.dsst.forces;
18
19 import java.io.NotSerializableException;
20 import java.io.Serializable;
21 import java.util.ArrayList;
22 import java.util.HashMap;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.Set;
26 import java.util.SortedSet;
27 import java.util.TreeMap;
28
29 import org.hipparchus.analysis.differentiation.DSFactory;
30 import org.hipparchus.analysis.differentiation.DerivativeStructure;
31 import org.hipparchus.geometry.euclidean.threed.Vector3D;
32 import org.hipparchus.util.FastMath;
33 import org.orekit.attitudes.AttitudeProvider;
34 import org.orekit.bodies.CelestialBody;
35 import org.orekit.errors.OrekitException;
36 import org.orekit.orbits.Orbit;
37 import org.orekit.propagation.SpacecraftState;
38 import org.orekit.propagation.events.EventDetector;
39 import org.orekit.propagation.semianalytical.dsst.utilities.AuxiliaryElements;
40 import org.orekit.propagation.semianalytical.dsst.utilities.CjSjCoefficient;
41 import org.orekit.propagation.semianalytical.dsst.utilities.CoefficientsFactory;
42 import org.orekit.propagation.semianalytical.dsst.utilities.CoefficientsFactory.NSKey;
43 import org.orekit.propagation.semianalytical.dsst.utilities.JacobiPolynomials;
44 import org.orekit.propagation.semianalytical.dsst.utilities.ShortPeriodicsInterpolatedCoefficient;
45 import org.orekit.propagation.semianalytical.dsst.utilities.UpperBounds;
46 import org.orekit.propagation.semianalytical.dsst.utilities.hansen.HansenThirdBodyLinear;
47 import org.orekit.time.AbsoluteDate;
48 import org.orekit.utils.TimeSpanMap;
49
50 /** Third body attraction perturbation to the
51 * {@link org.orekit.propagation.semianalytical.dsst.DSSTPropagator DSSTPropagator}.
52 *
53 * @author Romain Di Costanzo
54 * @author Pascal Parraud
55 */
56 public class DSSTThirdBody implements DSSTForceModel {
57
58 /** Max power for summation. */
59 private static final int MAX_POWER = 22;
60
61 /** Truncation tolerance for big, eccentric orbits. */
62 private static final double BIG_TRUNCATION_TOLERANCE = 1.e-1;
63
64 /** Truncation tolerance for small orbits. */
65 private static final double SMALL_TRUNCATION_TOLERANCE = 1.9e-6;
66
67 /** Number of points for interpolation. */
68 private static final int INTERPOLATION_POINTS = 3;
69
70 /** Maximum power for eccentricity used in short periodic computation. */
71 private static final int MAX_ECCPOWER_SP = 4;
72
73 /** Retrograde factor I.
74 * <p>
75 * DSST model needs equinoctial orbit as internal representation.
76 * Classical equinoctial elements have discontinuities when inclination
77 * is close to zero. In this representation, I = +1. <br>
78 * To avoid this discontinuity, another representation exists and equinoctial
79 * elements can be expressed in a different way, called "retrograde" orbit.
80 * This implies I = -1. <br>
81 * As Orekit doesn't implement the retrograde orbit, I is always set to +1.
82 * But for the sake of consistency with the theory, the retrograde factor
83 * has been kept in the formulas.
84 * </p>
85 */
86 private static final int I = 1;
87
88 /** The 3rd body to consider. */
89 private final CelestialBody body;
90
91 /** Standard gravitational parameter μ for the body in m³/s². */
92 private final double gm;
93
94 /** Factorial. */
95 private final double[] fact;
96
97 /** V<sub>ns</sub> coefficients. */
98 private final TreeMap<NSKey, Double> Vns;
99
100 /** Distance from center of mass of the central body to the 3rd body. */
101 private double R3;
102
103 /** Short period terms. */
104 private ThirdBodyShortPeriodicCoefficients shortPeriods;
105
106 // Equinoctial elements (according to DSST notation)
107 /** a. */
108 private double a;
109 /** e<sub>x</sub>. */
110 private double k;
111 /** e<sub>y</sub>. */
112 private double h;
113 /** h<sub>x</sub>. */
114 private double q;
115 /** h<sub>y</sub>. */
116 private double p;
117
118 /** Eccentricity. */
119 private double ecc;
120
121 // Direction cosines of the symmetry axis
122 /** α. */
123 private double alpha;
124 /** β. */
125 private double beta;
126 /** γ. */
127 private double gamma;
128
129 // Common factors for potential computation
130 /** A = n * a². */
131 private double A;
132 /** B = sqrt(1 - e²). */
133 private double B;
134 /** C = 1 + p² + q². */
135 private double C;
136 /** B². */
137 private double BB;
138 /** B³. */
139 private double BBB;
140
141 /** The mean motion (n). */
142 private double meanMotion;
143
144 /** Χ = 1 / sqrt(1 - e²) = 1 / B. */
145 private double X;
146 /** Χ². */
147 private double XX;
148 /** Χ³. */
149 private double XXX;
150 /** -2 * a / A. */
151 private double m2aoA;
152 /** B / A. */
153 private double BoA;
154 /** 1 / (A * B). */
155 private double ooAB;
156 /** -C / (2 * A * B). */
157 private double mCo2AB;
158 /** B / A(1 + B). */
159 private double BoABpo;
160
161 /** Max power for a/R3 in the serie expansion. */
162 private int maxAR3Pow;
163
164 /** Max power for e in the serie expansion. */
165 private int maxEccPow;
166
167 /** Max power for e in the serie expansion (for short periodics). */
168 private int maxEccPowShort;
169
170 /** Max frequency of F. */
171 private int maxFreqF;
172
173 /** An array that contains the objects needed to build the Hansen coefficients. <br/>
174 * The index is s */
175 private final HansenThirdBodyLinear[] hansenObjects;
176
177 /** The current value of the U function. <br/>
178 * Needed for the short periodic contribution */
179 private double U;
180
181 /** Qns coefficients. */
182 private double[][] Qns;
183 /** a / R3 up to power maxAR3Pow. */
184 private double[] aoR3Pow;
185 /** mu3 / R3. */
186 private double muoR3;
187
188 /** b = 1 / (1 + sqrt(1 - e²)) = 1 / (1 + B).*/
189 private double b;
190
191 /** h * Χ³. */
192 private double hXXX;
193 /** k * Χ³. */
194 private double kXXX;
195
196 /** Factory for the DerivativeStructure instances. */
197 private final DSFactory factory;
198
199 /** Complete constructor.
200 * @param body the 3rd body to consider
201 * @see org.orekit.bodies.CelestialBodyFactory
202 */
203 public DSSTThirdBody(final CelestialBody body) {
204 this.body = body;
205 this.gm = body.getGM();
206
207 this.maxAR3Pow = Integer.MIN_VALUE;
208 this.maxEccPow = Integer.MIN_VALUE;
209
210 this.Vns = CoefficientsFactory.computeVns(MAX_POWER);
211
212 // Factorials computation
213 final int dim = 2 * MAX_POWER;
214 this.fact = new double[dim];
215 fact[0] = 1.;
216 for (int i = 1; i < dim; i++) {
217 fact[i] = i * fact[i - 1];
218 }
219
220 //Initialise the HansenCoefficient generator
221 this.hansenObjects = new HansenThirdBodyLinear[MAX_POWER + 1];
222 for (int s = 0; s <= MAX_POWER; s++) {
223 this.hansenObjects[s] = new HansenThirdBodyLinear(MAX_POWER, s);
224 }
225
226 this.factory = new DSFactory(1, 1);
227
228 }
229
230 /** Get third body.
231 * @return third body
232 */
233 public CelestialBody getBody() {
234 return body;
235 }
236
237 /** Computes the highest power of the eccentricity and the highest power
238 * of a/R3 to appear in the truncated analytical power series expansion.
239 * <p>
240 * This method computes the upper value for the 3rd body potential and
241 * determines the maximal powers for the eccentricity and a/R3 producing
242 * potential terms bigger than a defined tolerance.
243 * </p>
244 * @param aux auxiliary elements related to the current orbit
245 * @param meanOnly only mean elements will be used for the propagation
246 * @throws OrekitException if some specific error occurs
247 */
248 @Override
249 public List<ShortPeriodTerms> initialize(final AuxiliaryElements aux, final boolean meanOnly)
250 throws OrekitException {
251
252 // Initializes specific parameters.
253 initializeStep(aux);
254
255 // Truncation tolerance.
256 final double aor = a / R3;
257 final double tol = ( aor > .3 || (aor > .15 && ecc > .25) ) ? BIG_TRUNCATION_TOLERANCE : SMALL_TRUNCATION_TOLERANCE;
258
259 // Utilities for truncation
260 // Set a lower bound for eccentricity
261 final double eo2 = FastMath.max(0.0025, ecc / 2.);
262 final double x2o2 = XX / 2.;
263 final double[] eccPwr = new double[MAX_POWER];
264 final double[] chiPwr = new double[MAX_POWER];
265 eccPwr[0] = 1.;
266 chiPwr[0] = X;
267 for (int i = 1; i < MAX_POWER; i++) {
268 eccPwr[i] = eccPwr[i - 1] * eo2;
269 chiPwr[i] = chiPwr[i - 1] * x2o2;
270 }
271
272 // Auxiliary quantities.
273 final double ao2rxx = aor / (2. * XX);
274 double xmuarn = ao2rxx * ao2rxx * gm / (X * R3);
275 double term = 0.;
276
277 // Compute max power for a/R3 and e.
278 maxAR3Pow = 2;
279 maxEccPow = 0;
280 int n = 2;
281 int m = 2;
282 int nsmd2 = 0;
283
284 do {
285 // Upper bound for Tnm.
286 term = xmuarn *
287 (fact[n + m] / (fact[nsmd2] * fact[nsmd2 + m])) *
288 (fact[n + m + 1] / (fact[m] * fact[n + 1])) *
289 (fact[n - m + 1] / fact[n + 1]) *
290 eccPwr[m] * UpperBounds.getDnl(XX, chiPwr[m], n + 2, m);
291
292 if (term < tol) {
293 if (m == 0) {
294 break;
295 } else if (m < 2) {
296 xmuarn *= ao2rxx;
297 m = 0;
298 n++;
299 nsmd2++;
300 } else {
301 m -= 2;
302 nsmd2++;
303 }
304 } else {
305 maxAR3Pow = n;
306 maxEccPow = FastMath.max(m, maxEccPow);
307 xmuarn *= ao2rxx;
308 m++;
309 n++;
310 }
311 } while (n < MAX_POWER);
312
313 maxEccPow = FastMath.min(maxAR3Pow, maxEccPow);
314
315 // allocate the array aoR3Pow
316 aoR3Pow = new double[maxAR3Pow + 1];
317
318 maxFreqF = maxAR3Pow + 1;
319 maxEccPowShort = MAX_ECCPOWER_SP;
320
321 Qns = CoefficientsFactory.computeQns(gamma, maxAR3Pow, FastMath.max(maxEccPow, maxEccPowShort));
322 final int jMax = maxAR3Pow + 1;
323 shortPeriods = new ThirdBodyShortPeriodicCoefficients(jMax, INTERPOLATION_POINTS,
324 maxFreqF, body.getName(),
325 new TimeSpanMap<Slot>(new Slot(jMax, INTERPOLATION_POINTS)));
326
327 final List<ShortPeriodTerms> list = new ArrayList<ShortPeriodTerms>();
328 list.add(shortPeriods);
329 return list;
330
331 }
332
333 /** {@inheritDoc} */
334 @Override
335 public void initializeStep(final AuxiliaryElements aux) throws OrekitException {
336
337 // Equinoctial elements
338 a = aux.getSma();
339 k = aux.getK();
340 h = aux.getH();
341 q = aux.getQ();
342 p = aux.getP();
343
344 // Eccentricity
345 ecc = aux.getEcc();
346
347 // Distance from center of mass of the central body to the 3rd body
348 final Vector3D bodyPos = body.getPVCoordinates(aux.getDate(), aux.getFrame()).getPosition();
349 R3 = bodyPos.getNorm();
350
351 // Direction cosines
352 final Vector3D bodyDir = bodyPos.normalize();
353 alpha = bodyDir.dotProduct(aux.getVectorF());
354 beta = bodyDir.dotProduct(aux.getVectorG());
355 gamma = bodyDir.dotProduct(aux.getVectorW());
356
357 // Equinoctial coefficients
358 A = aux.getA();
359 B = aux.getB();
360 C = aux.getC();
361 meanMotion = aux.getMeanMotion();
362
363 //Χ<sup>-2</sup>.
364 BB = B * B;
365 //Χ<sup>-3</sup>.
366 BBB = BB * B;
367
368 //b = 1 / (1 + B)
369 b = 1. / (1. + B);
370
371 // Χ
372 X = 1. / B;
373 XX = X * X;
374 XXX = X * XX;
375 // -2 * a / A
376 m2aoA = -2. * a / A;
377 // B / A
378 BoA = B / A;
379 // 1 / AB
380 ooAB = 1. / (A * B);
381 // -C / 2AB
382 mCo2AB = -C * ooAB / 2.;
383 // B / A(1 + B)
384 BoABpo = BoA / (1. + B);
385
386 // mu3 / R3
387 muoR3 = gm / R3;
388
389 //h * Χ³
390 hXXX = h * XXX;
391 //k * Χ³
392 kXXX = k * XXX;
393 }
394
395 /** {@inheritDoc} */
396 @Override
397 public double[] getMeanElementRate(final SpacecraftState currentState) {
398
399 // Qns coefficients
400 Qns = CoefficientsFactory.computeQns(gamma, maxAR3Pow, maxEccPow);
401 // a / R3 up to power maxAR3Pow
402 final double aoR3 = a / R3;
403 aoR3Pow[0] = 1.;
404 for (int i = 1; i <= maxAR3Pow; i++) {
405 aoR3Pow[i] = aoR3 * aoR3Pow[i - 1];
406 }
407
408 // Compute potential U derivatives
409 final double[] dU = computeUDerivatives();
410 final double dUda = dU[0];
411 final double dUdk = dU[1];
412 final double dUdh = dU[2];
413 final double dUdAl = dU[3];
414 final double dUdBe = dU[4];
415 final double dUdGa = dU[5];
416
417 // Compute cross derivatives [Eq. 2.2-(8)]
418 // U(alpha,gamma) = alpha * dU/dgamma - gamma * dU/dalpha
419 final double UAlphaGamma = alpha * dUdGa - gamma * dUdAl;
420 // U(beta,gamma) = beta * dU/dgamma - gamma * dU/dbeta
421 final double UBetaGamma = beta * dUdGa - gamma * dUdBe;
422 // Common factor
423 final double pUAGmIqUBGoAB = (p * UAlphaGamma - I * q * UBetaGamma) * ooAB;
424
425 // Compute mean elements rates [Eq. 3.1-(1)]
426 final double da = 0.;
427 final double dh = BoA * dUdk + k * pUAGmIqUBGoAB;
428 final double dk = -BoA * dUdh - h * pUAGmIqUBGoAB;
429 final double dp = mCo2AB * UBetaGamma;
430 final double dq = mCo2AB * UAlphaGamma * I;
431 final double dM = m2aoA * dUda + BoABpo * (h * dUdh + k * dUdk) + pUAGmIqUBGoAB;
432
433 return new double[] {da, dk, dh, dq, dp, dM};
434
435 }
436
437 /** {@inheritDoc} */
438 @Override
439 public void updateShortPeriodTerms(final SpacecraftState... meanStates)
440 throws OrekitException {
441
442 final Slot slot = shortPeriods.createSlot(meanStates);
443
444 for (final SpacecraftState meanState : meanStates) {
445
446 initializeStep(new AuxiliaryElements(meanState.getOrbit(), I));
447
448 // a / R3 up to power maxAR3Pow
449 final double aoR3 = a / R3;
450 aoR3Pow[0] = 1.;
451 for (int i = 1; i <= maxAR3Pow; i++) {
452 aoR3Pow[i] = aoR3 * aoR3Pow[i - 1];
453 }
454
455 // Qns coefficients
456 Qns = CoefficientsFactory.computeQns(gamma, maxAR3Pow, FastMath.max(maxEccPow, maxEccPowShort));
457 final GeneratingFunctionCoefficients gfCoefs =
458 new GeneratingFunctionCoefficients(maxAR3Pow, MAX_ECCPOWER_SP, maxAR3Pow + 1);
459
460 //Compute additional quantities
461 // 2 * a / An
462 final double ax2oAn = -m2aoA / meanMotion;
463 // B / An
464 final double BoAn = BoA / meanMotion;
465 // 1 / ABn
466 final double ooABn = ooAB / meanMotion;
467 // C / 2ABn
468 final double Co2ABn = -mCo2AB / meanMotion;
469 // B / (A * (1 + B) * n)
470 final double BoABpon = BoABpo / meanMotion;
471 // -3 / n²a² = -3 / nA
472 final double m3onA = -3 / (A * meanMotion);
473
474 //Compute the C<sub>i</sub><sup>j</sup> and S<sub>i</sub><sup>j</sup> coefficients.
475 for (int j = 1; j < slot.cij.length; j++) {
476 // First compute the C<sub>i</sub><sup>j</sup> coefficients
477 final double[] currentCij = new double[6];
478
479 // Compute the cross derivatives operator :
480 final double SAlphaGammaCj = alpha * gfCoefs.getdSdgammaCj(j) - gamma * gfCoefs.getdSdalphaCj(j);
481 final double SAlphaBetaCj = alpha * gfCoefs.getdSdbetaCj(j) - beta * gfCoefs.getdSdalphaCj(j);
482 final double SBetaGammaCj = beta * gfCoefs.getdSdgammaCj(j) - gamma * gfCoefs.getdSdbetaCj(j);
483 final double ShkCj = h * gfCoefs.getdSdkCj(j) - k * gfCoefs.getdSdhCj(j);
484 final double pSagmIqSbgoABnCj = (p * SAlphaGammaCj - I * q * SBetaGammaCj) * ooABn;
485 final double ShkmSabmdSdlCj = ShkCj - SAlphaBetaCj - gfCoefs.getdSdlambdaCj(j);
486
487 currentCij[0] = ax2oAn * gfCoefs.getdSdlambdaCj(j);
488 currentCij[1] = -(BoAn * gfCoefs.getdSdhCj(j) + h * pSagmIqSbgoABnCj + k * BoABpon * gfCoefs.getdSdlambdaCj(j));
489 currentCij[2] = BoAn * gfCoefs.getdSdkCj(j) + k * pSagmIqSbgoABnCj - h * BoABpon * gfCoefs.getdSdlambdaCj(j);
490 currentCij[3] = Co2ABn * (q * ShkmSabmdSdlCj - I * SAlphaGammaCj);
491 currentCij[4] = Co2ABn * (p * ShkmSabmdSdlCj - SBetaGammaCj);
492 currentCij[5] = -ax2oAn * gfCoefs.getdSdaCj(j) + BoABpon * (h * gfCoefs.getdSdhCj(j) + k * gfCoefs.getdSdkCj(j)) + pSagmIqSbgoABnCj + m3onA * gfCoefs.getSCj(j);
493
494 // add the computed coefficients to the interpolators
495 slot.cij[j].addGridPoint(meanState.getDate(), currentCij);
496
497 // Compute the S<sub>i</sub><sup>j</sup> coefficients
498 final double[] currentSij = new double[6];
499
500 // Compute the cross derivatives operator :
501 final double SAlphaGammaSj = alpha * gfCoefs.getdSdgammaSj(j) - gamma * gfCoefs.getdSdalphaSj(j);
502 final double SAlphaBetaSj = alpha * gfCoefs.getdSdbetaSj(j) - beta * gfCoefs.getdSdalphaSj(j);
503 final double SBetaGammaSj = beta * gfCoefs.getdSdgammaSj(j) - gamma * gfCoefs.getdSdbetaSj(j);
504 final double ShkSj = h * gfCoefs.getdSdkSj(j) - k * gfCoefs.getdSdhSj(j);
505 final double pSagmIqSbgoABnSj = (p * SAlphaGammaSj - I * q * SBetaGammaSj) * ooABn;
506 final double ShkmSabmdSdlSj = ShkSj - SAlphaBetaSj - gfCoefs.getdSdlambdaSj(j);
507
508 currentSij[0] = ax2oAn * gfCoefs.getdSdlambdaSj(j);
509 currentSij[1] = -(BoAn * gfCoefs.getdSdhSj(j) + h * pSagmIqSbgoABnSj + k * BoABpon * gfCoefs.getdSdlambdaSj(j));
510 currentSij[2] = BoAn * gfCoefs.getdSdkSj(j) + k * pSagmIqSbgoABnSj - h * BoABpon * gfCoefs.getdSdlambdaSj(j);
511 currentSij[3] = Co2ABn * (q * ShkmSabmdSdlSj - I * SAlphaGammaSj);
512 currentSij[4] = Co2ABn * (p * ShkmSabmdSdlSj - SBetaGammaSj);
513 currentSij[5] = -ax2oAn * gfCoefs.getdSdaSj(j) + BoABpon * (h * gfCoefs.getdSdhSj(j) + k * gfCoefs.getdSdkSj(j)) + pSagmIqSbgoABnSj + m3onA * gfCoefs.getSSj(j);
514
515 // add the computed coefficients to the interpolators
516 slot.sij[j].addGridPoint(meanState.getDate(), currentSij);
517
518 if (j == 1) {
519 //Compute the C⁰ coefficients using Danielson 2.5.2-15a.
520 final double[] value = new double[6];
521 for (int i = 0; i < 6; ++i) {
522 value[i] = currentCij[i] * k / 2. + currentSij[i] * h / 2.;
523 }
524 slot.cij[0].addGridPoint(meanState.getDate(), value);
525 }
526 }
527 }
528 }
529
530 /** {@inheritDoc} */
531 @Override
532 public EventDetector[] getEventsDetectors() {
533 return null;
534 }
535
536 /** Compute potential derivatives.
537 * @return derivatives of the potential with respect to orbital parameters
538 */
539 private double[] computeUDerivatives() {
540
541 // Gs and Hs coefficients
542 final double[][] GsHs = CoefficientsFactory.computeGsHs(k, h, alpha, beta, maxEccPow);
543
544 // Initialise U.
545 U = 0.;
546
547 // Potential derivatives
548 double dUda = 0.;
549 double dUdk = 0.;
550 double dUdh = 0.;
551 double dUdAl = 0.;
552 double dUdBe = 0.;
553 double dUdGa = 0.;
554
555 for (int s = 0; s <= maxEccPow; s++) {
556 // initialise the Hansen roots
557 this.hansenObjects[s].computeInitValues(B, BB, BBB);
558
559 // Get the current Gs coefficient
560 final double gs = GsHs[0][s];
561
562 // Compute Gs partial derivatives from 3.1-(9)
563 double dGsdh = 0.;
564 double dGsdk = 0.;
565 double dGsdAl = 0.;
566 double dGsdBe = 0.;
567 if (s > 0) {
568 // First get the G(s-1) and the H(s-1) coefficients
569 final double sxGsm1 = s * GsHs[0][s - 1];
570 final double sxHsm1 = s * GsHs[1][s - 1];
571 // Then compute derivatives
572 dGsdh = beta * sxGsm1 - alpha * sxHsm1;
573 dGsdk = alpha * sxGsm1 + beta * sxHsm1;
574 dGsdAl = k * sxGsm1 - h * sxHsm1;
575 dGsdBe = h * sxGsm1 + k * sxHsm1;
576 }
577
578 // Kronecker symbol (2 - delta(0,s))
579 final double delta0s = (s == 0) ? 1. : 2.;
580
581 for (int n = FastMath.max(2, s); n <= maxAR3Pow; n++) {
582 // (n - s) must be even
583 if ((n - s) % 2 == 0) {
584 // Extract data from previous computation :
585 final double kns = this.hansenObjects[s].getValue(n, B);
586 final double dkns = this.hansenObjects[s].getDerivative(n, B);
587
588 final double vns = Vns.get(new NSKey(n, s));
589 final double coef0 = delta0s * aoR3Pow[n] * vns;
590 final double coef1 = coef0 * Qns[n][s];
591 final double coef2 = coef1 * kns;
592 // dQns/dGamma = Q(n, s + 1) from Equation 3.1-(8)
593 // for n = s, Q(n, n + 1) = 0. (Cefola & Broucke, 1975)
594 final double dqns = (n == s) ? 0. : Qns[n][s + 1];
595
596 //Compute U:
597 U += coef2 * gs;
598
599 // Compute dU / da :
600 dUda += coef2 * n * gs;
601 // Compute dU / dh
602 dUdh += coef1 * (kns * dGsdh + hXXX * gs * dkns);
603 // Compute dU / dk
604 dUdk += coef1 * (kns * dGsdk + kXXX * gs * dkns);
605 // Compute dU / dAlpha
606 dUdAl += coef2 * dGsdAl;
607 // Compute dU / dBeta
608 dUdBe += coef2 * dGsdBe;
609 // Compute dU / dGamma
610 dUdGa += coef0 * kns * dqns * gs;
611 }
612 }
613 }
614
615 // multiply by mu3 / R3
616 U *= muoR3;
617
618 return new double[] {
619 dUda * muoR3 / a,
620 dUdk * muoR3,
621 dUdh * muoR3,
622 dUdAl * muoR3,
623 dUdBe * muoR3,
624 dUdGa * muoR3
625 };
626
627 }
628
629 /** {@inheritDoc} */
630 @Override
631 public void registerAttitudeProvider(final AttitudeProvider provider) {
632 //nothing is done since this contribution is not sensitive to attitude
633 }
634
635 /** Computes the C<sup>j</sup> and S<sup>j</sup> coefficients Danielson 4.2-(15,16)
636 * and their derivatives.
637 * <p>
638 * CS Mathematical Report $3.5.3.2
639 * </p>
640 */
641 private class FourierCjSjCoefficients {
642
643 /** The coefficients G<sub>n, s</sub> and their derivatives. */
644 private final GnsCoefficients gns;
645
646 /** the coefficients e<sup>-|j-s|</sup>*w<sub>j</sub><sup>n, s</sup> and their derivatives by h and k. */
647 private final WnsjEtomjmsCoefficient wnsjEtomjmsCoefficient;
648
649 /** The terms containing the coefficients C<sub>j</sub> and S<sub>j</sub> of (α, β) or (k, h). */
650 private final CjSjAlphaBetaKH ABDECoefficients;
651
652 /** The Fourier coefficients C<sup>j</sup> and their derivatives.
653 * <p>
654 * Each column of the matrix contains the following values: <br/>
655 * - C<sup>j</sup> <br/>
656 * - dC<sup>j</sup> / da <br/>
657 * - dC<sup>j</sup> / dk <br/>
658 * - dC<sup>j</sup> / dh <br/>
659 * - dC<sup>j</sup> / dα <br/>
660 * - dC<sup>j</sup> / dβ <br/>
661 * - dC<sup>j</sup> / dγ <br/>
662 * </p>
663 */
664 private final double[][] cj;
665
666 /** The S<sup>j</sup> coefficients and their derivatives.
667 * <p>
668 * Each column of the matrix contains the following values: <br/>
669 * - S<sup>j</sup> <br/>
670 * - dS<sup>j</sup> / da <br/>
671 * - dS<sup>j</sup> / dk <br/>
672 * - dS<sup>j</sup> / dh <br/>
673 * - dS<sup>j</sup> / dα <br/>
674 * - dS<sup>j</sup> / dβ <br/>
675 * - dS<sup>j</sup> / dγ <br/>
676 * </p>
677 */
678 private final double[][] sj;
679
680 /** The Coefficients C<sup>j</sup><sub>,λ</sub>.
681 * <p>
682 * See Danielson 4.2-21
683 * </p>
684 */
685 private final double[] cjlambda;
686
687 /** The Coefficients S<sup>j</sup><sub>,λ</sub>.
688 * <p>
689 * See Danielson 4.2-21
690 * </p>
691 */
692 private final double[] sjlambda;
693
694 /** Maximum value for n. */
695 private final int nMax;
696
697 /** Maximum value for s. */
698 private final int sMax;
699
700 /** Maximum value for j. */
701 private final int jMax;
702
703 /**
704 * Private constructor.
705 *
706 * @param nMax maximum value for n index
707 * @param sMax maximum value for s index
708 * @param jMax maximum value for j index
709 */
710 FourierCjSjCoefficients(final int nMax, final int sMax, final int jMax) {
711 //Save parameters
712 this.nMax = nMax;
713 this.sMax = sMax;
714 this.jMax = jMax;
715
716 //Create objects
717 wnsjEtomjmsCoefficient = new WnsjEtomjmsCoefficient();
718 ABDECoefficients = new CjSjAlphaBetaKH();
719 gns = new GnsCoefficients(nMax, sMax);
720
721 //create arays
722 this.cj = new double[7][jMax + 1];
723 this.sj = new double[7][jMax + 1];
724 this.cjlambda = new double[jMax];
725 this.sjlambda = new double[jMax];
726
727 computeCoefficients();
728 }
729
730 /**
731 * Compute all coefficients.
732 */
733 private void computeCoefficients() {
734
735 for (int j = 1; j <= jMax; j++) {
736 // initialise the coefficients
737 for (int i = 0; i <= 6; i++) {
738 cj[i][j] = 0.;
739 sj[i][j] = 0.;
740 }
741 if (j < jMax) {
742 // initialise the C<sup>j</sup><sub>,λ</sub> and S<sup>j</sup><sub>,λ</sub> coefficients
743 cjlambda[j] = 0.;
744 sjlambda[j] = 0.;
745 }
746 for (int s = 0; s <= sMax; s++) {
747
748 // Compute the coefficients A<sub>j, s</sub>, B<sub>j, s</sub>, D<sub>j, s</sub> and E<sub>j, s</sub>
749 ABDECoefficients.computeCoefficients(j, s);
750
751 // compute starting value for n
752 final int minN = FastMath.max(2, FastMath.max(j - 1, s));
753
754 for (int n = minN; n <= nMax; n++) {
755 // check if n-s is even
756 if ((n - s) % 2 == 0) {
757 // compute the coefficient e<sup>-|j-s|</sup>*w<sub>j</sub><sup>n+1, s</sup> and its derivatives
758 final double[] wjnp1semjms = wnsjEtomjmsCoefficient.computeWjnsEmjmsAndDeriv(j, s, n + 1);
759
760 // compute the coefficient e<sup>-|j-s|</sup>*w<sub>-j</sub><sup>n+1, s</sup> and its derivatives
761 final double[] wmjnp1semjms = wnsjEtomjmsCoefficient.computeWjnsEmjmsAndDeriv(-j, s, n + 1);
762
763 // compute common factors
764 final double coef1 = -(wjnp1semjms[0] * ABDECoefficients.getCoefA() + wmjnp1semjms[0] * ABDECoefficients.getCoefB());
765 final double coef2 = wjnp1semjms[0] * ABDECoefficients.getCoefD() + wmjnp1semjms[0] * ABDECoefficients.getCoefE();
766
767 //Compute C<sup>j</sup>
768 cj[0][j] += gns.getGns(n, s) * coef1;
769 //Compute dC<sup>j</sup> / da
770 cj[1][j] += gns.getdGnsda(n, s) * coef1;
771 //Compute dC<sup>j</sup> / dk
772 cj[2][j] += -gns.getGns(n, s) *
773 (
774 wjnp1semjms[1] * ABDECoefficients.getCoefA() +
775 wjnp1semjms[0] * ABDECoefficients.getdCoefAdk() +
776 wmjnp1semjms[1] * ABDECoefficients.getCoefB() +
777 wmjnp1semjms[0] * ABDECoefficients.getdCoefBdk()
778 );
779 //Compute dC<sup>j</sup> / dh
780 cj[3][j] += -gns.getGns(n, s) *
781 (
782 wjnp1semjms[2] * ABDECoefficients.getCoefA() +
783 wjnp1semjms[0] * ABDECoefficients.getdCoefAdh() +
784 wmjnp1semjms[2] * ABDECoefficients.getCoefB() +
785 wmjnp1semjms[0] * ABDECoefficients.getdCoefBdh()
786 );
787 //Compute dC<sup>j</sup> / dα
788 cj[4][j] += -gns.getGns(n, s) *
789 (
790 wjnp1semjms[0] * ABDECoefficients.getdCoefAdalpha() +
791 wmjnp1semjms[0] * ABDECoefficients.getdCoefBdalpha()
792 );
793 //Compute dC<sup>j</sup> / dβ
794 cj[5][j] += -gns.getGns(n, s) *
795 (
796 wjnp1semjms[0] * ABDECoefficients.getdCoefAdbeta() +
797 wmjnp1semjms[0] * ABDECoefficients.getdCoefBdbeta()
798 );
799 //Compute dC<sup>j</sup> / dγ
800 cj[6][j] += gns.getdGnsdgamma(n, s) * coef1;
801
802 //Compute S<sup>j</sup>
803 sj[0][j] += gns.getGns(n, s) * coef2;
804 //Compute dS<sup>j</sup> / da
805 sj[1][j] += gns.getdGnsda(n, s) * coef2;
806 //Compute dS<sup>j</sup> / dk
807 sj[2][j] += gns.getGns(n, s) *
808 (
809 wjnp1semjms[1] * ABDECoefficients.getCoefD() +
810 wjnp1semjms[0] * ABDECoefficients.getdCoefDdk() +
811 wmjnp1semjms[1] * ABDECoefficients.getCoefE() +
812 wmjnp1semjms[0] * ABDECoefficients.getdCoefEdk()
813 );
814 //Compute dS<sup>j</sup> / dh
815 sj[3][j] += gns.getGns(n, s) *
816 (
817 wjnp1semjms[2] * ABDECoefficients.getCoefD() +
818 wjnp1semjms[0] * ABDECoefficients.getdCoefDdh() +
819 wmjnp1semjms[2] * ABDECoefficients.getCoefE() +
820 wmjnp1semjms[0] * ABDECoefficients.getdCoefEdh()
821 );
822 //Compute dS<sup>j</sup> / dα
823 sj[4][j] += gns.getGns(n, s) *
824 (
825 wjnp1semjms[0] * ABDECoefficients.getdCoefDdalpha() +
826 wmjnp1semjms[0] * ABDECoefficients.getdCoefEdalpha()
827 );
828 //Compute dS<sup>j</sup> / dβ
829 sj[5][j] += gns.getGns(n, s) *
830 (
831 wjnp1semjms[0] * ABDECoefficients.getdCoefDdbeta() +
832 wmjnp1semjms[0] * ABDECoefficients.getdCoefEdbeta()
833 );
834 //Compute dS<sup>j</sup> / dγ
835 sj[6][j] += gns.getdGnsdgamma(n, s) * coef2;
836
837 //Check if n is greater or equal to j and j is at most jMax-1
838 if (n >= j && j < jMax) {
839 // compute the coefficient e<sup>-|j-s|</sup>*w<sub>j</sub><sup>n, s</sup> and its derivatives
840 final double[] wjnsemjms = wnsjEtomjmsCoefficient.computeWjnsEmjmsAndDeriv(j, s, n);
841
842 // compute the coefficient e<sup>-|j-s|</sup>*w<sub>-j</sub><sup>n, s</sup> and its derivatives
843 final double[] wmjnsemjms = wnsjEtomjmsCoefficient.computeWjnsEmjmsAndDeriv(-j, s, n);
844
845 //Compute C<sup>j</sup><sub>,λ</sub>
846 cjlambda[j] += gns.getGns(n, s) * (wjnsemjms[0] * ABDECoefficients.getCoefD() + wmjnsemjms[0] * ABDECoefficients.getCoefE());
847 //Compute S<sup>j</sup><sub>,λ</sub>
848 sjlambda[j] += gns.getGns(n, s) * (wjnsemjms[0] * ABDECoefficients.getCoefA() + wmjnsemjms[0] * ABDECoefficients.getCoefB());
849 }
850 }
851 }
852 }
853 // Divide by j
854 for (int i = 0; i <= 6; i++) {
855 cj[i][j] /= j;
856 sj[i][j] /= j;
857 }
858 }
859 //The C⁰ coefficients are not computed here.
860 //They are evaluated at the final point.
861
862 //C⁰<sub>,λ</sub>
863 cjlambda[0] = k * cjlambda[1] / 2. + h * sjlambda[1] / 2.;
864 }
865
866 /** Get the Fourier coefficient C<sup>j</sup>.
867 * @param j j index
868 * @return C<sup>j</sup>
869 */
870 public double getCj(final int j) {
871 return cj[0][j];
872 }
873
874 /** Get the derivative dC<sup>j</sup>/da.
875 * @param j j index
876 * @return dC<sup>j</sup>/da
877 */
878 public double getdCjda(final int j) {
879 return cj[1][j];
880 }
881
882 /** Get the derivative dC<sup>j</sup>/dk.
883 * @param j j index
884 * @return dC<sup>j</sup>/dk
885 */
886 public double getdCjdk(final int j) {
887 return cj[2][j];
888 }
889
890 /** Get the derivative dC<sup>j</sup>/dh.
891 * @param j j index
892 * @return dC<sup>j</sup>/dh
893 */
894 public double getdCjdh(final int j) {
895 return cj[3][j];
896 }
897
898 /** Get the derivative dC<sup>j</sup>/dα.
899 * @param j j index
900 * @return dC<sup>j</sup>/dα
901 */
902 public double getdCjdalpha(final int j) {
903 return cj[4][j];
904 }
905
906 /** Get the derivative dC<sup>j</sup>/dβ.
907 * @param j j index
908 * @return dC<sup>j</sup>/dβ
909 */
910 public double getdCjdbeta(final int j) {
911 return cj[5][j];
912 }
913
914 /** Get the derivative dC<sup>j</sup>/dγ.
915 * @param j j index
916 * @return dC<sup>j</sup>/dγ
917 */
918 public double getdCjdgamma(final int j) {
919 return cj[6][j];
920 }
921
922 /** Get the Fourier coefficient S<sup>j</sup>.
923 * @param j j index
924 * @return S<sup>j</sup>
925 */
926 public double getSj(final int j) {
927 return sj[0][j];
928 }
929
930 /** Get the derivative dS<sup>j</sup>/da.
931 * @param j j index
932 * @return dS<sup>j</sup>/da
933 */
934 public double getdSjda(final int j) {
935 return sj[1][j];
936 }
937
938 /** Get the derivative dS<sup>j</sup>/dk.
939 * @param j j index
940 * @return dS<sup>j</sup>/dk
941 */
942 public double getdSjdk(final int j) {
943 return sj[2][j];
944 }
945
946 /** Get the derivative dS<sup>j</sup>/dh.
947 * @param j j index
948 * @return dS<sup>j</sup>/dh
949 */
950 public double getdSjdh(final int j) {
951 return sj[3][j];
952 }
953
954 /** Get the derivative dS<sup>j</sup>/dα.
955 * @param j j index
956 * @return dS<sup>j</sup>/dα
957 */
958 public double getdSjdalpha(final int j) {
959 return sj[4][j];
960 }
961
962 /** Get the derivative dS<sup>j</sup>/dβ.
963 * @param j j index
964 * @return dS<sup>j</sup>/dβ
965 */
966 public double getdSjdbeta(final int j) {
967 return sj[5][j];
968 }
969
970 /** Get the derivative dS<sup>j</sup>/dγ.
971 * @param j j index
972 * @return dS<sup>j</sup>/dγ
973 */
974 public double getdSjdgamma(final int j) {
975 return sj[6][j];
976 }
977
978 /** Get the coefficient C⁰<sub>,λ</sub>.
979 * @return C⁰<sub>,λ</sub>
980 */
981 public double getC0Lambda() {
982 return cjlambda[0];
983 }
984
985 /** Get the coefficient C<sup>j</sup><sub>,λ</sub>.
986 * @param j j index
987 * @return C<sup>j</sup><sub>,λ</sub>
988 */
989 public double getCjLambda(final int j) {
990 if (j < 1 || j >= jMax) {
991 return 0.;
992 }
993 return cjlambda[j];
994 }
995
996 /** Get the coefficient S<sup>j</sup><sub>,λ</sub>.
997 * @param j j index
998 * @return S<sup>j</sup><sub>,λ</sub>
999 */
1000 public double getSjLambda(final int j) {
1001 if (j < 1 || j >= jMax) {
1002 return 0.;
1003 }
1004 return sjlambda[j];
1005 }
1006 }
1007
1008 /** This class covers the coefficients e<sup>-|j-s|</sup>*w<sub>j</sub><sup>n, s</sup> and their derivatives by h and k.
1009 *
1010 * <p>
1011 * Starting from Danielson 4.2-9,10,11 and taking into account that fact that: <br />
1012 * c = e / (1 + (1 - e²)<sup>1/2</sup>) = e / (1 + B) = e * b <br/>
1013 * the expression e<sup>-|j-s|</sup>*w<sub>j</sub><sup>n, s</sup>
1014 * can be written as: <br/ >
1015 * - for |s| > |j| <br />
1016 * e<sup>-|j-s|</sup>*w<sub>j</sub><sup>n, s</sup> =
1017 * (((n + s)!(n - s)!)/((n + j)!(n - j)!)) *
1018 * (-b)<sup>|j-s|</sup> *
1019 * ((1 - c²)<sup>n-|s|</sup>/(1 + c²)<sup>n</sup>) *
1020 * P<sub>n-|s|</sub><sup>|j-s|, |j+s|</sup>(χ) <br />
1021 * <br />
1022 * - for |s| <= |j| <br />
1023 * e<sup>-|j-s|</sup>*w<sub>j</sub><sup>n, s</sup> =
1024 * (-b)<sup>|j-s|</sup> *
1025 * ((1 - c²)<sup>n-|j|</sup>/(1 + c²)<sup>n</sup>) *
1026 * P<sub>n-|j|</sub><sup>|j-s|, |j+s|</sup>(χ)
1027 * </p>
1028 *
1029 * @author Lucian Barbulescu
1030 */
1031 private class WnsjEtomjmsCoefficient {
1032
1033 /** The value c.
1034 * <p>
1035 * c = e / (1 + (1 - e²)<sup>1/2</sup>) = e / (1 + B) = e * b <br/>
1036 * </p>
1037 * */
1038 private final double c;
1039
1040 /** c².*/
1041 private final double c2;
1042
1043 /** db / dh. */
1044 private final double dbdh;
1045
1046 /** db / dk. */
1047 private final double dbdk;
1048
1049 /** de / dh. */
1050 private final double dedh;
1051
1052 /** de / dk. */
1053 private final double dedk;
1054
1055 /** dc / dh = e * db/dh + b * de/dh. */
1056 private final double dcdh;
1057
1058 /** dc / dk = e * db/dk + b * de/dk. */
1059 private final double dcdk;
1060
1061 /** The values (1 - c²)<sup>n</sup>. <br />
1062 * The maximum possible value for the power is N + 1 */
1063 private final double[] omc2tn;
1064
1065 /** The values (1 + c²)<sup>n</sup>. <br />
1066 * The maximum possible value for the power is N + 1 */
1067 private final double[] opc2tn;
1068
1069 /** The values b<sup>|j-s|</sup>. */
1070 private final double[] btjms;
1071
1072 /**
1073 * Standard constructor.
1074 */
1075 WnsjEtomjmsCoefficient() {
1076 //initialise fields
1077 c = ecc * b;
1078 c2 = c * c;
1079
1080 //b² * χ
1081 final double b2Chi = b * b * X;
1082 //Compute derivatives of b
1083 dbdh = h * b2Chi;
1084 dbdk = k * b2Chi;
1085
1086 //Compute derivatives of e
1087 dedh = h / ecc;
1088 dedk = k / ecc;
1089
1090 //Compute derivatives of c
1091 dcdh = ecc * dbdh + b * dedh;
1092 dcdk = ecc * dbdk + b * dedk;
1093
1094 //Compute the powers (1 - c²)<sup>n</sup> and (1 + c²)<sup>n</sup>
1095 omc2tn = new double[maxAR3Pow + maxFreqF + 2];
1096 opc2tn = new double[maxAR3Pow + maxFreqF + 2];
1097 final double omc2 = 1. - c2;
1098 final double opc2 = 1. + c2;
1099 omc2tn[0] = 1.;
1100 opc2tn[0] = 1.;
1101 for (int i = 1; i <= maxAR3Pow + maxFreqF + 1; i++) {
1102 omc2tn[i] = omc2tn[i - 1] * omc2;
1103 opc2tn[i] = opc2tn[i - 1] * opc2;
1104 }
1105
1106 //Compute the powers of b
1107 btjms = new double[maxAR3Pow + maxFreqF + 1];
1108 btjms[0] = 1.;
1109 for (int i = 1; i <= maxAR3Pow + maxFreqF; i++) {
1110 btjms[i] = btjms[i - 1] * b;
1111 }
1112 }
1113
1114 /** Compute the value of the coefficient e<sup>-|j-s|</sup>*w<sub>j</sub><sup>n, s</sup> and its derivatives by h and k. <br />
1115 *
1116 * @param j j index
1117 * @param s s index
1118 * @param n n index
1119 * @return an array containing the value of the coefficient at index 0, the derivative by k at index 1 and the derivative by h at index 2
1120 */
1121 public double[] computeWjnsEmjmsAndDeriv(final int j, final int s, final int n) {
1122 final double[] wjnsemjms = new double[] {0., 0., 0.};
1123
1124 // |j|
1125 final int absJ = FastMath.abs(j);
1126 // |s|
1127 final int absS = FastMath.abs(s);
1128 // |j - s|
1129 final int absJmS = FastMath.abs(j - s);
1130 // |j + s|
1131 final int absJpS = FastMath.abs(j + s);
1132
1133 //The lower index of P. Also the power of (1 - c²)
1134 final int l;
1135 // the factorial ratio coefficient or 1. if |s| <= |j|
1136 final double factCoef;
1137 if (absS > absJ) {
1138 factCoef = (fact[n + s] / fact[n + j]) * (fact[n - s] / fact[n - j]);
1139 l = n - absS;
1140 } else {
1141 factCoef = 1.;
1142 l = n - absJ;
1143 }
1144
1145 // (-1)<sup>|j-s|</sup>
1146 final double sign = absJmS % 2 != 0 ? -1. : 1.;
1147 //(1 - c²)<sup>n-|s|</sup> / (1 + c²)<sup>n</sup>
1148 final double coef1 = omc2tn[l] / opc2tn[n];
1149 //-b<sup>|j-s|</sup>
1150 final double coef2 = sign * btjms[absJmS];
1151 // P<sub>l</sub><sup>|j-s|, |j+s|</sup>(χ)
1152 final DerivativeStructure jac =
1153 JacobiPolynomials.getValue(l, absJmS, absJpS, factory.variable(0, X));
1154
1155 // the derivative of coef1 by c
1156 final double dcoef1dc = -coef1 * 2. * c * (((double) n) / opc2tn[1] + ((double) l) / omc2tn[1]);
1157 // the derivative of coef1 by h
1158 final double dcoef1dh = dcoef1dc * dcdh;
1159 // the derivative of coef1 by k
1160 final double dcoef1dk = dcoef1dc * dcdk;
1161
1162 // the derivative of coef2 by b
1163 final double dcoef2db = absJmS == 0 ? 0 : sign * (double) absJmS * btjms[absJmS - 1];
1164 // the derivative of coef2 by h
1165 final double dcoef2dh = dcoef2db * dbdh;
1166 // the derivative of coef2 by k
1167 final double dcoef2dk = dcoef2db * dbdk;
1168
1169 // the jacobi polinomial value
1170 final double jacobi = jac.getValue();
1171 // the derivative of the Jacobi polynomial by h
1172 final double djacobidh = jac.getPartialDerivative(1) * hXXX;
1173 // the derivative of the Jacobi polynomial by k
1174 final double djacobidk = jac.getPartialDerivative(1) * kXXX;
1175
1176 //group the above coefficients to limit the mathematical operations
1177 final double term1 = factCoef * coef1 * coef2;
1178 final double term2 = factCoef * coef1 * jacobi;
1179 final double term3 = factCoef * coef2 * jacobi;
1180
1181 //compute e<sup>-|j-s|</sup>*w<sub>j</sub><sup>n, s</sup> and its derivatives by k and h
1182 wjnsemjms[0] = term1 * jacobi;
1183 wjnsemjms[1] = dcoef1dk * term3 + dcoef2dk * term2 + djacobidk * term1;
1184 wjnsemjms[2] = dcoef1dh * term3 + dcoef2dh * term2 + djacobidh * term1;
1185
1186 return wjnsemjms;
1187 }
1188 }
1189
1190 /** The G<sub>n,s</sub> coefficients and their derivatives.
1191 * <p>
1192 * See Danielson, 4.2-17
1193 *
1194 * @author Lucian Barbulescu
1195 */
1196 private class GnsCoefficients {
1197
1198 /** Maximum value for n index. */
1199 private final int nMax;
1200
1201 /** Maximum value for s index. */
1202 private final int sMax;
1203
1204 /** The coefficients G<sub>n,s</sub>. */
1205 private final double gns[][];
1206
1207 /** The derivatives of the coefficients G<sub>n,s</sub> by a. */
1208 private final double dgnsda[][];
1209
1210 /** The derivatives of the coefficients G<sub>n,s</sub> by γ. */
1211 private final double dgnsdgamma[][];
1212
1213 /** Standard constructor.
1214 *
1215 * @param nMax maximum value for n indes
1216 * @param sMax maximum value for s index
1217 */
1218 GnsCoefficients(final int nMax, final int sMax) {
1219 this.nMax = nMax;
1220 this.sMax = sMax;
1221
1222 final int rows = nMax + 1;
1223 final int columns = sMax + 1;
1224 this.gns = new double[rows][columns];
1225 this.dgnsda = new double[rows][columns];
1226 this.dgnsdgamma = new double[rows][columns];
1227
1228 // Generate the coefficients
1229 generateCoefficients();
1230 }
1231 /**
1232 * Compute the coefficient G<sub>n,s</sub> and its derivatives.
1233 * <p>
1234 * Only the derivatives by a and γ are computed as all others are 0
1235 * </p>
1236 */
1237 private void generateCoefficients() {
1238 for (int s = 0; s <= sMax; s++) {
1239 // The n index is always at least the maximum between 2 and s
1240 final int minN = FastMath.max(2, s);
1241 for (int n = minN; n <= nMax; n++) {
1242 // compute the coefficients only if (n - s) % 2 == 0
1243 if ( (n - s) % 2 == 0 ) {
1244 // Kronecker symbol (2 - delta(0,s))
1245 final double delta0s = (s == 0) ? 1. : 2.;
1246 final double vns = Vns.get(new NSKey(n, s));
1247 final double coef0 = delta0s * aoR3Pow[n] * vns * muoR3;
1248 final double coef1 = coef0 * Qns[n][s];
1249 // dQns/dGamma = Q(n, s + 1) from Equation 3.1-(8)
1250 // for n = s, Q(n, n + 1) = 0. (Cefola & Broucke, 1975)
1251 final double dqns = (n == s) ? 0. : Qns[n][s + 1];
1252
1253 //Compute the coefficient and its derivatives.
1254 this.gns[n][s] = coef1;
1255 this.dgnsda[n][s] = coef1 * n / a;
1256 this.dgnsdgamma[n][s] = coef0 * dqns;
1257 } else {
1258 // the coefficient and its derivatives is 0
1259 this.gns[n][s] = 0.;
1260 this.dgnsda[n][s] = 0.;
1261 this.dgnsdgamma[n][s] = 0.;
1262 }
1263 }
1264 }
1265 }
1266
1267 /** Get the coefficient G<sub>n,s</sub>.
1268 *
1269 * @param n n index
1270 * @param s s index
1271 * @return the coefficient G<sub>n,s</sub>
1272 */
1273 public double getGns(final int n, final int s) {
1274 return this.gns[n][s];
1275 }
1276
1277 /** Get the derivative dG<sub>n,s</sub> / da.
1278 *
1279 * @param n n index
1280 * @param s s index
1281 * @return the derivative dG<sub>n,s</sub> / da
1282 */
1283 public double getdGnsda(final int n, final int s) {
1284 return this.dgnsda[n][s];
1285 }
1286
1287 /** Get the derivative dG<sub>n,s</sub> / dγ.
1288 *
1289 * @param n n index
1290 * @param s s index
1291 * @return the derivative dG<sub>n,s</sub> / dγ
1292 */
1293 public double getdGnsdgamma(final int n, final int s) {
1294 return this.dgnsdgamma[n][s];
1295 }
1296 }
1297
1298 /** This class computes the terms containing the coefficients C<sub>j</sub> and S<sub>j</sub> of (α, β) or (k, h).
1299 *
1300 * <p>
1301 * The following terms and their derivatives by k, h, alpha and beta are considered: <br/ >
1302 * - sign(j-s) * C<sub>s</sub>(α, β) * S<sub>|j-s|</sub>(k, h) + S<sub>s</sub>(α, β) * C<sub>|j-s|</sub>(k, h) <br />
1303 * - C<sub>s</sub>(α, β) * S<sub>j+s</sub>(k, h) - S<sub>s</sub>(α, β) * C<sub>j+s</sub>(k, h) <br />
1304 * - C<sub>s</sub>(α, β) * C<sub>|j-s|</sub>(k, h) - sign(j-s) * S<sub>s</sub>(α, β) * S<sub>|j-s|</sub>(k, h) <br />
1305 * - C<sub>s</sub>(α, β) * C<sub>j+s</sub>(k, h) + S<sub>s</sub>(α, β) * S<sub>j+s</sub>(k, h) <br />
1306 * For the ease of usage the above terms are renamed A<sub>js</sub>, B<sub>js</sub>, D<sub>js</sub> and E<sub>js</sub> respectively <br />
1307 * See the CS Mathematical report $3.5.3.2 for more details
1308 * </p>
1309 * @author Lucian Barbulescu
1310 */
1311 private class CjSjAlphaBetaKH {
1312
1313 /** The C<sub>j</sub>(k, h) and the S<sub>j</sub>(k, h) series. */
1314 private final CjSjCoefficient cjsjkh;
1315
1316 /** The C<sub>j</sub>(α, β) and the S<sub>j</sub>(α, β) series. */
1317 private final CjSjCoefficient cjsjalbe;
1318
1319 /** The coeficient sign(j-s) * C<sub>s</sub>(α, β) * S<sub>|j-s|</sub>(k, h) + S<sub>s</sub>(α, β) * C<sub>|j-s|</sub>(k, h)
1320 * and its derivative by k, h, α and β. */
1321 private final double coefAandDeriv[];
1322
1323 /** The coeficient C<sub>s</sub>(α, β) * S<sub>j+s</sub>(k, h) - S<sub>s</sub>(α, β) * C<sub>j+s</sub>(k, h)
1324 * and its derivative by k, h, α and β. */
1325 private final double coefBandDeriv[];
1326
1327 /** The coeficient C<sub>s</sub>(α, β) * C<sub>|j-s|</sub>(k, h) - sign(j-s) * S<sub>s</sub>(α, β) * S<sub>|j-s|</sub>(k, h)
1328 * and its derivative by k, h, α and β. */
1329 private final double coefDandDeriv[];
1330
1331 /** The coeficient C<sub>s</sub>(α, β) * C<sub>j+s</sub>(k, h) + S<sub>s</sub>(α, β) * S<sub>j+s</sub>(k, h)
1332 * and its derivative by k, h, α and β. */
1333 private final double coefEandDeriv[];
1334
1335 /**
1336 * Standard constructor.
1337 */
1338 CjSjAlphaBetaKH() {
1339 cjsjkh = new CjSjCoefficient(k, h);
1340 cjsjalbe = new CjSjCoefficient(alpha, beta);
1341
1342 coefAandDeriv = new double[5];
1343 coefBandDeriv = new double[5];
1344 coefDandDeriv = new double[5];
1345 coefEandDeriv = new double[5];
1346 }
1347
1348 /** Compute the coefficients and their derivatives for a given (j,s) pair.
1349 * @param j j index
1350 * @param s s index
1351 */
1352 public void computeCoefficients(final int j, final int s) {
1353 // sign of j-s
1354 final int sign = j < s ? -1 : 1;
1355
1356 //|j-s|
1357 final int absJmS = FastMath.abs(j - s);
1358
1359 //j+s
1360 final int jps = j + s;
1361
1362 //Compute the coefficient A and its derivatives
1363 coefAandDeriv[0] = sign * cjsjalbe.getCj(s) * cjsjkh.getSj(absJmS) + cjsjalbe.getSj(s) * cjsjkh.getCj(absJmS);
1364 coefAandDeriv[1] = sign * cjsjalbe.getCj(s) * cjsjkh.getDsjDk(absJmS) + cjsjalbe.getSj(s) * cjsjkh.getDcjDk(absJmS);
1365 coefAandDeriv[2] = sign * cjsjalbe.getCj(s) * cjsjkh.getDsjDh(absJmS) + cjsjalbe.getSj(s) * cjsjkh.getDcjDh(absJmS);
1366 coefAandDeriv[3] = sign * cjsjalbe.getDcjDk(s) * cjsjkh.getSj(absJmS) + cjsjalbe.getDsjDk(s) * cjsjkh.getCj(absJmS);
1367 coefAandDeriv[4] = sign * cjsjalbe.getDcjDh(s) * cjsjkh.getSj(absJmS) + cjsjalbe.getDsjDh(s) * cjsjkh.getCj(absJmS);
1368
1369 //Compute the coefficient B and its derivatives
1370 coefBandDeriv[0] = cjsjalbe.getCj(s) * cjsjkh.getSj(jps) - cjsjalbe.getSj(s) * cjsjkh.getCj(jps);
1371 coefBandDeriv[1] = cjsjalbe.getCj(s) * cjsjkh.getDsjDk(jps) - cjsjalbe.getSj(s) * cjsjkh.getDcjDk(jps);
1372 coefBandDeriv[2] = cjsjalbe.getCj(s) * cjsjkh.getDsjDh(jps) - cjsjalbe.getSj(s) * cjsjkh.getDcjDh(jps);
1373 coefBandDeriv[3] = cjsjalbe.getDcjDk(s) * cjsjkh.getSj(jps) - cjsjalbe.getDsjDk(s) * cjsjkh.getCj(jps);
1374 coefBandDeriv[4] = cjsjalbe.getDcjDh(s) * cjsjkh.getSj(jps) - cjsjalbe.getDsjDh(s) * cjsjkh.getCj(jps);
1375
1376 //Compute the coefficient D and its derivatives
1377 coefDandDeriv[0] = cjsjalbe.getCj(s) * cjsjkh.getCj(absJmS) - sign * cjsjalbe.getSj(s) * cjsjkh.getSj(absJmS);
1378 coefDandDeriv[1] = cjsjalbe.getCj(s) * cjsjkh.getDcjDk(absJmS) - sign * cjsjalbe.getSj(s) * cjsjkh.getDsjDk(absJmS);
1379 coefDandDeriv[2] = cjsjalbe.getCj(s) * cjsjkh.getDcjDh(absJmS) - sign * cjsjalbe.getSj(s) * cjsjkh.getDsjDh(absJmS);
1380 coefDandDeriv[3] = cjsjalbe.getDcjDk(s) * cjsjkh.getCj(absJmS) - sign * cjsjalbe.getDsjDk(s) * cjsjkh.getSj(absJmS);
1381 coefDandDeriv[4] = cjsjalbe.getDcjDh(s) * cjsjkh.getCj(absJmS) - sign * cjsjalbe.getDsjDh(s) * cjsjkh.getSj(absJmS);
1382
1383 //Compute the coefficient E and its derivatives
1384 coefEandDeriv[0] = cjsjalbe.getCj(s) * cjsjkh.getCj(jps) + cjsjalbe.getSj(s) * cjsjkh.getSj(jps);
1385 coefEandDeriv[1] = cjsjalbe.getCj(s) * cjsjkh.getDcjDk(jps) + cjsjalbe.getSj(s) * cjsjkh.getDsjDk(jps);
1386 coefEandDeriv[2] = cjsjalbe.getCj(s) * cjsjkh.getDcjDh(jps) + cjsjalbe.getSj(s) * cjsjkh.getDsjDh(jps);
1387 coefEandDeriv[3] = cjsjalbe.getDcjDk(s) * cjsjkh.getCj(jps) + cjsjalbe.getDsjDk(s) * cjsjkh.getSj(jps);
1388 coefEandDeriv[4] = cjsjalbe.getDcjDh(s) * cjsjkh.getCj(jps) + cjsjalbe.getDsjDh(s) * cjsjkh.getSj(jps);
1389 }
1390
1391 /** Get the value of coefficient A<sub>j,s</sub>.
1392 *
1393 * @return the coefficient A<sub>j,s</sub>
1394 */
1395 public double getCoefA() {
1396 return coefAandDeriv[0];
1397 }
1398
1399 /** Get the value of coefficient dA<sub>j,s</sub>/dk.
1400 *
1401 * @return the coefficient dA<sub>j,s</sub>/dk
1402 */
1403 public double getdCoefAdk() {
1404 return coefAandDeriv[1];
1405 }
1406
1407 /** Get the value of coefficient dA<sub>j,s</sub>/dh.
1408 *
1409 * @return the coefficient dA<sub>j,s</sub>/dh
1410 */
1411 public double getdCoefAdh() {
1412 return coefAandDeriv[2];
1413 }
1414
1415 /** Get the value of coefficient dA<sub>j,s</sub>/dα.
1416 *
1417 * @return the coefficient dA<sub>j,s</sub>/dα
1418 */
1419 public double getdCoefAdalpha() {
1420 return coefAandDeriv[3];
1421 }
1422
1423 /** Get the value of coefficient dA<sub>j,s</sub>/dβ.
1424 *
1425 * @return the coefficient dA<sub>j,s</sub>/dβ
1426 */
1427 public double getdCoefAdbeta() {
1428 return coefAandDeriv[4];
1429 }
1430
1431 /** Get the value of coefficient B<sub>j,s</sub>.
1432 *
1433 * @return the coefficient B<sub>j,s</sub>
1434 */
1435 public double getCoefB() {
1436 return coefBandDeriv[0];
1437 }
1438
1439 /** Get the value of coefficient dB<sub>j,s</sub>/dk.
1440 *
1441 * @return the coefficient dB<sub>j,s</sub>/dk
1442 */
1443 public double getdCoefBdk() {
1444 return coefBandDeriv[1];
1445 }
1446
1447 /** Get the value of coefficient dB<sub>j,s</sub>/dh.
1448 *
1449 * @return the coefficient dB<sub>j,s</sub>/dh
1450 */
1451 public double getdCoefBdh() {
1452 return coefBandDeriv[2];
1453 }
1454
1455 /** Get the value of coefficient dB<sub>j,s</sub>/dα.
1456 *
1457 * @return the coefficient dB<sub>j,s</sub>/dα
1458 */
1459 public double getdCoefBdalpha() {
1460 return coefBandDeriv[3];
1461 }
1462
1463 /** Get the value of coefficient dB<sub>j,s</sub>/dβ.
1464 *
1465 * @return the coefficient dB<sub>j,s</sub>/dβ
1466 */
1467 public double getdCoefBdbeta() {
1468 return coefBandDeriv[4];
1469 }
1470
1471 /** Get the value of coefficient D<sub>j,s</sub>.
1472 *
1473 * @return the coefficient D<sub>j,s</sub>
1474 */
1475 public double getCoefD() {
1476 return coefDandDeriv[0];
1477 }
1478
1479 /** Get the value of coefficient dD<sub>j,s</sub>/dk.
1480 *
1481 * @return the coefficient dD<sub>j,s</sub>/dk
1482 */
1483 public double getdCoefDdk() {
1484 return coefDandDeriv[1];
1485 }
1486
1487 /** Get the value of coefficient dD<sub>j,s</sub>/dh.
1488 *
1489 * @return the coefficient dD<sub>j,s</sub>/dh
1490 */
1491 public double getdCoefDdh() {
1492 return coefDandDeriv[2];
1493 }
1494
1495 /** Get the value of coefficient dD<sub>j,s</sub>/dα.
1496 *
1497 * @return the coefficient dD<sub>j,s</sub>/dα
1498 */
1499 public double getdCoefDdalpha() {
1500 return coefDandDeriv[3];
1501 }
1502
1503 /** Get the value of coefficient dD<sub>j,s</sub>/dβ.
1504 *
1505 * @return the coefficient dD<sub>j,s</sub>/dβ
1506 */
1507 public double getdCoefDdbeta() {
1508 return coefDandDeriv[4];
1509 }
1510
1511 /** Get the value of coefficient E<sub>j,s</sub>.
1512 *
1513 * @return the coefficient E<sub>j,s</sub>
1514 */
1515 public double getCoefE() {
1516 return coefEandDeriv[0];
1517 }
1518
1519 /** Get the value of coefficient dE<sub>j,s</sub>/dk.
1520 *
1521 * @return the coefficient dE<sub>j,s</sub>/dk
1522 */
1523 public double getdCoefEdk() {
1524 return coefEandDeriv[1];
1525 }
1526
1527 /** Get the value of coefficient dE<sub>j,s</sub>/dh.
1528 *
1529 * @return the coefficient dE<sub>j,s</sub>/dh
1530 */
1531 public double getdCoefEdh() {
1532 return coefEandDeriv[2];
1533 }
1534
1535 /** Get the value of coefficient dE<sub>j,s</sub>/dα.
1536 *
1537 * @return the coefficient dE<sub>j,s</sub>/dα
1538 */
1539 public double getdCoefEdalpha() {
1540 return coefEandDeriv[3];
1541 }
1542
1543 /** Get the value of coefficient dE<sub>j,s</sub>/dβ.
1544 *
1545 * @return the coefficient dE<sub>j,s</sub>/dβ
1546 */
1547 public double getdCoefEdbeta() {
1548 return coefEandDeriv[4];
1549 }
1550 }
1551
1552 /** This class computes the coefficients for the generating function S and its derivatives.
1553 * <p>
1554 * The form of the generating functions is: <br>
1555 * S = C⁰ + Σ<sub>j=1</sub><sup>N+1</sup>(C<sup>j</sup> * cos(jF) + S<sup>j</sup> * sin(jF)) <br>
1556 * The coefficients C⁰, C<sup>j</sup>, S<sup>j</sup> are the Fourrier coefficients
1557 * presented in Danielson 4.2-14,15 except for the case j=1 where
1558 * C¹ = C¹<sub>Fourier</sub> - hU and
1559 * S¹ = S¹<sub>Fourier</sub> + kU <br>
1560 * Also the coefficients of the derivatives of S by a, k, h, α, β, γ and λ
1561 * are computed end expressed in a similar manner. The formulas used are 4.2-19, 20, 23, 24
1562 * </p>
1563 * @author Lucian Barbulescu
1564 */
1565 private class GeneratingFunctionCoefficients {
1566
1567 /** The Fourier coefficients as presented in Danielson 4.2-14,15. */
1568 private final FourierCjSjCoefficients cjsjFourier;
1569
1570 /** Maximum value of j index. */
1571 private final int jMax;
1572
1573 /** The coefficients C<sup>j</sup> of the function S and its derivatives.
1574 * <p>
1575 * The index j belongs to the interval [0,jMax]. The coefficient C⁰ is the free coefficient.<br>
1576 * Each column of the matrix contains the coefficient corresponding to the following functions: <br/>
1577 * - S <br/>
1578 * - dS / da <br/>
1579 * - dS / dk <br/>
1580 * - dS / dh <br/>
1581 * - dS / dα <br/>
1582 * - dS / dβ <br/>
1583 * - dS / dγ <br/>
1584 * - dS / dλ
1585 * </p>
1586 */
1587 private final double[][] cjCoefs;
1588
1589 /** The coefficients S<sup>j</sup> of the function S and its derivatives.
1590 * <p>
1591 * The index j belongs to the interval [0,jMax].<br>
1592 * Each column of the matrix contains the coefficient corresponding to the following functions: <br/>
1593 * - S <br/>
1594 * - dS / da <br/>
1595 * - dS / dk <br/>
1596 * - dS / dh <br/>
1597 * - dS / dα <br/>
1598 * - dS / dβ <br/>
1599 * - dS / dγ <br/>
1600 * - dS / dλ
1601 * </p>
1602 */
1603 private final double[][] sjCoefs;
1604
1605 /**
1606 * Standard constructor.
1607 *
1608 * @param nMax maximum value of n index
1609 * @param sMax maximum value of s index
1610 * @param jMax maximum value of j index
1611 */
1612 GeneratingFunctionCoefficients(final int nMax, final int sMax, final int jMax) {
1613 this.jMax = jMax;
1614 this.cjsjFourier = new FourierCjSjCoefficients(nMax, sMax, jMax);
1615 this.cjCoefs = new double[8][jMax + 1];
1616 this.sjCoefs = new double[8][jMax + 1];
1617
1618 computeGeneratingFunctionCoefficients();
1619 }
1620
1621 /**
1622 * Compute the coefficients for the generating function S and its derivatives.
1623 */
1624 private void computeGeneratingFunctionCoefficients() {
1625
1626 // Compute potential U and derivatives
1627 final double[] dU = computeUDerivatives();
1628
1629 //Compute the C<sup>j</sup> coefficients
1630 for (int j = 1; j <= jMax; j++) {
1631 //Compute the C<sup>j</sup> coefficients
1632 cjCoefs[0][j] = cjsjFourier.getCj(j);
1633 cjCoefs[1][j] = cjsjFourier.getdCjda(j);
1634 cjCoefs[2][j] = cjsjFourier.getdCjdk(j) - (cjsjFourier.getSjLambda(j - 1) - cjsjFourier.getSjLambda(j + 1)) / 2;
1635 cjCoefs[3][j] = cjsjFourier.getdCjdh(j) - (cjsjFourier.getCjLambda(j - 1) + cjsjFourier.getCjLambda(j + 1)) / 2;
1636 cjCoefs[4][j] = cjsjFourier.getdCjdalpha(j);
1637 cjCoefs[5][j] = cjsjFourier.getdCjdbeta(j);
1638 cjCoefs[6][j] = cjsjFourier.getdCjdgamma(j);
1639 cjCoefs[7][j] = cjsjFourier.getCjLambda(j);
1640
1641 //Compute the S<sup>j</sup> coefficients
1642 sjCoefs[0][j] = cjsjFourier.getSj(j);
1643 sjCoefs[1][j] = cjsjFourier.getdSjda(j);
1644 sjCoefs[2][j] = cjsjFourier.getdSjdk(j) + (cjsjFourier.getCjLambda(j - 1) - cjsjFourier.getCjLambda(j + 1)) / 2;
1645 sjCoefs[3][j] = cjsjFourier.getdSjdh(j) - (cjsjFourier.getSjLambda(j - 1) + cjsjFourier.getSjLambda(j + 1)) / 2;
1646 sjCoefs[4][j] = cjsjFourier.getdSjdalpha(j);
1647 sjCoefs[5][j] = cjsjFourier.getdSjdbeta(j);
1648 sjCoefs[6][j] = cjsjFourier.getdSjdgamma(j);
1649 sjCoefs[7][j] = cjsjFourier.getSjLambda(j);
1650
1651 //In the special case j == 1 there are some additional terms to be added
1652 if (j == 1) {
1653 //Additional terms for C<sup>j</sup> coefficients
1654 cjCoefs[0][j] += -h * U;
1655 cjCoefs[1][j] += -h * dU[0];
1656 cjCoefs[2][j] += -h * dU[1];
1657 cjCoefs[3][j] += -(h * dU[2] + U + cjsjFourier.getC0Lambda());
1658 cjCoefs[4][j] += -h * dU[3];
1659 cjCoefs[5][j] += -h * dU[4];
1660 cjCoefs[6][j] += -h * dU[5];
1661
1662 //Additional terms for S<sup>j</sup> coefficients
1663 sjCoefs[0][j] += k * U;
1664 sjCoefs[1][j] += k * dU[0];
1665 sjCoefs[2][j] += k * dU[1] + U + cjsjFourier.getC0Lambda();
1666 sjCoefs[3][j] += k * dU[2];
1667 sjCoefs[4][j] += k * dU[3];
1668 sjCoefs[5][j] += k * dU[4];
1669 sjCoefs[6][j] += k * dU[5];
1670 }
1671 }
1672 }
1673
1674 /** Get the coefficient C<sup>j</sup> for the function S.
1675 * <br>
1676 * Possible values for j are within the interval [0,jMax].
1677 * The value 0 is used to obtain the free coefficient C⁰
1678 * @param j j index
1679 * @return C<sup>j</sup> for the function S
1680 */
1681 public double getSCj(final int j) {
1682 return cjCoefs[0][j];
1683 }
1684
1685 /** Get the coefficient S<sup>j</sup> for the function S.
1686 * <br>
1687 * Possible values for j are within the interval [1,jMax].
1688 * @param j j index
1689 * @return S<sup>j</sup> for the function S
1690 */
1691 public double getSSj(final int j) {
1692 return sjCoefs[0][j];
1693 }
1694
1695 /** Get the coefficient C<sup>j</sup> for the derivative dS/da.
1696 * <br>
1697 * Possible values for j are within the interval [0,jMax].
1698 * The value 0 is used to obtain the free coefficient C⁰
1699 * @param j j index
1700 * @return C<sup>j</sup> for the function dS/da
1701 */
1702 public double getdSdaCj(final int j) {
1703 return cjCoefs[1][j];
1704 }
1705
1706 /** Get the coefficient S<sup>j</sup> for the derivative dS/da.
1707 * <br>
1708 * Possible values for j are within the interval [1,jMax].
1709 * @param j j index
1710 * @return S<sup>j</sup> for the derivative dS/da
1711 */
1712 public double getdSdaSj(final int j) {
1713 return sjCoefs[1][j];
1714 }
1715
1716 /** Get the coefficient C<sup>j</sup> for the derivative dS/dk
1717 * <br>
1718 * Possible values for j are within the interval [0,jMax].
1719 * The value 0 is used to obtain the free coefficient C⁰
1720 * @param j j index
1721 * @return C<sup>j</sup> for the function dS/dk
1722 */
1723 public double getdSdkCj(final int j) {
1724 return cjCoefs[2][j];
1725 }
1726
1727 /** Get the coefficient S<sup>j</sup> for the derivative dS/dk.
1728 * <br>
1729 * Possible values for j are within the interval [1,jMax].
1730 * @param j j index
1731 * @return S<sup>j</sup> for the derivative dS/dk
1732 */
1733 public double getdSdkSj(final int j) {
1734 return sjCoefs[2][j];
1735 }
1736
1737 /** Get the coefficient C<sup>j</sup> for the derivative dS/dh
1738 * <br>
1739 * Possible values for j are within the interval [0,jMax].
1740 * The value 0 is used to obtain the free coefficient C⁰
1741 * @param j j index
1742 * @return C<sup>j</sup> for the function dS/dh
1743 */
1744 public double getdSdhCj(final int j) {
1745 return cjCoefs[3][j];
1746 }
1747
1748 /** Get the coefficient S<sup>j</sup> for the derivative dS/dh.
1749 * <br>
1750 * Possible values for j are within the interval [1,jMax].
1751 * @param j j index
1752 * @return S<sup>j</sup> for the derivative dS/dh
1753 */
1754 public double getdSdhSj(final int j) {
1755 return sjCoefs[3][j];
1756 }
1757
1758 /** Get the coefficient C<sup>j</sup> for the derivative dS/dα
1759 * <br>
1760 * Possible values for j are within the interval [0,jMax].
1761 * The value 0 is used to obtain the free coefficient C⁰
1762 * @param j j index
1763 * @return C<sup>j</sup> for the function dS/dα
1764 */
1765 public double getdSdalphaCj(final int j) {
1766 return cjCoefs[4][j];
1767 }
1768
1769 /** Get the coefficient S<sup>j</sup> for the derivative dS/dα.
1770 * <br>
1771 * Possible values for j are within the interval [1,jMax].
1772 * @param j j index
1773 * @return S<sup>j</sup> for the derivative dS/dα
1774 */
1775 public double getdSdalphaSj(final int j) {
1776 return sjCoefs[4][j];
1777 }
1778
1779 /** Get the coefficient C<sup>j</sup> for the derivative dS/dβ
1780 * <br>
1781 * Possible values for j are within the interval [0,jMax].
1782 * The value 0 is used to obtain the free coefficient C⁰
1783 * @param j j index
1784 * @return C<sup>j</sup> for the function dS/dβ
1785 */
1786 public double getdSdbetaCj(final int j) {
1787 return cjCoefs[5][j];
1788 }
1789
1790 /** Get the coefficient S<sup>j</sup> for the derivative dS/dβ.
1791 * <br>
1792 * Possible values for j are within the interval [1,jMax].
1793 * @param j j index
1794 * @return S<sup>j</sup> for the derivative dS/dβ
1795 */
1796 public double getdSdbetaSj(final int j) {
1797 return sjCoefs[5][j];
1798 }
1799
1800 /** Get the coefficient C<sup>j</sup> for the derivative dS/dγ
1801 * <br>
1802 * Possible values for j are within the interval [0,jMax].
1803 * The value 0 is used to obtain the free coefficient C⁰
1804 * @param j j index
1805 * @return C<sup>j</sup> for the function dS/dγ
1806 */
1807 public double getdSdgammaCj(final int j) {
1808 return cjCoefs[6][j];
1809 }
1810
1811 /** Get the coefficient S<sup>j</sup> for the derivative dS/dγ.
1812 * <br>
1813 * Possible values for j are within the interval [1,jMax].
1814 * @param j j index
1815 * @return S<sup>j</sup> for the derivative dS/dγ
1816 */
1817 public double getdSdgammaSj(final int j) {
1818 return sjCoefs[6][j];
1819 }
1820
1821 /** Get the coefficient C<sup>j</sup> for the derivative dS/dλ
1822 * <br>
1823 * Possible values for j are within the interval [0,jMax].
1824 * The value 0 is used to obtain the free coefficient C⁰
1825 * @param j j index
1826 * @return C<sup>j</sup> for the function dS/dλ
1827 */
1828 public double getdSdlambdaCj(final int j) {
1829 return cjCoefs[7][j];
1830 }
1831
1832 /** Get the coefficient S<sup>j</sup> for the derivative dS/dλ.
1833 * <br>
1834 * Possible values for j are within the interval [1,jMax].
1835 * @param j j index
1836 * @return S<sup>j</sup> for the derivative dS/dλ
1837 */
1838 public double getdSdlambdaSj(final int j) {
1839 return sjCoefs[7][j];
1840 }
1841 }
1842
1843 /**
1844 * The coefficients used to compute the short periodic contribution for the Third body perturbation.
1845 * <p>
1846 * The short periodic contribution for the Third Body is expressed in Danielson 4.2-25.<br>
1847 * The coefficients C<sub>i</sub>⁰, C<sub>i</sub><sup>j</sup>, S<sub>i</sub><sup>j</sup>
1848 * are computed by replacing the corresponding values in formula 2.5.5-10.
1849 * </p>
1850 * @author Lucian Barbulescu
1851 */
1852 private static class ThirdBodyShortPeriodicCoefficients implements ShortPeriodTerms {
1853
1854 /** Serializable UID. */
1855 private static final long serialVersionUID = 20151119L;
1856
1857 /** Maximal value for j. */
1858 private final int jMax;
1859
1860 /** Number of points used in the interpolation process. */
1861 private final int interpolationPoints;
1862
1863 /** Max frequency of F. */
1864 private final int maxFreqF;
1865
1866 /** Coefficients prefix. */
1867 private final String prefix;
1868
1869 /** All coefficients slots. */
1870 private final transient TimeSpanMap<Slot> slots;
1871
1872 /**
1873 * Standard constructor.
1874 * @param interpolationPoints number of points used in the interpolation process
1875 * @param jMax maximal value for j
1876 * @param maxFreqF Max frequency of F
1877 * @param bodyName third body name
1878 * @param slots all coefficients slots
1879 */
1880 ThirdBodyShortPeriodicCoefficients(final int jMax, final int interpolationPoints,
1881 final int maxFreqF, final String bodyName,
1882 final TimeSpanMap<Slot> slots) {
1883 this.jMax = jMax;
1884 this.interpolationPoints = interpolationPoints;
1885 this.maxFreqF = maxFreqF;
1886 this.prefix = "DSST-3rd-body-" + bodyName + "-";
1887 this.slots = slots;
1888 }
1889
1890 /** Get the slot valid for some date.
1891 * @param meanStates mean states defining the slot
1892 * @return slot valid at the specified date
1893 */
1894 public Slot createSlot(final SpacecraftState... meanStates) {
1895 final Slot slot = new Slot(jMax, interpolationPoints);
1896 final AbsoluteDate first = meanStates[0].getDate();
1897 final AbsoluteDate last = meanStates[meanStates.length - 1].getDate();
1898 if (first.compareTo(last) <= 0) {
1899 slots.addValidAfter(slot, first);
1900 } else {
1901 slots.addValidBefore(slot, first);
1902 }
1903 return slot;
1904 }
1905
1906 /** {@inheritDoc} */
1907 @Override
1908 public double[] value(final Orbit meanOrbit) {
1909
1910 // select the coefficients slot
1911 final Slot slot = slots.get(meanOrbit.getDate());
1912
1913 // the current eccentric longitude
1914 final double F = meanOrbit.getLE();
1915
1916 //initialize the short periodic contribution with the corresponding C⁰ coeficient
1917 final double[] shortPeriodic = slot.cij[0].value(meanOrbit.getDate());
1918
1919 // Add the cos and sin dependent terms
1920 for (int j = 1; j <= maxFreqF; j++) {
1921 //compute cos and sin
1922 final double cosjF = FastMath.cos(j * F);
1923 final double sinjF = FastMath.sin(j * F);
1924
1925 final double[] c = slot.cij[j].value(meanOrbit.getDate());
1926 final double[] s = slot.sij[j].value(meanOrbit.getDate());
1927 for (int i = 0; i < 6; i++) {
1928 shortPeriodic[i] += c[i] * cosjF + s[i] * sinjF;
1929 }
1930 }
1931
1932 return shortPeriodic;
1933
1934 }
1935
1936 /** {@inheritDoc} */
1937 @Override
1938 public String getCoefficientsKeyPrefix() {
1939 return prefix;
1940 }
1941
1942 /** {@inheritDoc}
1943 * <p>
1944 * For third body attraction forces,there are maxFreqF + 1 cj coefficients,
1945 * maxFreqF sj coefficients where maxFreqF depends on the orbit.
1946 * The j index is the integer multiplier for the eccentric longitude argument
1947 * in the cj and sj coefficients.
1948 * </p>
1949 */
1950 @Override
1951 public Map<String, double[]> getCoefficients(final AbsoluteDate date, final Set<String> selected)
1952 throws OrekitException {
1953
1954 // select the coefficients slot
1955 final Slot slot = slots.get(date);
1956
1957 final Map<String, double[]> coefficients = new HashMap<String, double[]>(2 * maxFreqF + 1);
1958 storeIfSelected(coefficients, selected, slot.cij[0].value(date), "c", 0);
1959 for (int j = 1; j <= maxFreqF; j++) {
1960 storeIfSelected(coefficients, selected, slot.cij[j].value(date), "c", j);
1961 storeIfSelected(coefficients, selected, slot.sij[j].value(date), "s", j);
1962 }
1963 return coefficients;
1964
1965 }
1966
1967 /** Put a coefficient in a map if selected.
1968 * @param map map to populate
1969 * @param selected set of coefficients that should be put in the map
1970 * (empty set means all coefficients are selected)
1971 * @param value coefficient value
1972 * @param id coefficient identifier
1973 * @param indices list of coefficient indices
1974 */
1975 private void storeIfSelected(final Map<String, double[]> map, final Set<String> selected,
1976 final double[] value, final String id, final int... indices) {
1977 final StringBuilder keyBuilder = new StringBuilder(getCoefficientsKeyPrefix());
1978 keyBuilder.append(id);
1979 for (int index : indices) {
1980 keyBuilder.append('[').append(index).append(']');
1981 }
1982 final String key = keyBuilder.toString();
1983 if (selected.isEmpty() || selected.contains(key)) {
1984 map.put(key, value);
1985 }
1986 }
1987
1988 /** Replace the instance with a data transfer object for serialization.
1989 * @return data transfer object that will be serialized
1990 * @exception NotSerializableException if an additional state provider is not serializable
1991 */
1992 private Object writeReplace() throws NotSerializableException {
1993
1994 // slots transitions
1995 final SortedSet<TimeSpanMap.Transition<Slot>> transitions = slots.getTransitions();
1996 final AbsoluteDate[] transitionDates = new AbsoluteDate[transitions.size()];
1997 final Slot[] allSlots = new Slot[transitions.size() + 1];
1998 int i = 0;
1999 for (final TimeSpanMap.Transition<Slot> transition : transitions) {
2000 if (i == 0) {
2001 // slot before the first transition
2002 allSlots[i] = transition.getBefore();
2003 }
2004 if (i < transitionDates.length) {
2005 transitionDates[i] = transition.getDate();
2006 allSlots[++i] = transition.getAfter();
2007 }
2008 }
2009
2010 return new DataTransferObject(jMax, interpolationPoints, maxFreqF, prefix,
2011 transitionDates, allSlots);
2012
2013 }
2014
2015
2016 /** Internal class used only for serialization. */
2017 private static class DataTransferObject implements Serializable {
2018
2019 /** Serializable UID. */
2020 private static final long serialVersionUID = 20160319L;
2021
2022 /** Maximum value for j index. */
2023 private final int jMax;
2024
2025 /** Number of points used in the interpolation process. */
2026 private final int interpolationPoints;
2027
2028 /** Max frequency of F. */
2029 private final int maxFreqF;
2030
2031 /** Coefficients prefix. */
2032 private final String prefix;
2033
2034 /** Transitions dates. */
2035 private final AbsoluteDate[] transitionDates;
2036
2037 /** All slots. */
2038 private final Slot[] allSlots;
2039
2040 /** Simple constructor.
2041 * @param jMax maximum value for j index
2042 * @param interpolationPoints number of points used in the interpolation process
2043 * @param maxFreqF max frequency of F
2044 * @param prefix prefix for coefficients keys
2045 * @param transitionDates transitions dates
2046 * @param allSlots all slots
2047 */
2048 DataTransferObject(final int jMax, final int interpolationPoints,
2049 final int maxFreqF, final String prefix,
2050 final AbsoluteDate[] transitionDates, final Slot[] allSlots) {
2051 this.jMax = jMax;
2052 this.interpolationPoints = interpolationPoints;
2053 this.maxFreqF = maxFreqF;
2054 this.prefix = prefix;
2055 this.transitionDates = transitionDates;
2056 this.allSlots = allSlots;
2057 }
2058
2059 /** Replace the deserialized data transfer object with a {@link ThirdBodyShortPeriodicCoefficients}.
2060 * @return replacement {@link ThirdBodyShortPeriodicCoefficients}
2061 */
2062 private Object readResolve() {
2063
2064 final TimeSpanMap<Slot> slots = new TimeSpanMap<Slot>(allSlots[0]);
2065 for (int i = 0; i < transitionDates.length; ++i) {
2066 slots.addValidAfter(allSlots[i + 1], transitionDates[i]);
2067 }
2068
2069 return new ThirdBodyShortPeriodicCoefficients(jMax, interpolationPoints, maxFreqF, prefix, slots);
2070
2071 }
2072
2073 }
2074
2075 }
2076
2077 /** Coefficients valid for one time slot. */
2078 private static class Slot implements Serializable {
2079
2080 /** Serializable UID. */
2081 private static final long serialVersionUID = 20160319L;
2082
2083 /** The coefficients C<sub>i</sub><sup>j</sup>.
2084 * <p>
2085 * The index order is cij[j][i] <br/>
2086 * i corresponds to the equinoctial element, as follows: <br/>
2087 * - i=0 for a <br/>
2088 * - i=1 for k <br/>
2089 * - i=2 for h <br/>
2090 * - i=3 for q <br/>
2091 * - i=4 for p <br/>
2092 * - i=5 for λ <br/>
2093 * </p>
2094 */
2095 private final ShortPeriodicsInterpolatedCoefficient[] cij;
2096
2097 /** The coefficients S<sub>i</sub><sup>j</sup>.
2098 * <p>
2099 * The index order is sij[j][i] <br/>
2100 * i corresponds to the equinoctial element, as follows: <br/>
2101 * - i=0 for a <br/>
2102 * - i=1 for k <br/>
2103 * - i=2 for h <br/>
2104 * - i=3 for q <br/>
2105 * - i=4 for p <br/>
2106 * - i=5 for λ <br/>
2107 * </p>
2108 */
2109 private final ShortPeriodicsInterpolatedCoefficient[] sij;
2110
2111 /** Simple constructor.
2112 * @param jMax maximum value for j index
2113 * @param interpolationPoints number of points used in the interpolation process
2114 */
2115 Slot(final int jMax, final int interpolationPoints) {
2116 // allocate the coefficients arrays
2117 cij = new ShortPeriodicsInterpolatedCoefficient[jMax + 1];
2118 sij = new ShortPeriodicsInterpolatedCoefficient[jMax + 1];
2119 for (int j = 0; j <= jMax; j++) {
2120 cij[j] = new ShortPeriodicsInterpolatedCoefficient(interpolationPoints);
2121 sij[j] = new ShortPeriodicsInterpolatedCoefficient(interpolationPoints);
2122 }
2123
2124
2125 }
2126 }
2127
2128 }