DSSTThirdBody.java

  1. /* Copyright 2002-2020 CS GROUP
  2.  * Licensed to CS GROUP (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.lang.reflect.Array;
  19. import java.util.ArrayList;
  20. import java.util.Arrays;
  21. import java.util.Collections;
  22. import java.util.HashMap;
  23. import java.util.List;
  24. import java.util.Map;
  25. import java.util.Set;
  26. import java.util.TreeMap;

  27. import org.hipparchus.Field;
  28. import org.hipparchus.RealFieldElement;
  29. import org.hipparchus.analysis.differentiation.FieldGradient;
  30. import org.hipparchus.analysis.differentiation.Gradient;
  31. import org.hipparchus.util.CombinatoricsUtils;
  32. import org.hipparchus.util.FastMath;
  33. import org.hipparchus.util.FieldSinCos;
  34. import org.hipparchus.util.MathArrays;
  35. import org.hipparchus.util.SinCos;
  36. import org.orekit.attitudes.AttitudeProvider;
  37. import org.orekit.bodies.CelestialBodies;
  38. import org.orekit.bodies.CelestialBody;
  39. import org.orekit.orbits.FieldOrbit;
  40. import org.orekit.orbits.Orbit;
  41. import org.orekit.propagation.FieldSpacecraftState;
  42. import org.orekit.propagation.PropagationType;
  43. import org.orekit.propagation.SpacecraftState;
  44. import org.orekit.propagation.events.EventDetector;
  45. import org.orekit.propagation.events.FieldEventDetector;
  46. import org.orekit.propagation.semianalytical.dsst.utilities.AuxiliaryElements;
  47. import org.orekit.propagation.semianalytical.dsst.utilities.CjSjCoefficient;
  48. import org.orekit.propagation.semianalytical.dsst.utilities.CoefficientsFactory;
  49. import org.orekit.propagation.semianalytical.dsst.utilities.CoefficientsFactory.NSKey;
  50. import org.orekit.propagation.semianalytical.dsst.utilities.FieldAuxiliaryElements;
  51. import org.orekit.propagation.semianalytical.dsst.utilities.FieldCjSjCoefficient;
  52. import org.orekit.propagation.semianalytical.dsst.utilities.FieldShortPeriodicsInterpolatedCoefficient;
  53. import org.orekit.propagation.semianalytical.dsst.utilities.JacobiPolynomials;
  54. import org.orekit.propagation.semianalytical.dsst.utilities.ShortPeriodicsInterpolatedCoefficient;
  55. import org.orekit.propagation.semianalytical.dsst.utilities.hansen.FieldHansenThirdBodyLinear;
  56. import org.orekit.propagation.semianalytical.dsst.utilities.hansen.HansenThirdBodyLinear;
  57. import org.orekit.time.AbsoluteDate;
  58. import org.orekit.time.FieldAbsoluteDate;
  59. import org.orekit.utils.FieldTimeSpanMap;
  60. import org.orekit.utils.ParameterDriver;
  61. import org.orekit.utils.TimeSpanMap;

  62. /** Third body attraction perturbation to the
  63.  *  {@link org.orekit.propagation.semianalytical.dsst.DSSTPropagator DSSTPropagator}.
  64.  *
  65.  *  @author Romain Di Costanzo
  66.  *  @author Pascal Parraud
  67.  *  @author Bryan Cazabonne (field translation)
  68.  */
  69. public class DSSTThirdBody implements DSSTForceModel {

  70.     /**  Name of the prefix for short period coefficients keys. */
  71.     public static final String SHORT_PERIOD_PREFIX = "DSST-3rd-body-";

  72.     /** Name of the single parameter of this model: the attraction coefficient. */
  73.     public static final String ATTRACTION_COEFFICIENT = " attraction coefficient";

  74.     /** Central attraction scaling factor.
  75.      * <p>
  76.      * We use a power of 2 to avoid numeric noise introduction
  77.      * in the multiplications/divisions sequences.
  78.      * </p>
  79.      */
  80.     private static final double MU_SCALE = FastMath.scalb(1.0, 32);

  81.     /** Retrograde factor I.
  82.      *  <p>
  83.      *  DSST model needs equinoctial orbit as internal representation.
  84.      *  Classical equinoctial elements have discontinuities when inclination
  85.      *  is close to zero. In this representation, I = +1. <br>
  86.      *  To avoid this discontinuity, another representation exists and equinoctial
  87.      *  elements can be expressed in a different way, called "retrograde" orbit.
  88.      *  This implies I = -1. <br>
  89.      *  As Orekit doesn't implement the retrograde orbit, I is always set to +1.
  90.      *  But for the sake of consistency with the theory, the retrograde factor
  91.      *  has been kept in the formulas.
  92.      *  </p>
  93.      */
  94.     private static final int    I = 1;

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

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

  99.     /** Max power for summation. */
  100.     private static final int    MAX_POWER = 22;

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

  103.     /** Max frequency of F. */
  104.     private int    maxFreqF;

  105.     /** Max frequency of F for field initialization. */
  106.     private int    maxFieldFreqF;

  107.     /** The 3rd body to consider. */
  108.     private final CelestialBody    body;

  109.     /** Short period terms. */
  110.     private ThirdBodyShortPeriodicCoefficients shortPeriods;

  111.     /** Short period terms. */
  112.     private Map<Field<?>, FieldThirdBodyShortPeriodicCoefficients<?>> fieldShortPeriods;

  113.     /** Drivers for third body attraction coefficient. */
  114.     private final ParameterDriver bodyParameterDriver;

  115.     /** Driver for gravitational parameter. */
  116.     private final ParameterDriver gmParameterDriver;

  117.     /** Hansen objects. */
  118.     private HansenObjects hansen;

  119.     /** Hansen objects for field elements. */
  120.     private Map<Field<?>, FieldHansenObjects<?>> fieldHansen;

  121.     /** Flag for force model initialization with field elements. */
  122.     private boolean pendingInitialization;

  123.     /** Complete constructor.
  124.      *  @param body the 3rd body to consider
  125.      *  @param mu central attraction coefficient
  126.      *  @see CelestialBodies
  127.      */
  128.     public DSSTThirdBody(final CelestialBody body, final double mu) {
  129.         bodyParameterDriver = new ParameterDriver(body.getName() + DSSTThirdBody.ATTRACTION_COEFFICIENT,
  130.                                                   body.getGM(), MU_SCALE,
  131.                                                   0.0, Double.POSITIVE_INFINITY);
  132.         gmParameterDriver = new ParameterDriver(DSSTNewtonianAttraction.CENTRAL_ATTRACTION_COEFFICIENT,
  133.                                                 mu, MU_SCALE,
  134.                                                 0.0, Double.POSITIVE_INFINITY);

  135.         this.body = body;
  136.         this.Vns  = CoefficientsFactory.computeVns(MAX_POWER);

  137.         pendingInitialization = true;

  138.         fieldShortPeriods = new HashMap<>();
  139.         fieldHansen       = new HashMap<>();
  140.     }

  141.     /** Get third body.
  142.      *  @return third body
  143.      */
  144.     public CelestialBody getBody() {
  145.         return body;
  146.     }

  147.     /** Computes the highest power of the eccentricity and the highest power
  148.      *  of a/R3 to appear in the truncated analytical power series expansion.
  149.      *  <p>
  150.      *  This method computes the upper value for the 3rd body potential and
  151.      *  determines the maximal powers for the eccentricity and a/R3 producing
  152.      *  potential terms bigger than a defined tolerance.
  153.      *  </p>
  154.      *  @param auxiliaryElements auxiliary elements related to the current orbit
  155.      *  @param type type of the elements used during the propagation
  156.      *  @param parameters values of the force model parameters
  157.      */
  158.     @Override
  159.     public List<ShortPeriodTerms> initialize(final AuxiliaryElements auxiliaryElements,
  160.                                              final PropagationType type,
  161.                                              final double[] parameters) {

  162.         // Initializes specific parameters.
  163.         final DSSTThirdBodyContext context = initializeStep(auxiliaryElements, parameters);

  164.         maxFreqF = context.getMaxFreqF();

  165.         hansen = new HansenObjects();

  166.         final int jMax = maxFreqF;
  167.         shortPeriods = new ThirdBodyShortPeriodicCoefficients(jMax, INTERPOLATION_POINTS,
  168.                                                               maxFreqF, body.getName(),
  169.                                                               new TimeSpanMap<Slot>(new Slot(jMax, INTERPOLATION_POINTS)));

  170.         final List<ShortPeriodTerms> list = new ArrayList<ShortPeriodTerms>();
  171.         list.add(shortPeriods);
  172.         return list;

  173.     }

  174.     /** {@inheritDoc} */
  175.     @Override
  176.     public <T extends RealFieldElement<T>> List<FieldShortPeriodTerms<T>> initialize(final FieldAuxiliaryElements<T> auxiliaryElements,
  177.                                                                                      final PropagationType type,
  178.                                                                                      final T[] parameters) {

  179.         // Field used by default
  180.         final Field<T> field = auxiliaryElements.getDate().getField();

  181.         if (pendingInitialization == true) {
  182.             // Initializes specific parameters.
  183.             final FieldDSSTThirdBodyContext<T> context = initializeStep(auxiliaryElements, parameters);

  184.             maxFieldFreqF = context.getMaxFreqF();

  185.             fieldHansen.put(field, new FieldHansenObjects<>(field));

  186.             pendingInitialization = false;
  187.         }

  188.         final int jMax = maxFieldFreqF;
  189.         final FieldThirdBodyShortPeriodicCoefficients<T> ftbspc =
  190.                         new FieldThirdBodyShortPeriodicCoefficients<>(jMax, INTERPOLATION_POINTS,
  191.                                                                       maxFieldFreqF, body.getName(),
  192.                                                                       new FieldTimeSpanMap<>(new FieldSlot<>(jMax,
  193.                                                                                                              INTERPOLATION_POINTS),
  194.                                                                                              field));
  195.         fieldShortPeriods.put(field, ftbspc);
  196.         return Collections.singletonList(ftbspc);
  197.     }

  198.     /** Performs initialization at each integration step for the current force model.
  199.      *  <p>
  200.      *  This method aims at being called before mean elements rates computation.
  201.      *  </p>
  202.      *  @param auxiliaryElements auxiliary elements related to the current orbit
  203.      *  @param parameters values of the force model parameters
  204.      *  @return new force model context
  205.      */
  206.     private DSSTThirdBodyContext initializeStep(final AuxiliaryElements auxiliaryElements, final double[] parameters) {
  207.         return new DSSTThirdBodyContext(auxiliaryElements, body, parameters);
  208.     }

  209.     /** Performs initialization at each integration step for the current force model.
  210.      *  <p>
  211.      *  This method aims at being called before mean elements rates computation.
  212.      *  </p>
  213.      *  @param <T> type of the elements
  214.      *  @param auxiliaryElements auxiliary elements related to the current orbit
  215.      *  @param parameters values of the force model parameters
  216.      *  @return new force model context
  217.      */
  218.     private <T extends RealFieldElement<T>> FieldDSSTThirdBodyContext<T> initializeStep(final FieldAuxiliaryElements<T> auxiliaryElements,
  219.                                                                                         final T[] parameters) {
  220.         return new FieldDSSTThirdBodyContext<>(auxiliaryElements, body, parameters);
  221.     }

  222.     /** {@inheritDoc} */
  223.     @Override
  224.     public double[] getMeanElementRate(final SpacecraftState currentState,
  225.                                        final AuxiliaryElements auxiliaryElements, final double[] parameters) {

  226.         // Container for attributes
  227.         final DSSTThirdBodyContext context = initializeStep(auxiliaryElements, parameters);
  228.         // Access to potential U derivatives
  229.         final UAnddU udu = new UAnddU(context, hansen);

  230.         // Compute cross derivatives [Eq. 2.2-(8)]
  231.         // U(alpha,gamma) = alpha * dU/dgamma - gamma * dU/dalpha
  232.         final double UAlphaGamma   = context.getAlpha() * udu.getdUdGa() - context.getGamma() * udu.getdUdAl();
  233.         // U(beta,gamma) = beta * dU/dgamma - gamma * dU/dbeta
  234.         final double UBetaGamma    =  context.getBeta() * udu.getdUdGa() - context.getGamma() * udu.getdUdBe();
  235.         // Common factor
  236.         final double pUAGmIqUBGoAB = (auxiliaryElements.getP() * UAlphaGamma - I * auxiliaryElements.getQ() * UBetaGamma) * context.getOoAB();

  237.         // Compute mean elements rates [Eq. 3.1-(1)]
  238.         final double da =  0.;
  239.         final double dh =  context.getBoA() * udu.getdUdk() + auxiliaryElements.getK() * pUAGmIqUBGoAB;
  240.         final double dk = -context.getBoA() * udu.getdUdh() - auxiliaryElements.getH() * pUAGmIqUBGoAB;
  241.         final double dp =  context.getMCo2AB() * UBetaGamma;
  242.         final double dq =  context.getMCo2AB() * UAlphaGamma * I;
  243.         final double dM =  context.getM2aoA() * udu.getdUda() + context.getBoABpo() * (auxiliaryElements.getH() * udu.getdUdh() + auxiliaryElements.getK() * udu.getdUdk()) + pUAGmIqUBGoAB;

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

  245.     }

  246.     /** {@inheritDoc} */
  247.     @Override
  248.     public <T extends RealFieldElement<T>> T[] getMeanElementRate(final FieldSpacecraftState<T> currentState,
  249.                                                                   final FieldAuxiliaryElements<T> auxiliaryElements,
  250.                                                                   final T[] parameters) {

  251.         // Parameters for array building
  252.         final Field<T> field = currentState.getDate().getField();
  253.         final T        zero  = field.getZero();

  254.         // Container for attributes
  255.         final FieldDSSTThirdBodyContext<T> context = initializeStep(auxiliaryElements, parameters);

  256.         @SuppressWarnings("unchecked")
  257.         final FieldHansenObjects<T> fho = (FieldHansenObjects<T>) fieldHansen.get(field);

  258.         // Access to potential U derivatives
  259.         final FieldUAnddU<T> udu = new FieldUAnddU<>(context, fho);

  260.         // Compute cross derivatives [Eq. 2.2-(8)]
  261.         // U(alpha,gamma) = alpha * dU/dgamma - gamma * dU/dalpha
  262.         final T UAlphaGamma   = udu.getdUdGa().multiply(context.getAlpha()).subtract(udu.getdUdAl().multiply(context.getGamma()));
  263.         // U(beta,gamma) = beta * dU/dgamma - gamma * dU/dbeta
  264.         final T UBetaGamma    = udu.getdUdGa().multiply(context.getBeta()).subtract(udu.getdUdBe().multiply(context.getGamma()));
  265.         // Common factor
  266.         final T pUAGmIqUBGoAB = (UAlphaGamma.multiply(auxiliaryElements.getP()).subtract(UBetaGamma.multiply(auxiliaryElements.getQ()).multiply(I))).multiply(context.getOoAB());

  267.         // Compute mean elements rates [Eq. 3.1-(1)]
  268.         final T da =  zero;
  269.         final T dh =  udu.getdUdk().multiply(context.getBoA()).add(pUAGmIqUBGoAB.multiply(auxiliaryElements.getK()));
  270.         final T dk =  ((udu.getdUdh().multiply(context.getBoA())).negate()).subtract(pUAGmIqUBGoAB.multiply(auxiliaryElements.getH()));
  271.         final T dp =  UBetaGamma.multiply(context.getMCo2AB());
  272.         final T dq =  UAlphaGamma.multiply(I).multiply(context.getMCo2AB());
  273.         final T dM =  pUAGmIqUBGoAB.add(udu.getdUda().multiply(context.getM2aoA())).add((udu.getdUdh().multiply(auxiliaryElements.getH()).add(udu.getdUdk().multiply(auxiliaryElements.getK()))).multiply(context.getBoABpo()));

  274.         final T[] elements = MathArrays.buildArray(field, 6);
  275.         elements[0] = da;
  276.         elements[1] = dk;
  277.         elements[2] = dh;
  278.         elements[3] = dq;
  279.         elements[4] = dp;
  280.         elements[5] = dM;

  281.         return elements;

  282.     }

  283.     /** {@inheritDoc} */
  284.     @Override
  285.     public void updateShortPeriodTerms(final double[] parameters, final SpacecraftState... meanStates) {

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

  287.         for (final SpacecraftState meanState : meanStates) {

  288.             // Auxiliary elements related to the current orbit
  289.             final AuxiliaryElements auxiliaryElements = new AuxiliaryElements(meanState.getOrbit(), I);

  290.             // Container of attributes
  291.             final DSSTThirdBodyContext context = initializeStep(auxiliaryElements, parameters);

  292.             final GeneratingFunctionCoefficients gfCoefs =
  293.                             new GeneratingFunctionCoefficients(context.getMaxAR3Pow(), MAX_ECCPOWER_SP, context.getMaxAR3Pow() + 1, context, hansen);

  294.             //Compute additional quantities
  295.             // 2 * a / An
  296.             final double ax2oAn  = -context.getM2aoA() / context.getMeanMotion();
  297.             // B / An
  298.             final double BoAn    = context.getBoA() / context.getMeanMotion();
  299.             // 1 / ABn
  300.             final double ooABn   = context.getOoAB() / context.getMeanMotion();
  301.             // C / 2ABn
  302.             final double Co2ABn  = -context.getMCo2AB() / context.getMeanMotion();
  303.             // B / (A * (1 + B) * n)
  304.             final double BoABpon = context.getBoABpo() / context.getMeanMotion();
  305.             // -3 / n²a² = -3 / nA
  306.             final double m3onA   = -3 / (context.getA() * context.getMeanMotion());

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

  311.                 // Compute the cross derivatives operator :
  312.                 final double SAlphaGammaCj    = context.getAlpha() * gfCoefs.getdSdgammaCj(j) - context.getGamma() * gfCoefs.getdSdalphaCj(j);
  313.                 final double SAlphaBetaCj     = context.getAlpha() * gfCoefs.getdSdbetaCj(j)  - context.getBeta()  * gfCoefs.getdSdalphaCj(j);
  314.                 final double SBetaGammaCj     = context.getBeta() * gfCoefs.getdSdgammaCj(j) - context.getGamma() * gfCoefs.getdSdbetaCj(j);
  315.                 final double ShkCj            = auxiliaryElements.getH() * gfCoefs.getdSdkCj(j)     -  auxiliaryElements.getK()    * gfCoefs.getdSdhCj(j);
  316.                 final double pSagmIqSbgoABnCj = (auxiliaryElements.getP() * SAlphaGammaCj - I * auxiliaryElements.getQ() * SBetaGammaCj) * ooABn;
  317.                 final double ShkmSabmdSdlCj   = ShkCj - SAlphaBetaCj - gfCoefs.getdSdlambdaCj(j);

  318.                 currentCij[0] =  ax2oAn * gfCoefs.getdSdlambdaCj(j);
  319.                 currentCij[1] =  -(BoAn * gfCoefs.getdSdhCj(j) + auxiliaryElements.getH() * pSagmIqSbgoABnCj + auxiliaryElements.getK() * BoABpon * gfCoefs.getdSdlambdaCj(j));
  320.                 currentCij[2] =    BoAn * gfCoefs.getdSdkCj(j) + auxiliaryElements.getK() * pSagmIqSbgoABnCj - auxiliaryElements.getH() * BoABpon * gfCoefs.getdSdlambdaCj(j);
  321.                 currentCij[3] =  Co2ABn * (auxiliaryElements.getQ() * ShkmSabmdSdlCj - I * SAlphaGammaCj);
  322.                 currentCij[4] =  Co2ABn * (auxiliaryElements.getP() * ShkmSabmdSdlCj - SBetaGammaCj);
  323.                 currentCij[5] = -ax2oAn * gfCoefs.getdSdaCj(j) + BoABpon * (auxiliaryElements.getH() * gfCoefs.getdSdhCj(j) + auxiliaryElements.getK() * gfCoefs.getdSdkCj(j)) + pSagmIqSbgoABnCj + m3onA * gfCoefs.getSCj(j);

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

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

  328.                 // Compute the cross derivatives operator :
  329.                 final double SAlphaGammaSj    = context.getAlpha() * gfCoefs.getdSdgammaSj(j) - context.getGamma() * gfCoefs.getdSdalphaSj(j);
  330.                 final double SAlphaBetaSj     = context.getAlpha() * gfCoefs.getdSdbetaSj(j)  - context.getBeta()  * gfCoefs.getdSdalphaSj(j);
  331.                 final double SBetaGammaSj     =  context.getBeta() * gfCoefs.getdSdgammaSj(j) - context.getGamma() * gfCoefs.getdSdbetaSj(j);
  332.                 final double ShkSj            =     auxiliaryElements.getH() * gfCoefs.getdSdkSj(j)     -  auxiliaryElements.getK()    * gfCoefs.getdSdhSj(j);
  333.                 final double pSagmIqSbgoABnSj = (auxiliaryElements.getP() * SAlphaGammaSj - I * auxiliaryElements.getQ() * SBetaGammaSj) * ooABn;
  334.                 final double ShkmSabmdSdlSj   =  ShkSj - SAlphaBetaSj - gfCoefs.getdSdlambdaSj(j);

  335.                 currentSij[0] =  ax2oAn * gfCoefs.getdSdlambdaSj(j);
  336.                 currentSij[1] =  -(BoAn * gfCoefs.getdSdhSj(j) + auxiliaryElements.getH() * pSagmIqSbgoABnSj + auxiliaryElements.getK() * BoABpon * gfCoefs.getdSdlambdaSj(j));
  337.                 currentSij[2] =    BoAn * gfCoefs.getdSdkSj(j) + auxiliaryElements.getK() * pSagmIqSbgoABnSj - auxiliaryElements.getH() * BoABpon * gfCoefs.getdSdlambdaSj(j);
  338.                 currentSij[3] =  Co2ABn * (auxiliaryElements.getQ() * ShkmSabmdSdlSj - I * SAlphaGammaSj);
  339.                 currentSij[4] =  Co2ABn * (auxiliaryElements.getP() * ShkmSabmdSdlSj - SBetaGammaSj);
  340.                 currentSij[5] = -ax2oAn * gfCoefs.getdSdaSj(j) + BoABpon * (auxiliaryElements.getH() * gfCoefs.getdSdhSj(j) + auxiliaryElements.getK() * gfCoefs.getdSdkSj(j)) + pSagmIqSbgoABnSj + m3onA * gfCoefs.getSSj(j);

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

  343.                 if (j == 1) {
  344.                     //Compute the C⁰ coefficients using Danielson 2.5.2-15a.
  345.                     final double[] value = new double[6];
  346.                     for (int i = 0; i < 6; ++i) {
  347.                         value[i] = currentCij[i] * auxiliaryElements.getK() / 2. + currentSij[i] * auxiliaryElements.getH() / 2.;
  348.                     }
  349.                     slot.cij[0].addGridPoint(meanState.getDate(), value);
  350.                 }
  351.             }
  352.         }
  353.     }

  354.     /** {@inheritDoc} */
  355.     @Override
  356.     @SuppressWarnings("unchecked")
  357.     public <T extends RealFieldElement<T>> void updateShortPeriodTerms(final T[] parameters,
  358.                                                                        final FieldSpacecraftState<T>... meanStates) {

  359.         // Field used by default
  360.         final Field<T> field = meanStates[0].getDate().getField();

  361.         final FieldThirdBodyShortPeriodicCoefficients<T> ftbspc = (FieldThirdBodyShortPeriodicCoefficients<T>) fieldShortPeriods.get(field);
  362.         final FieldSlot<T> slot = ftbspc.createSlot(meanStates);
  363.         for (final FieldSpacecraftState<T> meanState : meanStates) {

  364.             // Auxiliary elements related to the current orbit
  365.             final FieldAuxiliaryElements<T> auxiliaryElements = new FieldAuxiliaryElements<>(meanState.getOrbit(), I);

  366.             // Container of attributes
  367.             final FieldDSSTThirdBodyContext<T> context = initializeStep(auxiliaryElements, parameters);

  368.             final FieldHansenObjects<T> fho = (FieldHansenObjects<T>) fieldHansen.get(field);

  369.             final FieldGeneratingFunctionCoefficients<T> gfCoefs =
  370.                             new FieldGeneratingFunctionCoefficients<>(context.getMaxAR3Pow(), MAX_ECCPOWER_SP, context.getMaxAR3Pow() + 1, context, fho, field);

  371.             //Compute additional quantities
  372.             // 2 * a / An
  373.             final T ax2oAn  = context.getM2aoA().negate().divide(context.getMeanMotion());
  374.             // B / An
  375.             final T BoAn    = context.getBoA().divide(context.getMeanMotion());
  376.             // 1 / ABn
  377.             final T ooABn   = context.getOoAB().divide(context.getMeanMotion());
  378.             // C / 2ABn
  379.             final T Co2ABn  = context.getMCo2AB().negate().divide(context.getMeanMotion());
  380.             // B / (A * (1 + B) * n)
  381.             final T BoABpon = context.getBoABpo().divide(context.getMeanMotion());
  382.             // -3 / n²a² = -3 / nA
  383.             final T m3onA   = context.getA().multiply(context.getMeanMotion()).divide(-3.).reciprocal();

  384.             //Compute the C<sub>i</sub><sup>j</sup> and S<sub>i</sub><sup>j</sup> coefficients.
  385.             for (int j = 1; j < slot.cij.length; j++) {
  386.                 // First compute the C<sub>i</sub><sup>j</sup> coefficients
  387.                 final T[] currentCij = MathArrays.buildArray(field, 6);

  388.                 // Compute the cross derivatives operator :
  389.                 final T SAlphaGammaCj    = context.getAlpha().multiply(gfCoefs.getdSdgammaCj(j)).subtract(context.getGamma().multiply(gfCoefs.getdSdalphaCj(j)));
  390.                 final T SAlphaBetaCj     = context.getAlpha().multiply(gfCoefs.getdSdbetaCj(j)).subtract(context.getBeta().multiply(gfCoefs.getdSdalphaCj(j)));
  391.                 final T SBetaGammaCj     = context.getBeta().multiply(gfCoefs.getdSdgammaCj(j)).subtract(context.getGamma().multiply(gfCoefs.getdSdbetaCj(j)));
  392.                 final T ShkCj            = auxiliaryElements.getH().multiply(gfCoefs.getdSdkCj(j)).subtract(auxiliaryElements.getK().multiply(gfCoefs.getdSdhCj(j)));
  393.                 final T pSagmIqSbgoABnCj = ooABn.multiply(auxiliaryElements.getP().multiply(SAlphaGammaCj).subtract(auxiliaryElements.getQ().multiply(SBetaGammaCj).multiply(I)));
  394.                 final T ShkmSabmdSdlCj   = ShkCj.subtract(SAlphaBetaCj).subtract(gfCoefs.getdSdlambdaCj(j));

  395.                 currentCij[0] = ax2oAn.multiply(gfCoefs.getdSdlambdaCj(j));
  396.                 currentCij[1] = BoAn.multiply(gfCoefs.getdSdhCj(j)).add(auxiliaryElements.getH().multiply(pSagmIqSbgoABnCj)).add(auxiliaryElements.getK().multiply(BoABpon).multiply(gfCoefs.getdSdlambdaCj(j))).negate();
  397.                 currentCij[2] = BoAn.multiply(gfCoefs.getdSdkCj(j)).add(auxiliaryElements.getK().multiply(pSagmIqSbgoABnCj)).subtract(auxiliaryElements.getH().multiply(BoABpon).multiply(gfCoefs.getdSdlambdaCj(j)));
  398.                 currentCij[3] = Co2ABn.multiply(auxiliaryElements.getQ().multiply(ShkmSabmdSdlCj).subtract(SAlphaGammaCj.multiply(I)));
  399.                 currentCij[4] = Co2ABn.multiply(auxiliaryElements.getP().multiply(ShkmSabmdSdlCj).subtract(SBetaGammaCj));
  400.                 currentCij[5] = ax2oAn.negate().multiply(gfCoefs.getdSdaCj(j)).add(BoABpon.multiply(auxiliaryElements.getH().multiply(gfCoefs.getdSdhCj(j)).add(auxiliaryElements.getK().multiply(gfCoefs.getdSdkCj(j))))).add(pSagmIqSbgoABnCj).add(m3onA.multiply(gfCoefs.getSCj(j)));

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

  403.                 // Compute the S<sub>i</sub><sup>j</sup> coefficients
  404.                 final T[] currentSij = MathArrays.buildArray(field, 6);

  405.                 // Compute the cross derivatives operator :
  406.                 final T SAlphaGammaSj    = context.getAlpha().multiply(gfCoefs.getdSdgammaSj(j)).subtract(context.getGamma().multiply(gfCoefs.getdSdalphaSj(j)));
  407.                 final T SAlphaBetaSj     = context.getAlpha().multiply(gfCoefs.getdSdbetaSj(j)).subtract(context.getBeta().multiply(gfCoefs.getdSdalphaSj(j)));
  408.                 final T SBetaGammaSj     = context.getBeta().multiply(gfCoefs.getdSdgammaSj(j)).subtract(context.getGamma().multiply(gfCoefs.getdSdbetaSj(j)));
  409.                 final T ShkSj            = auxiliaryElements.getH().multiply(gfCoefs.getdSdkSj(j)).subtract(auxiliaryElements.getK().multiply(gfCoefs.getdSdhSj(j)));
  410.                 final T pSagmIqSbgoABnSj = ooABn.multiply(auxiliaryElements.getP().multiply(SAlphaGammaSj).subtract(auxiliaryElements.getQ().multiply(SBetaGammaSj).multiply(I)));
  411.                 final T ShkmSabmdSdlSj   = ShkSj.subtract(SAlphaBetaSj).subtract(gfCoefs.getdSdlambdaSj(j));

  412.                 currentSij[0] = ax2oAn.multiply(gfCoefs.getdSdlambdaSj(j));
  413.                 currentSij[1] = BoAn.multiply(gfCoefs.getdSdhSj(j)).add(auxiliaryElements.getH().multiply(pSagmIqSbgoABnSj)).add(auxiliaryElements.getK().multiply(BoABpon).multiply(gfCoefs.getdSdlambdaSj(j))).negate();
  414.                 currentSij[2] = BoAn.multiply(gfCoefs.getdSdkSj(j)).add(auxiliaryElements.getK().multiply(pSagmIqSbgoABnSj)).subtract(auxiliaryElements.getH().multiply(BoABpon).multiply(gfCoefs.getdSdlambdaSj(j)));
  415.                 currentSij[3] = Co2ABn.multiply(auxiliaryElements.getQ().multiply(ShkmSabmdSdlSj).subtract(SAlphaGammaSj.multiply(I)));
  416.                 currentSij[4] = Co2ABn.multiply(auxiliaryElements.getP().multiply(ShkmSabmdSdlSj).subtract(SBetaGammaSj));
  417.                 currentSij[5] = ax2oAn.negate().multiply(gfCoefs.getdSdaSj(j)).add(BoABpon.multiply(auxiliaryElements.getH().multiply(gfCoefs.getdSdhSj(j)).add(auxiliaryElements.getK().multiply(gfCoefs.getdSdkSj(j))))).add(pSagmIqSbgoABnSj).add(m3onA.multiply(gfCoefs.getSSj(j)));

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

  420.                 if (j == 1) {
  421.                     //Compute the C⁰ coefficients using Danielson 2.5.2-15a.
  422.                     final T[] value = MathArrays.buildArray(field, 6);
  423.                     for (int i = 0; i < 6; ++i) {
  424.                         value[i] = currentCij[i].multiply(auxiliaryElements.getK()).divide(2.).add(currentSij[i].multiply(auxiliaryElements.getH()).divide(2.));
  425.                     }
  426.                     slot.cij[0].addGridPoint(meanState.getDate(), value);
  427.                 }
  428.             }
  429.         }
  430.     }

  431.     /** {@inheritDoc} */
  432.     @Override
  433.     public EventDetector[] getEventsDetectors() {
  434.         return null;
  435.     }

  436.     /** {@inheritDoc} */
  437.     @Override
  438.     public <T extends RealFieldElement<T>> FieldEventDetector<T>[] getFieldEventsDetectors(final Field<T> field) {
  439.         return null;
  440.     }

  441.     /** {@inheritDoc} */
  442.     @Override
  443.     public void registerAttitudeProvider(final AttitudeProvider provider) {
  444.         //nothing is done since this contribution is not sensitive to attitude
  445.     }

  446.     /** {@inheritDoc} */
  447.     @Override
  448.     public ParameterDriver[] getParametersDrivers() {
  449.         return new ParameterDriver[] {
  450.             bodyParameterDriver,
  451.             gmParameterDriver
  452.         };
  453.     }

  454.     /** Computes the C<sup>j</sup> and S<sup>j</sup> coefficients Danielson 4.2-(15,16)
  455.      * and their derivatives.
  456.      *  <p>
  457.      *  CS Mathematical Report $3.5.3.2
  458.      *  </p>
  459.      */
  460.     private class FourierCjSjCoefficients {

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

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

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

  467.         /** The Fourier coefficients C<sup>j</sup> and their derivatives.
  468.          * <p>
  469.          * Each column of the matrix contains the following values: <br/>
  470.          * - C<sup>j</sup> <br/>
  471.          * - dC<sup>j</sup> / da <br/>
  472.          * - dC<sup>j</sup> / dk <br/>
  473.          * - dC<sup>j</sup> / dh <br/>
  474.          * - dC<sup>j</sup> / dα <br/>
  475.          * - dC<sup>j</sup> / dβ <br/>
  476.          * - dC<sup>j</sup> / dγ <br/>
  477.          * </p>
  478.          */
  479.         private final double[][] cj;

  480.         /** The S<sup>j</sup> coefficients and their derivatives.
  481.          * <p>
  482.          * Each column of the matrix contains the following values: <br/>
  483.          * - S<sup>j</sup> <br/>
  484.          * - dS<sup>j</sup> / da <br/>
  485.          * - dS<sup>j</sup> / dk <br/>
  486.          * - dS<sup>j</sup> / dh <br/>
  487.          * - dS<sup>j</sup> / dα <br/>
  488.          * - dS<sup>j</sup> / dβ <br/>
  489.          * - dS<sup>j</sup> / dγ <br/>
  490.          * </p>
  491.          */
  492.         private final double[][] sj;

  493.         /** The Coefficients C<sup>j</sup><sub>,λ</sub>.
  494.          * <p>
  495.          * See Danielson 4.2-21
  496.          * </p>
  497.          */
  498.         private final double[] cjlambda;

  499.         /** The Coefficients S<sup>j</sup><sub>,λ</sub>.
  500.         * <p>
  501.         * See Danielson 4.2-21
  502.         * </p>
  503.         */
  504.         private final double[] sjlambda;

  505.         /** Maximum value for n. */
  506.         private final int nMax;

  507.         /** Maximum value for s. */
  508.         private final int sMax;

  509.         /** Maximum value for j. */
  510.         private final int jMax;

  511.         /**
  512.          * Private constructor.
  513.          *
  514.          * @param nMax maximum value for n index
  515.          * @param sMax maximum value for s index
  516.          * @param jMax maximum value for j index
  517.          * @param context container for attributes
  518.          */
  519.         FourierCjSjCoefficients(final int nMax, final int sMax, final int jMax, final DSSTThirdBodyContext context) {
  520.             //Save parameters
  521.             this.nMax = nMax;
  522.             this.sMax = sMax;
  523.             this.jMax = jMax;

  524.             //Create objects
  525.             wnsjEtomjmsCoefficient = new WnsjEtomjmsCoefficient(context);
  526.             ABDECoefficients = new CjSjAlphaBetaKH(context);
  527.             gns = new GnsCoefficients(nMax, sMax, context);

  528.             //create arays
  529.             this.cj = new double[7][jMax + 1];
  530.             this.sj = new double[7][jMax + 1];
  531.             this.cjlambda = new double[jMax];
  532.             this.sjlambda = new double[jMax];

  533.             computeCoefficients(context);
  534.         }

  535.         /**
  536.          * Compute all coefficients.
  537.          * @param context container for attributes
  538.          */
  539.         private void computeCoefficients(final DSSTThirdBodyContext context) {

  540.             final AuxiliaryElements auxiliaryElements = context.getAuxiliaryElements();

  541.             for (int j = 1; j <= jMax; j++) {
  542.                 // initialise the coefficients
  543.                 for (int i = 0; i <= 6; i++) {
  544.                     cj[i][j] = 0.;
  545.                     sj[i][j] = 0.;
  546.                 }
  547.                 if (j < jMax) {
  548.                     // initialise the C<sup>j</sup><sub>,λ</sub> and S<sup>j</sup><sub>,λ</sub> coefficients
  549.                     cjlambda[j] = 0.;
  550.                     sjlambda[j] = 0.;
  551.                 }
  552.                 for (int s = 0; s <= sMax; s++) {

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

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

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

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

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

  567.                             //Compute C<sup>j</sup>
  568.                             cj[0][j] += gns.getGns(n, s) * coef1;
  569.                             //Compute dC<sup>j</sup> / da
  570.                             cj[1][j] += gns.getdGnsda(n, s) * coef1;
  571.                             //Compute dC<sup>j</sup> / dk
  572.                             cj[2][j] += -gns.getGns(n, s) *
  573.                                         (
  574.                                             wjnp1semjms[1] * ABDECoefficients.getCoefA() +
  575.                                             wjnp1semjms[0] * ABDECoefficients.getdCoefAdk() +
  576.                                             wmjnp1semjms[1] * ABDECoefficients.getCoefB() +
  577.                                             wmjnp1semjms[0] * ABDECoefficients.getdCoefBdk()
  578.                                          );
  579.                             //Compute dC<sup>j</sup> / dh
  580.                             cj[3][j] += -gns.getGns(n, s) *
  581.                                         (
  582.                                             wjnp1semjms[2] * ABDECoefficients.getCoefA() +
  583.                                             wjnp1semjms[0] * ABDECoefficients.getdCoefAdh() +
  584.                                             wmjnp1semjms[2] * ABDECoefficients.getCoefB() +
  585.                                             wmjnp1semjms[0] * ABDECoefficients.getdCoefBdh()
  586.                                          );
  587.                             //Compute dC<sup>j</sup> / dα
  588.                             cj[4][j] += -gns.getGns(n, s) *
  589.                                         (
  590.                                             wjnp1semjms[0] * ABDECoefficients.getdCoefAdalpha() +
  591.                                             wmjnp1semjms[0] * ABDECoefficients.getdCoefBdalpha()
  592.                                         );
  593.                             //Compute dC<sup>j</sup> / dβ
  594.                             cj[5][j] += -gns.getGns(n, s) *
  595.                                         (
  596.                                             wjnp1semjms[0] * ABDECoefficients.getdCoefAdbeta() +
  597.                                             wmjnp1semjms[0] * ABDECoefficients.getdCoefBdbeta()
  598.                                         );
  599.                             //Compute dC<sup>j</sup> / dγ
  600.                             cj[6][j] += gns.getdGnsdgamma(n, s) * coef1;

  601.                             //Compute S<sup>j</sup>
  602.                             sj[0][j] += gns.getGns(n, s) * coef2;
  603.                             //Compute dS<sup>j</sup> / da
  604.                             sj[1][j] += gns.getdGnsda(n, s) * coef2;
  605.                             //Compute dS<sup>j</sup> / dk
  606.                             sj[2][j] += gns.getGns(n, s) *
  607.                                         (
  608.                                             wjnp1semjms[1] * ABDECoefficients.getCoefD() +
  609.                                             wjnp1semjms[0] * ABDECoefficients.getdCoefDdk() +
  610.                                             wmjnp1semjms[1] * ABDECoefficients.getCoefE() +
  611.                                             wmjnp1semjms[0] * ABDECoefficients.getdCoefEdk()
  612.                                          );
  613.                             //Compute dS<sup>j</sup> / dh
  614.                             sj[3][j] += gns.getGns(n, s) *
  615.                                         (
  616.                                             wjnp1semjms[2] * ABDECoefficients.getCoefD() +
  617.                                             wjnp1semjms[0] * ABDECoefficients.getdCoefDdh() +
  618.                                             wmjnp1semjms[2] * ABDECoefficients.getCoefE() +
  619.                                             wmjnp1semjms[0] * ABDECoefficients.getdCoefEdh()
  620.                                          );
  621.                             //Compute dS<sup>j</sup> / dα
  622.                             sj[4][j] += gns.getGns(n, s) *
  623.                                         (
  624.                                             wjnp1semjms[0] * ABDECoefficients.getdCoefDdalpha() +
  625.                                             wmjnp1semjms[0] * ABDECoefficients.getdCoefEdalpha()
  626.                                         );
  627.                             //Compute dS<sup>j</sup> / dβ
  628.                             sj[5][j] += gns.getGns(n, s) *
  629.                                         (
  630.                                             wjnp1semjms[0] * ABDECoefficients.getdCoefDdbeta() +
  631.                                             wmjnp1semjms[0] * ABDECoefficients.getdCoefEdbeta()
  632.                                         );
  633.                             //Compute dS<sup>j</sup> / dγ
  634.                             sj[6][j] += gns.getdGnsdgamma(n, s) * coef2;

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

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

  641.                                 //Compute C<sup>j</sup><sub>,λ</sub>
  642.                                 cjlambda[j] += gns.getGns(n, s) * (wjnsemjms[0] * ABDECoefficients.getCoefD() + wmjnsemjms[0] * ABDECoefficients.getCoefE());
  643.                                 //Compute S<sup>j</sup><sub>,λ</sub>
  644.                                 sjlambda[j] += gns.getGns(n, s) * (wjnsemjms[0] * ABDECoefficients.getCoefA() + wmjnsemjms[0] * ABDECoefficients.getCoefB());
  645.                             }
  646.                         }
  647.                     }
  648.                 }
  649.                 // Divide by j
  650.                 for (int i = 0; i <= 6; i++) {
  651.                     cj[i][j] /= j;
  652.                     sj[i][j] /= j;
  653.                 }
  654.             }
  655.             //The C⁰ coefficients are not computed here.
  656.             //They are evaluated at the final point.

  657.             //C⁰<sub>,λ</sub>
  658.             cjlambda[0] = auxiliaryElements.getK() * cjlambda[1] / 2. + auxiliaryElements.getH() * sjlambda[1] / 2.;
  659.         }

  660.         /** Get the Fourier coefficient C<sup>j</sup>.
  661.          * @param j j index
  662.          * @return C<sup>j</sup>
  663.          */
  664.         public double getCj(final int j) {
  665.             return cj[0][j];
  666.         }

  667.         /** Get the derivative dC<sup>j</sup>/da.
  668.          * @param j j index
  669.          * @return dC<sup>j</sup>/da
  670.          */
  671.         public double getdCjda(final int j) {
  672.             return cj[1][j];
  673.         }

  674.         /** Get the derivative dC<sup>j</sup>/dk.
  675.          * @param j j index
  676.          * @return dC<sup>j</sup>/dk
  677.          */
  678.         public double getdCjdk(final int j) {
  679.             return cj[2][j];
  680.         }

  681.         /** Get the derivative dC<sup>j</sup>/dh.
  682.          * @param j j index
  683.          * @return dC<sup>j</sup>/dh
  684.          */
  685.         public double getdCjdh(final int j) {
  686.             return cj[3][j];
  687.         }

  688.         /** Get the derivative dC<sup>j</sup>/dα.
  689.          * @param j j index
  690.          * @return dC<sup>j</sup>/dα
  691.          */
  692.         public double getdCjdalpha(final int j) {
  693.             return cj[4][j];
  694.         }

  695.         /** Get the derivative dC<sup>j</sup>/dβ.
  696.          * @param j j index
  697.          * @return dC<sup>j</sup>/dβ
  698.          */
  699.         public double getdCjdbeta(final int j) {
  700.             return cj[5][j];
  701.         }

  702.         /** Get the derivative dC<sup>j</sup>/dγ.
  703.          * @param j j index
  704.          * @return dC<sup>j</sup>/dγ
  705.          */
  706.         public double getdCjdgamma(final int j) {
  707.             return cj[6][j];
  708.         }

  709.         /** Get the Fourier coefficient S<sup>j</sup>.
  710.          * @param j j index
  711.          * @return S<sup>j</sup>
  712.          */
  713.         public double getSj(final int j) {
  714.             return sj[0][j];
  715.         }

  716.         /** Get the derivative dS<sup>j</sup>/da.
  717.          * @param j j index
  718.          * @return dS<sup>j</sup>/da
  719.          */
  720.         public double getdSjda(final int j) {
  721.             return sj[1][j];
  722.         }

  723.         /** Get the derivative dS<sup>j</sup>/dk.
  724.          * @param j j index
  725.          * @return dS<sup>j</sup>/dk
  726.          */
  727.         public double getdSjdk(final int j) {
  728.             return sj[2][j];
  729.         }

  730.         /** Get the derivative dS<sup>j</sup>/dh.
  731.          * @param j j index
  732.          * @return dS<sup>j</sup>/dh
  733.          */
  734.         public double getdSjdh(final int j) {
  735.             return sj[3][j];
  736.         }

  737.         /** Get the derivative dS<sup>j</sup>/dα.
  738.          * @param j j index
  739.          * @return dS<sup>j</sup>/dα
  740.          */
  741.         public double getdSjdalpha(final int j) {
  742.             return sj[4][j];
  743.         }

  744.         /** Get the derivative dS<sup>j</sup>/dβ.
  745.          * @param j j index
  746.          * @return dS<sup>j</sup>/dβ
  747.          */
  748.         public double getdSjdbeta(final int j) {
  749.             return sj[5][j];
  750.         }

  751.         /** Get the derivative dS<sup>j</sup>/dγ.
  752.          * @param j j index
  753.          * @return dS<sup>j</sup>/dγ
  754.          */
  755.         public double getdSjdgamma(final int j) {
  756.             return sj[6][j];
  757.         }

  758.         /** Get the coefficient C⁰<sub>,λ</sub>.
  759.          * @return C⁰<sub>,λ</sub>
  760.          */
  761.         public double getC0Lambda() {
  762.             return cjlambda[0];
  763.         }

  764.         /** Get the coefficient C<sup>j</sup><sub>,λ</sub>.
  765.          * @param j j index
  766.          * @return C<sup>j</sup><sub>,λ</sub>
  767.          */
  768.         public double getCjLambda(final int j) {
  769.             if (j < 1 || j >= jMax) {
  770.                 return 0.;
  771.             }
  772.             return cjlambda[j];
  773.         }

  774.         /** Get the coefficient S<sup>j</sup><sub>,λ</sub>.
  775.          * @param j j index
  776.          * @return S<sup>j</sup><sub>,λ</sub>
  777.          */
  778.         public double getSjLambda(final int j) {
  779.             if (j < 1 || j >= jMax) {
  780.                 return 0.;
  781.             }
  782.             return sjlambda[j];
  783.         }
  784.     }

  785.     /** Computes the C<sup>j</sup> and S<sup>j</sup> coefficients Danielson 4.2-(15,16)
  786.      * and their derivatives.
  787.      *  <p>
  788.      *  CS Mathematical Report $3.5.3.2
  789.      *  </p>
  790.      */
  791.     private class FieldFourierCjSjCoefficients <T extends RealFieldElement<T>> {

  792.         /** The coefficients G<sub>n, s</sub> and their derivatives. */
  793.         private final FieldGnsCoefficients<T> gns;

  794.         /** the coefficients e<sup>-|j-s|</sup>*w<sub>j</sub><sup>n, s</sup> and their derivatives by h and k. */
  795.         private final FieldWnsjEtomjmsCoefficient<T> wnsjEtomjmsCoefficient;

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

  798.         /** The Fourier coefficients C<sup>j</sup> and their derivatives.
  799.          * <p>
  800.          * Each column of the matrix contains the following values: <br/>
  801.          * - C<sup>j</sup> <br/>
  802.          * - dC<sup>j</sup> / da <br/>
  803.          * - dC<sup>j</sup> / dk <br/>
  804.          * - dC<sup>j</sup> / dh <br/>
  805.          * - dC<sup>j</sup> / dα <br/>
  806.          * - dC<sup>j</sup> / dβ <br/>
  807.          * - dC<sup>j</sup> / dγ <br/>
  808.          * </p>
  809.          */
  810.         private final T[][] cj;

  811.         /** The S<sup>j</sup> coefficients and their derivatives.
  812.          * <p>
  813.          * Each column of the matrix contains the following values: <br/>
  814.          * - S<sup>j</sup> <br/>
  815.          * - dS<sup>j</sup> / da <br/>
  816.          * - dS<sup>j</sup> / dk <br/>
  817.          * - dS<sup>j</sup> / dh <br/>
  818.          * - dS<sup>j</sup> / dα <br/>
  819.          * - dS<sup>j</sup> / dβ <br/>
  820.          * - dS<sup>j</sup> / dγ <br/>
  821.          * </p>
  822.          */
  823.         private final T[][] sj;

  824.         /** The Coefficients C<sup>j</sup><sub>,λ</sub>.
  825.          * <p>
  826.          * See Danielson 4.2-21
  827.          * </p>
  828.          */
  829.         private final T[] cjlambda;

  830.         /** The Coefficients S<sup>j</sup><sub>,λ</sub>.
  831.         * <p>
  832.         * See Danielson 4.2-21
  833.         * </p>
  834.         */
  835.         private final T[] sjlambda;

  836.         /** Zero. */
  837.         private final T zero;

  838.         /** Maximum value for n. */
  839.         private final int nMax;

  840.         /** Maximum value for s. */
  841.         private final int sMax;

  842.         /** Maximum value for j. */
  843.         private final int jMax;

  844.         /**
  845.          * Private constructor.
  846.          *
  847.          * @param nMax maximum value for n index
  848.          * @param sMax maximum value for s index
  849.          * @param jMax maximum value for j index
  850.          * @param context container for attributes
  851.          * @param field field used by default
  852.          */
  853.         FieldFourierCjSjCoefficients(final int nMax, final int sMax, final int jMax,
  854.                                      final FieldDSSTThirdBodyContext<T> context,
  855.                                      final Field<T> field) {
  856.             //Zero
  857.             this.zero = field.getZero();

  858.             //Save parameters
  859.             this.nMax = nMax;
  860.             this.sMax = sMax;
  861.             this.jMax = jMax;

  862.             //Create objects
  863.             wnsjEtomjmsCoefficient = new FieldWnsjEtomjmsCoefficient<>(context, field);
  864.             ABDECoefficients       = new FieldCjSjAlphaBetaKH<>(context, field);
  865.             gns                    = new FieldGnsCoefficients<>(nMax, sMax, context, field);

  866.             //create arays
  867.             this.cj = MathArrays.buildArray(field, 7, jMax + 1);
  868.             this.sj = MathArrays.buildArray(field, 7, jMax + 1);
  869.             this.cjlambda = MathArrays.buildArray(field, jMax);
  870.             this.sjlambda = MathArrays.buildArray(field, jMax);

  871.             computeCoefficients(context, field);
  872.         }

  873.         /**
  874.          * Compute all coefficients.
  875.          * @param context container for attributes
  876.          * @param field field used by default
  877.          */
  878.         private void computeCoefficients(final FieldDSSTThirdBodyContext<T> context,
  879.                                          final Field<T> field) {

  880.             final FieldAuxiliaryElements<T> auxiliaryElements = context.getFieldAuxiliaryElements();

  881.             for (int j = 1; j <= jMax; j++) {
  882.                 // initialise the coefficients
  883.                 for (int i = 0; i <= 6; i++) {
  884.                     cj[i][j] = zero;
  885.                     sj[i][j] = zero;
  886.                 }
  887.                 if (j < jMax) {
  888.                     // initialise the C<sup>j</sup><sub>,λ</sub> and S<sup>j</sup><sub>,λ</sub> coefficients
  889.                     cjlambda[j] = zero;
  890.                     sjlambda[j] = zero;
  891.                 }
  892.                 for (int s = 0; s <= sMax; s++) {

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

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

  897.                     for (int n = minN; n <= nMax; n++) {
  898.                         // check if n-s is even
  899.                         if ((n - s) % 2 == 0) {
  900.                             // compute the coefficient e<sup>-|j-s|</sup>*w<sub>j</sub><sup>n+1, s</sup> and its derivatives
  901.                             final T[] wjnp1semjms = wnsjEtomjmsCoefficient.computeWjnsEmjmsAndDeriv(j, s, n + 1, context, field);

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

  904.                             // compute common factors
  905.                             final T coef1 = (wjnp1semjms[0].multiply(ABDECoefficients.getCoefA()).add(wmjnp1semjms[0].multiply(ABDECoefficients.getCoefB()))).negate();
  906.                             final T coef2 =  wjnp1semjms[0].multiply(ABDECoefficients.getCoefD()).add(wmjnp1semjms[0].multiply(ABDECoefficients.getCoefE()));

  907.                             //Compute C<sup>j</sup>
  908.                             cj[0][j] = cj[0][j].add(gns.getGns(n, s).multiply(coef1));
  909.                             //Compute dC<sup>j</sup> / da
  910.                             cj[1][j] = cj[1][j].add(gns.getdGnsda(n, s).multiply(coef1));
  911.                             //Compute dC<sup>j</sup> / dk
  912.                             cj[2][j] = cj[2][j].add(gns.getGns(n, s).negate().
  913.                                        multiply(
  914.                                             wjnp1semjms[1].multiply(ABDECoefficients.getCoefA()).
  915.                                             add(wjnp1semjms[0].multiply(ABDECoefficients.getdCoefAdk())).
  916.                                             add(wmjnp1semjms[1].multiply(ABDECoefficients.getCoefB())).
  917.                                             add(wmjnp1semjms[0].multiply(ABDECoefficients.getdCoefBdk()))
  918.                                          ));
  919.                             //Compute dC<sup>j</sup> / dh
  920.                             cj[3][j] = cj[3][j].add(gns.getGns(n, s).negate().
  921.                                        multiply(
  922.                                              wjnp1semjms[2].multiply(ABDECoefficients.getCoefA()).
  923.                                              add(wjnp1semjms[0].multiply(ABDECoefficients.getdCoefAdh())).
  924.                                              add(wmjnp1semjms[2].multiply(ABDECoefficients.getCoefB())).
  925.                                              add(wmjnp1semjms[0].multiply(ABDECoefficients.getdCoefBdh()))
  926.                                              ));
  927.                             //Compute dC<sup>j</sup> / dα
  928.                             cj[4][j] = cj[4][j].add(gns.getGns(n, s).negate().
  929.                                        multiply(
  930.                                            wjnp1semjms[0].multiply(ABDECoefficients.getdCoefAdalpha()).
  931.                                            add(wmjnp1semjms[0].multiply(ABDECoefficients.getdCoefBdalpha()))
  932.                                        ));
  933.                             //Compute dC<sup>j</sup> / dβ
  934.                             cj[5][j] = cj[5][j].add(gns.getGns(n, s).negate().
  935.                                        multiply(
  936.                                            wjnp1semjms[0].multiply(ABDECoefficients.getdCoefAdbeta()).
  937.                                            add(wmjnp1semjms[0].multiply(ABDECoefficients.getdCoefBdbeta()))
  938.                                        ));
  939.                             //Compute dC<sup>j</sup> / dγ
  940.                             cj[6][j] = cj[6][j].add(gns.getdGnsdgamma(n, s).multiply(coef1));

  941.                             //Compute S<sup>j</sup>
  942.                             sj[0][j] = sj[0][j].add(gns.getGns(n, s).multiply(coef2));
  943.                             //Compute dS<sup>j</sup> / da
  944.                             sj[1][j] = sj[1][j].add(gns.getdGnsda(n, s).multiply(coef2));
  945.                             //Compute dS<sup>j</sup> / dk
  946.                             sj[2][j] = sj[2][j].add(gns.getGns(n, s).
  947.                                        multiply(
  948.                                            wjnp1semjms[1].multiply(ABDECoefficients.getCoefD()).
  949.                                            add(wjnp1semjms[0].multiply(ABDECoefficients.getdCoefDdk())).
  950.                                            add(wmjnp1semjms[1].multiply(ABDECoefficients.getCoefE())).
  951.                                            add(wmjnp1semjms[0].multiply(ABDECoefficients.getdCoefEdk()))
  952.                                        ));
  953.                             //Compute dS<sup>j</sup> / dh
  954.                             sj[3][j] = sj[3][j].add(gns.getGns(n, s).
  955.                                        multiply(
  956.                                            wjnp1semjms[2].multiply(ABDECoefficients.getCoefD()).
  957.                                            add(wjnp1semjms[0].multiply(ABDECoefficients.getdCoefDdh())).
  958.                                            add(wmjnp1semjms[2].multiply(ABDECoefficients.getCoefE())).
  959.                                            add(wmjnp1semjms[0].multiply(ABDECoefficients.getdCoefEdh()))
  960.                                        ));
  961.                             //Compute dS<sup>j</sup> / dα
  962.                             sj[4][j] = sj[4][j].add(gns.getGns(n, s).
  963.                                        multiply(
  964.                                             wjnp1semjms[0].multiply(ABDECoefficients.getdCoefDdalpha()).
  965.                                             add(wmjnp1semjms[0].multiply(ABDECoefficients.getdCoefEdalpha()))
  966.                                        ));
  967.                             //Compute dS<sup>j</sup> / dβ
  968.                             sj[5][j] = sj[5][j].add(gns.getGns(n, s).
  969.                                         multiply(
  970.                                             wjnp1semjms[0].multiply(ABDECoefficients.getdCoefDdbeta()).
  971.                                             add(wmjnp1semjms[0].multiply(ABDECoefficients.getdCoefEdbeta()))
  972.                                        ));
  973.                             //Compute dS<sup>j</sup> / dγ
  974.                             sj[6][j] = sj[6][j].add(gns.getdGnsdgamma(n, s).multiply(coef2));

  975.                             //Check if n is greater or equal to j and j is at most jMax-1
  976.                             if (n >= j && j < jMax) {
  977.                                 // compute the coefficient e<sup>-|j-s|</sup>*w<sub>j</sub><sup>n, s</sup> and its derivatives
  978.                                 final T[] wjnsemjms = wnsjEtomjmsCoefficient.computeWjnsEmjmsAndDeriv(j, s, n, context, field);

  979.                                 // compute the coefficient e<sup>-|j-s|</sup>*w<sub>-j</sub><sup>n, s</sup> and its derivatives
  980.                                 final T[] wmjnsemjms = wnsjEtomjmsCoefficient.computeWjnsEmjmsAndDeriv(-j, s, n, context, field);

  981.                                 //Compute C<sup>j</sup><sub>,λ</sub>
  982.                                 cjlambda[j] = cjlambda[j].add(gns.getGns(n, s).multiply(wjnsemjms[0].multiply(ABDECoefficients.getCoefD()).add(wmjnsemjms[0].multiply(ABDECoefficients.getCoefE()))));
  983.                                 //Compute S<sup>j</sup><sub>,λ</sub>
  984.                                 sjlambda[j] = sjlambda[j].add(gns.getGns(n, s).multiply(wjnsemjms[0].multiply(ABDECoefficients.getCoefA()).add(wmjnsemjms[0].multiply(ABDECoefficients.getCoefB()))));
  985.                             }
  986.                         }
  987.                     }
  988.                 }
  989.                 // Divide by j
  990.                 for (int i = 0; i <= 6; i++) {
  991.                     cj[i][j] = cj[i][j].divide(j);
  992.                     sj[i][j] = sj[i][j].divide(j);
  993.                 }
  994.             }
  995.             //The C⁰ coefficients are not computed here.
  996.             //They are evaluated at the final point.

  997.             //C⁰<sub>,λ</sub>
  998.             cjlambda[0] = auxiliaryElements.getK().multiply(cjlambda[1]).divide(2.).add(auxiliaryElements.getH().multiply(sjlambda[1]).divide(2.));
  999.         }

  1000.         /** Get the Fourier coefficient C<sup>j</sup>.
  1001.          * @param j j index
  1002.          * @return C<sup>j</sup>
  1003.          */
  1004.         public T getCj(final int j) {
  1005.             return cj[0][j];
  1006.         }

  1007.         /** Get the derivative dC<sup>j</sup>/da.
  1008.          * @param j j index
  1009.          * @return dC<sup>j</sup>/da
  1010.          */
  1011.         public T getdCjda(final int j) {
  1012.             return cj[1][j];
  1013.         }

  1014.         /** Get the derivative dC<sup>j</sup>/dk.
  1015.          * @param j j index
  1016.          * @return dC<sup>j</sup>/dk
  1017.          */
  1018.         public T getdCjdk(final int j) {
  1019.             return cj[2][j];
  1020.         }

  1021.         /** Get the derivative dC<sup>j</sup>/dh.
  1022.          * @param j j index
  1023.          * @return dC<sup>j</sup>/dh
  1024.          */
  1025.         public T getdCjdh(final int j) {
  1026.             return cj[3][j];
  1027.         }

  1028.         /** Get the derivative dC<sup>j</sup>/dα.
  1029.          * @param j j index
  1030.          * @return dC<sup>j</sup>/dα
  1031.          */
  1032.         public T getdCjdalpha(final int j) {
  1033.             return cj[4][j];
  1034.         }

  1035.         /** Get the derivative dC<sup>j</sup>/dβ.
  1036.          * @param j j index
  1037.          * @return dC<sup>j</sup>/dβ
  1038.          */
  1039.         public T getdCjdbeta(final int j) {
  1040.             return cj[5][j];
  1041.         }

  1042.         /** Get the derivative dC<sup>j</sup>/dγ.
  1043.          * @param j j index
  1044.          * @return dC<sup>j</sup>/dγ
  1045.          */
  1046.         public T getdCjdgamma(final int j) {
  1047.             return cj[6][j];
  1048.         }

  1049.         /** Get the Fourier coefficient S<sup>j</sup>.
  1050.          * @param j j index
  1051.          * @return S<sup>j</sup>
  1052.          */
  1053.         public T getSj(final int j) {
  1054.             return sj[0][j];
  1055.         }

  1056.         /** Get the derivative dS<sup>j</sup>/da.
  1057.          * @param j j index
  1058.          * @return dS<sup>j</sup>/da
  1059.          */
  1060.         public T getdSjda(final int j) {
  1061.             return sj[1][j];
  1062.         }

  1063.         /** Get the derivative dS<sup>j</sup>/dk.
  1064.          * @param j j index
  1065.          * @return dS<sup>j</sup>/dk
  1066.          */
  1067.         public T getdSjdk(final int j) {
  1068.             return sj[2][j];
  1069.         }

  1070.         /** Get the derivative dS<sup>j</sup>/dh.
  1071.          * @param j j index
  1072.          * @return dS<sup>j</sup>/dh
  1073.          */
  1074.         public T getdSjdh(final int j) {
  1075.             return sj[3][j];
  1076.         }

  1077.         /** Get the derivative dS<sup>j</sup>/dα.
  1078.          * @param j j index
  1079.          * @return dS<sup>j</sup>/dα
  1080.          */
  1081.         public T getdSjdalpha(final int j) {
  1082.             return sj[4][j];
  1083.         }

  1084.         /** Get the derivative dS<sup>j</sup>/dβ.
  1085.          * @param j j index
  1086.          * @return dS<sup>j</sup>/dβ
  1087.          */
  1088.         public T getdSjdbeta(final int j) {
  1089.             return sj[5][j];
  1090.         }

  1091.         /** Get the derivative dS<sup>j</sup>/dγ.
  1092.          * @param j j index
  1093.          * @return dS<sup>j</sup>/dγ
  1094.          */
  1095.         public T getdSjdgamma(final int j) {
  1096.             return sj[6][j];
  1097.         }

  1098.         /** Get the coefficient C⁰<sub>,λ</sub>.
  1099.          * @return C⁰<sub>,λ</sub>
  1100.          */
  1101.         public T getC0Lambda() {
  1102.             return cjlambda[0];
  1103.         }

  1104.         /** Get the coefficient C<sup>j</sup><sub>,λ</sub>.
  1105.          * @param j j index
  1106.          * @return C<sup>j</sup><sub>,λ</sub>
  1107.          */
  1108.         public T getCjLambda(final int j) {
  1109.             if (j < 1 || j >= jMax) {
  1110.                 return zero;
  1111.             }
  1112.             return cjlambda[j];
  1113.         }

  1114.         /** Get the coefficient S<sup>j</sup><sub>,λ</sub>.
  1115.          * @param j j index
  1116.          * @return S<sup>j</sup><sub>,λ</sub>
  1117.          */
  1118.         public T getSjLambda(final int j) {
  1119.             if (j < 1 || j >= jMax) {
  1120.                 return zero;
  1121.             }
  1122.             return sjlambda[j];
  1123.         }
  1124.     }

  1125.     /** 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.
  1126.      *
  1127.      * <p>
  1128.      * Starting from Danielson 4.2-9,10,11 and taking into account that fact that: <br />
  1129.      * c = e / (1 + (1 - e²)<sup>1/2</sup>) = e / (1 + B) = e * b <br/>
  1130.      * the expression e<sup>-|j-s|</sup>*w<sub>j</sub><sup>n, s</sup>
  1131.      * can be written as: <br/ >
  1132.      * - for |s| > |j| <br />
  1133.      * e<sup>-|j-s|</sup>*w<sub>j</sub><sup>n, s</sup> =
  1134.      *          (((n + s)!(n - s)!)/((n + j)!(n - j)!)) *
  1135.      *          (-b)<sup>|j-s|</sup> *
  1136.      *          ((1 - c²)<sup>n-|s|</sup>/(1 + c²)<sup>n</sup>) *
  1137.      *          P<sub>n-|s|</sub><sup>|j-s|, |j+s|</sup>(χ) <br />
  1138.      * <br />
  1139.      * - for |s| <= |j| <br />
  1140.      * e<sup>-|j-s|</sup>*w<sub>j</sub><sup>n, s</sup> =
  1141.      *          (-b)<sup>|j-s|</sup> *
  1142.      *          ((1 - c²)<sup>n-|j|</sup>/(1 + c²)<sup>n</sup>) *
  1143.      *          P<sub>n-|j|</sub><sup>|j-s|, |j+s|</sup>(χ)
  1144.      * </p>
  1145.      *
  1146.      * @author Lucian Barbulescu
  1147.      */
  1148.     private static class WnsjEtomjmsCoefficient {

  1149.         /** The value c.
  1150.          * <p>
  1151.          *  c = e / (1 + (1 - e²)<sup>1/2</sup>) = e / (1 + B) = e * b <br/>
  1152.          * </p>
  1153.          *  */
  1154.         private final double c;

  1155.         /** db / dh. */
  1156.         private final double dbdh;

  1157.         /** db / dk. */
  1158.         private final double dbdk;

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

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

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

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

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

  1171.         /**
  1172.          * Standard constructor.
  1173.          * @param context container for attributes
  1174.          */
  1175.         WnsjEtomjmsCoefficient(final DSSTThirdBodyContext context) {

  1176.             final AuxiliaryElements auxiliaryElements = context.getAuxiliaryElements();

  1177.             //initialise fields
  1178.             c = auxiliaryElements.getEcc() * context.getb();
  1179.             final double c2 = c * c;

  1180.             //b² * χ
  1181.             final double b2Chi = context.getb() * context.getb() * context.getX();
  1182.             //Compute derivatives of b
  1183.             dbdh = auxiliaryElements.getH() * b2Chi;
  1184.             dbdk = auxiliaryElements.getK() * b2Chi;

  1185.             //Compute derivatives of c
  1186.             if (auxiliaryElements.getEcc() == 0.0) {
  1187.                 // we are at a perfectly circular orbit singularity here
  1188.                 // we arbitrarily consider the perigee is along the X axis,
  1189.                 // i.e cos(ω + Ω) = h/ecc 1 and sin(ω + Ω) = k/ecc = 0
  1190.                 dcdh = auxiliaryElements.getEcc() * dbdh + context.getb();
  1191.                 dcdk = auxiliaryElements.getEcc() * dbdk;
  1192.             } else {
  1193.                 dcdh = auxiliaryElements.getEcc() * dbdh + context.getb() * auxiliaryElements.getH() / auxiliaryElements.getEcc();
  1194.                 dcdk = auxiliaryElements.getEcc() * dbdk + context.getb() * auxiliaryElements.getK() / auxiliaryElements.getEcc();
  1195.             }

  1196.             //Compute the powers (1 - c²)<sup>n</sup> and (1 + c²)<sup>n</sup>
  1197.             omc2tn = new double[context.getMaxAR3Pow() + context.getMaxFreqF() + 2];
  1198.             opc2tn = new double[context.getMaxAR3Pow() + context.getMaxFreqF() + 2];
  1199.             final double omc2 = 1. - c2;
  1200.             final double opc2 = 1. + c2;
  1201.             omc2tn[0] = 1.;
  1202.             opc2tn[0] = 1.;
  1203.             for (int i = 1; i <= context.getMaxAR3Pow() + context.getMaxFreqF() + 1; i++) {
  1204.                 omc2tn[i] = omc2tn[i - 1] * omc2;
  1205.                 opc2tn[i] = opc2tn[i - 1] * opc2;
  1206.             }

  1207.             //Compute the powers of b
  1208.             btjms = new double[context.getMaxAR3Pow() + context.getMaxFreqF() + 1];
  1209.             btjms[0] = 1.;
  1210.             for (int i = 1; i <= context.getMaxAR3Pow() + context.getMaxFreqF(); i++) {
  1211.                 btjms[i] = btjms[i - 1] * context.getb();
  1212.             }
  1213.         }

  1214.         /** 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 />
  1215.          *
  1216.          * @param j j index
  1217.          * @param s s index
  1218.          * @param n n index
  1219.          * @param context container for attributes
  1220.          * @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
  1221.          */
  1222.         public double[] computeWjnsEmjmsAndDeriv(final int j, final int s, final int n, final DSSTThirdBodyContext context) {
  1223.             final double[] wjnsemjms = new double[] {0., 0., 0.};

  1224.             // |j|
  1225.             final int absJ = FastMath.abs(j);
  1226.             // |s|
  1227.             final int absS = FastMath.abs(s);
  1228.             // |j - s|
  1229.             final int absJmS = FastMath.abs(j - s);
  1230.             // |j + s|
  1231.             final int absJpS = FastMath.abs(j + s);

  1232.             //The lower index of P. Also the power of (1 - c²)
  1233.             final int l;
  1234.             // the factorial ratio coefficient or 1. if |s| <= |j|
  1235.             final double factCoef;
  1236.             if (absS > absJ) {
  1237.                 //factCoef = (fact[n + s] / fact[n + j]) * (fact[n - s] / fact[n - j]);
  1238.                 factCoef = (CombinatoricsUtils.factorialDouble(n + s) / CombinatoricsUtils.factorialDouble(n + j)) * (CombinatoricsUtils.factorialDouble(n - s) / CombinatoricsUtils.factorialDouble(n - j));
  1239.                 l = n - absS;
  1240.             } else {
  1241.                 factCoef = 1.;
  1242.                 l = n - absJ;
  1243.             }

  1244.             // (-1)<sup>|j-s|</sup>
  1245.             final double sign = absJmS % 2 != 0 ? -1. : 1.;
  1246.             //(1 - c²)<sup>n-|s|</sup> / (1 + c²)<sup>n</sup>
  1247.             final double coef1 = omc2tn[l] / opc2tn[n];
  1248.             //-b<sup>|j-s|</sup>
  1249.             final double coef2 = sign * btjms[absJmS];
  1250.             // P<sub>l</sub><sup>|j-s|, |j+s|</sup>(χ)
  1251.             final Gradient jac =
  1252.                     JacobiPolynomials.getValue(l, absJmS, absJpS, Gradient.variable(1, 0, context.getX()));

  1253.             // the derivative of coef1 by c
  1254.             final double dcoef1dc = -coef1 * 2. * c * (((double) n) / opc2tn[1] + ((double) l) / omc2tn[1]);
  1255.             // the derivative of coef1 by h
  1256.             final double dcoef1dh = dcoef1dc * dcdh;
  1257.             // the derivative of coef1 by k
  1258.             final double dcoef1dk = dcoef1dc * dcdk;

  1259.             // the derivative of coef2 by b
  1260.             final double dcoef2db = absJmS == 0 ? 0 : sign * (double) absJmS * btjms[absJmS - 1];
  1261.             // the derivative of coef2 by h
  1262.             final double dcoef2dh = dcoef2db * dbdh;
  1263.             // the derivative of coef2 by k
  1264.             final double dcoef2dk = dcoef2db * dbdk;

  1265.             // the jacobi polynomial value
  1266.             final double jacobi = jac.getValue();
  1267.             // the derivative of the Jacobi polynomial by h
  1268.             final double djacobidh = jac.getGradient()[0] * context.getHXXX();
  1269.             // the derivative of the Jacobi polynomial by k
  1270.             final double djacobidk = jac.getGradient()[0] * context.getKXXX();

  1271.             //group the above coefficients to limit the mathematical operations
  1272.             final double term1 = factCoef * coef1 * coef2;
  1273.             final double term2 = factCoef * coef1 * jacobi;
  1274.             final double term3 = factCoef * coef2 * jacobi;

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

  1279.             return wjnsemjms;
  1280.         }
  1281.     }

  1282.     /** 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.
  1283.     *
  1284.     * <p>
  1285.     * Starting from Danielson 4.2-9,10,11 and taking into account that fact that: <br />
  1286.     * c = e / (1 + (1 - e²)<sup>1/2</sup>) = e / (1 + B) = e * b <br/>
  1287.     * the expression e<sup>-|j-s|</sup>*w<sub>j</sub><sup>n, s</sup>
  1288.     * can be written as: <br/ >
  1289.     * - for |s| > |j| <br />
  1290.     * e<sup>-|j-s|</sup>*w<sub>j</sub><sup>n, s</sup> =
  1291.     *          (((n + s)!(n - s)!)/((n + j)!(n - j)!)) *
  1292.     *          (-b)<sup>|j-s|</sup> *
  1293.     *          ((1 - c²)<sup>n-|s|</sup>/(1 + c²)<sup>n</sup>) *
  1294.     *          P<sub>n-|s|</sub><sup>|j-s|, |j+s|</sup>(χ) <br />
  1295.     * <br />
  1296.     * - for |s| <= |j| <br />
  1297.     * e<sup>-|j-s|</sup>*w<sub>j</sub><sup>n, s</sup> =
  1298.     *          (-b)<sup>|j-s|</sup> *
  1299.     *          ((1 - c²)<sup>n-|j|</sup>/(1 + c²)<sup>n</sup>) *
  1300.     *          P<sub>n-|j|</sub><sup>|j-s|, |j+s|</sup>(χ)
  1301.     * </p>
  1302.     *
  1303.     * @author Lucian Barbulescu
  1304.     */
  1305.     private static class FieldWnsjEtomjmsCoefficient <T extends RealFieldElement<T>> {

  1306.         /** The value c.
  1307.          * <p>
  1308.          *  c = e / (1 + (1 - e²)<sup>1/2</sup>) = e / (1 + B) = e * b <br/>
  1309.          * </p>
  1310.          *  */
  1311.         private final T c;

  1312.         /** db / dh. */
  1313.         private final T dbdh;

  1314.         /** db / dk. */
  1315.         private final T dbdk;

  1316.         /** dc / dh = e * db/dh + b * de/dh. */
  1317.         private final T dcdh;

  1318.         /** dc / dk = e * db/dk + b * de/dk. */
  1319.         private final T dcdk;

  1320.         /** The values (1 - c²)<sup>n</sup>. <br />
  1321.          * The maximum possible value for the power is N + 1 */
  1322.         private final T[] omc2tn;

  1323.         /** The values (1 + c²)<sup>n</sup>. <br />
  1324.          * The maximum possible value for the power is N + 1 */
  1325.         private final T[] opc2tn;

  1326.         /** The values b<sup>|j-s|</sup>. */
  1327.         private final T[] btjms;

  1328.         /**
  1329.          * Standard constructor.
  1330.          * @param context container for attributes
  1331.          * @param field field used by default
  1332.          */
  1333.         FieldWnsjEtomjmsCoefficient(final FieldDSSTThirdBodyContext<T> context, final Field<T> field) {

  1334.             final FieldAuxiliaryElements<T> auxiliaryElements = context.getFieldAuxiliaryElements();

  1335.             //Zero
  1336.             final T zero = field.getZero();

  1337.             //initialise fields
  1338.             c = auxiliaryElements.getEcc().multiply(context.getb());
  1339.             final T c2 = c.multiply(c);

  1340.             //b² * χ
  1341.             final T b2Chi = context.getb().multiply(context.getb()).multiply(context.getX());
  1342.             //Compute derivatives of b
  1343.             dbdh = auxiliaryElements.getH().multiply(b2Chi);
  1344.             dbdk = auxiliaryElements.getK().multiply(b2Chi);

  1345.             //Compute derivatives of c
  1346.             if (auxiliaryElements.getEcc().getReal() == 0.0) {
  1347.                 // we are at a perfectly circular orbit singularity here
  1348.                 // we arbitrarily consider the perigee is along the X axis,
  1349.                 // i.e cos(ω + Ω) = h/ecc 1 and sin(ω + Ω) = k/ecc = 0
  1350.                 dcdh = auxiliaryElements.getEcc().multiply(dbdh).add(context.getb());
  1351.                 dcdk = auxiliaryElements.getEcc().multiply(dbdk);
  1352.             } else {
  1353.                 dcdh = auxiliaryElements.getEcc().multiply(dbdh).add(context.getb().multiply(auxiliaryElements.getH()).divide(auxiliaryElements.getEcc()));
  1354.                 dcdk = auxiliaryElements.getEcc().multiply(dbdk).add(context.getb().multiply(auxiliaryElements.getK()).divide(auxiliaryElements.getEcc()));
  1355.             }

  1356.             //Compute the powers (1 - c²)<sup>n</sup> and (1 + c²)<sup>n</sup>
  1357.             omc2tn = MathArrays.buildArray(field, context.getMaxAR3Pow() + context.getMaxFreqF() + 2);
  1358.             opc2tn = MathArrays.buildArray(field, context.getMaxAR3Pow() + context.getMaxFreqF() + 2);
  1359.             final T omc2 = c2.negate().add(1.);
  1360.             final T opc2 = c2.add(1.);
  1361.             omc2tn[0] = zero.add(1.);
  1362.             opc2tn[0] = zero.add(1.);
  1363.             for (int i = 1; i <= context.getMaxAR3Pow() + context.getMaxFreqF() + 1; i++) {
  1364.                 omc2tn[i] = omc2tn[i - 1].multiply(omc2);
  1365.                 opc2tn[i] = opc2tn[i - 1].multiply(opc2);
  1366.             }

  1367.             //Compute the powers of b
  1368.             btjms = MathArrays.buildArray(field, context.getMaxAR3Pow() + context.getMaxFreqF() + 1);
  1369.             btjms[0] = zero.add(1.);
  1370.             for (int i = 1; i <= context.getMaxAR3Pow() + context.getMaxFreqF(); i++) {
  1371.                 btjms[i] = btjms[i - 1].multiply(context.getb());
  1372.             }
  1373.         }

  1374.         /** 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 />
  1375.          *
  1376.          * @param j j index
  1377.          * @param s s index
  1378.          * @param n n index
  1379.          * @param context container for attributes
  1380.          * @param field field used by default
  1381.          * @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
  1382.          */
  1383.         public T[] computeWjnsEmjmsAndDeriv(final int j, final int s, final int n,
  1384.                                             final FieldDSSTThirdBodyContext<T> context,
  1385.                                             final Field<T> field) {
  1386.             //Zero
  1387.             final T zero = field.getZero();

  1388.             final T[] wjnsemjms = MathArrays.buildArray(field, 3);
  1389.             Arrays.fill(wjnsemjms, zero);

  1390.             // |j|
  1391.             final int absJ = FastMath.abs(j);
  1392.             // |s|
  1393.             final int absS = FastMath.abs(s);
  1394.             // |j - s|
  1395.             final int absJmS = FastMath.abs(j - s);
  1396.             // |j + s|
  1397.             final int absJpS = FastMath.abs(j + s);

  1398.             //The lower index of P. Also the power of (1 - c²)
  1399.             final int l;
  1400.             // the factorial ratio coefficient or 1. if |s| <= |j|
  1401.             final T factCoef;
  1402.             if (absS > absJ) {
  1403.                 //factCoef = (fact[n + s] / fact[n + j]) * (fact[n - s] / fact[n - j]);
  1404.                 factCoef = zero.add((CombinatoricsUtils.factorialDouble(n + s) / CombinatoricsUtils.factorialDouble(n + j)) * (CombinatoricsUtils.factorialDouble(n - s) / CombinatoricsUtils.factorialDouble(n - j)));
  1405.                 l = n - absS;
  1406.             } else {
  1407.                 factCoef = zero.add(1.);
  1408.                 l = n - absJ;
  1409.             }

  1410.             // (-1)<sup>|j-s|</sup>
  1411.             final T sign = absJmS % 2 != 0 ? zero.add(-1.) : zero.add(1.);
  1412.             //(1 - c²)<sup>n-|s|</sup> / (1 + c²)<sup>n</sup>
  1413.             final T coef1 = omc2tn[l].divide(opc2tn[n]);
  1414.             //-b<sup>|j-s|</sup>
  1415.             final T coef2 = btjms[absJmS].multiply(sign);
  1416.             // P<sub>l</sub><sup>|j-s|, |j+s|</sup>(χ)
  1417.             final FieldGradient<T> jac =
  1418.                     JacobiPolynomials.getValue(l, absJmS, absJpS, FieldGradient.variable(1, 0, context.getX()));

  1419.             // the derivative of coef1 by c
  1420.             final T dcoef1dc = coef1.negate().multiply(2.).multiply(c).multiply(opc2tn[1].reciprocal().multiply(n).add(omc2tn[1].reciprocal().multiply(l)));
  1421.             // the derivative of coef1 by h
  1422.             final T dcoef1dh = dcoef1dc.multiply(dcdh);
  1423.             // the derivative of coef1 by k
  1424.             final T dcoef1dk = dcoef1dc.multiply(dcdk);

  1425.             // the derivative of coef2 by b
  1426.             final T dcoef2db = absJmS == 0 ? zero : sign.multiply(absJmS).multiply(btjms[absJmS - 1]);
  1427.             // the derivative of coef2 by h
  1428.             final T dcoef2dh = dcoef2db.multiply(dbdh);
  1429.             // the derivative of coef2 by k
  1430.             final T dcoef2dk = dcoef2db.multiply(dbdk);

  1431.             // the jacobi polynomial value
  1432.             final T jacobi = jac.getValue();
  1433.             // the derivative of the Jacobi polynomial by h
  1434.             final T djacobidh = jac.getGradient()[0].multiply(context.getHXXX());
  1435.             // the derivative of the Jacobi polynomial by k
  1436.             final T djacobidk = jac.getGradient()[0].multiply(context.getKXXX());

  1437.             //group the above coefficients to limit the mathematical operations
  1438.             final T term1 = factCoef.multiply(coef1).multiply(coef2);
  1439.             final T term2 = factCoef.multiply(coef1).multiply(jacobi);
  1440.             final T term3 = factCoef.multiply(coef2).multiply(jacobi);

  1441.             //compute e<sup>-|j-s|</sup>*w<sub>j</sub><sup>n, s</sup> and its derivatives by k and h
  1442.             wjnsemjms[0] = term1.multiply(jacobi);
  1443.             wjnsemjms[1] = dcoef1dk.multiply(term3).add(dcoef2dk.multiply(term2)).add(djacobidk.multiply(term1));
  1444.             wjnsemjms[2] = dcoef1dh.multiply(term3).add(dcoef2dh.multiply(term2)).add(djacobidh.multiply(term1));

  1445.             return wjnsemjms;
  1446.         }
  1447.     }

  1448.     /** The G<sub>n,s</sub> coefficients and their derivatives.
  1449.      * <p>
  1450.      * See Danielson, 4.2-17
  1451.      *
  1452.      * @author Lucian Barbulescu
  1453.      */
  1454.     private class GnsCoefficients {

  1455.         /** Maximum value for n index. */
  1456.         private final int nMax;

  1457.         /** Maximum value for s index. */
  1458.         private final int sMax;

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

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

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

  1465.         /** Standard constructor.
  1466.          *
  1467.          * @param nMax maximum value for n indes
  1468.          * @param sMax maximum value for s index
  1469.          * @param context container for attributes
  1470.          */
  1471.         GnsCoefficients(final int nMax, final int sMax, final DSSTThirdBodyContext context) {
  1472.             this.nMax = nMax;
  1473.             this.sMax = sMax;

  1474.             final int rows    = nMax + 1;
  1475.             final int columns = sMax + 1;
  1476.             this.gns          = new double[rows][columns];
  1477.             this.dgnsda       = new double[rows][columns];
  1478.             this.dgnsdgamma   = new double[rows][columns];

  1479.             // Generate the coefficients
  1480.             generateCoefficients(context);
  1481.         }
  1482.         /**
  1483.          * Compute the coefficient G<sub>n,s</sub> and its derivatives.
  1484.          * <p>
  1485.          * Only the derivatives by a and γ are computed as all others are 0
  1486.          * </p>
  1487.          * @param context container for attributes
  1488.          */
  1489.         private void generateCoefficients(final DSSTThirdBodyContext context) {

  1490.             final AuxiliaryElements auxiliaryElements = context.getAuxiliaryElements();

  1491.             for (int s = 0; s <= sMax; s++) {
  1492.                 // The n index is always at least the maximum between 2 and s
  1493.                 final int minN = FastMath.max(2, s);
  1494.                 for (int n = minN; n <= nMax; n++) {
  1495.                     // compute the coefficients only if (n - s) % 2 == 0
  1496.                     if ( (n - s) % 2 == 0 ) {
  1497.                         // Kronecker symbol (2 - delta(0,s))
  1498.                         final double delta0s = (s == 0) ? 1. : 2.;
  1499.                         final double vns   = Vns.get(new NSKey(n, s));
  1500.                         final double coef0 = delta0s * context.getAoR3Pow()[n] * vns * context.getMuoR3();
  1501.                         final double coef1 = coef0 * context.getQns()[n][s];
  1502.                         // dQns/dGamma = Q(n, s + 1) from Equation 3.1-(8)
  1503.                         // for n = s, Q(n, n + 1) = 0. (Cefola & Broucke, 1975)
  1504.                         final double dqns = (n == s) ? 0. : context.getQns()[n][s + 1];

  1505.                         //Compute the coefficient and its derivatives.
  1506.                         this.gns[n][s] = coef1;
  1507.                         this.dgnsda[n][s] = coef1 * n / auxiliaryElements.getSma();
  1508.                         this.dgnsdgamma[n][s] = coef0 * dqns;
  1509.                     } else {
  1510.                         // the coefficient and its derivatives is 0
  1511.                         this.gns[n][s] = 0.;
  1512.                         this.dgnsda[n][s] = 0.;
  1513.                         this.dgnsdgamma[n][s] = 0.;
  1514.                     }
  1515.                 }
  1516.             }
  1517.         }

  1518.         /** Get the coefficient G<sub>n,s</sub>.
  1519.          *
  1520.          * @param n n index
  1521.          * @param s s index
  1522.          * @return the coefficient G<sub>n,s</sub>
  1523.          */
  1524.         public double getGns(final int n, final int s) {
  1525.             return this.gns[n][s];
  1526.         }

  1527.         /** Get the derivative dG<sub>n,s</sub> / da.
  1528.          *
  1529.          * @param n n index
  1530.          * @param s s index
  1531.          * @return the derivative dG<sub>n,s</sub> / da
  1532.          */
  1533.         public double getdGnsda(final int n, final int s) {
  1534.             return this.dgnsda[n][s];
  1535.         }

  1536.         /** Get the derivative dG<sub>n,s</sub> / dγ.
  1537.          *
  1538.          * @param n n index
  1539.          * @param s s index
  1540.          * @return the derivative dG<sub>n,s</sub> / dγ
  1541.          */
  1542.         public double getdGnsdgamma(final int n, final int s) {
  1543.             return this.dgnsdgamma[n][s];
  1544.         }
  1545.     }

  1546.     /** The G<sub>n,s</sub> coefficients and their derivatives.
  1547.      * <p>
  1548.      * See Danielson, 4.2-17
  1549.      *
  1550.      * @author Lucian Barbulescu
  1551.      */
  1552.     private class FieldGnsCoefficients  <T extends RealFieldElement<T>> {

  1553.         /** Maximum value for n index. */
  1554.         private final int nMax;

  1555.         /** Maximum value for s index. */
  1556.         private final int sMax;

  1557.         /** The coefficients G<sub>n,s</sub>. */
  1558.         private final T gns[][];

  1559.         /** The derivatives of the coefficients G<sub>n,s</sub> by a. */
  1560.         private final T dgnsda[][];

  1561.         /** The derivatives of the coefficients G<sub>n,s</sub> by γ. */
  1562.         private final T dgnsdgamma[][];

  1563.         /** Standard constructor.
  1564.          *
  1565.          * @param nMax maximum value for n indes
  1566.          * @param sMax maximum value for s index
  1567.          * @param context container for attributes
  1568.          * @param field field used by default
  1569.          */
  1570.         FieldGnsCoefficients(final int nMax, final int sMax,
  1571.                              final FieldDSSTThirdBodyContext<T> context,
  1572.                              final Field<T> field) {
  1573.             this.nMax = nMax;
  1574.             this.sMax = sMax;

  1575.             final int rows    = nMax + 1;
  1576.             final int columns = sMax + 1;
  1577.             this.gns          = MathArrays.buildArray(field, rows, columns);
  1578.             this.dgnsda       = MathArrays.buildArray(field, rows, columns);
  1579.             this.dgnsdgamma   = MathArrays.buildArray(field, rows, columns);

  1580.             // Generate the coefficients
  1581.             generateCoefficients(context, field);
  1582.         }
  1583.         /**
  1584.          * Compute the coefficient G<sub>n,s</sub> and its derivatives.
  1585.          * <p>
  1586.          * Only the derivatives by a and γ are computed as all others are 0
  1587.          * </p>
  1588.          * @param context container for attributes
  1589.          * @param field field used by default
  1590.          */
  1591.         private void generateCoefficients(final FieldDSSTThirdBodyContext<T> context,
  1592.                                           final Field<T> field) {

  1593.             //Zero
  1594.             final T zero = field.getZero();

  1595.             final FieldAuxiliaryElements<T> auxiliaryElements = context.getFieldAuxiliaryElements();

  1596.             for (int s = 0; s <= sMax; s++) {
  1597.                 // The n index is always at least the maximum between 2 and s
  1598.                 final int minN = FastMath.max(2, s);
  1599.                 for (int n = minN; n <= nMax; n++) {
  1600.                     // compute the coefficients only if (n - s) % 2 == 0
  1601.                     if ( (n - s) % 2 == 0 ) {
  1602.                         // Kronecker symbol (2 - delta(0,s))
  1603.                         final T delta0s = (s == 0) ? zero.add(1.) : zero.add(2.);
  1604.                         final double vns = Vns.get(new NSKey(n, s));
  1605.                         final T coef0 = context.getAoR3Pow()[n].multiply(vns).multiply(context.getMuoR3()).multiply(delta0s);
  1606.                         final T coef1 = coef0.multiply(context.getQns()[n][s]);
  1607.                         // dQns/dGamma = Q(n, s + 1) from Equation 3.1-(8)
  1608.                         // for n = s, Q(n, n + 1) = 0. (Cefola & Broucke, 1975)
  1609.                         final T dqns = (n == s) ? zero : context.getQns()[n][s + 1];

  1610.                         //Compute the coefficient and its derivatives.
  1611.                         this.gns[n][s] = coef1;
  1612.                         this.dgnsda[n][s] = coef1.multiply(n).divide(auxiliaryElements.getSma());
  1613.                         this.dgnsdgamma[n][s] = coef0.multiply(dqns);
  1614.                     } else {
  1615.                         // the coefficient and its derivatives is 0
  1616.                         this.gns[n][s] = zero;
  1617.                         this.dgnsda[n][s] = zero;
  1618.                         this.dgnsdgamma[n][s] = zero;
  1619.                     }
  1620.                 }
  1621.             }
  1622.         }

  1623.         /** Get the coefficient G<sub>n,s</sub>.
  1624.          *
  1625.          * @param n n index
  1626.          * @param s s index
  1627.          * @return the coefficient G<sub>n,s</sub>
  1628.          */
  1629.         public T getGns(final int n, final int s) {
  1630.             return this.gns[n][s];
  1631.         }

  1632.         /** Get the derivative dG<sub>n,s</sub> / da.
  1633.          *
  1634.          * @param n n index
  1635.          * @param s s index
  1636.          * @return the derivative dG<sub>n,s</sub> / da
  1637.          */
  1638.         public T getdGnsda(final int n, final int s) {
  1639.             return this.dgnsda[n][s];
  1640.         }

  1641.         /** Get the derivative dG<sub>n,s</sub> / dγ.
  1642.          *
  1643.          * @param n n index
  1644.          * @param s s index
  1645.          * @return the derivative dG<sub>n,s</sub> / dγ
  1646.          */
  1647.         public T getdGnsdgamma(final int n, final int s) {
  1648.             return this.dgnsdgamma[n][s];
  1649.         }
  1650.     }

  1651.     /** This class computes the terms containing the coefficients C<sub>j</sub> and S<sub>j</sub> of (α, β) or (k, h).
  1652.      *
  1653.      * <p>
  1654.      * The following terms and their derivatives by k, h, alpha and beta are considered: <br/ >
  1655.      * - 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 />
  1656.      * - C<sub>s</sub>(α, β) * S<sub>j+s</sub>(k, h) - S<sub>s</sub>(α, β) * C<sub>j+s</sub>(k, h) <br />
  1657.      * - 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 />
  1658.      * - C<sub>s</sub>(α, β) * C<sub>j+s</sub>(k, h) + S<sub>s</sub>(α, β) * S<sub>j+s</sub>(k, h) <br />
  1659.      * 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 />
  1660.      * See the CS Mathematical report $3.5.3.2 for more details
  1661.      * </p>
  1662.      * @author Lucian Barbulescu
  1663.      */
  1664.     private static class CjSjAlphaBetaKH {

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

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

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

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

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

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

  1681.         /**
  1682.          * Standard constructor.
  1683.          * @param context container for attributes
  1684.          */
  1685.         CjSjAlphaBetaKH(final DSSTThirdBodyContext context) {

  1686.             final AuxiliaryElements auxiliaryElements = context.getAuxiliaryElements();

  1687.             cjsjkh = new CjSjCoefficient(auxiliaryElements.getK(), auxiliaryElements.getH());
  1688.             cjsjalbe = new CjSjCoefficient(context.getAlpha(), context.getBeta());

  1689.             coefAandDeriv = new double[5];
  1690.             coefBandDeriv = new double[5];
  1691.             coefDandDeriv = new double[5];
  1692.             coefEandDeriv = new double[5];
  1693.         }

  1694.         /** Compute the coefficients and their derivatives for a given (j,s) pair.
  1695.          * @param j j index
  1696.          * @param s s index
  1697.          */
  1698.         public void computeCoefficients(final int j, final int s) {
  1699.             // sign of j-s
  1700.             final int sign = j < s ? -1 : 1;

  1701.             //|j-s|
  1702.             final int absJmS = FastMath.abs(j - s);

  1703.             //j+s
  1704.             final int jps = j + s;

  1705.             //Compute the coefficient A and its derivatives
  1706.             coefAandDeriv[0] = sign * cjsjalbe.getCj(s) * cjsjkh.getSj(absJmS) + cjsjalbe.getSj(s) * cjsjkh.getCj(absJmS);
  1707.             coefAandDeriv[1] = sign * cjsjalbe.getCj(s) * cjsjkh.getDsjDk(absJmS) + cjsjalbe.getSj(s) * cjsjkh.getDcjDk(absJmS);
  1708.             coefAandDeriv[2] = sign * cjsjalbe.getCj(s) * cjsjkh.getDsjDh(absJmS) + cjsjalbe.getSj(s) * cjsjkh.getDcjDh(absJmS);
  1709.             coefAandDeriv[3] = sign * cjsjalbe.getDcjDk(s) * cjsjkh.getSj(absJmS) + cjsjalbe.getDsjDk(s) * cjsjkh.getCj(absJmS);
  1710.             coefAandDeriv[4] = sign * cjsjalbe.getDcjDh(s) * cjsjkh.getSj(absJmS) + cjsjalbe.getDsjDh(s) * cjsjkh.getCj(absJmS);

  1711.             //Compute the coefficient B and its derivatives
  1712.             coefBandDeriv[0] = cjsjalbe.getCj(s) * cjsjkh.getSj(jps) - cjsjalbe.getSj(s) * cjsjkh.getCj(jps);
  1713.             coefBandDeriv[1] = cjsjalbe.getCj(s) * cjsjkh.getDsjDk(jps) - cjsjalbe.getSj(s) * cjsjkh.getDcjDk(jps);
  1714.             coefBandDeriv[2] = cjsjalbe.getCj(s) * cjsjkh.getDsjDh(jps) - cjsjalbe.getSj(s) * cjsjkh.getDcjDh(jps);
  1715.             coefBandDeriv[3] = cjsjalbe.getDcjDk(s) * cjsjkh.getSj(jps) - cjsjalbe.getDsjDk(s) * cjsjkh.getCj(jps);
  1716.             coefBandDeriv[4] = cjsjalbe.getDcjDh(s) * cjsjkh.getSj(jps) - cjsjalbe.getDsjDh(s) * cjsjkh.getCj(jps);

  1717.             //Compute the coefficient D and its derivatives
  1718.             coefDandDeriv[0] = cjsjalbe.getCj(s) * cjsjkh.getCj(absJmS) - sign * cjsjalbe.getSj(s) * cjsjkh.getSj(absJmS);
  1719.             coefDandDeriv[1] = cjsjalbe.getCj(s) * cjsjkh.getDcjDk(absJmS) - sign * cjsjalbe.getSj(s) * cjsjkh.getDsjDk(absJmS);
  1720.             coefDandDeriv[2] = cjsjalbe.getCj(s) * cjsjkh.getDcjDh(absJmS) - sign * cjsjalbe.getSj(s) * cjsjkh.getDsjDh(absJmS);
  1721.             coefDandDeriv[3] = cjsjalbe.getDcjDk(s) * cjsjkh.getCj(absJmS) - sign * cjsjalbe.getDsjDk(s) * cjsjkh.getSj(absJmS);
  1722.             coefDandDeriv[4] = cjsjalbe.getDcjDh(s) * cjsjkh.getCj(absJmS) - sign * cjsjalbe.getDsjDh(s) * cjsjkh.getSj(absJmS);

  1723.             //Compute the coefficient E and its derivatives
  1724.             coefEandDeriv[0] = cjsjalbe.getCj(s) * cjsjkh.getCj(jps) + cjsjalbe.getSj(s) * cjsjkh.getSj(jps);
  1725.             coefEandDeriv[1] = cjsjalbe.getCj(s) * cjsjkh.getDcjDk(jps) + cjsjalbe.getSj(s) * cjsjkh.getDsjDk(jps);
  1726.             coefEandDeriv[2] = cjsjalbe.getCj(s) * cjsjkh.getDcjDh(jps) + cjsjalbe.getSj(s) * cjsjkh.getDsjDh(jps);
  1727.             coefEandDeriv[3] = cjsjalbe.getDcjDk(s) * cjsjkh.getCj(jps) + cjsjalbe.getDsjDk(s) * cjsjkh.getSj(jps);
  1728.             coefEandDeriv[4] = cjsjalbe.getDcjDh(s) * cjsjkh.getCj(jps) + cjsjalbe.getDsjDh(s) * cjsjkh.getSj(jps);
  1729.         }

  1730.         /** Get the value of coefficient A<sub>j,s</sub>.
  1731.          *
  1732.          * @return the coefficient A<sub>j,s</sub>
  1733.          */
  1734.         public double getCoefA() {
  1735.             return coefAandDeriv[0];
  1736.         }

  1737.         /** Get the value of coefficient dA<sub>j,s</sub>/dk.
  1738.          *
  1739.          * @return the coefficient dA<sub>j,s</sub>/dk
  1740.          */
  1741.         public double getdCoefAdk() {
  1742.             return coefAandDeriv[1];
  1743.         }

  1744.         /** Get the value of coefficient dA<sub>j,s</sub>/dh.
  1745.          *
  1746.          * @return the coefficient dA<sub>j,s</sub>/dh
  1747.          */
  1748.         public double getdCoefAdh() {
  1749.             return coefAandDeriv[2];
  1750.         }

  1751.         /** Get the value of coefficient dA<sub>j,s</sub>/dα.
  1752.          *
  1753.          * @return the coefficient dA<sub>j,s</sub>/dα
  1754.          */
  1755.         public double getdCoefAdalpha() {
  1756.             return coefAandDeriv[3];
  1757.         }

  1758.         /** Get the value of coefficient dA<sub>j,s</sub>/dβ.
  1759.          *
  1760.          * @return the coefficient dA<sub>j,s</sub>/dβ
  1761.          */
  1762.         public double getdCoefAdbeta() {
  1763.             return coefAandDeriv[4];
  1764.         }

  1765.         /** Get the value of coefficient B<sub>j,s</sub>.
  1766.          *
  1767.          * @return the coefficient B<sub>j,s</sub>
  1768.          */
  1769.         public double getCoefB() {
  1770.             return coefBandDeriv[0];
  1771.         }

  1772.         /** Get the value of coefficient dB<sub>j,s</sub>/dk.
  1773.          *
  1774.          * @return the coefficient dB<sub>j,s</sub>/dk
  1775.          */
  1776.         public double getdCoefBdk() {
  1777.             return coefBandDeriv[1];
  1778.         }

  1779.         /** Get the value of coefficient dB<sub>j,s</sub>/dh.
  1780.          *
  1781.          * @return the coefficient dB<sub>j,s</sub>/dh
  1782.          */
  1783.         public double getdCoefBdh() {
  1784.             return coefBandDeriv[2];
  1785.         }

  1786.         /** Get the value of coefficient dB<sub>j,s</sub>/dα.
  1787.          *
  1788.          * @return the coefficient dB<sub>j,s</sub>/dα
  1789.          */
  1790.         public double getdCoefBdalpha() {
  1791.             return coefBandDeriv[3];
  1792.         }

  1793.         /** Get the value of coefficient dB<sub>j,s</sub>/dβ.
  1794.          *
  1795.          * @return the coefficient dB<sub>j,s</sub>/dβ
  1796.          */
  1797.         public double getdCoefBdbeta() {
  1798.             return coefBandDeriv[4];
  1799.         }

  1800.         /** Get the value of coefficient D<sub>j,s</sub>.
  1801.          *
  1802.          * @return the coefficient D<sub>j,s</sub>
  1803.          */
  1804.         public double getCoefD() {
  1805.             return coefDandDeriv[0];
  1806.         }

  1807.         /** Get the value of coefficient dD<sub>j,s</sub>/dk.
  1808.          *
  1809.          * @return the coefficient dD<sub>j,s</sub>/dk
  1810.          */
  1811.         public double getdCoefDdk() {
  1812.             return coefDandDeriv[1];
  1813.         }

  1814.         /** Get the value of coefficient dD<sub>j,s</sub>/dh.
  1815.          *
  1816.          * @return the coefficient dD<sub>j,s</sub>/dh
  1817.          */
  1818.         public double getdCoefDdh() {
  1819.             return coefDandDeriv[2];
  1820.         }

  1821.         /** Get the value of coefficient dD<sub>j,s</sub>/dα.
  1822.          *
  1823.          * @return the coefficient dD<sub>j,s</sub>/dα
  1824.          */
  1825.         public double getdCoefDdalpha() {
  1826.             return coefDandDeriv[3];
  1827.         }

  1828.         /** Get the value of coefficient dD<sub>j,s</sub>/dβ.
  1829.          *
  1830.          * @return the coefficient dD<sub>j,s</sub>/dβ
  1831.          */
  1832.         public double getdCoefDdbeta() {
  1833.             return coefDandDeriv[4];
  1834.         }

  1835.         /** Get the value of coefficient E<sub>j,s</sub>.
  1836.          *
  1837.          * @return the coefficient E<sub>j,s</sub>
  1838.          */
  1839.         public double getCoefE() {
  1840.             return coefEandDeriv[0];
  1841.         }

  1842.         /** Get the value of coefficient dE<sub>j,s</sub>/dk.
  1843.          *
  1844.          * @return the coefficient dE<sub>j,s</sub>/dk
  1845.          */
  1846.         public double getdCoefEdk() {
  1847.             return coefEandDeriv[1];
  1848.         }

  1849.         /** Get the value of coefficient dE<sub>j,s</sub>/dh.
  1850.          *
  1851.          * @return the coefficient dE<sub>j,s</sub>/dh
  1852.          */
  1853.         public double getdCoefEdh() {
  1854.             return coefEandDeriv[2];
  1855.         }

  1856.         /** Get the value of coefficient dE<sub>j,s</sub>/dα.
  1857.          *
  1858.          * @return the coefficient dE<sub>j,s</sub>/dα
  1859.          */
  1860.         public double getdCoefEdalpha() {
  1861.             return coefEandDeriv[3];
  1862.         }

  1863.         /** Get the value of coefficient dE<sub>j,s</sub>/dβ.
  1864.          *
  1865.          * @return the coefficient dE<sub>j,s</sub>/dβ
  1866.          */
  1867.         public double getdCoefEdbeta() {
  1868.             return coefEandDeriv[4];
  1869.         }
  1870.     }

  1871.      /** This class computes the terms containing the coefficients C<sub>j</sub> and S<sub>j</sub> of (α, β) or (k, h).
  1872.      *
  1873.      * <p>
  1874.      * The following terms and their derivatives by k, h, alpha and beta are considered: <br/ >
  1875.      * - 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 />
  1876.      * - C<sub>s</sub>(α, β) * S<sub>j+s</sub>(k, h) - S<sub>s</sub>(α, β) * C<sub>j+s</sub>(k, h) <br />
  1877.      * - 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 />
  1878.      * - C<sub>s</sub>(α, β) * C<sub>j+s</sub>(k, h) + S<sub>s</sub>(α, β) * S<sub>j+s</sub>(k, h) <br />
  1879.      * 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 />
  1880.      * See the CS Mathematical report $3.5.3.2 for more details
  1881.      * </p>
  1882.      * @author Lucian Barbulescu
  1883.      */
  1884.     private static class FieldCjSjAlphaBetaKH <T extends RealFieldElement<T>> {

  1885.         /** The C<sub>j</sub>(k, h) and the S<sub>j</sub>(k, h) series. */
  1886.         private final FieldCjSjCoefficient<T> cjsjkh;

  1887.         /** The C<sub>j</sub>(α, β) and the S<sub>j</sub>(α, β) series. */
  1888.         private final FieldCjSjCoefficient<T> cjsjalbe;

  1889.         /** 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)
  1890.          * and its derivative by k, h, α and β. */
  1891.         private final T coefAandDeriv[];

  1892.         /** The coeficient C<sub>s</sub>(α, β) * S<sub>j+s</sub>(k, h) - S<sub>s</sub>(α, β) * C<sub>j+s</sub>(k, h)
  1893.          * and its derivative by k, h, α and β. */
  1894.         private final T coefBandDeriv[];

  1895.         /** 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)
  1896.          * and its derivative by k, h, α and β. */
  1897.         private final T coefDandDeriv[];

  1898.         /** The coeficient C<sub>s</sub>(α, β) * C<sub>j+s</sub>(k, h) + S<sub>s</sub>(α, β) * S<sub>j+s</sub>(k, h)
  1899.          * and its derivative by k, h, α and β. */
  1900.         private final T coefEandDeriv[];

  1901.         /**
  1902.          * Standard constructor.
  1903.          * @param context container for attributes
  1904.          * @param field field used by default
  1905.          */
  1906.         FieldCjSjAlphaBetaKH(final FieldDSSTThirdBodyContext<T> context, final Field<T> field) {

  1907.             final FieldAuxiliaryElements<T> auxiliaryElements = context.getFieldAuxiliaryElements();

  1908.             cjsjkh   = new FieldCjSjCoefficient<>(auxiliaryElements.getK(), auxiliaryElements.getH(), field);
  1909.             cjsjalbe = new FieldCjSjCoefficient<>(context.getAlpha(), context.getBeta(), field);

  1910.             coefAandDeriv = MathArrays.buildArray(field, 5);
  1911.             coefBandDeriv = MathArrays.buildArray(field, 5);
  1912.             coefDandDeriv = MathArrays.buildArray(field, 5);
  1913.             coefEandDeriv = MathArrays.buildArray(field, 5);
  1914.         }

  1915.         /** Compute the coefficients and their derivatives for a given (j,s) pair.
  1916.          * @param j j index
  1917.          * @param s s index
  1918.          */
  1919.         public void computeCoefficients(final int j, final int s) {
  1920.             // sign of j-s
  1921.             final int sign = j < s ? -1 : 1;

  1922.             //|j-s|
  1923.             final int absJmS = FastMath.abs(j - s);

  1924.             //j+s
  1925.             final int jps = j + s;

  1926.             //Compute the coefficient A and its derivatives
  1927.             coefAandDeriv[0] = cjsjalbe.getCj(s).multiply(cjsjkh.getSj(absJmS)).multiply(sign).add(cjsjalbe.getSj(s).multiply(cjsjkh.getCj(absJmS)));
  1928.             coefAandDeriv[1] = cjsjalbe.getCj(s).multiply(cjsjkh.getDsjDk(absJmS)).multiply(sign).add(cjsjalbe.getSj(s).multiply(cjsjkh.getDcjDk(absJmS)));
  1929.             coefAandDeriv[2] = cjsjalbe.getCj(s).multiply(cjsjkh.getDsjDh(absJmS)).multiply(sign).add(cjsjalbe.getSj(s).multiply(cjsjkh.getDcjDh(absJmS)));
  1930.             coefAandDeriv[3] = cjsjalbe.getDcjDk(s).multiply(cjsjkh.getSj(absJmS)).multiply(sign).add(cjsjalbe.getDsjDk(s).multiply(cjsjkh.getCj(absJmS)));
  1931.             coefAandDeriv[4] = cjsjalbe.getDcjDh(s).multiply(cjsjkh.getSj(absJmS)).multiply(sign).add(cjsjalbe.getDsjDh(s).multiply(cjsjkh.getCj(absJmS)));

  1932.             //Compute the coefficient B and its derivatives
  1933.             coefBandDeriv[0] = cjsjalbe.getCj(s).multiply(cjsjkh.getSj(jps)).subtract(cjsjalbe.getSj(s).multiply(cjsjkh.getCj(jps)));
  1934.             coefBandDeriv[1] = cjsjalbe.getCj(s).multiply(cjsjkh.getDsjDk(jps)).subtract(cjsjalbe.getSj(s).multiply(cjsjkh.getDcjDk(jps)));
  1935.             coefBandDeriv[2] = cjsjalbe.getCj(s).multiply(cjsjkh.getDsjDh(jps)).subtract(cjsjalbe.getSj(s).multiply(cjsjkh.getDcjDh(jps)));
  1936.             coefBandDeriv[3] = cjsjalbe.getDcjDk(s).multiply(cjsjkh.getSj(jps)).subtract(cjsjalbe.getDsjDk(s).multiply(cjsjkh.getCj(jps)));
  1937.             coefBandDeriv[4] = cjsjalbe.getDcjDh(s).multiply(cjsjkh.getSj(jps)).subtract(cjsjalbe.getDsjDh(s).multiply(cjsjkh.getCj(jps)));

  1938.             //Compute the coefficient D and its derivatives
  1939.             coefDandDeriv[0] = cjsjalbe.getCj(s).multiply(cjsjkh.getCj(absJmS)).subtract(cjsjalbe.getSj(s).multiply(cjsjkh.getSj(absJmS)).multiply(sign));
  1940.             coefDandDeriv[1] = cjsjalbe.getCj(s).multiply(cjsjkh.getDcjDk(absJmS)).subtract(cjsjalbe.getSj(s).multiply(cjsjkh.getDsjDk(absJmS)).multiply(sign));
  1941.             coefDandDeriv[2] = cjsjalbe.getCj(s).multiply(cjsjkh.getDcjDh(absJmS)).subtract(cjsjalbe.getSj(s).multiply(cjsjkh.getDsjDh(absJmS)).multiply(sign));
  1942.             coefDandDeriv[3] = cjsjalbe.getDcjDk(s).multiply(cjsjkh.getCj(absJmS)).subtract(cjsjalbe.getDsjDk(s).multiply(cjsjkh.getSj(absJmS)).multiply(sign));
  1943.             coefDandDeriv[4] = cjsjalbe.getDcjDh(s).multiply(cjsjkh.getCj(absJmS)).subtract(cjsjalbe.getDsjDh(s).multiply(cjsjkh.getSj(absJmS)).multiply(sign));

  1944.             //Compute the coefficient E and its derivatives
  1945.             coefEandDeriv[0] = cjsjalbe.getCj(s).multiply(cjsjkh.getCj(jps)).add(cjsjalbe.getSj(s).multiply(cjsjkh.getSj(jps)));
  1946.             coefEandDeriv[1] = cjsjalbe.getCj(s).multiply(cjsjkh.getDcjDk(jps)).add(cjsjalbe.getSj(s).multiply(cjsjkh.getDsjDk(jps)));
  1947.             coefEandDeriv[2] = cjsjalbe.getCj(s).multiply(cjsjkh.getDcjDh(jps)).add(cjsjalbe.getSj(s).multiply(cjsjkh.getDsjDh(jps)));
  1948.             coefEandDeriv[3] = cjsjalbe.getDcjDk(s).multiply(cjsjkh.getCj(jps)).add(cjsjalbe.getDsjDk(s).multiply(cjsjkh.getSj(jps)));
  1949.             coefEandDeriv[4] = cjsjalbe.getDcjDh(s).multiply(cjsjkh.getCj(jps)).add(cjsjalbe.getDsjDh(s).multiply(cjsjkh.getSj(jps)));
  1950.         }

  1951.         /** Get the value of coefficient A<sub>j,s</sub>.
  1952.          *
  1953.          * @return the coefficient A<sub>j,s</sub>
  1954.          */
  1955.         public T getCoefA() {
  1956.             return coefAandDeriv[0];
  1957.         }

  1958.         /** Get the value of coefficient dA<sub>j,s</sub>/dk.
  1959.          *
  1960.          * @return the coefficient dA<sub>j,s</sub>/dk
  1961.          */
  1962.         public T getdCoefAdk() {
  1963.             return coefAandDeriv[1];
  1964.         }

  1965.         /** Get the value of coefficient dA<sub>j,s</sub>/dh.
  1966.          *
  1967.          * @return the coefficient dA<sub>j,s</sub>/dh
  1968.          */
  1969.         public T getdCoefAdh() {
  1970.             return coefAandDeriv[2];
  1971.         }

  1972.         /** Get the value of coefficient dA<sub>j,s</sub>/dα.
  1973.          *
  1974.          * @return the coefficient dA<sub>j,s</sub>/dα
  1975.          */
  1976.         public T getdCoefAdalpha() {
  1977.             return coefAandDeriv[3];
  1978.         }

  1979.         /** Get the value of coefficient dA<sub>j,s</sub>/dβ.
  1980.          *
  1981.          * @return the coefficient dA<sub>j,s</sub>/dβ
  1982.          */
  1983.         public T getdCoefAdbeta() {
  1984.             return coefAandDeriv[4];
  1985.         }

  1986.        /** Get the value of coefficient B<sub>j,s</sub>.
  1987.         *
  1988.         * @return the coefficient B<sub>j,s</sub>
  1989.         */
  1990.         public T getCoefB() {
  1991.             return coefBandDeriv[0];
  1992.         }

  1993.         /** Get the value of coefficient dB<sub>j,s</sub>/dk.
  1994.          *
  1995.          * @return the coefficient dB<sub>j,s</sub>/dk
  1996.          */
  1997.         public T getdCoefBdk() {
  1998.             return coefBandDeriv[1];
  1999.         }

  2000.         /** Get the value of coefficient dB<sub>j,s</sub>/dh.
  2001.          *
  2002.          * @return the coefficient dB<sub>j,s</sub>/dh
  2003.          */
  2004.         public T getdCoefBdh() {
  2005.             return coefBandDeriv[2];
  2006.         }

  2007.         /** Get the value of coefficient dB<sub>j,s</sub>/dα.
  2008.          *
  2009.          * @return the coefficient dB<sub>j,s</sub>/dα
  2010.          */
  2011.         public T getdCoefBdalpha() {
  2012.             return coefBandDeriv[3];
  2013.         }

  2014.         /** Get the value of coefficient dB<sub>j,s</sub>/dβ.
  2015.          *
  2016.          * @return the coefficient dB<sub>j,s</sub>/dβ
  2017.          */
  2018.         public T getdCoefBdbeta() {
  2019.             return coefBandDeriv[4];
  2020.         }

  2021.         /** Get the value of coefficient D<sub>j,s</sub>.
  2022.          *
  2023.          * @return the coefficient D<sub>j,s</sub>
  2024.          */
  2025.         public T getCoefD() {
  2026.             return coefDandDeriv[0];
  2027.         }

  2028.         /** Get the value of coefficient dD<sub>j,s</sub>/dk.
  2029.          *
  2030.          * @return the coefficient dD<sub>j,s</sub>/dk
  2031.          */
  2032.         public T getdCoefDdk() {
  2033.             return coefDandDeriv[1];
  2034.         }

  2035.         /** Get the value of coefficient dD<sub>j,s</sub>/dh.
  2036.          *
  2037.          * @return the coefficient dD<sub>j,s</sub>/dh
  2038.          */
  2039.         public T getdCoefDdh() {
  2040.             return coefDandDeriv[2];
  2041.         }

  2042.         /** Get the value of coefficient dD<sub>j,s</sub>/dα.
  2043.          *
  2044.          * @return the coefficient dD<sub>j,s</sub>/dα
  2045.          */
  2046.         public T getdCoefDdalpha() {
  2047.             return coefDandDeriv[3];
  2048.         }

  2049.         /** Get the value of coefficient dD<sub>j,s</sub>/dβ.
  2050.          *
  2051.          * @return the coefficient dD<sub>j,s</sub>/dβ
  2052.          */
  2053.         public T getdCoefDdbeta() {
  2054.             return coefDandDeriv[4];
  2055.         }

  2056.         /** Get the value of coefficient E<sub>j,s</sub>.
  2057.          *
  2058.          * @return the coefficient E<sub>j,s</sub>
  2059.          */
  2060.         public T getCoefE() {
  2061.             return coefEandDeriv[0];
  2062.         }

  2063.         /** Get the value of coefficient dE<sub>j,s</sub>/dk.
  2064.          *
  2065.          * @return the coefficient dE<sub>j,s</sub>/dk
  2066.          */
  2067.         public T getdCoefEdk() {
  2068.             return coefEandDeriv[1];
  2069.         }

  2070.         /** Get the value of coefficient dE<sub>j,s</sub>/dh.
  2071.          *
  2072.          * @return the coefficient dE<sub>j,s</sub>/dh
  2073.          */
  2074.         public T getdCoefEdh() {
  2075.             return coefEandDeriv[2];
  2076.         }

  2077.         /** Get the value of coefficient dE<sub>j,s</sub>/dα.
  2078.          *
  2079.          * @return the coefficient dE<sub>j,s</sub>/dα
  2080.          */
  2081.         public T getdCoefEdalpha() {
  2082.             return coefEandDeriv[3];
  2083.         }

  2084.         /** Get the value of coefficient dE<sub>j,s</sub>/dβ.
  2085.          *
  2086.          * @return the coefficient dE<sub>j,s</sub>/dβ
  2087.          */
  2088.         public T getdCoefEdbeta() {
  2089.             return coefEandDeriv[4];
  2090.         }
  2091.     }

  2092.     /** This class computes the coefficients for the generating function S and its derivatives.
  2093.      * <p>
  2094.      * The form of the generating functions is: <br>
  2095.      *  S = C⁰ + &Sigma;<sub>j=1</sub><sup>N+1</sup>(C<sup>j</sup> * cos(jF) + S<sup>j</sup> * sin(jF)) <br>
  2096.      *  The coefficients C⁰, C<sup>j</sup>, S<sup>j</sup> are the Fourrier coefficients
  2097.      *  presented in Danielson 4.2-14,15 except for the case j=1 where
  2098.      *  C¹ = C¹<sub>Fourier</sub> - hU and
  2099.      *  S¹ = S¹<sub>Fourier</sub> + kU <br>
  2100.      *  Also the coefficients of the derivatives of S by a, k, h, α, β, γ and λ
  2101.      *  are computed end expressed in a similar manner. The formulas used are 4.2-19, 20, 23, 24
  2102.      * </p>
  2103.      * @author Lucian Barbulescu
  2104.      */
  2105.     private class GeneratingFunctionCoefficients {

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

  2108.         /** Maximum value of j index. */
  2109.         private final int jMax;

  2110.         /** The coefficients C<sup>j</sup> of the function S and its derivatives.
  2111.          * <p>
  2112.          * The index j belongs to the interval [0,jMax]. The coefficient C⁰ is the free coefficient.<br>
  2113.          * Each column of the matrix contains the coefficient corresponding to the following functions: <br/>
  2114.          * - S <br/>
  2115.          * - dS / da <br/>
  2116.          * - dS / dk <br/>
  2117.          * - dS / dh <br/>
  2118.          * - dS / dα <br/>
  2119.          * - dS / dβ <br/>
  2120.          * - dS / dγ <br/>
  2121.          * - dS / dλ
  2122.          * </p>
  2123.          */
  2124.         private final double[][] cjCoefs;

  2125.         /** The coefficients S<sup>j</sup> of the function S and its derivatives.
  2126.          * <p>
  2127.          * The index j belongs to the interval [0,jMax].<br>
  2128.          * Each column of the matrix contains the coefficient corresponding to the following functions: <br/>
  2129.          * - S <br/>
  2130.          * - dS / da <br/>
  2131.          * - dS / dk <br/>
  2132.          * - dS / dh <br/>
  2133.          * - dS / dα <br/>
  2134.          * - dS / dβ <br/>
  2135.          * - dS / dγ <br/>
  2136.          * - dS / dλ
  2137.          * </p>
  2138.          */
  2139.         private final double[][] sjCoefs;

  2140.         /**
  2141.          * Standard constructor.
  2142.          *
  2143.          * @param nMax maximum value of n index
  2144.          * @param sMax maximum value of s index
  2145.          * @param jMax maximum value of j index
  2146.          * @param context container for attributes
  2147.          * @param hansen hansen objects
  2148.          */
  2149.         GeneratingFunctionCoefficients(final int nMax, final int sMax, final int jMax,
  2150.                                        final DSSTThirdBodyContext context, final HansenObjects hansen) {
  2151.             this.jMax = jMax;
  2152.             this.cjsjFourier = new FourierCjSjCoefficients(nMax, sMax, jMax, context);
  2153.             this.cjCoefs = new double[8][jMax + 1];
  2154.             this.sjCoefs = new double[8][jMax + 1];

  2155.             computeGeneratingFunctionCoefficients(context, hansen);
  2156.         }

  2157.         /**
  2158.          * Compute the coefficients for the generating function S and its derivatives.
  2159.          * @param context container for attributes
  2160.          * @param hansenObjects hansen objects
  2161.          */
  2162.         private void computeGeneratingFunctionCoefficients(final DSSTThirdBodyContext context, final HansenObjects hansenObjects) {

  2163.             final AuxiliaryElements auxiliaryElements = context.getAuxiliaryElements();

  2164.             // Access to potential U derivatives
  2165.             final UAnddU udu = new UAnddU(context, hansenObjects);

  2166.             //Compute the C<sup>j</sup> coefficients
  2167.             for (int j = 1; j <= jMax; j++) {
  2168.                 //Compute the C<sup>j</sup> coefficients
  2169.                 cjCoefs[0][j] = cjsjFourier.getCj(j);
  2170.                 cjCoefs[1][j] = cjsjFourier.getdCjda(j);
  2171.                 cjCoefs[2][j] = cjsjFourier.getdCjdk(j) - (cjsjFourier.getSjLambda(j - 1) - cjsjFourier.getSjLambda(j + 1)) / 2;
  2172.                 cjCoefs[3][j] = cjsjFourier.getdCjdh(j) - (cjsjFourier.getCjLambda(j - 1) + cjsjFourier.getCjLambda(j + 1)) / 2;
  2173.                 cjCoefs[4][j] = cjsjFourier.getdCjdalpha(j);
  2174.                 cjCoefs[5][j] = cjsjFourier.getdCjdbeta(j);
  2175.                 cjCoefs[6][j] = cjsjFourier.getdCjdgamma(j);
  2176.                 cjCoefs[7][j] = cjsjFourier.getCjLambda(j);

  2177.                 //Compute the S<sup>j</sup> coefficients
  2178.                 sjCoefs[0][j] = cjsjFourier.getSj(j);
  2179.                 sjCoefs[1][j] = cjsjFourier.getdSjda(j);
  2180.                 sjCoefs[2][j] = cjsjFourier.getdSjdk(j) + (cjsjFourier.getCjLambda(j - 1) - cjsjFourier.getCjLambda(j + 1)) / 2;
  2181.                 sjCoefs[3][j] = cjsjFourier.getdSjdh(j) - (cjsjFourier.getSjLambda(j - 1) + cjsjFourier.getSjLambda(j + 1)) / 2;
  2182.                 sjCoefs[4][j] = cjsjFourier.getdSjdalpha(j);
  2183.                 sjCoefs[5][j] = cjsjFourier.getdSjdbeta(j);
  2184.                 sjCoefs[6][j] = cjsjFourier.getdSjdgamma(j);
  2185.                 sjCoefs[7][j] = cjsjFourier.getSjLambda(j);

  2186.                 //In the special case j == 1 there are some additional terms to be added
  2187.                 if (j == 1) {
  2188.                     //Additional terms for C<sup>j</sup> coefficients
  2189.                     cjCoefs[0][j] += -auxiliaryElements.getH() * udu.getU();
  2190.                     cjCoefs[1][j] += -auxiliaryElements.getH() * udu.getdUda();
  2191.                     cjCoefs[2][j] += -auxiliaryElements.getH() * udu.getdUdk();
  2192.                     cjCoefs[3][j] += -(auxiliaryElements.getH() * udu.getdUdh() + udu.getU() + cjsjFourier.getC0Lambda());
  2193.                     cjCoefs[4][j] += -auxiliaryElements.getH() * udu.getdUdAl();
  2194.                     cjCoefs[5][j] += -auxiliaryElements.getH() * udu.getdUdBe();
  2195.                     cjCoefs[6][j] += -auxiliaryElements.getH() * udu.getdUdGa();

  2196.                     //Additional terms for S<sup>j</sup> coefficients
  2197.                     sjCoefs[0][j] += auxiliaryElements.getK() * udu.getU();
  2198.                     sjCoefs[1][j] += auxiliaryElements.getK() * udu.getdUda();
  2199.                     sjCoefs[2][j] += auxiliaryElements.getK() * udu.getdUdk() + udu.getU() + cjsjFourier.getC0Lambda();
  2200.                     sjCoefs[3][j] += auxiliaryElements.getK() * udu.getdUdh();
  2201.                     sjCoefs[4][j] += auxiliaryElements.getK() * udu.getdUdAl();
  2202.                     sjCoefs[5][j] += auxiliaryElements.getK() * udu.getdUdBe();
  2203.                     sjCoefs[6][j] += auxiliaryElements.getK() * udu.getdUdGa();
  2204.                 }
  2205.             }
  2206.         }

  2207.         /** Get the coefficient C<sup>j</sup> for the function S.
  2208.          * <br>
  2209.          * Possible values for j are within the interval [0,jMax].
  2210.          * The value 0 is used to obtain the free coefficient C⁰
  2211.          * @param j j index
  2212.          * @return C<sup>j</sup> for the function S
  2213.          */
  2214.         public double getSCj(final int j) {
  2215.             return cjCoefs[0][j];
  2216.         }

  2217.         /** Get the coefficient S<sup>j</sup> for the function S.
  2218.          * <br>
  2219.          * Possible values for j are within the interval [1,jMax].
  2220.          * @param j j index
  2221.          * @return S<sup>j</sup> for the function S
  2222.          */
  2223.         public double getSSj(final int j) {
  2224.             return sjCoefs[0][j];
  2225.         }

  2226.         /** Get the coefficient C<sup>j</sup> for the derivative dS/da.
  2227.          * <br>
  2228.          * Possible values for j are within the interval [0,jMax].
  2229.          * The value 0 is used to obtain the free coefficient C⁰
  2230.          * @param j j index
  2231.          * @return C<sup>j</sup> for the function dS/da
  2232.          */
  2233.         public double getdSdaCj(final int j) {
  2234.             return cjCoefs[1][j];
  2235.         }

  2236.         /** Get the coefficient S<sup>j</sup> for the derivative dS/da.
  2237.          * <br>
  2238.          * Possible values for j are within the interval [1,jMax].
  2239.          * @param j j index
  2240.          * @return S<sup>j</sup> for the derivative dS/da
  2241.          */
  2242.         public double getdSdaSj(final int j) {
  2243.             return sjCoefs[1][j];
  2244.         }

  2245.         /** Get the coefficient C<sup>j</sup> for the derivative dS/dk
  2246.          * <br>
  2247.          * Possible values for j are within the interval [0,jMax].
  2248.          * The value 0 is used to obtain the free coefficient C⁰
  2249.          * @param j j index
  2250.          * @return C<sup>j</sup> for the function dS/dk
  2251.          */
  2252.         public double getdSdkCj(final int j) {
  2253.             return cjCoefs[2][j];
  2254.         }

  2255.         /** Get the coefficient S<sup>j</sup> for the derivative dS/dk.
  2256.          * <br>
  2257.          * Possible values for j are within the interval [1,jMax].
  2258.          * @param j j index
  2259.          * @return S<sup>j</sup> for the derivative dS/dk
  2260.          */
  2261.         public double getdSdkSj(final int j) {
  2262.             return sjCoefs[2][j];
  2263.         }

  2264.         /** Get the coefficient C<sup>j</sup> for the derivative dS/dh
  2265.          * <br>
  2266.          * Possible values for j are within the interval [0,jMax].
  2267.          * The value 0 is used to obtain the free coefficient C⁰
  2268.          * @param j j index
  2269.          * @return C<sup>j</sup> for the function dS/dh
  2270.          */
  2271.         public double getdSdhCj(final int j) {
  2272.             return cjCoefs[3][j];
  2273.         }

  2274.         /** Get the coefficient S<sup>j</sup> for the derivative dS/dh.
  2275.          * <br>
  2276.          * Possible values for j are within the interval [1,jMax].
  2277.          * @param j j index
  2278.          * @return S<sup>j</sup> for the derivative dS/dh
  2279.          */
  2280.         public double getdSdhSj(final int j) {
  2281.             return sjCoefs[3][j];
  2282.         }

  2283.         /** Get the coefficient C<sup>j</sup> for the derivative dS/dα
  2284.          * <br>
  2285.          * Possible values for j are within the interval [0,jMax].
  2286.          * The value 0 is used to obtain the free coefficient C⁰
  2287.          * @param j j index
  2288.          * @return C<sup>j</sup> for the function dS/dα
  2289.          */
  2290.         public double getdSdalphaCj(final int j) {
  2291.             return cjCoefs[4][j];
  2292.         }

  2293.         /** Get the coefficient S<sup>j</sup> for the derivative dS/dα.
  2294.          * <br>
  2295.          * Possible values for j are within the interval [1,jMax].
  2296.          * @param j j index
  2297.          * @return S<sup>j</sup> for the derivative dS/dα
  2298.          */
  2299.         public double getdSdalphaSj(final int j) {
  2300.             return sjCoefs[4][j];
  2301.         }

  2302.         /** Get the coefficient C<sup>j</sup> for the derivative dS/dβ
  2303.          * <br>
  2304.          * Possible values for j are within the interval [0,jMax].
  2305.          * The value 0 is used to obtain the free coefficient C⁰
  2306.          * @param j j index
  2307.          * @return C<sup>j</sup> for the function dS/dβ
  2308.          */
  2309.         public double getdSdbetaCj(final int j) {
  2310.             return cjCoefs[5][j];
  2311.         }

  2312.         /** Get the coefficient S<sup>j</sup> for the derivative dS/dβ.
  2313.          * <br>
  2314.          * Possible values for j are within the interval [1,jMax].
  2315.          * @param j j index
  2316.          * @return S<sup>j</sup> for the derivative dS/dβ
  2317.          */
  2318.         public double getdSdbetaSj(final int j) {
  2319.             return sjCoefs[5][j];
  2320.         }

  2321.         /** Get the coefficient C<sup>j</sup> for the derivative dS/dγ
  2322.          * <br>
  2323.          * Possible values for j are within the interval [0,jMax].
  2324.          * The value 0 is used to obtain the free coefficient C⁰
  2325.          * @param j j index
  2326.          * @return C<sup>j</sup> for the function dS/dγ
  2327.          */
  2328.         public double getdSdgammaCj(final int j) {
  2329.             return cjCoefs[6][j];
  2330.         }

  2331.         /** Get the coefficient S<sup>j</sup> for the derivative dS/dγ.
  2332.          * <br>
  2333.          * Possible values for j are within the interval [1,jMax].
  2334.          * @param j j index
  2335.          * @return S<sup>j</sup> for the derivative dS/dγ
  2336.          */
  2337.         public double getdSdgammaSj(final int j) {
  2338.             return sjCoefs[6][j];
  2339.         }

  2340.         /** Get the coefficient C<sup>j</sup> for the derivative dS/dλ
  2341.          * <br>
  2342.          * Possible values for j are within the interval [0,jMax].
  2343.          * The value 0 is used to obtain the free coefficient C⁰
  2344.          * @param j j index
  2345.          * @return C<sup>j</sup> for the function dS/dλ
  2346.          */
  2347.         public double getdSdlambdaCj(final int j) {
  2348.             return cjCoefs[7][j];
  2349.         }

  2350.         /** Get the coefficient S<sup>j</sup> for the derivative dS/dλ.
  2351.          * <br>
  2352.          * Possible values for j are within the interval [1,jMax].
  2353.          * @param j j index
  2354.          * @return S<sup>j</sup> for the derivative dS/dλ
  2355.          */
  2356.         public double getdSdlambdaSj(final int j) {
  2357.             return sjCoefs[7][j];
  2358.         }
  2359.     }

  2360.     /** This class computes the coefficients for the generating function S and its derivatives.
  2361.      * <p>
  2362.      * The form of the generating functions is: <br>
  2363.      *  S = C⁰ + &Sigma;<sub>j=1</sub><sup>N+1</sup>(C<sup>j</sup> * cos(jF) + S<sup>j</sup> * sin(jF)) <br>
  2364.      *  The coefficients C⁰, C<sup>j</sup>, S<sup>j</sup> are the Fourrier coefficients
  2365.      *  presented in Danielson 4.2-14,15 except for the case j=1 where
  2366.      *  C¹ = C¹<sub>Fourier</sub> - hU and
  2367.      *  S¹ = S¹<sub>Fourier</sub> + kU <br>
  2368.      *  Also the coefficients of the derivatives of S by a, k, h, α, β, γ and λ
  2369.      *  are computed end expressed in a similar manner. The formulas used are 4.2-19, 20, 23, 24
  2370.      * </p>
  2371.      * @author Lucian Barbulescu
  2372.      */
  2373.     private class FieldGeneratingFunctionCoefficients <T extends RealFieldElement<T>> {

  2374.         /** The Fourier coefficients as presented in Danielson 4.2-14,15. */
  2375.         private final FieldFourierCjSjCoefficients<T> cjsjFourier;

  2376.         /** Maximum value of j index. */
  2377.         private final int jMax;

  2378.         /** The coefficients C<sup>j</sup> of the function S and its derivatives.
  2379.          * <p>
  2380.          * The index j belongs to the interval [0,jMax]. The coefficient C⁰ is the free coefficient.<br>
  2381.          * Each column of the matrix contains the coefficient corresponding to the following functions: <br/>
  2382.          * - S <br/>
  2383.          * - dS / da <br/>
  2384.          * - dS / dk <br/>
  2385.          * - dS / dh <br/>
  2386.          * - dS / dα <br/>
  2387.          * - dS / dβ <br/>
  2388.          * - dS / dγ <br/>
  2389.          * - dS / dλ
  2390.          * </p>
  2391.          */
  2392.         private final T[][] cjCoefs;

  2393.         /** The coefficients S<sup>j</sup> of the function S and its derivatives.
  2394.          * <p>
  2395.          * The index j belongs to the interval [0,jMax].<br>
  2396.          * Each column of the matrix contains the coefficient corresponding to the following functions: <br/>
  2397.          * - S <br/>
  2398.          * - dS / da <br/>
  2399.          * - dS / dk <br/>
  2400.          * - dS / dh <br/>
  2401.          * - dS / dα <br/>
  2402.          * - dS / dβ <br/>
  2403.          * - dS / dγ <br/>
  2404.          * - dS / dλ
  2405.          * </p>
  2406.          */
  2407.         private final T[][] sjCoefs;

  2408.         /**
  2409.          * Standard constructor.
  2410.          *
  2411.          * @param nMax maximum value of n index
  2412.          * @param sMax maximum value of s index
  2413.          * @param jMax maximum value of j index
  2414.          * @param context container for attributes
  2415.          * @param hansen hansen objects
  2416.          * @param field field used by default
  2417.          */
  2418.         FieldGeneratingFunctionCoefficients(final int nMax, final int sMax, final int jMax,
  2419.                                             final FieldDSSTThirdBodyContext<T> context,
  2420.                                             final FieldHansenObjects<T> hansen,
  2421.                                             final Field<T> field) {
  2422.             this.jMax = jMax;
  2423.             this.cjsjFourier = new FieldFourierCjSjCoefficients<>(nMax, sMax, jMax, context, field);
  2424.             this.cjCoefs     = MathArrays.buildArray(field, 8, jMax + 1);
  2425.             this.sjCoefs     = MathArrays.buildArray(field, 8, jMax + 1);

  2426.             computeGeneratingFunctionCoefficients(context, hansen);
  2427.         }

  2428.         /**
  2429.          * Compute the coefficients for the generating function S and its derivatives.
  2430.          * @param context container for attributes
  2431.          * @param hansenObjects hansen objects
  2432.          */
  2433.         private void computeGeneratingFunctionCoefficients(final FieldDSSTThirdBodyContext<T> context,
  2434.                                                            final FieldHansenObjects<T> hansenObjects) {

  2435.             final FieldAuxiliaryElements<T> auxiliaryElements = context.getFieldAuxiliaryElements();

  2436.             // Access to potential U derivatives
  2437.             final FieldUAnddU<T> udu = new FieldUAnddU<>(context, hansenObjects);

  2438.             //Compute the C<sup>j</sup> coefficients
  2439.             for (int j = 1; j <= jMax; j++) {
  2440.                 //Compute the C<sup>j</sup> coefficients
  2441.                 cjCoefs[0][j] = cjsjFourier.getCj(j);
  2442.                 cjCoefs[1][j] = cjsjFourier.getdCjda(j);
  2443.                 cjCoefs[2][j] = cjsjFourier.getdCjdk(j).subtract((cjsjFourier.getSjLambda(j - 1).subtract(cjsjFourier.getSjLambda(j + 1))).divide(2.));
  2444.                 cjCoefs[3][j] = cjsjFourier.getdCjdh(j).subtract((cjsjFourier.getCjLambda(j - 1).add(cjsjFourier.getCjLambda(j + 1))).divide(2.));
  2445.                 cjCoefs[4][j] = cjsjFourier.getdCjdalpha(j);
  2446.                 cjCoefs[5][j] = cjsjFourier.getdCjdbeta(j);
  2447.                 cjCoefs[6][j] = cjsjFourier.getdCjdgamma(j);
  2448.                 cjCoefs[7][j] = cjsjFourier.getCjLambda(j);

  2449.                 //Compute the S<sup>j</sup> coefficients
  2450.                 sjCoefs[0][j] = cjsjFourier.getSj(j);
  2451.                 sjCoefs[1][j] = cjsjFourier.getdSjda(j);
  2452.                 sjCoefs[2][j] = cjsjFourier.getdSjdk(j).add((cjsjFourier.getCjLambda(j - 1).subtract(cjsjFourier.getCjLambda(j + 1))).divide(2.));
  2453.                 sjCoefs[3][j] = cjsjFourier.getdSjdh(j).subtract((cjsjFourier.getSjLambda(j - 1).add(cjsjFourier.getSjLambda(j + 1))).divide(2.));
  2454.                 sjCoefs[4][j] = cjsjFourier.getdSjdalpha(j);
  2455.                 sjCoefs[5][j] = cjsjFourier.getdSjdbeta(j);
  2456.                 sjCoefs[6][j] = cjsjFourier.getdSjdgamma(j);
  2457.                 sjCoefs[7][j] = cjsjFourier.getSjLambda(j);

  2458.                 //In the special case j == 1 there are some additional terms to be added
  2459.                 if (j == 1) {
  2460.                     //Additional terms for C<sup>j</sup> coefficients
  2461.                     cjCoefs[0][j] = cjCoefs[0][j].add(auxiliaryElements.getH().negate().multiply(udu.getU()));
  2462.                     cjCoefs[1][j] = cjCoefs[1][j].add(auxiliaryElements.getH().negate().multiply(udu.getdUda()));
  2463.                     cjCoefs[2][j] = cjCoefs[2][j].add(auxiliaryElements.getH().negate().multiply(udu.getdUdk()));
  2464.                     cjCoefs[3][j] = cjCoefs[3][j].add(auxiliaryElements.getH().multiply(udu.getdUdh()).add(udu.getU()).add(cjsjFourier.getC0Lambda()).negate());
  2465.                     cjCoefs[4][j] = cjCoefs[4][j].add(auxiliaryElements.getH().negate().multiply(udu.getdUdAl()));
  2466.                     cjCoefs[5][j] = cjCoefs[5][j].add(auxiliaryElements.getH().negate().multiply(udu.getdUdBe()));
  2467.                     cjCoefs[6][j] = cjCoefs[6][j].add(auxiliaryElements.getH().negate().multiply(udu.getdUdGa()));

  2468.                     //Additional terms for S<sup>j</sup> coefficients
  2469.                     sjCoefs[0][j] = sjCoefs[0][j].add(auxiliaryElements.getK().multiply(udu.getU()));
  2470.                     sjCoefs[1][j] = sjCoefs[1][j].add(auxiliaryElements.getK().multiply(udu.getdUda()));
  2471.                     sjCoefs[2][j] = sjCoefs[2][j].add(auxiliaryElements.getK().multiply(udu.getdUdk()).add(udu.getU()).add(cjsjFourier.getC0Lambda()));
  2472.                     sjCoefs[3][j] = sjCoefs[3][j].add(auxiliaryElements.getK().multiply(udu.getdUdh()));
  2473.                     sjCoefs[4][j] = sjCoefs[4][j].add(auxiliaryElements.getK().multiply(udu.getdUdAl()));
  2474.                     sjCoefs[5][j] = sjCoefs[5][j].add(auxiliaryElements.getK().multiply(udu.getdUdBe()));
  2475.                     sjCoefs[6][j] = sjCoefs[6][j].add(auxiliaryElements.getK().multiply(udu.getdUdGa()));
  2476.                 }
  2477.             }
  2478.         }

  2479.         /** Get the coefficient C<sup>j</sup> for the function S.
  2480.          * <br>
  2481.          * Possible values for j are within the interval [0,jMax].
  2482.          * The value 0 is used to obtain the free coefficient C⁰
  2483.          * @param j j index
  2484.          * @return C<sup>j</sup> for the function S
  2485.          */
  2486.         public T getSCj(final int j) {
  2487.             return cjCoefs[0][j];
  2488.         }

  2489.         /** Get the coefficient S<sup>j</sup> for the function S.
  2490.          * <br>
  2491.          * Possible values for j are within the interval [1,jMax].
  2492.          * @param j j index
  2493.          * @return S<sup>j</sup> for the function S
  2494.          */
  2495.         public T getSSj(final int j) {
  2496.             return sjCoefs[0][j];
  2497.         }

  2498.         /** Get the coefficient C<sup>j</sup> for the derivative dS/da.
  2499.          * <br>
  2500.          * Possible values for j are within the interval [0,jMax].
  2501.          * The value 0 is used to obtain the free coefficient C⁰
  2502.          * @param j j index
  2503.          * @return C<sup>j</sup> for the function dS/da
  2504.          */
  2505.         public T getdSdaCj(final int j) {
  2506.             return cjCoefs[1][j];
  2507.         }

  2508.         /** Get the coefficient S<sup>j</sup> for the derivative dS/da.
  2509.          * <br>
  2510.          * Possible values for j are within the interval [1,jMax].
  2511.          * @param j j index
  2512.          * @return S<sup>j</sup> for the derivative dS/da
  2513.          */
  2514.         public T getdSdaSj(final int j) {
  2515.             return sjCoefs[1][j];
  2516.         }

  2517.         /** Get the coefficient C<sup>j</sup> for the derivative dS/dk
  2518.          * <br>
  2519.          * Possible values for j are within the interval [0,jMax].
  2520.          * The value 0 is used to obtain the free coefficient C⁰
  2521.          * @param j j index
  2522.          * @return C<sup>j</sup> for the function dS/dk
  2523.          */
  2524.         public T getdSdkCj(final int j) {
  2525.             return cjCoefs[2][j];
  2526.         }

  2527.         /** Get the coefficient S<sup>j</sup> for the derivative dS/dk.
  2528.          * <br>
  2529.          * Possible values for j are within the interval [1,jMax].
  2530.          * @param j j index
  2531.          * @return S<sup>j</sup> for the derivative dS/dk
  2532.          */
  2533.         public T getdSdkSj(final int j) {
  2534.             return sjCoefs[2][j];
  2535.         }

  2536.         /** Get the coefficient C<sup>j</sup> for the derivative dS/dh
  2537.          * <br>
  2538.          * Possible values for j are within the interval [0,jMax].
  2539.          * The value 0 is used to obtain the free coefficient C⁰
  2540.          * @param j j index
  2541.          * @return C<sup>j</sup> for the function dS/dh
  2542.          */
  2543.         public T getdSdhCj(final int j) {
  2544.             return cjCoefs[3][j];
  2545.         }

  2546.         /** Get the coefficient S<sup>j</sup> for the derivative dS/dh.
  2547.          * <br>
  2548.          * Possible values for j are within the interval [1,jMax].
  2549.          * @param j j index
  2550.          * @return S<sup>j</sup> for the derivative dS/dh
  2551.          */
  2552.         public T getdSdhSj(final int j) {
  2553.             return sjCoefs[3][j];
  2554.         }

  2555.         /** Get the coefficient C<sup>j</sup> for the derivative dS/dα
  2556.          * <br>
  2557.          * Possible values for j are within the interval [0,jMax].
  2558.          * The value 0 is used to obtain the free coefficient C⁰
  2559.          * @param j j index
  2560.          * @return C<sup>j</sup> for the function dS/dα
  2561.          */
  2562.         public T getdSdalphaCj(final int j) {
  2563.             return cjCoefs[4][j];
  2564.         }

  2565.         /** Get the coefficient S<sup>j</sup> for the derivative dS/dα.
  2566.          * <br>
  2567.          * Possible values for j are within the interval [1,jMax].
  2568.          * @param j j index
  2569.          * @return S<sup>j</sup> for the derivative dS/dα
  2570.          */
  2571.         public T getdSdalphaSj(final int j) {
  2572.             return sjCoefs[4][j];
  2573.         }

  2574.         /** Get the coefficient C<sup>j</sup> for the derivative dS/dβ
  2575.          * <br>
  2576.          * Possible values for j are within the interval [0,jMax].
  2577.          * The value 0 is used to obtain the free coefficient C⁰
  2578.          * @param j j index
  2579.          * @return C<sup>j</sup> for the function dS/dβ
  2580.          */
  2581.         public T getdSdbetaCj(final int j) {
  2582.             return cjCoefs[5][j];
  2583.         }

  2584.         /** Get the coefficient S<sup>j</sup> for the derivative dS/dβ.
  2585.          * <br>
  2586.          * Possible values for j are within the interval [1,jMax].
  2587.          * @param j j index
  2588.          * @return S<sup>j</sup> for the derivative dS/dβ
  2589.          */
  2590.         public T getdSdbetaSj(final int j) {
  2591.             return sjCoefs[5][j];
  2592.         }

  2593.         /** Get the coefficient C<sup>j</sup> for the derivative dS/dγ
  2594.          * <br>
  2595.          * Possible values for j are within the interval [0,jMax].
  2596.          * The value 0 is used to obtain the free coefficient C⁰
  2597.          * @param j j index
  2598.          * @return C<sup>j</sup> for the function dS/dγ
  2599.          */
  2600.         public T getdSdgammaCj(final int j) {
  2601.             return cjCoefs[6][j];
  2602.         }

  2603.         /** Get the coefficient S<sup>j</sup> for the derivative dS/dγ.
  2604.          * <br>
  2605.          * Possible values for j are within the interval [1,jMax].
  2606.          * @param j j index
  2607.          * @return S<sup>j</sup> for the derivative dS/dγ
  2608.          */
  2609.         public T getdSdgammaSj(final int j) {
  2610.             return sjCoefs[6][j];
  2611.         }

  2612.         /** Get the coefficient C<sup>j</sup> for the derivative dS/dλ
  2613.          * <br>
  2614.          * Possible values for j are within the interval [0,jMax].
  2615.          * The value 0 is used to obtain the free coefficient C⁰
  2616.          * @param j j index
  2617.          * @return C<sup>j</sup> for the function dS/dλ
  2618.          */
  2619.         public T getdSdlambdaCj(final int j) {
  2620.             return cjCoefs[7][j];
  2621.         }

  2622.         /** Get the coefficient S<sup>j</sup> for the derivative dS/dλ.
  2623.          * <br>
  2624.          * Possible values for j are within the interval [1,jMax].
  2625.          * @param j j index
  2626.          * @return S<sup>j</sup> for the derivative dS/dλ
  2627.          */
  2628.         public T getdSdlambdaSj(final int j) {
  2629.             return sjCoefs[7][j];
  2630.         }
  2631.     }

  2632.     /**
  2633.      * The coefficients used to compute the short periodic contribution for the Third body perturbation.
  2634.      * <p>
  2635.      * The short periodic contribution for the Third Body is expressed in Danielson 4.2-25.<br>
  2636.      * The coefficients C<sub>i</sub>⁰, C<sub>i</sub><sup>j</sup>, S<sub>i</sub><sup>j</sup>
  2637.      * are computed by replacing the corresponding values in formula 2.5.5-10.
  2638.      * </p>
  2639.      * @author Lucian Barbulescu
  2640.      */
  2641.     private static class ThirdBodyShortPeriodicCoefficients implements ShortPeriodTerms {

  2642.         /** Maximal value for j. */
  2643.         private final int jMax;

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

  2646.         /** Max frequency of F. */
  2647.         private final int    maxFreqF;

  2648.         /** Coefficients prefix. */
  2649.         private final String prefix;

  2650.         /** All coefficients slots. */
  2651.         private final transient TimeSpanMap<Slot> slots;

  2652.         /**
  2653.          * Standard constructor.
  2654.          *  @param interpolationPoints number of points used in the interpolation process
  2655.          * @param jMax maximal value for j
  2656.          * @param maxFreqF Max frequency of F
  2657.          * @param bodyName third body name
  2658.          * @param slots all coefficients slots
  2659.          */
  2660.         ThirdBodyShortPeriodicCoefficients(final int jMax, final int interpolationPoints,
  2661.                                            final int maxFreqF, final String bodyName,
  2662.                                            final TimeSpanMap<Slot> slots) {
  2663.             this.jMax                = jMax;
  2664.             this.interpolationPoints = interpolationPoints;
  2665.             this.maxFreqF            = maxFreqF;
  2666.             this.prefix              = DSSTThirdBody.SHORT_PERIOD_PREFIX + bodyName + "-";
  2667.             this.slots               = slots;
  2668.         }

  2669.         /** Get the slot valid for some date.
  2670.          * @param meanStates mean states defining the slot
  2671.          * @return slot valid at the specified date
  2672.          */
  2673.         public Slot createSlot(final SpacecraftState... meanStates) {
  2674.             final Slot         slot  = new Slot(jMax, interpolationPoints);
  2675.             final AbsoluteDate first = meanStates[0].getDate();
  2676.             final AbsoluteDate last  = meanStates[meanStates.length - 1].getDate();
  2677.             if (first.compareTo(last) <= 0) {
  2678.                 slots.addValidAfter(slot, first);
  2679.             } else {
  2680.                 slots.addValidBefore(slot, first);
  2681.             }
  2682.             return slot;
  2683.         }

  2684.         /** {@inheritDoc} */
  2685.         @Override
  2686.         public double[] value(final Orbit meanOrbit) {

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

  2689.             // the current eccentric longitude
  2690.             final double F = meanOrbit.getLE();

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

  2693.             // Add the cos and sin dependent terms
  2694.             for (int j = 1; j <= maxFreqF; j++) {
  2695.                 //compute cos and sin
  2696.                 final SinCos scjF  = FastMath.sinCos(j * F);

  2697.                 final double[] c = slot.cij[j].value(meanOrbit.getDate());
  2698.                 final double[] s = slot.sij[j].value(meanOrbit.getDate());
  2699.                 for (int i = 0; i < 6; i++) {
  2700.                     shortPeriodic[i] += c[i] * scjF.cos() + s[i] * scjF.sin();
  2701.                 }
  2702.             }

  2703.             return shortPeriodic;

  2704.         }

  2705.         /** {@inheritDoc} */
  2706.         @Override
  2707.         public String getCoefficientsKeyPrefix() {
  2708.             return prefix;
  2709.         }

  2710.         /** {@inheritDoc}
  2711.          * <p>
  2712.          * For third body attraction forces,there are maxFreqF + 1 cj coefficients,
  2713.          * maxFreqF sj coefficients where maxFreqF depends on the orbit.
  2714.          * The j index is the integer multiplier for the eccentric longitude argument
  2715.          * in the cj and sj coefficients.
  2716.          * </p>
  2717.          */
  2718.         @Override
  2719.         public Map<String, double[]> getCoefficients(final AbsoluteDate date, final Set<String> selected) {

  2720.             // select the coefficients slot
  2721.             final Slot slot = slots.get(date);

  2722.             final Map<String, double[]> coefficients = new HashMap<String, double[]>(2 * maxFreqF + 1);
  2723.             storeIfSelected(coefficients, selected, slot.cij[0].value(date), "c", 0);
  2724.             for (int j = 1; j <= maxFreqF; j++) {
  2725.                 storeIfSelected(coefficients, selected, slot.cij[j].value(date), "c", j);
  2726.                 storeIfSelected(coefficients, selected, slot.sij[j].value(date), "s", j);
  2727.             }
  2728.             return coefficients;

  2729.         }

  2730.         /** Put a coefficient in a map if selected.
  2731.          * @param map map to populate
  2732.          * @param selected set of coefficients that should be put in the map
  2733.          * (empty set means all coefficients are selected)
  2734.          * @param value coefficient value
  2735.          * @param id coefficient identifier
  2736.          * @param indices list of coefficient indices
  2737.          */
  2738.         private void storeIfSelected(final Map<String, double[]> map, final Set<String> selected,
  2739.                                      final double[] value, final String id, final int... indices) {
  2740.             final StringBuilder keyBuilder = new StringBuilder(getCoefficientsKeyPrefix());
  2741.             keyBuilder.append(id);
  2742.             for (int index : indices) {
  2743.                 keyBuilder.append('[').append(index).append(']');
  2744.             }
  2745.             final String key = keyBuilder.toString();
  2746.             if (selected.isEmpty() || selected.contains(key)) {
  2747.                 map.put(key, value);
  2748.             }
  2749.         }

  2750.     }

  2751.     /**
  2752.      * The coefficients used to compute the short periodic contribution for the Third body perturbation.
  2753.      * <p>
  2754.      * The short periodic contribution for the Third Body is expressed in Danielson 4.2-25.<br>
  2755.      * The coefficients C<sub>i</sub>⁰, C<sub>i</sub><sup>j</sup>, S<sub>i</sub><sup>j</sup>
  2756.      * are computed by replacing the corresponding values in formula 2.5.5-10.
  2757.      * </p>
  2758.      * @author Lucian Barbulescu
  2759.      */
  2760.     private static class FieldThirdBodyShortPeriodicCoefficients <T extends RealFieldElement<T>> implements FieldShortPeriodTerms<T> {

  2761.         /** Maximal value for j. */
  2762.         private final int jMax;

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

  2765.         /** Max frequency of F. */
  2766.         private final int    maxFreqF;

  2767.         /** Coefficients prefix. */
  2768.         private final String prefix;

  2769.         /** All coefficients slots. */
  2770.         private final transient FieldTimeSpanMap<FieldSlot<T>, T> slots;

  2771.         /**
  2772.          * Standard constructor.
  2773.          * @param interpolationPoints number of points used in the interpolation process
  2774.          * @param jMax maximal value for j
  2775.          * @param maxFreqF Max frequency of F
  2776.          * @param bodyName third body name
  2777.          * @param slots all coefficients slots
  2778.          */
  2779.         FieldThirdBodyShortPeriodicCoefficients(final int jMax, final int interpolationPoints,
  2780.                                                 final int maxFreqF, final String bodyName,
  2781.                                                 final FieldTimeSpanMap<FieldSlot<T>, T> slots) {
  2782.             this.jMax                = jMax;
  2783.             this.interpolationPoints = interpolationPoints;
  2784.             this.maxFreqF            = maxFreqF;
  2785.             this.prefix              = DSSTThirdBody.SHORT_PERIOD_PREFIX + bodyName + "-";
  2786.             this.slots               = slots;
  2787.         }

  2788.         /** Get the slot valid for some date.
  2789.          * @param meanStates mean states defining the slot
  2790.          * @return slot valid at the specified date
  2791.          */
  2792.         @SuppressWarnings("unchecked")
  2793.         public FieldSlot<T> createSlot(final FieldSpacecraftState<T>... meanStates) {
  2794.             final FieldSlot<T>         slot  = new FieldSlot<>(jMax, interpolationPoints);
  2795.             final FieldAbsoluteDate<T> first = meanStates[0].getDate();
  2796.             final FieldAbsoluteDate<T> last  = meanStates[meanStates.length - 1].getDate();
  2797.             if (first.compareTo(last) <= 0) {
  2798.                 slots.addValidAfter(slot, first);
  2799.             } else {
  2800.                 slots.addValidBefore(slot, first);
  2801.             }
  2802.             return slot;
  2803.         }

  2804.         /** {@inheritDoc} */
  2805.         @Override
  2806.         public T[] value(final FieldOrbit<T> meanOrbit) {

  2807.             // select the coefficients slot
  2808.             final FieldSlot<T> slot = slots.get(meanOrbit.getDate());

  2809.             // the current eccentric longitude
  2810.             final T F = meanOrbit.getLE();

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

  2813.             // Add the cos and sin dependent terms
  2814.             for (int j = 1; j <= maxFreqF; j++) {
  2815.                 //compute cos and sin
  2816.                 final FieldSinCos<T> scjF = FastMath.sinCos(F.multiply(j));

  2817.                 final T[] c = (T[]) slot.cij[j].value(meanOrbit.getDate());
  2818.                 final T[] s = (T[]) slot.sij[j].value(meanOrbit.getDate());
  2819.                 for (int i = 0; i < 6; i++) {
  2820.                     shortPeriodic[i] = shortPeriodic[i].add(c[i].multiply(scjF.cos()).add(s[i].multiply(scjF.sin())));
  2821.                 }
  2822.             }

  2823.             return shortPeriodic;

  2824.         }

  2825.         /** {@inheritDoc} */
  2826.         @Override
  2827.         public String getCoefficientsKeyPrefix() {
  2828.             return prefix;
  2829.         }

  2830.         /** {@inheritDoc}
  2831.          * <p>
  2832.          * For third body attraction forces,there are maxFreqF + 1 cj coefficients,
  2833.          * maxFreqF sj coefficients where maxFreqF depends on the orbit.
  2834.          * The j index is the integer multiplier for the eccentric longitude argument
  2835.          * in the cj and sj coefficients.
  2836.          * </p>
  2837.          */
  2838.         @Override
  2839.         public Map<String, T[]> getCoefficients(final FieldAbsoluteDate<T> date, final Set<String> selected) {

  2840.             // select the coefficients slot
  2841.             final FieldSlot<T> slot = slots.get(date);

  2842.             final Map<String, T[]> coefficients = new HashMap<String, T[]>(2 * maxFreqF + 1);
  2843.             storeIfSelected(coefficients, selected, slot.cij[0].value(date), "c", 0);
  2844.             for (int j = 1; j <= maxFreqF; j++) {
  2845.                 storeIfSelected(coefficients, selected, slot.cij[j].value(date), "c", j);
  2846.                 storeIfSelected(coefficients, selected, slot.sij[j].value(date), "s", j);
  2847.             }
  2848.             return coefficients;

  2849.         }

  2850.         /** Put a coefficient in a map if selected.
  2851.          * @param map map to populate
  2852.          * @param selected set of coefficients that should be put in the map
  2853.          * (empty set means all coefficients are selected)
  2854.          * @param value coefficient value
  2855.          * @param id coefficient identifier
  2856.          * @param indices list of coefficient indices
  2857.          */
  2858.         private void storeIfSelected(final Map<String, T[]> map, final Set<String> selected,
  2859.                                      final T[] value, final String id, final int... indices) {
  2860.             final StringBuilder keyBuilder = new StringBuilder(getCoefficientsKeyPrefix());
  2861.             keyBuilder.append(id);
  2862.             for (int index : indices) {
  2863.                 keyBuilder.append('[').append(index).append(']');
  2864.             }
  2865.             final String key = keyBuilder.toString();
  2866.             if (selected.isEmpty() || selected.contains(key)) {
  2867.                 map.put(key, value);
  2868.             }
  2869.         }

  2870.     }

  2871.     /** Coefficients valid for one time slot. */
  2872.     private static class Slot {

  2873.         /** The coefficients C<sub>i</sub><sup>j</sup>.
  2874.          * <p>
  2875.          * The index order is cij[j][i] <br/>
  2876.          * i corresponds to the equinoctial element, as follows: <br/>
  2877.          * - i=0 for a <br/>
  2878.          * - i=1 for k <br/>
  2879.          * - i=2 for h <br/>
  2880.          * - i=3 for q <br/>
  2881.          * - i=4 for p <br/>
  2882.          * - i=5 for λ <br/>
  2883.          * </p>
  2884.          */
  2885.         private final ShortPeriodicsInterpolatedCoefficient[] cij;

  2886.         /** The coefficients S<sub>i</sub><sup>j</sup>.
  2887.          * <p>
  2888.          * The index order is sij[j][i] <br/>
  2889.          * i corresponds to the equinoctial element, as follows: <br/>
  2890.          * - i=0 for a <br/>
  2891.          * - i=1 for k <br/>
  2892.          * - i=2 for h <br/>
  2893.          * - i=3 for q <br/>
  2894.          * - i=4 for p <br/>
  2895.          * - i=5 for λ <br/>
  2896.          * </p>
  2897.          */
  2898.         private final ShortPeriodicsInterpolatedCoefficient[] sij;

  2899.         /** Simple constructor.
  2900.          *  @param jMax maximum value for j index
  2901.          *  @param interpolationPoints number of points used in the interpolation process
  2902.          */
  2903.         Slot(final int jMax, final int interpolationPoints) {
  2904.             // allocate the coefficients arrays
  2905.             cij = new ShortPeriodicsInterpolatedCoefficient[jMax + 1];
  2906.             sij = new ShortPeriodicsInterpolatedCoefficient[jMax + 1];
  2907.             for (int j = 0; j <= jMax; j++) {
  2908.                 cij[j] = new ShortPeriodicsInterpolatedCoefficient(interpolationPoints);
  2909.                 sij[j] = new ShortPeriodicsInterpolatedCoefficient(interpolationPoints);
  2910.             }


  2911.         }
  2912.     }

  2913.     /** Coefficients valid for one time slot. */
  2914.     private static class FieldSlot <T extends RealFieldElement<T>> {

  2915.         /** The coefficients C<sub>i</sub><sup>j</sup>.
  2916.          * <p>
  2917.          * The index order is cij[j][i] <br/>
  2918.          * i corresponds to the equinoctial element, as follows: <br/>
  2919.          * - i=0 for a <br/>
  2920.          * - i=1 for k <br/>
  2921.          * - i=2 for h <br/>
  2922.          * - i=3 for q <br/>
  2923.          * - i=4 for p <br/>
  2924.          * - i=5 for λ <br/>
  2925.          * </p>
  2926.          */
  2927.         private final FieldShortPeriodicsInterpolatedCoefficient<T>[] cij;

  2928.         /** The coefficients S<sub>i</sub><sup>j</sup>.
  2929.          * <p>
  2930.          * The index order is sij[j][i] <br/>
  2931.          * i corresponds to the equinoctial element, as follows: <br/>
  2932.          * - i=0 for a <br/>
  2933.          * - i=1 for k <br/>
  2934.          * - i=2 for h <br/>
  2935.          * - i=3 for q <br/>
  2936.          * - i=4 for p <br/>
  2937.          * - i=5 for λ <br/>
  2938.          * </p>
  2939.          */
  2940.         private final FieldShortPeriodicsInterpolatedCoefficient<T>[] sij;

  2941.         /** Simple constructor.
  2942.          *  @param jMax maximum value for j index
  2943.          *  @param interpolationPoints number of points used in the interpolation process
  2944.          */
  2945.         @SuppressWarnings("unchecked")
  2946.         FieldSlot(final int jMax, final int interpolationPoints) {
  2947.             // allocate the coefficients arrays
  2948.             cij = (FieldShortPeriodicsInterpolatedCoefficient<T>[]) Array.newInstance(FieldShortPeriodicsInterpolatedCoefficient.class, jMax + 1);
  2949.             sij = (FieldShortPeriodicsInterpolatedCoefficient<T>[]) Array.newInstance(FieldShortPeriodicsInterpolatedCoefficient.class, jMax + 1);
  2950.             for (int j = 0; j <= jMax; j++) {
  2951.                 cij[j] = new FieldShortPeriodicsInterpolatedCoefficient<>(interpolationPoints);
  2952.                 sij[j] = new FieldShortPeriodicsInterpolatedCoefficient<>(interpolationPoints);
  2953.             }


  2954.         }
  2955.     }

  2956.     /** Compute potential and potential derivatives with respect to orbital parameters. */
  2957.     private class UAnddU {

  2958.         /** The current value of the U function. <br/>
  2959.          * Needed for the short periodic contribution */
  2960.         private double U;

  2961.         /** dU / da. */
  2962.         private  double dUda;

  2963.         /** dU / dk. */
  2964.         private double dUdk;

  2965.         /** dU / dh. */
  2966.         private double dUdh;

  2967.         /** dU / dAlpha. */
  2968.         private double dUdAl;

  2969.         /** dU / dBeta. */
  2970.         private double dUdBe;

  2971.         /** dU / dGamma. */
  2972.         private double dUdGa;

  2973.         /** Simple constuctor.
  2974.          * @param context container for attributes
  2975.          * @param hansen hansen objects
  2976.          */
  2977.         UAnddU(final DSSTThirdBodyContext context,
  2978.                final HansenObjects hansen) {
  2979.             // Auxiliary elements related to the current orbit
  2980.             final AuxiliaryElements auxiliaryElements = context.getAuxiliaryElements();

  2981.             // Gs and Hs coefficients
  2982.             final double[][] GsHs = CoefficientsFactory.computeGsHs(auxiliaryElements.getK(), auxiliaryElements.getH(), context.getAlpha(), context.getBeta(), context.getMaxEccPow());

  2983.             // Initialise U.
  2984.             U = 0.;

  2985.             // Potential derivatives
  2986.             dUda  = 0.;
  2987.             dUdk  = 0.;
  2988.             dUdh  = 0.;
  2989.             dUdAl = 0.;
  2990.             dUdBe = 0.;
  2991.             dUdGa = 0.;

  2992.             for (int s = 0; s <= context.getMaxEccPow(); s++) {

  2993.                 // initialise the Hansen roots
  2994.                 hansen.computeHansenObjectsInitValues(context, auxiliaryElements.getB(), s);

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

  2997.                 // Compute Gs partial derivatives from 3.1-(9)
  2998.                 double dGsdh  = 0.;
  2999.                 double dGsdk  = 0.;
  3000.                 double dGsdAl = 0.;
  3001.                 double dGsdBe = 0.;
  3002.                 if (s > 0) {
  3003.                     // First get the G(s-1) and the H(s-1) coefficients
  3004.                     final double sxGsm1 = s * GsHs[0][s - 1];
  3005.                     final double sxHsm1 = s * GsHs[1][s - 1];
  3006.                     // Then compute derivatives
  3007.                     dGsdh  = context.getBeta()  * sxGsm1 - context.getAlpha() * sxHsm1;
  3008.                     dGsdk  = context.getAlpha() * sxGsm1 + context.getBeta()  * sxHsm1;
  3009.                     dGsdAl = auxiliaryElements.getK() * sxGsm1 - auxiliaryElements.getH() * sxHsm1;
  3010.                     dGsdBe = auxiliaryElements.getH() * sxGsm1 + auxiliaryElements.getK() * sxHsm1;
  3011.                 }

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

  3014.                 for (int n = FastMath.max(2, s); n <= context.getMaxAR3Pow(); n++) {
  3015.                     // (n - s) must be even
  3016.                     if ((n - s) % 2 == 0) {
  3017.                         // Extract data from previous computation :
  3018.                         final double kns   = hansen.getHansenObjects()[s].getValue(n, auxiliaryElements.getB());
  3019.                         final double dkns  = hansen.getHansenObjects()[s].getDerivative(n, auxiliaryElements.getB());

  3020.                         final double vns   = Vns.get(new NSKey(n, s));
  3021.                         final double coef0 = delta0s * context.getAoR3Pow()[n] * vns;
  3022.                         final double coef1 = coef0 * context.getQns()[n][s];
  3023.                         final double coef2 = coef1 * kns;
  3024.                         // dQns/dGamma = Q(n, s + 1) from Equation 3.1-(8)
  3025.                         // for n = s, Q(n, n + 1) = 0. (Cefola & Broucke, 1975)
  3026.                         final double dqns = (n == s) ? 0. : context.getQns()[n][s + 1];

  3027.                         //Compute U:
  3028.                         U += coef2 * gs;

  3029.                         // Compute dU / da :
  3030.                         dUda  += coef2 * n * gs;
  3031.                         // Compute dU / dh
  3032.                         dUdh  += coef1 * (kns * dGsdh + context.getHXXX() * gs * dkns);
  3033.                         // Compute dU / dk
  3034.                         dUdk  += coef1 * (kns * dGsdk + context.getKXXX() * gs * dkns);
  3035.                         // Compute dU / dAlpha
  3036.                         dUdAl += coef2 * dGsdAl;
  3037.                         // Compute dU / dBeta
  3038.                         dUdBe += coef2 * dGsdBe;
  3039.                         // Compute dU / dGamma
  3040.                         dUdGa += coef0 * kns * dqns * gs;
  3041.                     }
  3042.                 }
  3043.             }

  3044.             // multiply by mu3 / R3
  3045.             this.U = U * context.getMuoR3();

  3046.             this.dUda  = dUda  * context.getMuoR3() / auxiliaryElements.getSma();
  3047.             this.dUdk  = dUdk  * context.getMuoR3();
  3048.             this.dUdh  = dUdh  * context.getMuoR3();
  3049.             this.dUdAl = dUdAl * context.getMuoR3();
  3050.             this.dUdBe = dUdBe * context.getMuoR3();
  3051.             this.dUdGa = dUdGa * context.getMuoR3();

  3052.         }

  3053.         /** Return value of U.
  3054.          * @return U
  3055.          */
  3056.         public double getU() {
  3057.             return U;
  3058.         }

  3059.         /** Return value of dU / da.
  3060.          * @return dUda
  3061.          */
  3062.         public double getdUda() {
  3063.             return dUda;
  3064.         }

  3065.         /** Return value of dU / dk.
  3066.          * @return dUdk
  3067.          */
  3068.         public double getdUdk() {
  3069.             return dUdk;
  3070.         }

  3071.         /** Return value of dU / dh.
  3072.          * @return dUdh
  3073.          */
  3074.         public double getdUdh() {
  3075.             return dUdh;
  3076.         }

  3077.         /** Return value of dU / dAlpha.
  3078.          * @return dUdAl
  3079.          */
  3080.         public double getdUdAl() {
  3081.             return dUdAl;
  3082.         }

  3083.         /** Return value of dU / dBeta.
  3084.          * @return dUdBe
  3085.          */
  3086.         public double getdUdBe() {
  3087.             return dUdBe;
  3088.         }

  3089.         /** Return value of dU / dGamma.
  3090.          * @return dUdGa
  3091.          */
  3092.         public double getdUdGa() {
  3093.             return dUdGa;
  3094.         }

  3095.     }

  3096.     /** Compute potential and potential derivatives with respect to orbital parameters. */
  3097.     private class FieldUAnddU <T extends RealFieldElement<T>> {

  3098.         /** The current value of the U function. <br/>
  3099.          * Needed for the short periodic contribution */
  3100.         private T U;

  3101.         /** dU / da. */
  3102.         private T dUda;

  3103.         /** dU / dk. */
  3104.         private T dUdk;

  3105.         /** dU / dh. */
  3106.         private T dUdh;

  3107.         /** dU / dAlpha. */
  3108.         private T dUdAl;

  3109.         /** dU / dBeta. */
  3110.         private T dUdBe;

  3111.         /** dU / dGamma. */
  3112.         private T dUdGa;

  3113.         /** Simple constuctor.
  3114.          * @param context container for attributes
  3115.          * @param hansen hansen objects
  3116.          */
  3117.         FieldUAnddU(final FieldDSSTThirdBodyContext<T> context,
  3118.                     final FieldHansenObjects<T> hansen) {

  3119.             // Auxiliary elements related to the current orbit
  3120.             final FieldAuxiliaryElements<T> auxiliaryElements = context.getFieldAuxiliaryElements();

  3121.             // Field for array building
  3122.             final Field<T> field = auxiliaryElements.getDate().getField();
  3123.             // Zero for initialization
  3124.             final T zero = field.getZero();

  3125.             // Gs and Hs coefficients
  3126.             final T[][] GsHs = CoefficientsFactory.computeGsHs(auxiliaryElements.getK(), auxiliaryElements.getH(), context.getAlpha(), context.getBeta(), context.getMaxEccPow(), field);

  3127.             // Initialise U.
  3128.             U = zero;

  3129.             // Potential derivatives
  3130.             dUda  = zero;
  3131.             dUdk  = zero;
  3132.             dUdh  = zero;
  3133.             dUdAl = zero;
  3134.             dUdBe = zero;
  3135.             dUdGa = zero;

  3136.             for (int s = 0; s <= context.getMaxEccPow(); s++) {
  3137.                 // initialise the Hansen roots
  3138.                 hansen.computeHansenObjectsInitValues(context, auxiliaryElements.getB(), s);

  3139.                 // Get the current Gs coefficient
  3140.                 final T gs = GsHs[0][s];

  3141.                 // Compute Gs partial derivatives from 3.1-(9)
  3142.                 T dGsdh  = zero;
  3143.                 T dGsdk  = zero;
  3144.                 T dGsdAl = zero;
  3145.                 T dGsdBe = zero;
  3146.                 if (s > 0) {
  3147.                     // First get the G(s-1) and the H(s-1) coefficients
  3148.                     final T sxGsm1 = GsHs[0][s - 1].multiply(s);
  3149.                     final T sxHsm1 = GsHs[1][s - 1].multiply(s);
  3150.                     // Then compute derivatives
  3151.                     dGsdh  = sxGsm1.multiply(context.getBeta()).subtract(sxHsm1.multiply(context.getAlpha()));
  3152.                     dGsdk  = sxGsm1.multiply(context.getAlpha()).add(sxHsm1.multiply(context.getBeta()));
  3153.                     dGsdAl = sxGsm1.multiply(auxiliaryElements.getK()).subtract(sxHsm1.multiply(auxiliaryElements.getH()));
  3154.                     dGsdBe = sxGsm1.multiply(auxiliaryElements.getH()).add(sxHsm1.multiply(auxiliaryElements.getK()));
  3155.                 }

  3156.                 // Kronecker symbol (2 - delta(0,s))
  3157.                 final T delta0s = zero.add((s == 0) ? 1. : 2.);

  3158.                 for (int n = FastMath.max(2, s); n <= context.getMaxAR3Pow(); n++) {
  3159.                     // (n - s) must be even
  3160.                     if ((n - s) % 2 == 0) {
  3161.                         // Extract data from previous computation :
  3162.                         final T kns   = (T) hansen.getHansenObjects()[s].getValue(n, auxiliaryElements.getB());
  3163.                         final T dkns  = (T) hansen.getHansenObjects()[s].getDerivative(n, auxiliaryElements.getB());

  3164.                         final double vns = Vns.get(new NSKey(n, s));
  3165.                         final T coef0 = delta0s.multiply(vns).multiply(context.getAoR3Pow()[n]);
  3166.                         final T coef1 = coef0.multiply(context.getQns()[n][s]);
  3167.                         final T coef2 = coef1.multiply(kns);
  3168.                         // dQns/dGamma = Q(n, s + 1) from Equation 3.1-(8)
  3169.                         // for n = s, Q(n, n + 1) = 0. (Cefola & Broucke, 1975)
  3170.                         final T dqns = (n == s) ? zero : context.getQns()[n][s + 1];

  3171.                         //Compute U:
  3172.                         U = U.add(coef2.multiply(gs));

  3173.                         // Compute dU / da :
  3174.                         dUda  = dUda.add(coef2.multiply(n).multiply(gs));
  3175.                         // Compute dU / dh
  3176.                         dUdh  = dUdh.add(coef1.multiply(dGsdh.multiply(kns).add(context.getHXXX().multiply(gs).multiply(dkns))));
  3177.                         // Compute dU / dk
  3178.                         dUdk  = dUdk.add(coef1.multiply(dGsdk.multiply(kns).add(context.getKXXX().multiply(gs).multiply(dkns))));
  3179.                         // Compute dU / dAlpha
  3180.                         dUdAl = dUdAl.add(coef2.multiply(dGsdAl));
  3181.                         // Compute dU / dBeta
  3182.                         dUdBe = dUdBe.add(coef2.multiply(dGsdBe));
  3183.                         // Compute dU / dGamma
  3184.                         dUdGa = dUdGa.add(coef0.multiply(kns).multiply(dqns).multiply(gs));
  3185.                     }
  3186.                 }
  3187.             }

  3188.             // multiply by mu3 / R3
  3189.             this.U = U.multiply(context.getMuoR3());

  3190.             this.dUda  = dUda.multiply(context.getMuoR3().divide(auxiliaryElements.getSma()));
  3191.             this.dUdk  = dUdk.multiply(context.getMuoR3());
  3192.             this.dUdh  = dUdh.multiply(context.getMuoR3());
  3193.             this.dUdAl = dUdAl.multiply(context.getMuoR3());
  3194.             this.dUdBe = dUdBe.multiply(context.getMuoR3());
  3195.             this.dUdGa = dUdGa.multiply(context.getMuoR3());

  3196.         }

  3197.         /** Return value of U.
  3198.          * @return U
  3199.          */
  3200.         public T getU() {
  3201.             return U;
  3202.         }

  3203.         /** Return value of dU / da.
  3204.          * @return dUda
  3205.          */
  3206.         public T getdUda() {
  3207.             return dUda;
  3208.         }

  3209.         /** Return value of dU / dk.
  3210.          * @return dUdk
  3211.          */
  3212.         public T getdUdk() {
  3213.             return dUdk;
  3214.         }

  3215.         /** Return value of dU / dh.
  3216.          * @return dUdh
  3217.          */
  3218.         public T getdUdh() {
  3219.             return dUdh;
  3220.         }

  3221.         /** Return value of dU / dAlpha.
  3222.          * @return dUdAl
  3223.          */
  3224.         public T getdUdAl() {
  3225.             return dUdAl;
  3226.         }

  3227.         /** Return value of dU / dBeta.
  3228.          * @return dUdBe
  3229.          */
  3230.         public T getdUdBe() {
  3231.             return dUdBe;
  3232.         }

  3233.         /** Return value of dU / dGamma.
  3234.          * @return dUdGa
  3235.          */
  3236.         public T getdUdGa() {
  3237.             return dUdGa;
  3238.         }

  3239.     }

  3240.     /** Computes init values of the Hansen Objects. */
  3241.     private static class HansenObjects {

  3242.         /** Max power for summation. */
  3243.         private static final int    MAX_POWER = 22;

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

  3247.         /** Simple constructor. */
  3248.         HansenObjects() {
  3249.             this.hansenObjects = new HansenThirdBodyLinear[MAX_POWER + 1];
  3250.             for (int s = 0; s <= MAX_POWER; s++) {
  3251.                 this.hansenObjects[s] = new HansenThirdBodyLinear(MAX_POWER, s);
  3252.             }
  3253.         }

  3254.         /** Compute init values for hansen objects.
  3255.          * @param context container for attributes
  3256.          * @param B = sqrt(1 - e²).
  3257.          * @param element element of the array to compute the init values
  3258.          */
  3259.         public void computeHansenObjectsInitValues(final DSSTThirdBodyContext context, final double B, final int element) {
  3260.             hansenObjects[element].computeInitValues(B, context.getBB(), context.getBBB());
  3261.         }

  3262.         /** Get the Hansen Objects.
  3263.          * @return hansenObjects
  3264.          */
  3265.         public HansenThirdBodyLinear[] getHansenObjects() {
  3266.             return hansenObjects;
  3267.         }

  3268.     }

  3269.     /** Computes init values of the Hansen Objects. */
  3270.     private static class FieldHansenObjects<T extends RealFieldElement<T>> {

  3271.         /** Max power for summation. */
  3272.         private static final int    MAX_POWER = 22;

  3273.         /** An array that contains the objects needed to build the Hansen coefficients. <br/>
  3274.          * The index is s */
  3275.         private final FieldHansenThirdBodyLinear<T>[] hansenObjects;

  3276.         /** Simple constructor.
  3277.          * @param field field used by default
  3278.          */
  3279.         @SuppressWarnings("unchecked")
  3280.         FieldHansenObjects(final Field<T> field) {
  3281.             this.hansenObjects = (FieldHansenThirdBodyLinear<T>[]) Array.newInstance(FieldHansenThirdBodyLinear.class, MAX_POWER + 1);
  3282.             for (int s = 0; s <= MAX_POWER; s++) {
  3283.                 this.hansenObjects[s] = new FieldHansenThirdBodyLinear<>(MAX_POWER, s, field);
  3284.             }
  3285.         }

  3286.         /** Initialise the Hansen roots for third body problem.
  3287.          * @param context container for attributes
  3288.          * @param B = sqrt(1 - e²).
  3289.          * @param element element of the array to compute the init values
  3290.          */
  3291.         public void computeHansenObjectsInitValues(final FieldDSSTThirdBodyContext<T> context,
  3292.                                                    final T B, final int element) {
  3293.             hansenObjects[element].computeInitValues(B, context.getBB(), context.getBBB());
  3294.         }

  3295.         /** Get the Hansen Objects.
  3296.          * @return hansenObjects
  3297.          */
  3298.         public FieldHansenThirdBodyLinear<T>[] getHansenObjects() {
  3299.             return hansenObjects;
  3300.         }

  3301.     }

  3302. }