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     /** &Chi; = 1 / sqrt(1 - e²) = 1 / B. */
145     private double X;
146     /** &Chi;². */
147     private double XX;
148     /** &Chi;³. */
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 * &Chi;³. */
192     private double hXXX;
193     /** k * &Chi;³. */
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         //&Chi;<sup>-2</sup>.
364         BB = B * B;
365         //&Chi;<sup>-3</sup>.
366         BBB = BB * B;
367 
368         //b = 1 / (1 + B)
369         b = 1. / (1. + B);
370 
371         // &Chi;
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 * &Chi;³
390         hXXX = h * XXX;
391         //k * &Chi;³
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⁰ + &Sigma;<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 }