DSSTThirdBody.java

  1. /* Copyright 2002-2018 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. import java.io.NotSerializableException;
  19. import java.io.Serializable;
  20. import java.util.ArrayList;
  21. import java.util.HashMap;
  22. import java.util.List;
  23. import java.util.Map;
  24. import java.util.Set;
  25. import java.util.SortedSet;
  26. import java.util.TreeMap;

  27. import org.hipparchus.analysis.differentiation.DSFactory;
  28. import org.hipparchus.analysis.differentiation.DerivativeStructure;
  29. import org.hipparchus.geometry.euclidean.threed.Vector3D;
  30. import org.hipparchus.util.FastMath;
  31. import org.orekit.attitudes.AttitudeProvider;
  32. import org.orekit.bodies.CelestialBody;
  33. import org.orekit.errors.OrekitException;
  34. import org.orekit.orbits.Orbit;
  35. import org.orekit.propagation.SpacecraftState;
  36. import org.orekit.propagation.events.EventDetector;
  37. import org.orekit.propagation.semianalytical.dsst.utilities.AuxiliaryElements;
  38. import org.orekit.propagation.semianalytical.dsst.utilities.CjSjCoefficient;
  39. import org.orekit.propagation.semianalytical.dsst.utilities.CoefficientsFactory;
  40. import org.orekit.propagation.semianalytical.dsst.utilities.CoefficientsFactory.NSKey;
  41. import org.orekit.propagation.semianalytical.dsst.utilities.JacobiPolynomials;
  42. import org.orekit.propagation.semianalytical.dsst.utilities.ShortPeriodicsInterpolatedCoefficient;
  43. import org.orekit.propagation.semianalytical.dsst.utilities.UpperBounds;
  44. import org.orekit.propagation.semianalytical.dsst.utilities.hansen.HansenThirdBodyLinear;
  45. import org.orekit.time.AbsoluteDate;
  46. import org.orekit.utils.TimeSpanMap;

  47. /** Third body attraction perturbation to the
  48.  *  {@link org.orekit.propagation.semianalytical.dsst.DSSTPropagator DSSTPropagator}.
  49.  *
  50.  *  @author Romain Di Costanzo
  51.  *  @author Pascal Parraud
  52.  */
  53. public class DSSTThirdBody implements DSSTForceModel {

  54.     /** Max power for summation. */
  55.     private static final int    MAX_POWER = 22;

  56.     /** Truncation tolerance for big, eccentric  orbits. */
  57.     private static final double BIG_TRUNCATION_TOLERANCE = 1.e-1;

  58.     /** Truncation tolerance for small orbits. */
  59.     private static final double SMALL_TRUNCATION_TOLERANCE = 1.9e-6;

  60.     /** Number of points for interpolation. */
  61.     private static final int    INTERPOLATION_POINTS = 3;

  62.     /** Maximum power for eccentricity used in short periodic computation. */
  63.     private static final int    MAX_ECCPOWER_SP = 4;

  64.     /** Retrograde factor I.
  65.      *  <p>
  66.      *  DSST model needs equinoctial orbit as internal representation.
  67.      *  Classical equinoctial elements have discontinuities when inclination
  68.      *  is close to zero. In this representation, I = +1. <br>
  69.      *  To avoid this discontinuity, another representation exists and equinoctial
  70.      *  elements can be expressed in a different way, called "retrograde" orbit.
  71.      *  This implies I = -1. <br>
  72.      *  As Orekit doesn't implement the retrograde orbit, I is always set to +1.
  73.      *  But for the sake of consistency with the theory, the retrograde factor
  74.      *  has been kept in the formulas.
  75.      *  </p>
  76.      */
  77.     private static final int    I = 1;

  78.     /** The 3rd body to consider. */
  79.     private final CelestialBody    body;

  80.     /** Standard gravitational parameter μ for the body in m³/s². */
  81.     private final double           gm;

  82.     /** Factorial. */
  83.     private final double[]         fact;

  84.     /** V<sub>ns</sub> coefficients. */
  85.     private final TreeMap<NSKey, Double> Vns;

  86.     /** Distance from center of mass of the central body to the 3rd body. */
  87.     private double R3;

  88.     /** Short period terms. */
  89.     private ThirdBodyShortPeriodicCoefficients shortPeriods;

  90.     // Equinoctial elements (according to DSST notation)
  91.     /** a. */
  92.     private double a;
  93.     /** e<sub>x</sub>. */
  94.     private double k;
  95.     /** e<sub>y</sub>. */
  96.     private double h;
  97.     /** h<sub>x</sub>. */
  98.     private double q;
  99.     /** h<sub>y</sub>. */
  100.     private double p;

  101.     /** Eccentricity. */
  102.     private double ecc;

  103.     // Direction cosines of the symmetry axis
  104.     /** α. */
  105.     private double alpha;
  106.     /** β. */
  107.     private double beta;
  108.     /** γ. */
  109.     private double gamma;

  110.     // Common factors for potential computation
  111.     /** A = n * a². */
  112.     private double A;
  113.     /** B = sqrt(1 - e²). */
  114.     private double B;
  115.     /** C = 1 + p² + q². */
  116.     private double C;
  117.     /** B². */
  118.     private double BB;
  119.     /** B³. */
  120.     private double BBB;

  121.     /** The mean motion (n). */
  122.     private double meanMotion;

  123.     /** &Chi; = 1 / sqrt(1 - e²) = 1 / B. */
  124.     private double X;
  125.     /** &Chi;². */
  126.     private double XX;
  127.     /** &Chi;³. */
  128.     private double XXX;
  129.     /** -2 * a / A. */
  130.     private double m2aoA;
  131.     /** B / A. */
  132.     private double BoA;
  133.     /** 1 / (A * B). */
  134.     private double ooAB;
  135.     /** -C / (2 * A * B). */
  136.     private double mCo2AB;
  137.     /** B / A(1 + B). */
  138.     private double BoABpo;

  139.     /** Max power for a/R3 in the serie expansion. */
  140.     private int    maxAR3Pow;

  141.     /** Max power for e in the serie expansion. */
  142.     private int    maxEccPow;

  143.     /** Max power for e in the serie expansion (for short periodics). */
  144.     private int    maxEccPowShort;

  145.     /** Max frequency of F. */
  146.     private int    maxFreqF;

  147.     /** An array that contains the objects needed to build the Hansen coefficients. <br/>
  148.      * The index is s */
  149.     private final HansenThirdBodyLinear[] hansenObjects;

  150.     /** The current value of the U function. <br/>
  151.      * Needed for the short periodic contribution */
  152.     private double U;

  153.     /** Qns coefficients. */
  154.     private double[][] Qns;
  155.     /** a / R3 up to power maxAR3Pow. */
  156.     private double[] aoR3Pow;
  157.     /** mu3 / R3. */
  158.     private double muoR3;

  159.     /** b = 1 / (1 + sqrt(1 - e²)) = 1 / (1 + B).*/
  160.     private double b;

  161.     /** h * &Chi;³. */
  162.     private double hXXX;
  163.     /** k * &Chi;³. */
  164.     private double kXXX;

  165.     /** Factory for the DerivativeStructure instances. */
  166.     private final DSFactory factory;

  167.     /** Complete constructor.
  168.      *  @param body the 3rd body to consider
  169.      *  @see org.orekit.bodies.CelestialBodyFactory
  170.      */
  171.     public DSSTThirdBody(final CelestialBody body) {
  172.         this.body = body;
  173.         this.gm   = body.getGM();

  174.         this.maxAR3Pow = Integer.MIN_VALUE;
  175.         this.maxEccPow = Integer.MIN_VALUE;

  176.         this.Vns = CoefficientsFactory.computeVns(MAX_POWER);

  177.         // Factorials computation
  178.         final int dim = 2 * MAX_POWER;
  179.         this.fact = new double[dim];
  180.         fact[0] = 1.;
  181.         for (int i = 1; i < dim; i++) {
  182.             fact[i] = i * fact[i - 1];
  183.         }

  184.         //Initialise the HansenCoefficient generator
  185.         this.hansenObjects = new HansenThirdBodyLinear[MAX_POWER + 1];
  186.         for (int s = 0; s <= MAX_POWER; s++) {
  187.             this.hansenObjects[s] = new HansenThirdBodyLinear(MAX_POWER, s);
  188.         }

  189.         this.factory = new DSFactory(1, 1);

  190.     }

  191.     /** Get third body.
  192.      *  @return third body
  193.      */
  194.     public CelestialBody getBody() {
  195.         return body;
  196.     }

  197.     /** Computes the highest power of the eccentricity and the highest power
  198.      *  of a/R3 to appear in the truncated analytical power series expansion.
  199.      *  <p>
  200.      *  This method computes the upper value for the 3rd body potential and
  201.      *  determines the maximal powers for the eccentricity and a/R3 producing
  202.      *  potential terms bigger than a defined tolerance.
  203.      *  </p>
  204.      *  @param aux auxiliary elements related to the current orbit
  205.      *  @param meanOnly only mean elements will be used for the propagation
  206.      *  @throws OrekitException if some specific error occurs
  207.      */
  208.     @Override
  209.     public List<ShortPeriodTerms> initialize(final AuxiliaryElements aux, final boolean meanOnly)
  210.         throws OrekitException {

  211.         // Initializes specific parameters.
  212.         initializeStep(aux);

  213.         // Truncation tolerance.
  214.         final double aor = a / R3;
  215.         final double tol = ( aor > .3 || (aor > .15  && ecc > .25) ) ? BIG_TRUNCATION_TOLERANCE : SMALL_TRUNCATION_TOLERANCE;

  216.         // Utilities for truncation
  217.         // Set a lower bound for eccentricity
  218.         final double eo2  = FastMath.max(0.0025, ecc / 2.);
  219.         final double x2o2 = XX / 2.;
  220.         final double[] eccPwr = new double[MAX_POWER];
  221.         final double[] chiPwr = new double[MAX_POWER];
  222.         eccPwr[0] = 1.;
  223.         chiPwr[0] = X;
  224.         for (int i = 1; i < MAX_POWER; i++) {
  225.             eccPwr[i] = eccPwr[i - 1] * eo2;
  226.             chiPwr[i] = chiPwr[i - 1] * x2o2;
  227.         }

  228.         // Auxiliary quantities.
  229.         final double ao2rxx = aor / (2. * XX);
  230.         double xmuarn       = ao2rxx * ao2rxx * gm / (X * R3);
  231.         double term         = 0.;

  232.         // Compute max power for a/R3 and e.
  233.         maxAR3Pow = 2;
  234.         maxEccPow = 0;
  235.         int n     = 2;
  236.         int m     = 2;
  237.         int nsmd2 = 0;

  238.         do {
  239.             // Upper bound for Tnm.
  240.             term =  xmuarn *
  241.                    (fact[n + m] / (fact[nsmd2] * fact[nsmd2 + m])) *
  242.                    (fact[n + m + 1] / (fact[m] * fact[n + 1])) *
  243.                    (fact[n - m + 1] / fact[n + 1]) *
  244.                    eccPwr[m] * UpperBounds.getDnl(XX, chiPwr[m], n + 2, m);

  245.             if (term < tol) {
  246.                 if (m == 0) {
  247.                     break;
  248.                 } else if (m < 2) {
  249.                     xmuarn *= ao2rxx;
  250.                     m = 0;
  251.                     n++;
  252.                     nsmd2++;
  253.                 } else {
  254.                     m -= 2;
  255.                     nsmd2++;
  256.                 }
  257.             } else {
  258.                 maxAR3Pow = n;
  259.                 maxEccPow = FastMath.max(m, maxEccPow);
  260.                 xmuarn *= ao2rxx;
  261.                 m++;
  262.                 n++;
  263.             }
  264.         } while (n < MAX_POWER);

  265.         maxEccPow = FastMath.min(maxAR3Pow, maxEccPow);

  266.         // allocate the array aoR3Pow
  267.         aoR3Pow = new double[maxAR3Pow + 1];

  268.         maxFreqF = maxAR3Pow + 1;
  269.         maxEccPowShort = MAX_ECCPOWER_SP;

  270.         Qns = CoefficientsFactory.computeQns(gamma, maxAR3Pow, FastMath.max(maxEccPow, maxEccPowShort));
  271.         final int jMax = maxAR3Pow + 1;
  272.         shortPeriods = new ThirdBodyShortPeriodicCoefficients(jMax, INTERPOLATION_POINTS,
  273.                                                               maxFreqF, body.getName(),
  274.                                                               new TimeSpanMap<Slot>(new Slot(jMax, INTERPOLATION_POINTS)));

  275.         final List<ShortPeriodTerms> list = new ArrayList<ShortPeriodTerms>();
  276.         list.add(shortPeriods);
  277.         return list;

  278.     }

  279.     /** {@inheritDoc} */
  280.     @Override
  281.     public void initializeStep(final AuxiliaryElements aux) throws OrekitException {

  282.         // Equinoctial elements
  283.         a = aux.getSma();
  284.         k = aux.getK();
  285.         h = aux.getH();
  286.         q = aux.getQ();
  287.         p = aux.getP();

  288.         // Eccentricity
  289.         ecc = aux.getEcc();

  290.         // Distance from center of mass of the central body to the 3rd body
  291.         final Vector3D bodyPos = body.getPVCoordinates(aux.getDate(), aux.getFrame()).getPosition();
  292.         R3 = bodyPos.getNorm();

  293.         // Direction cosines
  294.         final Vector3D bodyDir = bodyPos.normalize();
  295.         alpha = bodyDir.dotProduct(aux.getVectorF());
  296.         beta  = bodyDir.dotProduct(aux.getVectorG());
  297.         gamma = bodyDir.dotProduct(aux.getVectorW());

  298.         // Equinoctial coefficients
  299.         A = aux.getA();
  300.         B = aux.getB();
  301.         C = aux.getC();
  302.         meanMotion = aux.getMeanMotion();

  303.         //&Chi;<sup>-2</sup>.
  304.         BB = B * B;
  305.         //&Chi;<sup>-3</sup>.
  306.         BBB = BB * B;

  307.         //b = 1 / (1 + B)
  308.         b = 1. / (1. + B);

  309.         // &Chi;
  310.         X = 1. / B;
  311.         XX = X * X;
  312.         XXX = X * XX;
  313.         // -2 * a / A
  314.         m2aoA = -2. * a / A;
  315.         // B / A
  316.         BoA = B / A;
  317.         // 1 / AB
  318.         ooAB = 1. / (A * B);
  319.         // -C / 2AB
  320.         mCo2AB = -C * ooAB / 2.;
  321.         // B / A(1 + B)
  322.         BoABpo = BoA / (1. + B);

  323.         // mu3 / R3
  324.         muoR3 = gm / R3;

  325.         //h * &Chi;³
  326.         hXXX = h * XXX;
  327.         //k * &Chi;³
  328.         kXXX = k * XXX;
  329.     }

  330.     /** {@inheritDoc} */
  331.     @Override
  332.     public double[] getMeanElementRate(final SpacecraftState currentState) {

  333.         // Qns coefficients
  334.         Qns = CoefficientsFactory.computeQns(gamma, maxAR3Pow, maxEccPow);
  335.         // a / R3 up to power maxAR3Pow
  336.         final double aoR3 = a / R3;
  337.         aoR3Pow[0] = 1.;
  338.         for (int i = 1; i <= maxAR3Pow; i++) {
  339.             aoR3Pow[i] = aoR3 * aoR3Pow[i - 1];
  340.         }

  341.         // Compute potential U derivatives
  342.         final double[] dU  = computeUDerivatives();
  343.         final double dUda  = dU[0];
  344.         final double dUdk  = dU[1];
  345.         final double dUdh  = dU[2];
  346.         final double dUdAl = dU[3];
  347.         final double dUdBe = dU[4];
  348.         final double dUdGa = dU[5];

  349.         // Compute cross derivatives [Eq. 2.2-(8)]
  350.         // U(alpha,gamma) = alpha * dU/dgamma - gamma * dU/dalpha
  351.         final double UAlphaGamma   = alpha * dUdGa - gamma * dUdAl;
  352.         // U(beta,gamma) = beta * dU/dgamma - gamma * dU/dbeta
  353.         final double UBetaGamma    =  beta * dUdGa - gamma * dUdBe;
  354.         // Common factor
  355.         final double pUAGmIqUBGoAB = (p * UAlphaGamma - I * q * UBetaGamma) * ooAB;

  356.         // Compute mean elements rates [Eq. 3.1-(1)]
  357.         final double da =  0.;
  358.         final double dh =  BoA * dUdk + k * pUAGmIqUBGoAB;
  359.         final double dk = -BoA * dUdh - h * pUAGmIqUBGoAB;
  360.         final double dp =  mCo2AB * UBetaGamma;
  361.         final double dq =  mCo2AB * UAlphaGamma * I;
  362.         final double dM =  m2aoA * dUda + BoABpo * (h * dUdh + k * dUdk) + pUAGmIqUBGoAB;

  363.         return new double[] {da, dk, dh, dq, dp, dM};

  364.     }

  365.     /** {@inheritDoc} */
  366.     @Override
  367.     public void updateShortPeriodTerms(final SpacecraftState... meanStates)
  368.         throws OrekitException {

  369.         final Slot slot = shortPeriods.createSlot(meanStates);

  370.         for (final SpacecraftState meanState : meanStates) {

  371.             initializeStep(new AuxiliaryElements(meanState.getOrbit(), I));

  372.             // a / R3 up to power maxAR3Pow
  373.             final double aoR3 = a / R3;
  374.             aoR3Pow[0] = 1.;
  375.             for (int i = 1; i <= maxAR3Pow; i++) {
  376.                 aoR3Pow[i] = aoR3 * aoR3Pow[i - 1];
  377.             }

  378.             // Qns coefficients
  379.             Qns = CoefficientsFactory.computeQns(gamma, maxAR3Pow, FastMath.max(maxEccPow, maxEccPowShort));
  380.             final GeneratingFunctionCoefficients gfCoefs =
  381.                             new GeneratingFunctionCoefficients(maxAR3Pow, MAX_ECCPOWER_SP, maxAR3Pow + 1);

  382.             //Compute additional quantities
  383.             // 2 * a / An
  384.             final double ax2oAn  = -m2aoA / meanMotion;
  385.             // B / An
  386.             final double BoAn  = BoA / meanMotion;
  387.             // 1 / ABn
  388.             final double ooABn = ooAB / meanMotion;
  389.             // C / 2ABn
  390.             final double Co2ABn = -mCo2AB / meanMotion;
  391.             // B / (A * (1 + B) * n)
  392.             final double BoABpon = BoABpo / meanMotion;
  393.             // -3 / n²a² = -3 / nA
  394.             final double m3onA = -3 / (A * meanMotion);

  395.             //Compute the C<sub>i</sub><sup>j</sup> and S<sub>i</sub><sup>j</sup> coefficients.
  396.             for (int j = 1; j < slot.cij.length; j++) {
  397.                 // First compute the C<sub>i</sub><sup>j</sup> coefficients
  398.                 final double[] currentCij = new double[6];

  399.                 // Compute the cross derivatives operator :
  400.                 final double SAlphaGammaCj   = alpha * gfCoefs.getdSdgammaCj(j) - gamma * gfCoefs.getdSdalphaCj(j);
  401.                 final double SAlphaBetaCj    = alpha * gfCoefs.getdSdbetaCj(j)  - beta  * gfCoefs.getdSdalphaCj(j);
  402.                 final double SBetaGammaCj    =  beta * gfCoefs.getdSdgammaCj(j) - gamma * gfCoefs.getdSdbetaCj(j);
  403.                 final double ShkCj           =     h * gfCoefs.getdSdkCj(j)     -  k    * gfCoefs.getdSdhCj(j);
  404.                 final double pSagmIqSbgoABnCj = (p * SAlphaGammaCj - I * q * SBetaGammaCj) * ooABn;
  405.                 final double ShkmSabmdSdlCj  =  ShkCj - SAlphaBetaCj - gfCoefs.getdSdlambdaCj(j);

  406.                 currentCij[0] =  ax2oAn * gfCoefs.getdSdlambdaCj(j);
  407.                 currentCij[1] =  -(BoAn * gfCoefs.getdSdhCj(j) + h * pSagmIqSbgoABnCj + k * BoABpon * gfCoefs.getdSdlambdaCj(j));
  408.                 currentCij[2] =    BoAn * gfCoefs.getdSdkCj(j) + k * pSagmIqSbgoABnCj - h * BoABpon * gfCoefs.getdSdlambdaCj(j);
  409.                 currentCij[3] =  Co2ABn * (q * ShkmSabmdSdlCj - I * SAlphaGammaCj);
  410.                 currentCij[4] =  Co2ABn * (p * ShkmSabmdSdlCj - SBetaGammaCj);
  411.                 currentCij[5] = -ax2oAn * gfCoefs.getdSdaCj(j) + BoABpon * (h * gfCoefs.getdSdhCj(j) + k * gfCoefs.getdSdkCj(j)) + pSagmIqSbgoABnCj + m3onA * gfCoefs.getSCj(j);

  412.                 // add the computed coefficients to the interpolators
  413.                 slot.cij[j].addGridPoint(meanState.getDate(), currentCij);

  414.                 // Compute the S<sub>i</sub><sup>j</sup> coefficients
  415.                 final double[] currentSij = new double[6];

  416.                 // Compute the cross derivatives operator :
  417.                 final double SAlphaGammaSj   = alpha * gfCoefs.getdSdgammaSj(j) - gamma * gfCoefs.getdSdalphaSj(j);
  418.                 final double SAlphaBetaSj    = alpha * gfCoefs.getdSdbetaSj(j)  - beta  * gfCoefs.getdSdalphaSj(j);
  419.                 final double SBetaGammaSj    =  beta * gfCoefs.getdSdgammaSj(j) - gamma * gfCoefs.getdSdbetaSj(j);
  420.                 final double ShkSj           =     h * gfCoefs.getdSdkSj(j)     -  k    * gfCoefs.getdSdhSj(j);
  421.                 final double pSagmIqSbgoABnSj = (p * SAlphaGammaSj - I * q * SBetaGammaSj) * ooABn;
  422.                 final double ShkmSabmdSdlSj  =  ShkSj - SAlphaBetaSj - gfCoefs.getdSdlambdaSj(j);

  423.                 currentSij[0] =  ax2oAn * gfCoefs.getdSdlambdaSj(j);
  424.                 currentSij[1] =  -(BoAn * gfCoefs.getdSdhSj(j) + h * pSagmIqSbgoABnSj + k * BoABpon * gfCoefs.getdSdlambdaSj(j));
  425.                 currentSij[2] =    BoAn * gfCoefs.getdSdkSj(j) + k * pSagmIqSbgoABnSj - h * BoABpon * gfCoefs.getdSdlambdaSj(j);
  426.                 currentSij[3] =  Co2ABn * (q * ShkmSabmdSdlSj - I * SAlphaGammaSj);
  427.                 currentSij[4] =  Co2ABn * (p * ShkmSabmdSdlSj - SBetaGammaSj);
  428.                 currentSij[5] = -ax2oAn * gfCoefs.getdSdaSj(j) + BoABpon * (h * gfCoefs.getdSdhSj(j) + k * gfCoefs.getdSdkSj(j)) + pSagmIqSbgoABnSj + m3onA * gfCoefs.getSSj(j);

  429.                 // add the computed coefficients to the interpolators
  430.                 slot.sij[j].addGridPoint(meanState.getDate(), currentSij);

  431.                 if (j == 1) {
  432.                     //Compute the C⁰ coefficients using Danielson 2.5.2-15a.
  433.                     final double[] value = new double[6];
  434.                     for (int i = 0; i < 6; ++i) {
  435.                         value[i] = currentCij[i] * k / 2. + currentSij[i] * h / 2.;
  436.                     }
  437.                     slot.cij[0].addGridPoint(meanState.getDate(), value);
  438.                 }
  439.             }
  440.         }
  441.     }

  442.     /** {@inheritDoc} */
  443.     @Override
  444.     public EventDetector[] getEventsDetectors() {
  445.         return null;
  446.     }

  447.     /** Compute potential derivatives.
  448.      *  @return derivatives of the potential with respect to orbital parameters
  449.      */
  450.     private double[] computeUDerivatives() {

  451.         // Gs and Hs coefficients
  452.         final double[][] GsHs = CoefficientsFactory.computeGsHs(k, h, alpha, beta, maxEccPow);

  453.         // Initialise U.
  454.         U = 0.;

  455.         // Potential derivatives
  456.         double dUda  = 0.;
  457.         double dUdk  = 0.;
  458.         double dUdh  = 0.;
  459.         double dUdAl = 0.;
  460.         double dUdBe = 0.;
  461.         double dUdGa = 0.;

  462.         for (int s = 0; s <= maxEccPow; s++) {
  463.             // initialise the Hansen roots
  464.             this.hansenObjects[s].computeInitValues(B, BB, BBB);

  465.             // Get the current Gs coefficient
  466.             final double gs = GsHs[0][s];

  467.             // Compute Gs partial derivatives from 3.1-(9)
  468.             double dGsdh  = 0.;
  469.             double dGsdk  = 0.;
  470.             double dGsdAl = 0.;
  471.             double dGsdBe = 0.;
  472.             if (s > 0) {
  473.                 // First get the G(s-1) and the H(s-1) coefficients
  474.                 final double sxGsm1 = s * GsHs[0][s - 1];
  475.                 final double sxHsm1 = s * GsHs[1][s - 1];
  476.                 // Then compute derivatives
  477.                 dGsdh  = beta  * sxGsm1 - alpha * sxHsm1;
  478.                 dGsdk  = alpha * sxGsm1 + beta  * sxHsm1;
  479.                 dGsdAl = k * sxGsm1 - h * sxHsm1;
  480.                 dGsdBe = h * sxGsm1 + k * sxHsm1;
  481.             }

  482.             // Kronecker symbol (2 - delta(0,s))
  483.             final double delta0s = (s == 0) ? 1. : 2.;

  484.             for (int n = FastMath.max(2, s); n <= maxAR3Pow; n++) {
  485.                 // (n - s) must be even
  486.                 if ((n - s) % 2 == 0) {
  487.                     // Extract data from previous computation :
  488.                     final double kns   = this.hansenObjects[s].getValue(n, B);
  489.                     final double dkns  = this.hansenObjects[s].getDerivative(n, B);

  490.                     final double vns   = Vns.get(new NSKey(n, s));
  491.                     final double coef0 = delta0s * aoR3Pow[n] * vns;
  492.                     final double coef1 = coef0 * Qns[n][s];
  493.                     final double coef2 = coef1 * kns;
  494.                     // dQns/dGamma = Q(n, s + 1) from Equation 3.1-(8)
  495.                     // for n = s, Q(n, n + 1) = 0. (Cefola & Broucke, 1975)
  496.                     final double dqns = (n == s) ? 0. : Qns[n][s + 1];

  497.                     //Compute U:
  498.                     U += coef2 * gs;

  499.                     // Compute dU / da :
  500.                     dUda += coef2 * n * gs;
  501.                     // Compute dU / dh
  502.                     dUdh += coef1 * (kns * dGsdh + hXXX * gs * dkns);
  503.                     // Compute dU / dk
  504.                     dUdk += coef1 * (kns * dGsdk + kXXX * gs * dkns);
  505.                     // Compute dU / dAlpha
  506.                     dUdAl += coef2 * dGsdAl;
  507.                     // Compute dU / dBeta
  508.                     dUdBe += coef2 * dGsdBe;
  509.                     // Compute dU / dGamma
  510.                     dUdGa += coef0 * kns * dqns * gs;
  511.                 }
  512.             }
  513.         }

  514.         // multiply by mu3 / R3
  515.         U *= muoR3;

  516.         return new double[] {
  517.             dUda  * muoR3 / a,
  518.             dUdk  * muoR3,
  519.             dUdh  * muoR3,
  520.             dUdAl * muoR3,
  521.             dUdBe * muoR3,
  522.             dUdGa * muoR3
  523.         };

  524.     }

  525.     /** {@inheritDoc} */
  526.     @Override
  527.     public void registerAttitudeProvider(final AttitudeProvider provider) {
  528.         //nothing is done since this contribution is not sensitive to attitude
  529.     }

  530.     /** Computes the C<sup>j</sup> and S<sup>j</sup> coefficients Danielson 4.2-(15,16)
  531.      * and their derivatives.
  532.      *  <p>
  533.      *  CS Mathematical Report $3.5.3.2
  534.      *  </p>
  535.      */
  536.     private class FourierCjSjCoefficients {

  537.         /** The coefficients G<sub>n, s</sub> and their derivatives. */
  538.         private final GnsCoefficients gns;

  539.         /** the coefficients e<sup>-|j-s|</sup>*w<sub>j</sub><sup>n, s</sup> and their derivatives by h and k. */
  540.         private final WnsjEtomjmsCoefficient wnsjEtomjmsCoefficient;

  541.         /** The terms containing the coefficients C<sub>j</sub> and S<sub>j</sub> of (α, β) or (k, h). */
  542.         private final CjSjAlphaBetaKH ABDECoefficients;

  543.         /** The Fourier coefficients C<sup>j</sup> and their derivatives.
  544.          * <p>
  545.          * Each column of the matrix contains the following values: <br/>
  546.          * - C<sup>j</sup> <br/>
  547.          * - dC<sup>j</sup> / da <br/>
  548.          * - dC<sup>j</sup> / dk <br/>
  549.          * - dC<sup>j</sup> / dh <br/>
  550.          * - dC<sup>j</sup> / dα <br/>
  551.          * - dC<sup>j</sup> / dβ <br/>
  552.          * - dC<sup>j</sup> / dγ <br/>
  553.          * </p>
  554.          */
  555.         private final double[][] cj;

  556.         /** The S<sup>j</sup> coefficients and their derivatives.
  557.          * <p>
  558.          * Each column of the matrix contains the following values: <br/>
  559.          * - S<sup>j</sup> <br/>
  560.          * - dS<sup>j</sup> / da <br/>
  561.          * - dS<sup>j</sup> / dk <br/>
  562.          * - dS<sup>j</sup> / dh <br/>
  563.          * - dS<sup>j</sup> / dα <br/>
  564.          * - dS<sup>j</sup> / dβ <br/>
  565.          * - dS<sup>j</sup> / dγ <br/>
  566.          * </p>
  567.          */
  568.         private final double[][] sj;

  569.         /** The Coefficients C<sup>j</sup><sub>,λ</sub>.
  570.          * <p>
  571.          * See Danielson 4.2-21
  572.          * </p>
  573.          */
  574.         private final double[] cjlambda;

  575.         /** The Coefficients S<sup>j</sup><sub>,λ</sub>.
  576.         * <p>
  577.         * See Danielson 4.2-21
  578.         * </p>
  579.         */
  580.         private final double[] sjlambda;

  581.         /** Maximum value for n. */
  582.         private final int nMax;

  583.         /** Maximum value for s. */
  584.         private final int sMax;

  585.         /** Maximum value for j. */
  586.         private final int jMax;

  587.         /**
  588.          * Private constructor.
  589.          *
  590.          * @param nMax maximum value for n index
  591.          * @param sMax maximum value for s index
  592.          * @param jMax maximum value for j index
  593.          */
  594.         FourierCjSjCoefficients(final int nMax, final int sMax, final int jMax) {
  595.             //Save parameters
  596.             this.nMax = nMax;
  597.             this.sMax = sMax;
  598.             this.jMax = jMax;

  599.             //Create objects
  600.             wnsjEtomjmsCoefficient = new WnsjEtomjmsCoefficient();
  601.             ABDECoefficients = new CjSjAlphaBetaKH();
  602.             gns = new GnsCoefficients(nMax, sMax);

  603.             //create arays
  604.             this.cj = new double[7][jMax + 1];
  605.             this.sj = new double[7][jMax + 1];
  606.             this.cjlambda = new double[jMax];
  607.             this.sjlambda = new double[jMax];

  608.             computeCoefficients();
  609.         }

  610.         /**
  611.          * Compute all coefficients.
  612.          */
  613.         private void computeCoefficients() {

  614.             for (int j = 1; j <= jMax; j++) {
  615.                 // initialise the coefficients
  616.                 for (int i = 0; i <= 6; i++) {
  617.                     cj[i][j] = 0.;
  618.                     sj[i][j] = 0.;
  619.                 }
  620.                 if (j < jMax) {
  621.                     // initialise the C<sup>j</sup><sub>,λ</sub> and S<sup>j</sup><sub>,λ</sub> coefficients
  622.                     cjlambda[j] = 0.;
  623.                     sjlambda[j] = 0.;
  624.                 }
  625.                 for (int s = 0; s <= sMax; s++) {

  626.                     // Compute the coefficients A<sub>j, s</sub>, B<sub>j, s</sub>, D<sub>j, s</sub> and E<sub>j, s</sub>
  627.                     ABDECoefficients.computeCoefficients(j, s);

  628.                     // compute starting value for n
  629.                     final int minN = FastMath.max(2, FastMath.max(j - 1, s));

  630.                     for (int n = minN; n <= nMax; n++) {
  631.                         // check if n-s is even
  632.                         if ((n - s) % 2 == 0) {
  633.                             // compute the coefficient e<sup>-|j-s|</sup>*w<sub>j</sub><sup>n+1, s</sup> and its derivatives
  634.                             final double[] wjnp1semjms = wnsjEtomjmsCoefficient.computeWjnsEmjmsAndDeriv(j, s, n + 1);

  635.                             // compute the coefficient e<sup>-|j-s|</sup>*w<sub>-j</sub><sup>n+1, s</sup> and its derivatives
  636.                             final double[] wmjnp1semjms = wnsjEtomjmsCoefficient.computeWjnsEmjmsAndDeriv(-j, s, n + 1);

  637.                             // compute common factors
  638.                             final double coef1 = -(wjnp1semjms[0] * ABDECoefficients.getCoefA() + wmjnp1semjms[0] * ABDECoefficients.getCoefB());
  639.                             final double coef2 =   wjnp1semjms[0] * ABDECoefficients.getCoefD() + wmjnp1semjms[0] * ABDECoefficients.getCoefE();

  640.                             //Compute C<sup>j</sup>
  641.                             cj[0][j] += gns.getGns(n, s) * coef1;
  642.                             //Compute dC<sup>j</sup> / da
  643.                             cj[1][j] += gns.getdGnsda(n, s) * coef1;
  644.                             //Compute dC<sup>j</sup> / dk
  645.                             cj[2][j] += -gns.getGns(n, s) *
  646.                                         (
  647.                                             wjnp1semjms[1] * ABDECoefficients.getCoefA() +
  648.                                             wjnp1semjms[0] * ABDECoefficients.getdCoefAdk() +
  649.                                             wmjnp1semjms[1] * ABDECoefficients.getCoefB() +
  650.                                             wmjnp1semjms[0] * ABDECoefficients.getdCoefBdk()
  651.                                          );
  652.                             //Compute dC<sup>j</sup> / dh
  653.                             cj[3][j] += -gns.getGns(n, s) *
  654.                                         (
  655.                                             wjnp1semjms[2] * ABDECoefficients.getCoefA() +
  656.                                             wjnp1semjms[0] * ABDECoefficients.getdCoefAdh() +
  657.                                             wmjnp1semjms[2] * ABDECoefficients.getCoefB() +
  658.                                             wmjnp1semjms[0] * ABDECoefficients.getdCoefBdh()
  659.                                          );
  660.                             //Compute dC<sup>j</sup> / dα
  661.                             cj[4][j] += -gns.getGns(n, s) *
  662.                                         (
  663.                                             wjnp1semjms[0] * ABDECoefficients.getdCoefAdalpha() +
  664.                                             wmjnp1semjms[0] * ABDECoefficients.getdCoefBdalpha()
  665.                                         );
  666.                             //Compute dC<sup>j</sup> / dβ
  667.                             cj[5][j] += -gns.getGns(n, s) *
  668.                                         (
  669.                                             wjnp1semjms[0] * ABDECoefficients.getdCoefAdbeta() +
  670.                                             wmjnp1semjms[0] * ABDECoefficients.getdCoefBdbeta()
  671.                                         );
  672.                             //Compute dC<sup>j</sup> / dγ
  673.                             cj[6][j] += gns.getdGnsdgamma(n, s) * coef1;

  674.                             //Compute S<sup>j</sup>
  675.                             sj[0][j] += gns.getGns(n, s) * coef2;
  676.                             //Compute dS<sup>j</sup> / da
  677.                             sj[1][j] += gns.getdGnsda(n, s) * coef2;
  678.                             //Compute dS<sup>j</sup> / dk
  679.                             sj[2][j] += gns.getGns(n, s) *
  680.                                         (
  681.                                             wjnp1semjms[1] * ABDECoefficients.getCoefD() +
  682.                                             wjnp1semjms[0] * ABDECoefficients.getdCoefDdk() +
  683.                                             wmjnp1semjms[1] * ABDECoefficients.getCoefE() +
  684.                                             wmjnp1semjms[0] * ABDECoefficients.getdCoefEdk()
  685.                                          );
  686.                             //Compute dS<sup>j</sup> / dh
  687.                             sj[3][j] += gns.getGns(n, s) *
  688.                                         (
  689.                                             wjnp1semjms[2] * ABDECoefficients.getCoefD() +
  690.                                             wjnp1semjms[0] * ABDECoefficients.getdCoefDdh() +
  691.                                             wmjnp1semjms[2] * ABDECoefficients.getCoefE() +
  692.                                             wmjnp1semjms[0] * ABDECoefficients.getdCoefEdh()
  693.                                          );
  694.                             //Compute dS<sup>j</sup> / dα
  695.                             sj[4][j] += gns.getGns(n, s) *
  696.                                         (
  697.                                             wjnp1semjms[0] * ABDECoefficients.getdCoefDdalpha() +
  698.                                             wmjnp1semjms[0] * ABDECoefficients.getdCoefEdalpha()
  699.                                         );
  700.                             //Compute dS<sup>j</sup> / dβ
  701.                             sj[5][j] += gns.getGns(n, s) *
  702.                                         (
  703.                                             wjnp1semjms[0] * ABDECoefficients.getdCoefDdbeta() +
  704.                                             wmjnp1semjms[0] * ABDECoefficients.getdCoefEdbeta()
  705.                                         );
  706.                             //Compute dS<sup>j</sup> / dγ
  707.                             sj[6][j] += gns.getdGnsdgamma(n, s) * coef2;

  708.                             //Check if n is greater or equal to j and j is at most jMax-1
  709.                             if (n >= j && j < jMax) {
  710.                                 // compute the coefficient e<sup>-|j-s|</sup>*w<sub>j</sub><sup>n, s</sup> and its derivatives
  711.                                 final double[] wjnsemjms = wnsjEtomjmsCoefficient.computeWjnsEmjmsAndDeriv(j, s, n);

  712.                                 // compute the coefficient e<sup>-|j-s|</sup>*w<sub>-j</sub><sup>n, s</sup> and its derivatives
  713.                                 final double[] wmjnsemjms = wnsjEtomjmsCoefficient.computeWjnsEmjmsAndDeriv(-j, s, n);

  714.                                 //Compute C<sup>j</sup><sub>,λ</sub>
  715.                                 cjlambda[j] += gns.getGns(n, s) * (wjnsemjms[0] * ABDECoefficients.getCoefD() + wmjnsemjms[0] * ABDECoefficients.getCoefE());
  716.                                 //Compute S<sup>j</sup><sub>,λ</sub>
  717.                                 sjlambda[j] += gns.getGns(n, s) * (wjnsemjms[0] * ABDECoefficients.getCoefA() + wmjnsemjms[0] * ABDECoefficients.getCoefB());
  718.                             }
  719.                         }
  720.                     }
  721.                 }
  722.                 // Divide by j
  723.                 for (int i = 0; i <= 6; i++) {
  724.                     cj[i][j] /= j;
  725.                     sj[i][j] /= j;
  726.                 }
  727.             }
  728.             //The C⁰ coefficients are not computed here.
  729.             //They are evaluated at the final point.

  730.             //C⁰<sub>,λ</sub>
  731.             cjlambda[0] = k * cjlambda[1] / 2. + h * sjlambda[1] / 2.;
  732.         }

  733.         /** Get the Fourier coefficient C<sup>j</sup>.
  734.          * @param j j index
  735.          * @return C<sup>j</sup>
  736.          */
  737.         public double getCj(final int j) {
  738.             return cj[0][j];
  739.         }

  740.         /** Get the derivative dC<sup>j</sup>/da.
  741.          * @param j j index
  742.          * @return dC<sup>j</sup>/da
  743.          */
  744.         public double getdCjda(final int j) {
  745.             return cj[1][j];
  746.         }

  747.         /** Get the derivative dC<sup>j</sup>/dk.
  748.          * @param j j index
  749.          * @return dC<sup>j</sup>/dk
  750.          */
  751.         public double getdCjdk(final int j) {
  752.             return cj[2][j];
  753.         }

  754.         /** Get the derivative dC<sup>j</sup>/dh.
  755.          * @param j j index
  756.          * @return dC<sup>j</sup>/dh
  757.          */
  758.         public double getdCjdh(final int j) {
  759.             return cj[3][j];
  760.         }

  761.         /** Get the derivative dC<sup>j</sup>/dα.
  762.          * @param j j index
  763.          * @return dC<sup>j</sup>/dα
  764.          */
  765.         public double getdCjdalpha(final int j) {
  766.             return cj[4][j];
  767.         }

  768.         /** Get the derivative dC<sup>j</sup>/dβ.
  769.          * @param j j index
  770.          * @return dC<sup>j</sup>/dβ
  771.          */
  772.         public double getdCjdbeta(final int j) {
  773.             return cj[5][j];
  774.         }

  775.         /** Get the derivative dC<sup>j</sup>/dγ.
  776.          * @param j j index
  777.          * @return dC<sup>j</sup>/dγ
  778.          */
  779.         public double getdCjdgamma(final int j) {
  780.             return cj[6][j];
  781.         }

  782.         /** Get the Fourier coefficient S<sup>j</sup>.
  783.          * @param j j index
  784.          * @return S<sup>j</sup>
  785.          */
  786.         public double getSj(final int j) {
  787.             return sj[0][j];
  788.         }

  789.         /** Get the derivative dS<sup>j</sup>/da.
  790.          * @param j j index
  791.          * @return dS<sup>j</sup>/da
  792.          */
  793.         public double getdSjda(final int j) {
  794.             return sj[1][j];
  795.         }

  796.         /** Get the derivative dS<sup>j</sup>/dk.
  797.          * @param j j index
  798.          * @return dS<sup>j</sup>/dk
  799.          */
  800.         public double getdSjdk(final int j) {
  801.             return sj[2][j];
  802.         }

  803.         /** Get the derivative dS<sup>j</sup>/dh.
  804.          * @param j j index
  805.          * @return dS<sup>j</sup>/dh
  806.          */
  807.         public double getdSjdh(final int j) {
  808.             return sj[3][j];
  809.         }

  810.         /** Get the derivative dS<sup>j</sup>/dα.
  811.          * @param j j index
  812.          * @return dS<sup>j</sup>/dα
  813.          */
  814.         public double getdSjdalpha(final int j) {
  815.             return sj[4][j];
  816.         }

  817.         /** Get the derivative dS<sup>j</sup>/dβ.
  818.          * @param j j index
  819.          * @return dS<sup>j</sup>/dβ
  820.          */
  821.         public double getdSjdbeta(final int j) {
  822.             return sj[5][j];
  823.         }

  824.         /** Get the derivative dS<sup>j</sup>/dγ.
  825.          * @param j j index
  826.          * @return dS<sup>j</sup>/dγ
  827.          */
  828.         public double getdSjdgamma(final int j) {
  829.             return sj[6][j];
  830.         }

  831.         /** Get the coefficient C⁰<sub>,λ</sub>.
  832.          * @return C⁰<sub>,λ</sub>
  833.          */
  834.         public double getC0Lambda() {
  835.             return cjlambda[0];
  836.         }

  837.         /** Get the coefficient C<sup>j</sup><sub>,λ</sub>.
  838.          * @param j j index
  839.          * @return C<sup>j</sup><sub>,λ</sub>
  840.          */
  841.         public double getCjLambda(final int j) {
  842.             if (j < 1 || j >= jMax) {
  843.                 return 0.;
  844.             }
  845.             return cjlambda[j];
  846.         }

  847.         /** Get the coefficient S<sup>j</sup><sub>,λ</sub>.
  848.          * @param j j index
  849.          * @return S<sup>j</sup><sub>,λ</sub>
  850.          */
  851.         public double getSjLambda(final int j) {
  852.             if (j < 1 || j >= jMax) {
  853.                 return 0.;
  854.             }
  855.             return sjlambda[j];
  856.         }
  857.     }

  858.     /** 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.
  859.      *
  860.      * <p>
  861.      * Starting from Danielson 4.2-9,10,11 and taking into account that fact that: <br />
  862.      * c = e / (1 + (1 - e²)<sup>1/2</sup>) = e / (1 + B) = e * b <br/>
  863.      * the expression e<sup>-|j-s|</sup>*w<sub>j</sub><sup>n, s</sup>
  864.      * can be written as: <br/ >
  865.      * - for |s| > |j| <br />
  866.      * e<sup>-|j-s|</sup>*w<sub>j</sub><sup>n, s</sup> =
  867.      *          (((n + s)!(n - s)!)/((n + j)!(n - j)!)) *
  868.      *          (-b)<sup>|j-s|</sup> *
  869.      *          ((1 - c²)<sup>n-|s|</sup>/(1 + c²)<sup>n</sup>) *
  870.      *          P<sub>n-|s|</sub><sup>|j-s|, |j+s|</sup>(χ) <br />
  871.      * <br />
  872.      * - for |s| <= |j| <br />
  873.      * e<sup>-|j-s|</sup>*w<sub>j</sub><sup>n, s</sup> =
  874.      *          (-b)<sup>|j-s|</sup> *
  875.      *          ((1 - c²)<sup>n-|j|</sup>/(1 + c²)<sup>n</sup>) *
  876.      *          P<sub>n-|j|</sub><sup>|j-s|, |j+s|</sup>(χ)
  877.      * </p>
  878.      *
  879.      * @author Lucian Barbulescu
  880.      */
  881.     private class WnsjEtomjmsCoefficient {

  882.         /** The value c.
  883.          * <p>
  884.          *  c = e / (1 + (1 - e²)<sup>1/2</sup>) = e / (1 + B) = e * b <br/>
  885.          * </p>
  886.          *  */
  887.         private final double c;

  888.         /** db / dh. */
  889.         private final double dbdh;

  890.         /** db / dk. */
  891.         private final double dbdk;

  892.         /** dc / dh = e * db/dh + b * de/dh. */
  893.         private final double dcdh;

  894.         /** dc / dk = e * db/dk + b * de/dk. */
  895.         private final double dcdk;

  896.         /** The values (1 - c²)<sup>n</sup>. <br />
  897.          * The maximum possible value for the power is N + 1 */
  898.         private final double[] omc2tn;

  899.         /** The values (1 + c²)<sup>n</sup>. <br />
  900.          * The maximum possible value for the power is N + 1 */
  901.         private final double[] opc2tn;

  902.         /** The values b<sup>|j-s|</sup>. */
  903.         private final double[] btjms;

  904.         /**
  905.          * Standard constructor.
  906.          */
  907.         WnsjEtomjmsCoefficient() {
  908.             //initialise fields
  909.             c = ecc * b;
  910.             final double c2 = c * c;

  911.             //b² * χ
  912.             final double b2Chi = b * b * X;
  913.             //Compute derivatives of b
  914.             dbdh = h * b2Chi;
  915.             dbdk = k * b2Chi;

  916.             //Compute derivatives of c
  917.             if (ecc == 0.0) {
  918.                 // we are at a perfectly circular orbit singularity here
  919.                 // we arbitrarily consider the perigee is along the X axis,
  920.                 // i.e cos(ω + Ω) = h/ecc 1 and sin(ω + Ω) = k/ecc = 0
  921.                 dcdh = ecc * dbdh + b;
  922.                 dcdk = ecc * dbdk;
  923.             } else {
  924.                 dcdh = ecc * dbdh + b * h / ecc;
  925.                 dcdk = ecc * dbdk + b * k / ecc;
  926.             }

  927.             //Compute the powers (1 - c²)<sup>n</sup> and (1 + c²)<sup>n</sup>
  928.             omc2tn = new double[maxAR3Pow + maxFreqF + 2];
  929.             opc2tn = new double[maxAR3Pow + maxFreqF + 2];
  930.             final double omc2 = 1. - c2;
  931.             final double opc2 = 1. + c2;
  932.             omc2tn[0] = 1.;
  933.             opc2tn[0] = 1.;
  934.             for (int i = 1; i <= maxAR3Pow + maxFreqF + 1; i++) {
  935.                 omc2tn[i] = omc2tn[i - 1] * omc2;
  936.                 opc2tn[i] = opc2tn[i - 1] * opc2;
  937.             }

  938.             //Compute the powers of b
  939.             btjms = new double[maxAR3Pow + maxFreqF + 1];
  940.             btjms[0] = 1.;
  941.             for (int i = 1; i <= maxAR3Pow + maxFreqF; i++) {
  942.                 btjms[i] = btjms[i - 1] * b;
  943.             }
  944.         }

  945.         /** 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 />
  946.          *
  947.          * @param j j index
  948.          * @param s s index
  949.          * @param n n index
  950.          * @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
  951.          */
  952.         public double[] computeWjnsEmjmsAndDeriv(final int j, final int s, final int n) {
  953.             final double[] wjnsemjms = new double[] {0., 0., 0.};

  954.             // |j|
  955.             final int absJ = FastMath.abs(j);
  956.             // |s|
  957.             final int absS = FastMath.abs(s);
  958.             // |j - s|
  959.             final int absJmS = FastMath.abs(j - s);
  960.             // |j + s|
  961.             final int absJpS = FastMath.abs(j + s);

  962.             //The lower index of P. Also the power of (1 - c²)
  963.             final int l;
  964.             // the factorial ratio coefficient or 1. if |s| <= |j|
  965.             final double factCoef;
  966.             if (absS > absJ) {
  967.                 factCoef = (fact[n + s] / fact[n + j]) * (fact[n - s] / fact[n - j]);
  968.                 l = n - absS;
  969.             } else {
  970.                 factCoef = 1.;
  971.                 l = n - absJ;
  972.             }

  973.             // (-1)<sup>|j-s|</sup>
  974.             final double sign = absJmS % 2 != 0 ? -1. : 1.;
  975.             //(1 - c²)<sup>n-|s|</sup> / (1 + c²)<sup>n</sup>
  976.             final double coef1 = omc2tn[l] / opc2tn[n];
  977.             //-b<sup>|j-s|</sup>
  978.             final double coef2 = sign * btjms[absJmS];
  979.             // P<sub>l</sub><sup>|j-s|, |j+s|</sup>(χ)
  980.             final DerivativeStructure jac =
  981.                     JacobiPolynomials.getValue(l, absJmS, absJpS, factory.variable(0, X));

  982.             // the derivative of coef1 by c
  983.             final double dcoef1dc = -coef1 * 2. * c * (((double) n) / opc2tn[1] + ((double) l) / omc2tn[1]);
  984.             // the derivative of coef1 by h
  985.             final double dcoef1dh = dcoef1dc * dcdh;
  986.             // the derivative of coef1 by k
  987.             final double dcoef1dk = dcoef1dc * dcdk;

  988.             // the derivative of coef2 by b
  989.             final double dcoef2db = absJmS == 0 ? 0 : sign * (double) absJmS * btjms[absJmS - 1];
  990.             // the derivative of coef2 by h
  991.             final double dcoef2dh = dcoef2db * dbdh;
  992.             // the derivative of coef2 by k
  993.             final double dcoef2dk = dcoef2db * dbdk;

  994.             // the jacobi polynomial value
  995.             final double jacobi = jac.getValue();
  996.             // the derivative of the Jacobi polynomial by h
  997.             final double djacobidh = jac.getPartialDerivative(1) * hXXX;
  998.             // the derivative of the Jacobi polynomial by k
  999.             final double djacobidk = jac.getPartialDerivative(1) * kXXX;

  1000.             //group the above coefficients to limit the mathematical operations
  1001.             final double term1 = factCoef * coef1 * coef2;
  1002.             final double term2 = factCoef * coef1 * jacobi;
  1003.             final double term3 = factCoef * coef2 * jacobi;

  1004.             //compute e<sup>-|j-s|</sup>*w<sub>j</sub><sup>n, s</sup> and its derivatives by k and h
  1005.             wjnsemjms[0] = term1 * jacobi;
  1006.             wjnsemjms[1] = dcoef1dk * term3 + dcoef2dk * term2 + djacobidk * term1;
  1007.             wjnsemjms[2] = dcoef1dh * term3 + dcoef2dh * term2 + djacobidh * term1;

  1008.             return wjnsemjms;
  1009.         }
  1010.     }

  1011.     /** The G<sub>n,s</sub> coefficients and their derivatives.
  1012.      * <p>
  1013.      * See Danielson, 4.2-17
  1014.      *
  1015.      * @author Lucian Barbulescu
  1016.      */
  1017.     private class GnsCoefficients {

  1018.         /** Maximum value for n index. */
  1019.         private final int nMax;

  1020.         /** Maximum value for s index. */
  1021.         private final int sMax;

  1022.         /** The coefficients G<sub>n,s</sub>. */
  1023.         private final double gns[][];

  1024.         /** The derivatives of the coefficients G<sub>n,s</sub> by a. */
  1025.         private final double dgnsda[][];

  1026.         /** The derivatives of the coefficients G<sub>n,s</sub> by γ. */
  1027.         private final double dgnsdgamma[][];

  1028.         /** Standard constructor.
  1029.          *
  1030.          * @param nMax maximum value for n indes
  1031.          * @param sMax maximum value for s index
  1032.          */
  1033.         GnsCoefficients(final int nMax, final int sMax) {
  1034.             this.nMax = nMax;
  1035.             this.sMax = sMax;

  1036.             final int rows    = nMax + 1;
  1037.             final int columns = sMax + 1;
  1038.             this.gns          = new double[rows][columns];
  1039.             this.dgnsda       = new double[rows][columns];
  1040.             this.dgnsdgamma   = new double[rows][columns];

  1041.             // Generate the coefficients
  1042.             generateCoefficients();
  1043.         }
  1044.         /**
  1045.          * Compute the coefficient G<sub>n,s</sub> and its derivatives.
  1046.          * <p>
  1047.          * Only the derivatives by a and γ are computed as all others are 0
  1048.          * </p>
  1049.          */
  1050.         private void generateCoefficients() {
  1051.             for (int s = 0; s <= sMax; s++) {
  1052.                 // The n index is always at least the maximum between 2 and s
  1053.                 final int minN = FastMath.max(2, s);
  1054.                 for (int n = minN; n <= nMax; n++) {
  1055.                     // compute the coefficients only if (n - s) % 2 == 0
  1056.                     if ( (n - s) % 2 == 0 ) {
  1057.                         // Kronecker symbol (2 - delta(0,s))
  1058.                         final double delta0s = (s == 0) ? 1. : 2.;
  1059.                         final double vns   = Vns.get(new NSKey(n, s));
  1060.                         final double coef0 = delta0s * aoR3Pow[n] * vns * muoR3;
  1061.                         final double coef1 = coef0 * Qns[n][s];
  1062.                         // dQns/dGamma = Q(n, s + 1) from Equation 3.1-(8)
  1063.                         // for n = s, Q(n, n + 1) = 0. (Cefola & Broucke, 1975)
  1064.                         final double dqns = (n == s) ? 0. : Qns[n][s + 1];

  1065.                         //Compute the coefficient and its derivatives.
  1066.                         this.gns[n][s] = coef1;
  1067.                         this.dgnsda[n][s] = coef1 * n / a;
  1068.                         this.dgnsdgamma[n][s] = coef0 * dqns;
  1069.                     } else {
  1070.                         // the coefficient and its derivatives is 0
  1071.                         this.gns[n][s] = 0.;
  1072.                         this.dgnsda[n][s] = 0.;
  1073.                         this.dgnsdgamma[n][s] = 0.;
  1074.                     }
  1075.                 }
  1076.             }
  1077.         }

  1078.         /** Get the coefficient G<sub>n,s</sub>.
  1079.          *
  1080.          * @param n n index
  1081.          * @param s s index
  1082.          * @return the coefficient G<sub>n,s</sub>
  1083.          */
  1084.         public double getGns(final int n, final int s) {
  1085.             return this.gns[n][s];
  1086.         }

  1087.         /** Get the derivative dG<sub>n,s</sub> / da.
  1088.          *
  1089.          * @param n n index
  1090.          * @param s s index
  1091.          * @return the derivative dG<sub>n,s</sub> / da
  1092.          */
  1093.         public double getdGnsda(final int n, final int s) {
  1094.             return this.dgnsda[n][s];
  1095.         }

  1096.         /** Get the derivative dG<sub>n,s</sub> / dγ.
  1097.          *
  1098.          * @param n n index
  1099.          * @param s s index
  1100.          * @return the derivative dG<sub>n,s</sub> / dγ
  1101.          */
  1102.         public double getdGnsdgamma(final int n, final int s) {
  1103.             return this.dgnsdgamma[n][s];
  1104.         }
  1105.     }

  1106.     /** This class computes the terms containing the coefficients C<sub>j</sub> and S<sub>j</sub> of (α, β) or (k, h).
  1107.      *
  1108.      * <p>
  1109.      * The following terms and their derivatives by k, h, alpha and beta are considered: <br/ >
  1110.      * - 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 />
  1111.      * - C<sub>s</sub>(α, β) * S<sub>j+s</sub>(k, h) - S<sub>s</sub>(α, β) * C<sub>j+s</sub>(k, h) <br />
  1112.      * - 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 />
  1113.      * - C<sub>s</sub>(α, β) * C<sub>j+s</sub>(k, h) + S<sub>s</sub>(α, β) * S<sub>j+s</sub>(k, h) <br />
  1114.      * 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 />
  1115.      * See the CS Mathematical report $3.5.3.2 for more details
  1116.      * </p>
  1117.      * @author Lucian Barbulescu
  1118.      */
  1119.     private class CjSjAlphaBetaKH {

  1120.         /** The C<sub>j</sub>(k, h) and the S<sub>j</sub>(k, h) series. */
  1121.         private final CjSjCoefficient cjsjkh;

  1122.         /** The C<sub>j</sub>(α, β) and the S<sub>j</sub>(α, β) series. */
  1123.         private final CjSjCoefficient cjsjalbe;

  1124.         /** 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)
  1125.          * and its derivative by k, h, α and β. */
  1126.         private final double coefAandDeriv[];

  1127.         /** The coeficient C<sub>s</sub>(α, β) * S<sub>j+s</sub>(k, h) - S<sub>s</sub>(α, β) * C<sub>j+s</sub>(k, h)
  1128.          * and its derivative by k, h, α and β. */
  1129.         private final double coefBandDeriv[];

  1130.         /** 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)
  1131.          * and its derivative by k, h, α and β. */
  1132.         private final double coefDandDeriv[];

  1133.         /** The coeficient C<sub>s</sub>(α, β) * C<sub>j+s</sub>(k, h) + S<sub>s</sub>(α, β) * S<sub>j+s</sub>(k, h)
  1134.          * and its derivative by k, h, α and β. */
  1135.         private final double coefEandDeriv[];

  1136.         /**
  1137.          * Standard constructor.
  1138.          */
  1139.         CjSjAlphaBetaKH() {
  1140.             cjsjkh = new CjSjCoefficient(k, h);
  1141.             cjsjalbe = new CjSjCoefficient(alpha, beta);

  1142.             coefAandDeriv = new double[5];
  1143.             coefBandDeriv = new double[5];
  1144.             coefDandDeriv = new double[5];
  1145.             coefEandDeriv = new double[5];
  1146.         }

  1147.         /** Compute the coefficients and their derivatives for a given (j,s) pair.
  1148.          * @param j j index
  1149.          * @param s s index
  1150.          */
  1151.         public void computeCoefficients(final int j, final int s) {
  1152.             // sign of j-s
  1153.             final int sign = j < s ? -1 : 1;

  1154.             //|j-s|
  1155.             final int absJmS = FastMath.abs(j - s);

  1156.             //j+s
  1157.             final int jps = j + s;

  1158.             //Compute the coefficient A and its derivatives
  1159.             coefAandDeriv[0] = sign * cjsjalbe.getCj(s) * cjsjkh.getSj(absJmS) + cjsjalbe.getSj(s) * cjsjkh.getCj(absJmS);
  1160.             coefAandDeriv[1] = sign * cjsjalbe.getCj(s) * cjsjkh.getDsjDk(absJmS) + cjsjalbe.getSj(s) * cjsjkh.getDcjDk(absJmS);
  1161.             coefAandDeriv[2] = sign * cjsjalbe.getCj(s) * cjsjkh.getDsjDh(absJmS) + cjsjalbe.getSj(s) * cjsjkh.getDcjDh(absJmS);
  1162.             coefAandDeriv[3] = sign * cjsjalbe.getDcjDk(s) * cjsjkh.getSj(absJmS) + cjsjalbe.getDsjDk(s) * cjsjkh.getCj(absJmS);
  1163.             coefAandDeriv[4] = sign * cjsjalbe.getDcjDh(s) * cjsjkh.getSj(absJmS) + cjsjalbe.getDsjDh(s) * cjsjkh.getCj(absJmS);

  1164.             //Compute the coefficient B and its derivatives
  1165.             coefBandDeriv[0] = cjsjalbe.getCj(s) * cjsjkh.getSj(jps) - cjsjalbe.getSj(s) * cjsjkh.getCj(jps);
  1166.             coefBandDeriv[1] = cjsjalbe.getCj(s) * cjsjkh.getDsjDk(jps) - cjsjalbe.getSj(s) * cjsjkh.getDcjDk(jps);
  1167.             coefBandDeriv[2] = cjsjalbe.getCj(s) * cjsjkh.getDsjDh(jps) - cjsjalbe.getSj(s) * cjsjkh.getDcjDh(jps);
  1168.             coefBandDeriv[3] = cjsjalbe.getDcjDk(s) * cjsjkh.getSj(jps) - cjsjalbe.getDsjDk(s) * cjsjkh.getCj(jps);
  1169.             coefBandDeriv[4] = cjsjalbe.getDcjDh(s) * cjsjkh.getSj(jps) - cjsjalbe.getDsjDh(s) * cjsjkh.getCj(jps);

  1170.             //Compute the coefficient D and its derivatives
  1171.             coefDandDeriv[0] = cjsjalbe.getCj(s) * cjsjkh.getCj(absJmS) - sign * cjsjalbe.getSj(s) * cjsjkh.getSj(absJmS);
  1172.             coefDandDeriv[1] = cjsjalbe.getCj(s) * cjsjkh.getDcjDk(absJmS) - sign * cjsjalbe.getSj(s) * cjsjkh.getDsjDk(absJmS);
  1173.             coefDandDeriv[2] = cjsjalbe.getCj(s) * cjsjkh.getDcjDh(absJmS) - sign * cjsjalbe.getSj(s) * cjsjkh.getDsjDh(absJmS);
  1174.             coefDandDeriv[3] = cjsjalbe.getDcjDk(s) * cjsjkh.getCj(absJmS) - sign * cjsjalbe.getDsjDk(s) * cjsjkh.getSj(absJmS);
  1175.             coefDandDeriv[4] = cjsjalbe.getDcjDh(s) * cjsjkh.getCj(absJmS) - sign * cjsjalbe.getDsjDh(s) * cjsjkh.getSj(absJmS);

  1176.             //Compute the coefficient E and its derivatives
  1177.             coefEandDeriv[0] = cjsjalbe.getCj(s) * cjsjkh.getCj(jps) + cjsjalbe.getSj(s) * cjsjkh.getSj(jps);
  1178.             coefEandDeriv[1] = cjsjalbe.getCj(s) * cjsjkh.getDcjDk(jps) + cjsjalbe.getSj(s) * cjsjkh.getDsjDk(jps);
  1179.             coefEandDeriv[2] = cjsjalbe.getCj(s) * cjsjkh.getDcjDh(jps) + cjsjalbe.getSj(s) * cjsjkh.getDsjDh(jps);
  1180.             coefEandDeriv[3] = cjsjalbe.getDcjDk(s) * cjsjkh.getCj(jps) + cjsjalbe.getDsjDk(s) * cjsjkh.getSj(jps);
  1181.             coefEandDeriv[4] = cjsjalbe.getDcjDh(s) * cjsjkh.getCj(jps) + cjsjalbe.getDsjDh(s) * cjsjkh.getSj(jps);
  1182.         }

  1183.         /** Get the value of coefficient A<sub>j,s</sub>.
  1184.          *
  1185.          * @return the coefficient A<sub>j,s</sub>
  1186.          */
  1187.         public double getCoefA() {
  1188.             return coefAandDeriv[0];
  1189.         }

  1190.         /** Get the value of coefficient dA<sub>j,s</sub>/dk.
  1191.          *
  1192.          * @return the coefficient dA<sub>j,s</sub>/dk
  1193.          */
  1194.         public double getdCoefAdk() {
  1195.             return coefAandDeriv[1];
  1196.         }

  1197.         /** Get the value of coefficient dA<sub>j,s</sub>/dh.
  1198.          *
  1199.          * @return the coefficient dA<sub>j,s</sub>/dh
  1200.          */
  1201.         public double getdCoefAdh() {
  1202.             return coefAandDeriv[2];
  1203.         }

  1204.         /** Get the value of coefficient dA<sub>j,s</sub>/dα.
  1205.          *
  1206.          * @return the coefficient dA<sub>j,s</sub>/dα
  1207.          */
  1208.         public double getdCoefAdalpha() {
  1209.             return coefAandDeriv[3];
  1210.         }

  1211.         /** Get the value of coefficient dA<sub>j,s</sub>/dβ.
  1212.          *
  1213.          * @return the coefficient dA<sub>j,s</sub>/dβ
  1214.          */
  1215.         public double getdCoefAdbeta() {
  1216.             return coefAandDeriv[4];
  1217.         }

  1218.         /** Get the value of coefficient B<sub>j,s</sub>.
  1219.          *
  1220.          * @return the coefficient B<sub>j,s</sub>
  1221.          */
  1222.         public double getCoefB() {
  1223.             return coefBandDeriv[0];
  1224.         }

  1225.         /** Get the value of coefficient dB<sub>j,s</sub>/dk.
  1226.          *
  1227.          * @return the coefficient dB<sub>j,s</sub>/dk
  1228.          */
  1229.         public double getdCoefBdk() {
  1230.             return coefBandDeriv[1];
  1231.         }

  1232.         /** Get the value of coefficient dB<sub>j,s</sub>/dh.
  1233.          *
  1234.          * @return the coefficient dB<sub>j,s</sub>/dh
  1235.          */
  1236.         public double getdCoefBdh() {
  1237.             return coefBandDeriv[2];
  1238.         }

  1239.         /** Get the value of coefficient dB<sub>j,s</sub>/dα.
  1240.          *
  1241.          * @return the coefficient dB<sub>j,s</sub>/dα
  1242.          */
  1243.         public double getdCoefBdalpha() {
  1244.             return coefBandDeriv[3];
  1245.         }

  1246.         /** Get the value of coefficient dB<sub>j,s</sub>/dβ.
  1247.          *
  1248.          * @return the coefficient dB<sub>j,s</sub>/dβ
  1249.          */
  1250.         public double getdCoefBdbeta() {
  1251.             return coefBandDeriv[4];
  1252.         }

  1253.         /** Get the value of coefficient D<sub>j,s</sub>.
  1254.          *
  1255.          * @return the coefficient D<sub>j,s</sub>
  1256.          */
  1257.         public double getCoefD() {
  1258.             return coefDandDeriv[0];
  1259.         }

  1260.         /** Get the value of coefficient dD<sub>j,s</sub>/dk.
  1261.          *
  1262.          * @return the coefficient dD<sub>j,s</sub>/dk
  1263.          */
  1264.         public double getdCoefDdk() {
  1265.             return coefDandDeriv[1];
  1266.         }

  1267.         /** Get the value of coefficient dD<sub>j,s</sub>/dh.
  1268.          *
  1269.          * @return the coefficient dD<sub>j,s</sub>/dh
  1270.          */
  1271.         public double getdCoefDdh() {
  1272.             return coefDandDeriv[2];
  1273.         }

  1274.         /** Get the value of coefficient dD<sub>j,s</sub>/dα.
  1275.          *
  1276.          * @return the coefficient dD<sub>j,s</sub>/dα
  1277.          */
  1278.         public double getdCoefDdalpha() {
  1279.             return coefDandDeriv[3];
  1280.         }

  1281.         /** Get the value of coefficient dD<sub>j,s</sub>/dβ.
  1282.          *
  1283.          * @return the coefficient dD<sub>j,s</sub>/dβ
  1284.          */
  1285.         public double getdCoefDdbeta() {
  1286.             return coefDandDeriv[4];
  1287.         }

  1288.         /** Get the value of coefficient E<sub>j,s</sub>.
  1289.          *
  1290.          * @return the coefficient E<sub>j,s</sub>
  1291.          */
  1292.         public double getCoefE() {
  1293.             return coefEandDeriv[0];
  1294.         }

  1295.         /** Get the value of coefficient dE<sub>j,s</sub>/dk.
  1296.          *
  1297.          * @return the coefficient dE<sub>j,s</sub>/dk
  1298.          */
  1299.         public double getdCoefEdk() {
  1300.             return coefEandDeriv[1];
  1301.         }

  1302.         /** Get the value of coefficient dE<sub>j,s</sub>/dh.
  1303.          *
  1304.          * @return the coefficient dE<sub>j,s</sub>/dh
  1305.          */
  1306.         public double getdCoefEdh() {
  1307.             return coefEandDeriv[2];
  1308.         }

  1309.         /** Get the value of coefficient dE<sub>j,s</sub>/dα.
  1310.          *
  1311.          * @return the coefficient dE<sub>j,s</sub>/dα
  1312.          */
  1313.         public double getdCoefEdalpha() {
  1314.             return coefEandDeriv[3];
  1315.         }

  1316.         /** Get the value of coefficient dE<sub>j,s</sub>/dβ.
  1317.          *
  1318.          * @return the coefficient dE<sub>j,s</sub>/dβ
  1319.          */
  1320.         public double getdCoefEdbeta() {
  1321.             return coefEandDeriv[4];
  1322.         }
  1323.     }

  1324.     /** This class computes the coefficients for the generating function S and its derivatives.
  1325.      * <p>
  1326.      * The form of the generating functions is: <br>
  1327.      *  S = C⁰ + &Sigma;<sub>j=1</sub><sup>N+1</sup>(C<sup>j</sup> * cos(jF) + S<sup>j</sup> * sin(jF)) <br>
  1328.      *  The coefficients C⁰, C<sup>j</sup>, S<sup>j</sup> are the Fourrier coefficients
  1329.      *  presented in Danielson 4.2-14,15 except for the case j=1 where
  1330.      *  C¹ = C¹<sub>Fourier</sub> - hU and
  1331.      *  S¹ = S¹<sub>Fourier</sub> + kU <br>
  1332.      *  Also the coefficients of the derivatives of S by a, k, h, α, β, γ and λ
  1333.      *  are computed end expressed in a similar manner. The formulas used are 4.2-19, 20, 23, 24
  1334.      * </p>
  1335.      * @author Lucian Barbulescu
  1336.      */
  1337.     private class GeneratingFunctionCoefficients {

  1338.         /** The Fourier coefficients as presented in Danielson 4.2-14,15. */
  1339.         private final FourierCjSjCoefficients cjsjFourier;

  1340.         /** Maximum value of j index. */
  1341.         private final int jMax;

  1342.         /** The coefficients C<sup>j</sup> of the function S and its derivatives.
  1343.          * <p>
  1344.          * The index j belongs to the interval [0,jMax]. The coefficient C⁰ is the free coefficient.<br>
  1345.          * Each column of the matrix contains the coefficient corresponding to the following functions: <br/>
  1346.          * - S <br/>
  1347.          * - dS / da <br/>
  1348.          * - dS / dk <br/>
  1349.          * - dS / dh <br/>
  1350.          * - dS / dα <br/>
  1351.          * - dS / dβ <br/>
  1352.          * - dS / dγ <br/>
  1353.          * - dS / dλ
  1354.          * </p>
  1355.          */
  1356.         private final double[][] cjCoefs;

  1357.         /** The coefficients S<sup>j</sup> of the function S and its derivatives.
  1358.          * <p>
  1359.          * The index j belongs to the interval [0,jMax].<br>
  1360.          * Each column of the matrix contains the coefficient corresponding to the following functions: <br/>
  1361.          * - S <br/>
  1362.          * - dS / da <br/>
  1363.          * - dS / dk <br/>
  1364.          * - dS / dh <br/>
  1365.          * - dS / dα <br/>
  1366.          * - dS / dβ <br/>
  1367.          * - dS / dγ <br/>
  1368.          * - dS / dλ
  1369.          * </p>
  1370.          */
  1371.         private final double[][] sjCoefs;

  1372.         /**
  1373.          * Standard constructor.
  1374.          *
  1375.          * @param nMax maximum value of n index
  1376.          * @param sMax maximum value of s index
  1377.          * @param jMax maximum value of j index
  1378.          */
  1379.         GeneratingFunctionCoefficients(final int nMax, final int sMax, final int jMax) {
  1380.             this.jMax = jMax;
  1381.             this.cjsjFourier = new FourierCjSjCoefficients(nMax, sMax, jMax);
  1382.             this.cjCoefs = new double[8][jMax + 1];
  1383.             this.sjCoefs = new double[8][jMax + 1];

  1384.             computeGeneratingFunctionCoefficients();
  1385.         }

  1386.         /**
  1387.          * Compute the coefficients for the generating function S and its derivatives.
  1388.          */
  1389.         private void computeGeneratingFunctionCoefficients() {

  1390.             // Compute potential U and derivatives
  1391.             final double[] dU  = computeUDerivatives();

  1392.             //Compute the C<sup>j</sup> coefficients
  1393.             for (int j = 1; j <= jMax; j++) {
  1394.                 //Compute the C<sup>j</sup> coefficients
  1395.                 cjCoefs[0][j] = cjsjFourier.getCj(j);
  1396.                 cjCoefs[1][j] = cjsjFourier.getdCjda(j);
  1397.                 cjCoefs[2][j] = cjsjFourier.getdCjdk(j) - (cjsjFourier.getSjLambda(j - 1) - cjsjFourier.getSjLambda(j + 1)) / 2;
  1398.                 cjCoefs[3][j] = cjsjFourier.getdCjdh(j) - (cjsjFourier.getCjLambda(j - 1) + cjsjFourier.getCjLambda(j + 1)) / 2;
  1399.                 cjCoefs[4][j] = cjsjFourier.getdCjdalpha(j);
  1400.                 cjCoefs[5][j] = cjsjFourier.getdCjdbeta(j);
  1401.                 cjCoefs[6][j] = cjsjFourier.getdCjdgamma(j);
  1402.                 cjCoefs[7][j] = cjsjFourier.getCjLambda(j);

  1403.                 //Compute the S<sup>j</sup> coefficients
  1404.                 sjCoefs[0][j] = cjsjFourier.getSj(j);
  1405.                 sjCoefs[1][j] = cjsjFourier.getdSjda(j);
  1406.                 sjCoefs[2][j] = cjsjFourier.getdSjdk(j) + (cjsjFourier.getCjLambda(j - 1) - cjsjFourier.getCjLambda(j + 1)) / 2;
  1407.                 sjCoefs[3][j] = cjsjFourier.getdSjdh(j) - (cjsjFourier.getSjLambda(j - 1) + cjsjFourier.getSjLambda(j + 1)) / 2;
  1408.                 sjCoefs[4][j] = cjsjFourier.getdSjdalpha(j);
  1409.                 sjCoefs[5][j] = cjsjFourier.getdSjdbeta(j);
  1410.                 sjCoefs[6][j] = cjsjFourier.getdSjdgamma(j);
  1411.                 sjCoefs[7][j] = cjsjFourier.getSjLambda(j);

  1412.                 //In the special case j == 1 there are some additional terms to be added
  1413.                 if (j == 1) {
  1414.                     //Additional terms for C<sup>j</sup> coefficients
  1415.                     cjCoefs[0][j] += -h * U;
  1416.                     cjCoefs[1][j] += -h * dU[0];
  1417.                     cjCoefs[2][j] += -h * dU[1];
  1418.                     cjCoefs[3][j] += -(h * dU[2] + U + cjsjFourier.getC0Lambda());
  1419.                     cjCoefs[4][j] += -h * dU[3];
  1420.                     cjCoefs[5][j] += -h * dU[4];
  1421.                     cjCoefs[6][j] += -h * dU[5];

  1422.                     //Additional terms for S<sup>j</sup> coefficients
  1423.                     sjCoefs[0][j] += k * U;
  1424.                     sjCoefs[1][j] += k * dU[0];
  1425.                     sjCoefs[2][j] += k * dU[1] + U + cjsjFourier.getC0Lambda();
  1426.                     sjCoefs[3][j] += k * dU[2];
  1427.                     sjCoefs[4][j] += k * dU[3];
  1428.                     sjCoefs[5][j] += k * dU[4];
  1429.                     sjCoefs[6][j] += k * dU[5];
  1430.                 }
  1431.             }
  1432.         }

  1433.         /** Get the coefficient C<sup>j</sup> for the function S.
  1434.          * <br>
  1435.          * Possible values for j are within the interval [0,jMax].
  1436.          * The value 0 is used to obtain the free coefficient C⁰
  1437.          * @param j j index
  1438.          * @return C<sup>j</sup> for the function S
  1439.          */
  1440.         public double getSCj(final int j) {
  1441.             return cjCoefs[0][j];
  1442.         }

  1443.         /** Get the coefficient S<sup>j</sup> for the function S.
  1444.          * <br>
  1445.          * Possible values for j are within the interval [1,jMax].
  1446.          * @param j j index
  1447.          * @return S<sup>j</sup> for the function S
  1448.          */
  1449.         public double getSSj(final int j) {
  1450.             return sjCoefs[0][j];
  1451.         }

  1452.         /** Get the coefficient C<sup>j</sup> for the derivative dS/da.
  1453.          * <br>
  1454.          * Possible values for j are within the interval [0,jMax].
  1455.          * The value 0 is used to obtain the free coefficient C⁰
  1456.          * @param j j index
  1457.          * @return C<sup>j</sup> for the function dS/da
  1458.          */
  1459.         public double getdSdaCj(final int j) {
  1460.             return cjCoefs[1][j];
  1461.         }

  1462.         /** Get the coefficient S<sup>j</sup> for the derivative dS/da.
  1463.          * <br>
  1464.          * Possible values for j are within the interval [1,jMax].
  1465.          * @param j j index
  1466.          * @return S<sup>j</sup> for the derivative dS/da
  1467.          */
  1468.         public double getdSdaSj(final int j) {
  1469.             return sjCoefs[1][j];
  1470.         }

  1471.         /** Get the coefficient C<sup>j</sup> for the derivative dS/dk
  1472.          * <br>
  1473.          * Possible values for j are within the interval [0,jMax].
  1474.          * The value 0 is used to obtain the free coefficient C⁰
  1475.          * @param j j index
  1476.          * @return C<sup>j</sup> for the function dS/dk
  1477.          */
  1478.         public double getdSdkCj(final int j) {
  1479.             return cjCoefs[2][j];
  1480.         }

  1481.         /** Get the coefficient S<sup>j</sup> for the derivative dS/dk.
  1482.          * <br>
  1483.          * Possible values for j are within the interval [1,jMax].
  1484.          * @param j j index
  1485.          * @return S<sup>j</sup> for the derivative dS/dk
  1486.          */
  1487.         public double getdSdkSj(final int j) {
  1488.             return sjCoefs[2][j];
  1489.         }

  1490.         /** Get the coefficient C<sup>j</sup> for the derivative dS/dh
  1491.          * <br>
  1492.          * Possible values for j are within the interval [0,jMax].
  1493.          * The value 0 is used to obtain the free coefficient C⁰
  1494.          * @param j j index
  1495.          * @return C<sup>j</sup> for the function dS/dh
  1496.          */
  1497.         public double getdSdhCj(final int j) {
  1498.             return cjCoefs[3][j];
  1499.         }

  1500.         /** Get the coefficient S<sup>j</sup> for the derivative dS/dh.
  1501.          * <br>
  1502.          * Possible values for j are within the interval [1,jMax].
  1503.          * @param j j index
  1504.          * @return S<sup>j</sup> for the derivative dS/dh
  1505.          */
  1506.         public double getdSdhSj(final int j) {
  1507.             return sjCoefs[3][j];
  1508.         }

  1509.         /** Get the coefficient C<sup>j</sup> for the derivative dS/dα
  1510.          * <br>
  1511.          * Possible values for j are within the interval [0,jMax].
  1512.          * The value 0 is used to obtain the free coefficient C⁰
  1513.          * @param j j index
  1514.          * @return C<sup>j</sup> for the function dS/dα
  1515.          */
  1516.         public double getdSdalphaCj(final int j) {
  1517.             return cjCoefs[4][j];
  1518.         }

  1519.         /** Get the coefficient S<sup>j</sup> for the derivative dS/dα.
  1520.          * <br>
  1521.          * Possible values for j are within the interval [1,jMax].
  1522.          * @param j j index
  1523.          * @return S<sup>j</sup> for the derivative dS/dα
  1524.          */
  1525.         public double getdSdalphaSj(final int j) {
  1526.             return sjCoefs[4][j];
  1527.         }

  1528.         /** Get the coefficient C<sup>j</sup> for the derivative dS/dβ
  1529.          * <br>
  1530.          * Possible values for j are within the interval [0,jMax].
  1531.          * The value 0 is used to obtain the free coefficient C⁰
  1532.          * @param j j index
  1533.          * @return C<sup>j</sup> for the function dS/dβ
  1534.          */
  1535.         public double getdSdbetaCj(final int j) {
  1536.             return cjCoefs[5][j];
  1537.         }

  1538.         /** Get the coefficient S<sup>j</sup> for the derivative dS/dβ.
  1539.          * <br>
  1540.          * Possible values for j are within the interval [1,jMax].
  1541.          * @param j j index
  1542.          * @return S<sup>j</sup> for the derivative dS/dβ
  1543.          */
  1544.         public double getdSdbetaSj(final int j) {
  1545.             return sjCoefs[5][j];
  1546.         }

  1547.         /** Get the coefficient C<sup>j</sup> for the derivative dS/dγ
  1548.          * <br>
  1549.          * Possible values for j are within the interval [0,jMax].
  1550.          * The value 0 is used to obtain the free coefficient C⁰
  1551.          * @param j j index
  1552.          * @return C<sup>j</sup> for the function dS/dγ
  1553.          */
  1554.         public double getdSdgammaCj(final int j) {
  1555.             return cjCoefs[6][j];
  1556.         }

  1557.         /** Get the coefficient S<sup>j</sup> for the derivative dS/dγ.
  1558.          * <br>
  1559.          * Possible values for j are within the interval [1,jMax].
  1560.          * @param j j index
  1561.          * @return S<sup>j</sup> for the derivative dS/dγ
  1562.          */
  1563.         public double getdSdgammaSj(final int j) {
  1564.             return sjCoefs[6][j];
  1565.         }

  1566.         /** Get the coefficient C<sup>j</sup> for the derivative dS/dλ
  1567.          * <br>
  1568.          * Possible values for j are within the interval [0,jMax].
  1569.          * The value 0 is used to obtain the free coefficient C⁰
  1570.          * @param j j index
  1571.          * @return C<sup>j</sup> for the function dS/dλ
  1572.          */
  1573.         public double getdSdlambdaCj(final int j) {
  1574.             return cjCoefs[7][j];
  1575.         }

  1576.         /** Get the coefficient S<sup>j</sup> for the derivative dS/dλ.
  1577.          * <br>
  1578.          * Possible values for j are within the interval [1,jMax].
  1579.          * @param j j index
  1580.          * @return S<sup>j</sup> for the derivative dS/dλ
  1581.          */
  1582.         public double getdSdlambdaSj(final int j) {
  1583.             return sjCoefs[7][j];
  1584.         }
  1585.     }

  1586.     /**
  1587.      * The coefficients used to compute the short periodic contribution for the Third body perturbation.
  1588.      * <p>
  1589.      * The short periodic contribution for the Third Body is expressed in Danielson 4.2-25.<br>
  1590.      * The coefficients C<sub>i</sub>⁰, C<sub>i</sub><sup>j</sup>, S<sub>i</sub><sup>j</sup>
  1591.      * are computed by replacing the corresponding values in formula 2.5.5-10.
  1592.      * </p>
  1593.      * @author Lucian Barbulescu
  1594.      */
  1595.     private static class ThirdBodyShortPeriodicCoefficients implements ShortPeriodTerms {

  1596.         /** Serializable UID. */
  1597.         private static final long serialVersionUID = 20151119L;

  1598.         /** Maximal value for j. */
  1599.         private final int jMax;

  1600.         /** Number of points used in the interpolation process. */
  1601.         private final int interpolationPoints;

  1602.         /** Max frequency of F. */
  1603.         private final int    maxFreqF;

  1604.         /** Coefficients prefix. */
  1605.         private final String prefix;

  1606.         /** All coefficients slots. */
  1607.         private final transient TimeSpanMap<Slot> slots;

  1608.         /**
  1609.          * Standard constructor.
  1610.          *  @param interpolationPoints number of points used in the interpolation process
  1611.          * @param jMax maximal value for j
  1612.          * @param maxFreqF Max frequency of F
  1613.          * @param bodyName third body name
  1614.          * @param slots all coefficients slots
  1615.          */
  1616.         ThirdBodyShortPeriodicCoefficients(final int jMax, final int interpolationPoints,
  1617.                                            final int maxFreqF, final String bodyName,
  1618.                                            final TimeSpanMap<Slot> slots) {
  1619.             this.jMax                = jMax;
  1620.             this.interpolationPoints = interpolationPoints;
  1621.             this.maxFreqF            = maxFreqF;
  1622.             this.prefix              = "DSST-3rd-body-" + bodyName + "-";
  1623.             this.slots               = slots;
  1624.         }

  1625.         /** Get the slot valid for some date.
  1626.          * @param meanStates mean states defining the slot
  1627.          * @return slot valid at the specified date
  1628.          */
  1629.         public Slot createSlot(final SpacecraftState... meanStates) {
  1630.             final Slot         slot  = new Slot(jMax, interpolationPoints);
  1631.             final AbsoluteDate first = meanStates[0].getDate();
  1632.             final AbsoluteDate last  = meanStates[meanStates.length - 1].getDate();
  1633.             if (first.compareTo(last) <= 0) {
  1634.                 slots.addValidAfter(slot, first);
  1635.             } else {
  1636.                 slots.addValidBefore(slot, first);
  1637.             }
  1638.             return slot;
  1639.         }

  1640.         /** {@inheritDoc} */
  1641.         @Override
  1642.         public double[] value(final Orbit meanOrbit) {

  1643.             // select the coefficients slot
  1644.             final Slot slot = slots.get(meanOrbit.getDate());

  1645.             // the current eccentric longitude
  1646.             final double F = meanOrbit.getLE();

  1647.             //initialize the short periodic contribution with the corresponding C⁰ coeficient
  1648.             final double[] shortPeriodic = slot.cij[0].value(meanOrbit.getDate());

  1649.             // Add the cos and sin dependent terms
  1650.             for (int j = 1; j <= maxFreqF; j++) {
  1651.                 //compute cos and sin
  1652.                 final double cosjF = FastMath.cos(j * F);
  1653.                 final double sinjF = FastMath.sin(j * F);

  1654.                 final double[] c = slot.cij[j].value(meanOrbit.getDate());
  1655.                 final double[] s = slot.sij[j].value(meanOrbit.getDate());
  1656.                 for (int i = 0; i < 6; i++) {
  1657.                     shortPeriodic[i] += c[i] * cosjF + s[i] * sinjF;
  1658.                 }
  1659.             }

  1660.             return shortPeriodic;

  1661.         }

  1662.         /** {@inheritDoc} */
  1663.         @Override
  1664.         public String getCoefficientsKeyPrefix() {
  1665.             return prefix;
  1666.         }

  1667.         /** {@inheritDoc}
  1668.          * <p>
  1669.          * For third body attraction forces,there are maxFreqF + 1 cj coefficients,
  1670.          * maxFreqF sj coefficients where maxFreqF depends on the orbit.
  1671.          * The j index is the integer multiplier for the eccentric longitude argument
  1672.          * in the cj and sj coefficients.
  1673.          * </p>
  1674.          */
  1675.         @Override
  1676.         public Map<String, double[]> getCoefficients(final AbsoluteDate date, final Set<String> selected)
  1677.             throws OrekitException {

  1678.             // select the coefficients slot
  1679.             final Slot slot = slots.get(date);

  1680.             final Map<String, double[]> coefficients = new HashMap<String, double[]>(2 * maxFreqF + 1);
  1681.             storeIfSelected(coefficients, selected, slot.cij[0].value(date), "c", 0);
  1682.             for (int j = 1; j <= maxFreqF; j++) {
  1683.                 storeIfSelected(coefficients, selected, slot.cij[j].value(date), "c", j);
  1684.                 storeIfSelected(coefficients, selected, slot.sij[j].value(date), "s", j);
  1685.             }
  1686.             return coefficients;

  1687.         }

  1688.         /** Put a coefficient in a map if selected.
  1689.          * @param map map to populate
  1690.          * @param selected set of coefficients that should be put in the map
  1691.          * (empty set means all coefficients are selected)
  1692.          * @param value coefficient value
  1693.          * @param id coefficient identifier
  1694.          * @param indices list of coefficient indices
  1695.          */
  1696.         private void storeIfSelected(final Map<String, double[]> map, final Set<String> selected,
  1697.                                      final double[] value, final String id, final int... indices) {
  1698.             final StringBuilder keyBuilder = new StringBuilder(getCoefficientsKeyPrefix());
  1699.             keyBuilder.append(id);
  1700.             for (int index : indices) {
  1701.                 keyBuilder.append('[').append(index).append(']');
  1702.             }
  1703.             final String key = keyBuilder.toString();
  1704.             if (selected.isEmpty() || selected.contains(key)) {
  1705.                 map.put(key, value);
  1706.             }
  1707.         }

  1708.         /** Replace the instance with a data transfer object for serialization.
  1709.          * @return data transfer object that will be serialized
  1710.          * @exception NotSerializableException if an additional state provider is not serializable
  1711.          */
  1712.         private Object writeReplace() throws NotSerializableException {

  1713.             // slots transitions
  1714.             final SortedSet<TimeSpanMap.Transition<Slot>> transitions     = slots.getTransitions();
  1715.             final AbsoluteDate[]                          transitionDates = new AbsoluteDate[transitions.size()];
  1716.             final Slot[]                                  allSlots        = new Slot[transitions.size() + 1];
  1717.             int i = 0;
  1718.             for (final TimeSpanMap.Transition<Slot> transition : transitions) {
  1719.                 if (i == 0) {
  1720.                     // slot before the first transition
  1721.                     allSlots[i] = transition.getBefore();
  1722.                 }
  1723.                 if (i < transitionDates.length) {
  1724.                     transitionDates[i] = transition.getDate();
  1725.                     allSlots[++i]      = transition.getAfter();
  1726.                 }
  1727.             }

  1728.             return new DataTransferObject(jMax, interpolationPoints, maxFreqF, prefix,
  1729.                                           transitionDates, allSlots);

  1730.         }


  1731.         /** Internal class used only for serialization. */
  1732.         private static class DataTransferObject implements Serializable {

  1733.             /** Serializable UID. */
  1734.             private static final long serialVersionUID = 20160319L;

  1735.             /** Maximum value for j index. */
  1736.             private final int jMax;

  1737.             /** Number of points used in the interpolation process. */
  1738.             private final int interpolationPoints;

  1739.             /** Max frequency of F. */
  1740.             private final int    maxFreqF;

  1741.             /** Coefficients prefix. */
  1742.             private final String prefix;

  1743.             /** Transitions dates. */
  1744.             private final AbsoluteDate[] transitionDates;

  1745.             /** All slots. */
  1746.             private final Slot[] allSlots;

  1747.             /** Simple constructor.
  1748.              * @param jMax maximum value for j index
  1749.              * @param interpolationPoints number of points used in the interpolation process
  1750.              * @param maxFreqF max frequency of F
  1751.              * @param prefix prefix for coefficients keys
  1752.              * @param transitionDates transitions dates
  1753.              * @param allSlots all slots
  1754.              */
  1755.             DataTransferObject(final int jMax, final int interpolationPoints,
  1756.                                final int maxFreqF, final String prefix,
  1757.                                final AbsoluteDate[] transitionDates, final Slot[] allSlots) {
  1758.                 this.jMax                  = jMax;
  1759.                 this.interpolationPoints   = interpolationPoints;
  1760.                 this.maxFreqF              = maxFreqF;
  1761.                 this.prefix                = prefix;
  1762.                 this.transitionDates       = transitionDates;
  1763.                 this.allSlots              = allSlots;
  1764.             }

  1765.             /** Replace the deserialized data transfer object with a {@link ThirdBodyShortPeriodicCoefficients}.
  1766.              * @return replacement {@link ThirdBodyShortPeriodicCoefficients}
  1767.              */
  1768.             private Object readResolve() {

  1769.                 final TimeSpanMap<Slot> slots = new TimeSpanMap<Slot>(allSlots[0]);
  1770.                 for (int i = 0; i < transitionDates.length; ++i) {
  1771.                     slots.addValidAfter(allSlots[i + 1], transitionDates[i]);
  1772.                 }

  1773.                 return new ThirdBodyShortPeriodicCoefficients(jMax, interpolationPoints, maxFreqF, prefix, slots);

  1774.             }

  1775.         }

  1776.     }

  1777.     /** Coefficients valid for one time slot. */
  1778.     private static class Slot implements Serializable {

  1779.         /** Serializable UID. */
  1780.         private static final long serialVersionUID = 20160319L;

  1781.         /** The coefficients C<sub>i</sub><sup>j</sup>.
  1782.          * <p>
  1783.          * The index order is cij[j][i] <br/>
  1784.          * i corresponds to the equinoctial element, as follows: <br/>
  1785.          * - i=0 for a <br/>
  1786.          * - i=1 for k <br/>
  1787.          * - i=2 for h <br/>
  1788.          * - i=3 for q <br/>
  1789.          * - i=4 for p <br/>
  1790.          * - i=5 for λ <br/>
  1791.          * </p>
  1792.          */
  1793.         private final ShortPeriodicsInterpolatedCoefficient[] cij;

  1794.         /** The coefficients S<sub>i</sub><sup>j</sup>.
  1795.          * <p>
  1796.          * The index order is sij[j][i] <br/>
  1797.          * i corresponds to the equinoctial element, as follows: <br/>
  1798.          * - i=0 for a <br/>
  1799.          * - i=1 for k <br/>
  1800.          * - i=2 for h <br/>
  1801.          * - i=3 for q <br/>
  1802.          * - i=4 for p <br/>
  1803.          * - i=5 for λ <br/>
  1804.          * </p>
  1805.          */
  1806.         private final ShortPeriodicsInterpolatedCoefficient[] sij;

  1807.         /** Simple constructor.
  1808.          *  @param jMax maximum value for j index
  1809.          *  @param interpolationPoints number of points used in the interpolation process
  1810.          */
  1811.         Slot(final int jMax, final int interpolationPoints) {
  1812.             // allocate the coefficients arrays
  1813.             cij = new ShortPeriodicsInterpolatedCoefficient[jMax + 1];
  1814.             sij = new ShortPeriodicsInterpolatedCoefficient[jMax + 1];
  1815.             for (int j = 0; j <= jMax; j++) {
  1816.                 cij[j] = new ShortPeriodicsInterpolatedCoefficient(interpolationPoints);
  1817.                 sij[j] = new ShortPeriodicsInterpolatedCoefficient(interpolationPoints);
  1818.             }


  1819.         }
  1820.     }

  1821. }