DSSTThirdBody.java

  1. /* Copyright 2002-2023 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.SortedMap;

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

  59. /** Third body attraction perturbation to the
  60.  *  {@link org.orekit.propagation.semianalytical.dsst.DSSTPropagator DSSTPropagator}.
  61.  *
  62.  *  @author Romain Di Costanzo
  63.  *  @author Pascal Parraud
  64.  *  @author Bryan Cazabonne (field translation)
  65.  */
  66. public class DSSTThirdBody implements DSSTForceModel {
  67.     /**  Name of the prefix for short period coefficients keys. */
  68.     public static final String SHORT_PERIOD_PREFIX = "DSST-3rd-body-";

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

  71.     /** Max power for summation. */
  72.     public static final int MAX_POWER = 22;

  73.     /** Truncation tolerance for big, eccentric orbits. */
  74.     public static final double BIG_TRUNCATION_TOLERANCE = 1.e-1;

  75.     /** Truncation tolerance for small orbits. */
  76.     public static final double SMALL_TRUNCATION_TOLERANCE = 1.9e-6;

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

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

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

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

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

  104.     /** Force model static context. Initialized with short period terms. */
  105.     private DSSTThirdBodyStaticContext staticContext;

  106.     /** If false, the static context needs to be initialized. */
  107.     private boolean doesStaticContextNeedsInitialization;

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

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

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

  114.     /** Drivers for third body attraction coefficient and gravitational parameter. */
  115.     private final List<ParameterDriver> parameterDrivers;

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

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

  120.     /** Complete constructor.
  121.      *  @param body the 3rd body to consider
  122.      *  @param mu central attraction coefficient
  123.      *            (<b>i.e., attraction coefficient of the central body, not the one of the 3rd body</b>)
  124.      *  @see CelestialBodies
  125.      */
  126.     public DSSTThirdBody(final CelestialBody body, final double mu) {
  127.         parameterDrivers = new ArrayList<>(2);
  128.         parameterDrivers.add(new ParameterDriver(body.getName() + DSSTThirdBody.ATTRACTION_COEFFICIENT,
  129.                                                  body.getGM(), MU_SCALE,
  130.                                                  0.0, Double.POSITIVE_INFINITY));
  131.         parameterDrivers.add(new ParameterDriver(DSSTNewtonianAttraction.CENTRAL_ATTRACTION_COEFFICIENT,
  132.                                                  mu, MU_SCALE,
  133.                                                  0.0, Double.POSITIVE_INFINITY));

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

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

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

  146.     /** Initializes the static 3rd body context if needed.
  147.      * @param auxiliaryElements auxiliary elements
  148.      * @param x DSST Chi element
  149.      * @param r3 distance from center of mass of the central body to the 3rd body
  150.      * @param parameters force model parameters
  151.      */
  152.     private void initializeStaticContextIfNeeded(final AuxiliaryElements auxiliaryElements,
  153.                                                  final double x, final double r3,
  154.                                                  final double[] parameters) {
  155.         if (doesStaticContextNeedsInitialization) {
  156.             staticContext = new DSSTThirdBodyStaticContext(auxiliaryElements, x, r3, parameters);
  157.             doesStaticContextNeedsInitialization = false;
  158.         }
  159.     }

  160.     /** Computes the highest power of the eccentricity and the highest power
  161.      *  of a/R3 to appear in the truncated analytical power series expansion.
  162.      *  <p>
  163.      *  This method computes the upper value for the 3rd body potential and
  164.      *  determines the maximal powers for the eccentricity and a/R3 producing
  165.      *  potential terms bigger than a defined tolerance.
  166.      *  </p>
  167.      *  @param auxiliaryElements auxiliary elements related to the current orbit
  168.      *  @param type type of the elements used during the propagation
  169.      *  @param parameters values of the force model parameters for state date (1 value for each parameters)
  170.      */
  171.     @Override
  172.     public List<ShortPeriodTerms> initializeShortPeriodTerms(final AuxiliaryElements auxiliaryElements,
  173.                                                              final PropagationType type,
  174.                                                              final double[] parameters) {

  175.         // Initializes specific parameters.
  176.         final DSSTThirdBodyDynamicContext context = initializeStep(auxiliaryElements, parameters);

  177.         // Static context
  178.         initializeStaticContextIfNeeded(auxiliaryElements, context.getX(), context.getR3(), parameters);

  179.         // Hansen objects
  180.         hansen = new HansenObjects();

  181.         // Initialize short period terms
  182.         final int jMax = staticContext.getMaxAR3Pow() + 1;
  183.         shortPeriods = new ThirdBodyShortPeriodicCoefficients(jMax, INTERPOLATION_POINTS,
  184.                                                               staticContext.getMaxFreqF(), body.getName(),
  185.                                                               new TimeSpanMap<Slot>(new Slot(jMax, INTERPOLATION_POINTS)));

  186.         final List<ShortPeriodTerms> list = new ArrayList<ShortPeriodTerms>();
  187.         list.add(shortPeriods);
  188.         return list;

  189.     }

  190.     /** {@inheritDoc} */
  191.     @Override
  192.     public <T extends CalculusFieldElement<T>> List<FieldShortPeriodTerms<T>> initializeShortPeriodTerms(final FieldAuxiliaryElements<T> auxiliaryElements,
  193.                                                                                                          final PropagationType type,
  194.                                                                                                          final T[] parameters) {

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

  197.         // Initializes specific parameters.
  198.         final FieldDSSTThirdBodyDynamicContext<T> context = initializeStep(auxiliaryElements, parameters);

  199.         // Static context (only provide integers. So, derivatives are not taken into account and getReal() method is accepted)
  200.         initializeStaticContextIfNeeded(auxiliaryElements.toAuxiliaryElements(), context.getX().getReal(), context.getR3().getReal(), getParameters());

  201.         // Hansen object
  202.         fieldHansen.put(field, new FieldHansenObjects<>(field));

  203.         // Initialize short period terms
  204.         final int jMax = staticContext.getMaxAR3Pow() + 1;
  205.         final FieldThirdBodyShortPeriodicCoefficients<T> ftbspc =
  206.                         new FieldThirdBodyShortPeriodicCoefficients<>(jMax, INTERPOLATION_POINTS,
  207.                                         staticContext.getMaxFreqF(), body.getName(),
  208.                                                                       new FieldTimeSpanMap<>(new FieldSlot<>(jMax,
  209.                                                                                                              INTERPOLATION_POINTS),
  210.                                                                                              field));
  211.         fieldShortPeriods.put(field, ftbspc);
  212.         return Collections.singletonList(ftbspc);
  213.     }

  214.     /** Performs initialization at each integration step for the current force model.
  215.      *  <p>
  216.      *  This method aims at being called before mean elements rates computation.
  217.      *  </p>
  218.      *  @param auxiliaryElements auxiliary elements related to the current orbit
  219.      *  @param parameters values of the force model parameters  (only 1 values for each parameters corresponding
  220.      * to state date) obtained by calling the extract parameter method {@link #extractParameters(double[], AbsoluteDate)}
  221.      * to selected the right value for state date or by getting the parameters for a specific date
  222.      * {@link #getParameters(AbsoluteDate)}.
  223.      *  @return new force model context
  224.      */
  225.     private DSSTThirdBodyDynamicContext initializeStep(final AuxiliaryElements auxiliaryElements, final double[] parameters) {
  226.         return new DSSTThirdBodyDynamicContext(auxiliaryElements, body, parameters);
  227.     }

  228.     /** Performs initialization at each integration step for the current force model.
  229.      *  <p>
  230.      *  This method aims at being called before mean elements rates computation.
  231.      *  </p>
  232.      *  @param <T> type of the elements
  233.      *  @param auxiliaryElements auxiliary elements related to the current orbit
  234.      *  @param parameters values of the force model parameters at state date (1 value per parameter driver)
  235.      *  @return new force model context
  236.      */
  237.     private <T extends CalculusFieldElement<T>> FieldDSSTThirdBodyDynamicContext<T> initializeStep(final FieldAuxiliaryElements<T> auxiliaryElements,
  238.                                                                                         final T[] parameters) {
  239.         return new FieldDSSTThirdBodyDynamicContext<>(auxiliaryElements, body, parameters);
  240.     }

  241.     /** {@inheritDoc} */
  242.     @Override
  243.     public double[] getMeanElementRate(final SpacecraftState currentState,
  244.                                        final AuxiliaryElements auxiliaryElements, final double[] parameters) {


  245.         // Container for attributes
  246.         final DSSTThirdBodyDynamicContext context = initializeStep(auxiliaryElements, parameters);

  247.         // a / R3 up to power maxAR3Pow
  248.         final double[] aoR3Pow = computeAoR3Pow(context);

  249.         // Qns coefficients
  250.         final double[][] Qns = CoefficientsFactory.computeQns(context.getGamma(), staticContext.getMaxAR3Pow(), FastMath.max(staticContext.getMaxEccPow(), MAX_ECCPOWER_SP));

  251.         // Access to potential U derivatives
  252.         final UAnddU udu = new UAnddU(context, hansen, aoR3Pow, Qns);

  253.         // Compute cross derivatives [Eq. 2.2-(8)]
  254.         // U(alpha,gamma) = alpha * dU/dgamma - gamma * dU/dalpha
  255.         final double UAlphaGamma   = context.getAlpha() * udu.getdUdGa() - context.getGamma() * udu.getdUdAl();
  256.         // U(beta,gamma) = beta * dU/dgamma - gamma * dU/dbeta
  257.         final double UBetaGamma    =  context.getBeta() * udu.getdUdGa() - context.getGamma() * udu.getdUdBe();
  258.         // Common factor
  259.         final double pUAGmIqUBGoAB = (auxiliaryElements.getP() * UAlphaGamma - I * auxiliaryElements.getQ() * UBetaGamma) * context.getOoAB();

  260.         // Compute mean elements rates [Eq. 3.1-(1)]
  261.         final double da =  0.;
  262.         final double dh =  context.getBoA() * udu.getdUdk() + auxiliaryElements.getK() * pUAGmIqUBGoAB;
  263.         final double dk = -context.getBoA() * udu.getdUdh() - auxiliaryElements.getH() * pUAGmIqUBGoAB;
  264.         final double dp =  context.getMCo2AB() * UBetaGamma;
  265.         final double dq =  context.getMCo2AB() * UAlphaGamma * I;
  266.         final double dM =  context.getM2aoA() * udu.getdUda() + context.getBoABpo() * (auxiliaryElements.getH() * udu.getdUdh() + auxiliaryElements.getK() * udu.getdUdk()) + pUAGmIqUBGoAB;

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

  268.     }

  269.     /** {@inheritDoc} */
  270.     @Override
  271.     public <T extends CalculusFieldElement<T>> T[] getMeanElementRate(final FieldSpacecraftState<T> currentState,
  272.                                                                   final FieldAuxiliaryElements<T> auxiliaryElements,
  273.                                                                   final T[] parameters) {

  274.         // Parameters for array building
  275.         final Field<T> field = currentState.getDate().getField();
  276.         final T        zero  = field.getZero();

  277.         // Container for attributes
  278.         final FieldDSSTThirdBodyDynamicContext<T> context = initializeStep(auxiliaryElements, parameters);

  279.         // a / R3 up to power maxAR3Pow
  280.         final T[] aoR3Pow = computeAoR3Pow(context);

  281.         // Qns coefficients
  282.         final T[][] Qns = CoefficientsFactory.computeQns(context.getGamma(), staticContext.getMaxAR3Pow(), FastMath.max(staticContext.getMaxEccPow(), MAX_ECCPOWER_SP));

  283.         // Hansen objects
  284.         @SuppressWarnings("unchecked")
  285.         final FieldHansenObjects<T> fho = (FieldHansenObjects<T>) fieldHansen.get(field);

  286.         // Access to potential U derivatives
  287.         final FieldUAnddU<T> udu = new FieldUAnddU<>(context, fho, aoR3Pow, Qns);

  288.         // Compute cross derivatives [Eq. 2.2-(8)]
  289.         // U(alpha,gamma) = alpha * dU/dgamma - gamma * dU/dalpha
  290.         final T UAlphaGamma   = udu.getdUdGa().multiply(context.getAlpha()).subtract(udu.getdUdAl().multiply(context.getGamma()));
  291.         // U(beta,gamma) = beta * dU/dgamma - gamma * dU/dbeta
  292.         final T UBetaGamma    = udu.getdUdGa().multiply(context.getBeta()).subtract(udu.getdUdBe().multiply(context.getGamma()));
  293.         // Common factor
  294.         final T pUAGmIqUBGoAB = (UAlphaGamma.multiply(auxiliaryElements.getP()).subtract(UBetaGamma.multiply(auxiliaryElements.getQ()).multiply(I))).multiply(context.getOoAB());

  295.         // Compute mean elements rates [Eq. 3.1-(1)]
  296.         final T da =  zero;
  297.         final T dh =  udu.getdUdk().multiply(context.getBoA()).add(pUAGmIqUBGoAB.multiply(auxiliaryElements.getK()));
  298.         final T dk =  ((udu.getdUdh().multiply(context.getBoA())).negate()).subtract(pUAGmIqUBGoAB.multiply(auxiliaryElements.getH()));
  299.         final T dp =  UBetaGamma.multiply(context.getMCo2AB());
  300.         final T dq =  UAlphaGamma.multiply(I).multiply(context.getMCo2AB());
  301.         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()));

  302.         final T[] elements = MathArrays.buildArray(field, 6);
  303.         elements[0] = da;
  304.         elements[1] = dk;
  305.         elements[2] = dh;
  306.         elements[3] = dq;
  307.         elements[4] = dp;
  308.         elements[5] = dM;

  309.         return elements;

  310.     }

  311.     /** {@inheritDoc} */
  312.     @Override
  313.     public void updateShortPeriodTerms(final double[] parameters, final SpacecraftState... meanStates) {

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

  315.         for (final SpacecraftState meanState : meanStates) {

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

  318.             // Extract the proper parameters valid for the corresponding meanState date from the input array
  319.             final double[] extractedParameters = this.extractParameters(parameters, auxiliaryElements.getDate());

  320.             // Container of attributes
  321.             final DSSTThirdBodyDynamicContext context = initializeStep(auxiliaryElements, extractedParameters);

  322.             // a / R3 up to power maxAR3Pow
  323.             final double[] aoR3Pow = computeAoR3Pow(context);

  324.             // Qns coefficients
  325.             final double[][] Qns = CoefficientsFactory.computeQns(context.getGamma(), staticContext.getMaxAR3Pow(), FastMath.max(staticContext.getMaxEccPow(), MAX_ECCPOWER_SP));

  326.             final GeneratingFunctionCoefficients gfCoefs =
  327.                             new GeneratingFunctionCoefficients(staticContext.getMaxAR3Pow(), MAX_ECCPOWER_SP, staticContext.getMaxAR3Pow() + 1, context, hansen, aoR3Pow, Qns);

  328.             //Compute additional quantities
  329.             // 2 * a / An
  330.             final double ax2oAn  = -context.getM2aoA() / context.getMeanMotion();
  331.             // B / An
  332.             final double BoAn    = context.getBoA() / context.getMeanMotion();
  333.             // 1 / ABn
  334.             final double ooABn   = context.getOoAB() / context.getMeanMotion();
  335.             // C / 2ABn
  336.             final double Co2ABn  = -context.getMCo2AB() / context.getMeanMotion();
  337.             // B / (A * (1 + B) * n)
  338.             final double BoABpon = context.getBoABpo() / context.getMeanMotion();
  339.             // -3 / n²a² = -3 / nA
  340.             final double m3onA   = -3 / (context.getA() * context.getMeanMotion());

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

  345.                 // Compute the cross derivatives operator :
  346.                 final double SAlphaGammaCj    = context.getAlpha() * gfCoefs.getdSdgammaCj(j) - context.getGamma() * gfCoefs.getdSdalphaCj(j);
  347.                 final double SAlphaBetaCj     = context.getAlpha() * gfCoefs.getdSdbetaCj(j)  - context.getBeta()  * gfCoefs.getdSdalphaCj(j);
  348.                 final double SBetaGammaCj     = context.getBeta() * gfCoefs.getdSdgammaCj(j) - context.getGamma() * gfCoefs.getdSdbetaCj(j);
  349.                 final double ShkCj            = auxiliaryElements.getH() * gfCoefs.getdSdkCj(j)     -  auxiliaryElements.getK()    * gfCoefs.getdSdhCj(j);
  350.                 final double pSagmIqSbgoABnCj = (auxiliaryElements.getP() * SAlphaGammaCj - I * auxiliaryElements.getQ() * SBetaGammaCj) * ooABn;
  351.                 final double ShkmSabmdSdlCj   = ShkCj - SAlphaBetaCj - gfCoefs.getdSdlambdaCj(j);

  352.                 currentCij[0] =  ax2oAn * gfCoefs.getdSdlambdaCj(j);
  353.                 currentCij[1] =  -(BoAn * gfCoefs.getdSdhCj(j) + auxiliaryElements.getH() * pSagmIqSbgoABnCj + auxiliaryElements.getK() * BoABpon * gfCoefs.getdSdlambdaCj(j));
  354.                 currentCij[2] =    BoAn * gfCoefs.getdSdkCj(j) + auxiliaryElements.getK() * pSagmIqSbgoABnCj - auxiliaryElements.getH() * BoABpon * gfCoefs.getdSdlambdaCj(j);
  355.                 currentCij[3] =  Co2ABn * (auxiliaryElements.getQ() * ShkmSabmdSdlCj - I * SAlphaGammaCj);
  356.                 currentCij[4] =  Co2ABn * (auxiliaryElements.getP() * ShkmSabmdSdlCj - SBetaGammaCj);
  357.                 currentCij[5] = -ax2oAn * gfCoefs.getdSdaCj(j) + BoABpon * (auxiliaryElements.getH() * gfCoefs.getdSdhCj(j) + auxiliaryElements.getK() * gfCoefs.getdSdkCj(j)) + pSagmIqSbgoABnCj + m3onA * gfCoefs.getSCj(j);

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

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

  362.                 // Compute the cross derivatives operator :
  363.                 final double SAlphaGammaSj    = context.getAlpha() * gfCoefs.getdSdgammaSj(j) - context.getGamma() * gfCoefs.getdSdalphaSj(j);
  364.                 final double SAlphaBetaSj     = context.getAlpha() * gfCoefs.getdSdbetaSj(j)  - context.getBeta()  * gfCoefs.getdSdalphaSj(j);
  365.                 final double SBetaGammaSj     =  context.getBeta() * gfCoefs.getdSdgammaSj(j) - context.getGamma() * gfCoefs.getdSdbetaSj(j);
  366.                 final double ShkSj            =     auxiliaryElements.getH() * gfCoefs.getdSdkSj(j)     -  auxiliaryElements.getK()    * gfCoefs.getdSdhSj(j);
  367.                 final double pSagmIqSbgoABnSj = (auxiliaryElements.getP() * SAlphaGammaSj - I * auxiliaryElements.getQ() * SBetaGammaSj) * ooABn;
  368.                 final double ShkmSabmdSdlSj   =  ShkSj - SAlphaBetaSj - gfCoefs.getdSdlambdaSj(j);

  369.                 currentSij[0] =  ax2oAn * gfCoefs.getdSdlambdaSj(j);
  370.                 currentSij[1] =  -(BoAn * gfCoefs.getdSdhSj(j) + auxiliaryElements.getH() * pSagmIqSbgoABnSj + auxiliaryElements.getK() * BoABpon * gfCoefs.getdSdlambdaSj(j));
  371.                 currentSij[2] =    BoAn * gfCoefs.getdSdkSj(j) + auxiliaryElements.getK() * pSagmIqSbgoABnSj - auxiliaryElements.getH() * BoABpon * gfCoefs.getdSdlambdaSj(j);
  372.                 currentSij[3] =  Co2ABn * (auxiliaryElements.getQ() * ShkmSabmdSdlSj - I * SAlphaGammaSj);
  373.                 currentSij[4] =  Co2ABn * (auxiliaryElements.getP() * ShkmSabmdSdlSj - SBetaGammaSj);
  374.                 currentSij[5] = -ax2oAn * gfCoefs.getdSdaSj(j) + BoABpon * (auxiliaryElements.getH() * gfCoefs.getdSdhSj(j) + auxiliaryElements.getK() * gfCoefs.getdSdkSj(j)) + pSagmIqSbgoABnSj + m3onA * gfCoefs.getSSj(j);

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

  377.                 if (j == 1) {
  378.                     //Compute the C⁰ coefficients using Danielson 2.5.2-15a.
  379.                     final double[] value = new double[6];
  380.                     for (int i = 0; i < 6; ++i) {
  381.                         value[i] = currentCij[i] * auxiliaryElements.getK() / 2. + currentSij[i] * auxiliaryElements.getH() / 2.;
  382.                     }
  383.                     slot.cij[0].addGridPoint(meanState.getDate(), value);
  384.                 }
  385.             }
  386.         }
  387.     }

  388.     /** {@inheritDoc} */
  389.     @Override
  390.     @SuppressWarnings("unchecked")
  391.     public <T extends CalculusFieldElement<T>> void updateShortPeriodTerms(final T[] parameters,
  392.                                                                        final FieldSpacecraftState<T>... meanStates) {

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

  395.         final FieldThirdBodyShortPeriodicCoefficients<T> ftbspc = (FieldThirdBodyShortPeriodicCoefficients<T>) fieldShortPeriods.get(field);
  396.         final FieldSlot<T> slot = ftbspc.createSlot(meanStates);
  397.         for (final FieldSpacecraftState<T> meanState : meanStates) {

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

  400.             // Extract the proper parameters valid for the corresponding meanState date from the input array
  401.             final T[] extractedParameters = this.extractParameters(parameters, auxiliaryElements.getDate());

  402.             // Container of attributes
  403.             final FieldDSSTThirdBodyDynamicContext<T> context = initializeStep(auxiliaryElements, extractedParameters);

  404.             // a / R3 up to power maxAR3Pow
  405.             final T[] aoR3Pow = computeAoR3Pow(context);

  406.             // Qns coefficients
  407.             final T[][] Qns = CoefficientsFactory.computeQns(context.getGamma(), staticContext.getMaxAR3Pow(), FastMath.max(staticContext.getMaxEccPow(), MAX_ECCPOWER_SP));

  408.             final FieldGeneratingFunctionCoefficients<T> gfCoefs =
  409.                             new FieldGeneratingFunctionCoefficients<>(staticContext.getMaxAR3Pow(), MAX_ECCPOWER_SP, staticContext.getMaxAR3Pow() + 1,
  410.                                                                       context, (FieldHansenObjects<T>) fieldHansen.get(field), field, aoR3Pow, Qns);

  411.             //Compute additional quantities
  412.             // 2 * a / An
  413.             final T ax2oAn  = context.getM2aoA().negate().divide(context.getMeanMotion());
  414.             // B / An
  415.             final T BoAn    = context.getBoA().divide(context.getMeanMotion());
  416.             // 1 / ABn
  417.             final T ooABn   = context.getOoAB().divide(context.getMeanMotion());
  418.             // C / 2ABn
  419.             final T Co2ABn  = context.getMCo2AB().negate().divide(context.getMeanMotion());
  420.             // B / (A * (1 + B) * n)
  421.             final T BoABpon = context.getBoABpo().divide(context.getMeanMotion());
  422.             // -3 / n²a² = -3 / nA
  423.             final T m3onA   = context.getA().multiply(context.getMeanMotion()).divide(-3.).reciprocal();

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

  428.                 // Compute the cross derivatives operator :
  429.                 final T SAlphaGammaCj    = context.getAlpha().multiply(gfCoefs.getdSdgammaCj(j)).subtract(context.getGamma().multiply(gfCoefs.getdSdalphaCj(j)));
  430.                 final T SAlphaBetaCj     = context.getAlpha().multiply(gfCoefs.getdSdbetaCj(j)).subtract(context.getBeta().multiply(gfCoefs.getdSdalphaCj(j)));
  431.                 final T SBetaGammaCj     = context.getBeta().multiply(gfCoefs.getdSdgammaCj(j)).subtract(context.getGamma().multiply(gfCoefs.getdSdbetaCj(j)));
  432.                 final T ShkCj            = auxiliaryElements.getH().multiply(gfCoefs.getdSdkCj(j)).subtract(auxiliaryElements.getK().multiply(gfCoefs.getdSdhCj(j)));
  433.                 final T pSagmIqSbgoABnCj = ooABn.multiply(auxiliaryElements.getP().multiply(SAlphaGammaCj).subtract(auxiliaryElements.getQ().multiply(SBetaGammaCj).multiply(I)));
  434.                 final T ShkmSabmdSdlCj   = ShkCj.subtract(SAlphaBetaCj).subtract(gfCoefs.getdSdlambdaCj(j));

  435.                 currentCij[0] = ax2oAn.multiply(gfCoefs.getdSdlambdaCj(j));
  436.                 currentCij[1] = BoAn.multiply(gfCoefs.getdSdhCj(j)).add(auxiliaryElements.getH().multiply(pSagmIqSbgoABnCj)).add(auxiliaryElements.getK().multiply(BoABpon).multiply(gfCoefs.getdSdlambdaCj(j))).negate();
  437.                 currentCij[2] = BoAn.multiply(gfCoefs.getdSdkCj(j)).add(auxiliaryElements.getK().multiply(pSagmIqSbgoABnCj)).subtract(auxiliaryElements.getH().multiply(BoABpon).multiply(gfCoefs.getdSdlambdaCj(j)));
  438.                 currentCij[3] = Co2ABn.multiply(auxiliaryElements.getQ().multiply(ShkmSabmdSdlCj).subtract(SAlphaGammaCj.multiply(I)));
  439.                 currentCij[4] = Co2ABn.multiply(auxiliaryElements.getP().multiply(ShkmSabmdSdlCj).subtract(SBetaGammaCj));
  440.                 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)));

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

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

  445.                 // Compute the cross derivatives operator :
  446.                 final T SAlphaGammaSj    = context.getAlpha().multiply(gfCoefs.getdSdgammaSj(j)).subtract(context.getGamma().multiply(gfCoefs.getdSdalphaSj(j)));
  447.                 final T SAlphaBetaSj     = context.getAlpha().multiply(gfCoefs.getdSdbetaSj(j)).subtract(context.getBeta().multiply(gfCoefs.getdSdalphaSj(j)));
  448.                 final T SBetaGammaSj     = context.getBeta().multiply(gfCoefs.getdSdgammaSj(j)).subtract(context.getGamma().multiply(gfCoefs.getdSdbetaSj(j)));
  449.                 final T ShkSj            = auxiliaryElements.getH().multiply(gfCoefs.getdSdkSj(j)).subtract(auxiliaryElements.getK().multiply(gfCoefs.getdSdhSj(j)));
  450.                 final T pSagmIqSbgoABnSj = ooABn.multiply(auxiliaryElements.getP().multiply(SAlphaGammaSj).subtract(auxiliaryElements.getQ().multiply(SBetaGammaSj).multiply(I)));
  451.                 final T ShkmSabmdSdlSj   = ShkSj.subtract(SAlphaBetaSj).subtract(gfCoefs.getdSdlambdaSj(j));

  452.                 currentSij[0] = ax2oAn.multiply(gfCoefs.getdSdlambdaSj(j));
  453.                 currentSij[1] = BoAn.multiply(gfCoefs.getdSdhSj(j)).add(auxiliaryElements.getH().multiply(pSagmIqSbgoABnSj)).add(auxiliaryElements.getK().multiply(BoABpon).multiply(gfCoefs.getdSdlambdaSj(j))).negate();
  454.                 currentSij[2] = BoAn.multiply(gfCoefs.getdSdkSj(j)).add(auxiliaryElements.getK().multiply(pSagmIqSbgoABnSj)).subtract(auxiliaryElements.getH().multiply(BoABpon).multiply(gfCoefs.getdSdlambdaSj(j)));
  455.                 currentSij[3] = Co2ABn.multiply(auxiliaryElements.getQ().multiply(ShkmSabmdSdlSj).subtract(SAlphaGammaSj.multiply(I)));
  456.                 currentSij[4] = Co2ABn.multiply(auxiliaryElements.getP().multiply(ShkmSabmdSdlSj).subtract(SBetaGammaSj));
  457.                 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)));

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

  460.                 if (j == 1) {
  461.                     //Compute the C⁰ coefficients using Danielson 2.5.2-15a.
  462.                     final T[] value = MathArrays.buildArray(field, 6);
  463.                     for (int i = 0; i < 6; ++i) {
  464.                         value[i] = currentCij[i].multiply(auxiliaryElements.getK()).divide(2.).add(currentSij[i].multiply(auxiliaryElements.getH()).divide(2.));
  465.                     }
  466.                     slot.cij[0].addGridPoint(meanState.getDate(), value);
  467.                 }
  468.             }
  469.         }
  470.     }

  471.     /**
  472.      * Computes a / R3 to the power maxAR3Pow.
  473.      * @param context force model dynamic context
  474.      * @return aoR3Pow array
  475.      */
  476.     private double[] computeAoR3Pow(final DSSTThirdBodyDynamicContext context) {
  477.         // a / R3 up to power maxAR3Pow
  478.         final double aoR3 = context.getAuxiliaryElements().getSma() / context.getR3();
  479.         final double[] aoR3Pow = new double[staticContext.getMaxAR3Pow() + 1];
  480.         aoR3Pow[0] = 1.;
  481.         for (int i = 1; i <= staticContext.getMaxAR3Pow(); i++) {
  482.             aoR3Pow[i] = aoR3 * aoR3Pow[i - 1];
  483.         }
  484.         return aoR3Pow;
  485.     }

  486.     /**
  487.      * Computes a / R3 to the power maxAR3Pow.
  488.      * @param context force model dynamic context
  489.      * @param <T> type of the elements
  490.      * @return aoR3Pow array
  491.      */
  492.     private <T extends CalculusFieldElement<T>> T[] computeAoR3Pow(final FieldDSSTThirdBodyDynamicContext<T> context) {
  493.         final Field<T> field = context.getA().getField();
  494.         // a / R3 up to power maxAR3Pow
  495.         final T aoR3 = context.getFieldAuxiliaryElements().getSma().divide(context.getR3());
  496.         final T[] aoR3Pow = MathArrays.buildArray(field, staticContext.getMaxAR3Pow() + 1);
  497.         aoR3Pow[0] = field.getOne();
  498.         for (int i = 1; i <= staticContext.getMaxAR3Pow(); i++) {
  499.             aoR3Pow[i] = aoR3.multiply(aoR3Pow[i - 1]);
  500.         }
  501.         return aoR3Pow;
  502.     }

  503.     /** {@inheritDoc} */
  504.     @Override
  505.     public void registerAttitudeProvider(final AttitudeProvider provider) {
  506.         //nothing is done since this contribution is not sensitive to attitude
  507.     }

  508.     /** {@inheritDoc} */
  509.     @Override
  510.     public List<ParameterDriver> getParametersDrivers() {
  511.         return Collections.unmodifiableList(parameterDrivers);
  512.     }

  513.     /** Computes the C<sup>j</sup> and S<sup>j</sup> coefficients Danielson 4.2-(15,16)
  514.      * and their derivatives.
  515.      *  <p>
  516.      *  CS Mathematical Report $3.5.3.2
  517.      *  </p>
  518.      */
  519.     private class FourierCjSjCoefficients {

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

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

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

  526.         /** The Fourier coefficients C<sup>j</sup> and their derivatives.
  527.          * <p>
  528.          * Each column of the matrix contains the following values: <br/>
  529.          * - C<sup>j</sup> <br/>
  530.          * - dC<sup>j</sup> / da <br/>
  531.          * - dC<sup>j</sup> / dk <br/>
  532.          * - dC<sup>j</sup> / dh <br/>
  533.          * - dC<sup>j</sup> / dα <br/>
  534.          * - dC<sup>j</sup> / dβ <br/>
  535.          * - dC<sup>j</sup> / dγ <br/>
  536.          * </p>
  537.          */
  538.         private final double[][] cj;

  539.         /** The S<sup>j</sup> coefficients and their derivatives.
  540.          * <p>
  541.          * Each column of the matrix contains the following values: <br/>
  542.          * - S<sup>j</sup> <br/>
  543.          * - dS<sup>j</sup> / da <br/>
  544.          * - dS<sup>j</sup> / dk <br/>
  545.          * - dS<sup>j</sup> / dh <br/>
  546.          * - dS<sup>j</sup> / dα <br/>
  547.          * - dS<sup>j</sup> / dβ <br/>
  548.          * - dS<sup>j</sup> / dγ <br/>
  549.          * </p>
  550.          */
  551.         private final double[][] sj;

  552.         /** The Coefficients C<sup>j</sup><sub>,λ</sub>.
  553.          * <p>
  554.          * See Danielson 4.2-21
  555.          * </p>
  556.          */
  557.         private final double[] cjlambda;

  558.         /** The Coefficients S<sup>j</sup><sub>,λ</sub>.
  559.         * <p>
  560.         * See Danielson 4.2-21
  561.         * </p>
  562.         */
  563.         private final double[] sjlambda;

  564.         /** Maximum value for n. */
  565.         private final int nMax;

  566.         /** Maximum value for s. */
  567.         private final int sMax;

  568.         /** Maximum value for j. */
  569.         private final int jMax;

  570.         /**
  571.          * Private constructor.
  572.          *
  573.          * @param nMax maximum value for n index
  574.          * @param sMax maximum value for s index
  575.          * @param jMax maximum value for j index
  576.          * @param context container for dynamic force model attributes
  577.          * @param aoR3Pow a / R3 up to power maxAR3Pow
  578.          * @param qns Qns coefficients
  579.          */
  580.         FourierCjSjCoefficients(final int nMax, final int sMax, final int jMax,
  581.                                 final DSSTThirdBodyDynamicContext context,
  582.                                 final double[] aoR3Pow, final double[][] qns) {
  583.             //Save parameters
  584.             this.nMax = nMax;
  585.             this.sMax = sMax;
  586.             this.jMax = jMax;

  587.             //Create objects
  588.             wnsjEtomjmsCoefficient = new WnsjEtomjmsCoefficient(context);
  589.             ABDECoefficients = new CjSjAlphaBetaKH(context);
  590.             gns = new GnsCoefficients(nMax, sMax, context, aoR3Pow, qns);

  591.             //create arays
  592.             this.cj = new double[7][jMax + 1];
  593.             this.sj = new double[7][jMax + 1];
  594.             this.cjlambda = new double[jMax];
  595.             this.sjlambda = new double[jMax];

  596.             computeCoefficients(context);
  597.         }

  598.         /**
  599.          * Compute all coefficients.
  600.          * @param context container for attributes
  601.          */
  602.         private void computeCoefficients(final DSSTThirdBodyDynamicContext context) {

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

  604.             for (int j = 1; j <= jMax; j++) {
  605.                 // initialise the coefficients
  606.                 for (int i = 0; i <= 6; i++) {
  607.                     cj[i][j] = 0.;
  608.                     sj[i][j] = 0.;
  609.                 }
  610.                 if (j < jMax) {
  611.                     // initialise the C<sup>j</sup><sub>,λ</sub> and S<sup>j</sup><sub>,λ</sub> coefficients
  612.                     cjlambda[j] = 0.;
  613.                     sjlambda[j] = 0.;
  614.                 }
  615.                 for (int s = 0; s <= sMax; s++) {

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

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

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

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

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

  630.                             //Compute C<sup>j</sup>
  631.                             cj[0][j] += gns.getGns(n, s) * coef1;
  632.                             //Compute dC<sup>j</sup> / da
  633.                             cj[1][j] += gns.getdGnsda(n, s) * coef1;
  634.                             //Compute dC<sup>j</sup> / dk
  635.                             cj[2][j] += -gns.getGns(n, s) *
  636.                                         (
  637.                                             wjnp1semjms[1] * ABDECoefficients.getCoefA() +
  638.                                             wjnp1semjms[0] * ABDECoefficients.getdCoefAdk() +
  639.                                             wmjnp1semjms[1] * ABDECoefficients.getCoefB() +
  640.                                             wmjnp1semjms[0] * ABDECoefficients.getdCoefBdk()
  641.                                          );
  642.                             //Compute dC<sup>j</sup> / dh
  643.                             cj[3][j] += -gns.getGns(n, s) *
  644.                                         (
  645.                                             wjnp1semjms[2] * ABDECoefficients.getCoefA() +
  646.                                             wjnp1semjms[0] * ABDECoefficients.getdCoefAdh() +
  647.                                             wmjnp1semjms[2] * ABDECoefficients.getCoefB() +
  648.                                             wmjnp1semjms[0] * ABDECoefficients.getdCoefBdh()
  649.                                          );
  650.                             //Compute dC<sup>j</sup> / dα
  651.                             cj[4][j] += -gns.getGns(n, s) *
  652.                                         (
  653.                                             wjnp1semjms[0] * ABDECoefficients.getdCoefAdalpha() +
  654.                                             wmjnp1semjms[0] * ABDECoefficients.getdCoefBdalpha()
  655.                                         );
  656.                             //Compute dC<sup>j</sup> / dβ
  657.                             cj[5][j] += -gns.getGns(n, s) *
  658.                                         (
  659.                                             wjnp1semjms[0] * ABDECoefficients.getdCoefAdbeta() +
  660.                                             wmjnp1semjms[0] * ABDECoefficients.getdCoefBdbeta()
  661.                                         );
  662.                             //Compute dC<sup>j</sup> / dγ
  663.                             cj[6][j] += gns.getdGnsdgamma(n, s) * coef1;

  664.                             //Compute S<sup>j</sup>
  665.                             sj[0][j] += gns.getGns(n, s) * coef2;
  666.                             //Compute dS<sup>j</sup> / da
  667.                             sj[1][j] += gns.getdGnsda(n, s) * coef2;
  668.                             //Compute dS<sup>j</sup> / dk
  669.                             sj[2][j] += gns.getGns(n, s) *
  670.                                         (
  671.                                             wjnp1semjms[1] * ABDECoefficients.getCoefD() +
  672.                                             wjnp1semjms[0] * ABDECoefficients.getdCoefDdk() +
  673.                                             wmjnp1semjms[1] * ABDECoefficients.getCoefE() +
  674.                                             wmjnp1semjms[0] * ABDECoefficients.getdCoefEdk()
  675.                                          );
  676.                             //Compute dS<sup>j</sup> / dh
  677.                             sj[3][j] += gns.getGns(n, s) *
  678.                                         (
  679.                                             wjnp1semjms[2] * ABDECoefficients.getCoefD() +
  680.                                             wjnp1semjms[0] * ABDECoefficients.getdCoefDdh() +
  681.                                             wmjnp1semjms[2] * ABDECoefficients.getCoefE() +
  682.                                             wmjnp1semjms[0] * ABDECoefficients.getdCoefEdh()
  683.                                          );
  684.                             //Compute dS<sup>j</sup> / dα
  685.                             sj[4][j] += gns.getGns(n, s) *
  686.                                         (
  687.                                             wjnp1semjms[0] * ABDECoefficients.getdCoefDdalpha() +
  688.                                             wmjnp1semjms[0] * ABDECoefficients.getdCoefEdalpha()
  689.                                         );
  690.                             //Compute dS<sup>j</sup> / dβ
  691.                             sj[5][j] += gns.getGns(n, s) *
  692.                                         (
  693.                                             wjnp1semjms[0] * ABDECoefficients.getdCoefDdbeta() +
  694.                                             wmjnp1semjms[0] * ABDECoefficients.getdCoefEdbeta()
  695.                                         );
  696.                             //Compute dS<sup>j</sup> / dγ
  697.                             sj[6][j] += gns.getdGnsdgamma(n, s) * coef2;

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

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

  704.                                 //Compute C<sup>j</sup><sub>,λ</sub>
  705.                                 cjlambda[j] += gns.getGns(n, s) * (wjnsemjms[0] * ABDECoefficients.getCoefD() + wmjnsemjms[0] * ABDECoefficients.getCoefE());
  706.                                 //Compute S<sup>j</sup><sub>,λ</sub>
  707.                                 sjlambda[j] += gns.getGns(n, s) * (wjnsemjms[0] * ABDECoefficients.getCoefA() + wmjnsemjms[0] * ABDECoefficients.getCoefB());
  708.                             }
  709.                         }
  710.                     }
  711.                 }
  712.                 // Divide by j
  713.                 for (int i = 0; i <= 6; i++) {
  714.                     cj[i][j] /= j;
  715.                     sj[i][j] /= j;
  716.                 }
  717.             }
  718.             //The C⁰ coefficients are not computed here.
  719.             //They are evaluated at the final point.

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

  723.         /** Get the Fourier coefficient C<sup>j</sup>.
  724.          * @param j j index
  725.          * @return C<sup>j</sup>
  726.          */
  727.         public double getCj(final int j) {
  728.             return cj[0][j];
  729.         }

  730.         /** Get the derivative dC<sup>j</sup>/da.
  731.          * @param j j index
  732.          * @return dC<sup>j</sup>/da
  733.          */
  734.         public double getdCjda(final int j) {
  735.             return cj[1][j];
  736.         }

  737.         /** Get the derivative dC<sup>j</sup>/dk.
  738.          * @param j j index
  739.          * @return dC<sup>j</sup>/dk
  740.          */
  741.         public double getdCjdk(final int j) {
  742.             return cj[2][j];
  743.         }

  744.         /** Get the derivative dC<sup>j</sup>/dh.
  745.          * @param j j index
  746.          * @return dC<sup>j</sup>/dh
  747.          */
  748.         public double getdCjdh(final int j) {
  749.             return cj[3][j];
  750.         }

  751.         /** Get the derivative dC<sup>j</sup>/dα.
  752.          * @param j j index
  753.          * @return dC<sup>j</sup>/dα
  754.          */
  755.         public double getdCjdalpha(final int j) {
  756.             return cj[4][j];
  757.         }

  758.         /** Get the derivative dC<sup>j</sup>/dβ.
  759.          * @param j j index
  760.          * @return dC<sup>j</sup>/dβ
  761.          */
  762.         public double getdCjdbeta(final int j) {
  763.             return cj[5][j];
  764.         }

  765.         /** Get the derivative dC<sup>j</sup>/dγ.
  766.          * @param j j index
  767.          * @return dC<sup>j</sup>/dγ
  768.          */
  769.         public double getdCjdgamma(final int j) {
  770.             return cj[6][j];
  771.         }

  772.         /** Get the Fourier coefficient S<sup>j</sup>.
  773.          * @param j j index
  774.          * @return S<sup>j</sup>
  775.          */
  776.         public double getSj(final int j) {
  777.             return sj[0][j];
  778.         }

  779.         /** Get the derivative dS<sup>j</sup>/da.
  780.          * @param j j index
  781.          * @return dS<sup>j</sup>/da
  782.          */
  783.         public double getdSjda(final int j) {
  784.             return sj[1][j];
  785.         }

  786.         /** Get the derivative dS<sup>j</sup>/dk.
  787.          * @param j j index
  788.          * @return dS<sup>j</sup>/dk
  789.          */
  790.         public double getdSjdk(final int j) {
  791.             return sj[2][j];
  792.         }

  793.         /** Get the derivative dS<sup>j</sup>/dh.
  794.          * @param j j index
  795.          * @return dS<sup>j</sup>/dh
  796.          */
  797.         public double getdSjdh(final int j) {
  798.             return sj[3][j];
  799.         }

  800.         /** Get the derivative dS<sup>j</sup>/dα.
  801.          * @param j j index
  802.          * @return dS<sup>j</sup>/dα
  803.          */
  804.         public double getdSjdalpha(final int j) {
  805.             return sj[4][j];
  806.         }

  807.         /** Get the derivative dS<sup>j</sup>/dβ.
  808.          * @param j j index
  809.          * @return dS<sup>j</sup>/dβ
  810.          */
  811.         public double getdSjdbeta(final int j) {
  812.             return sj[5][j];
  813.         }

  814.         /** Get the derivative dS<sup>j</sup>/dγ.
  815.          * @param j j index
  816.          * @return dS<sup>j</sup>/dγ
  817.          */
  818.         public double getdSjdgamma(final int j) {
  819.             return sj[6][j];
  820.         }

  821.         /** Get the coefficient C⁰<sub>,λ</sub>.
  822.          * @return C⁰<sub>,λ</sub>
  823.          */
  824.         public double getC0Lambda() {
  825.             return cjlambda[0];
  826.         }

  827.         /** Get the coefficient C<sup>j</sup><sub>,λ</sub>.
  828.          * @param j j index
  829.          * @return C<sup>j</sup><sub>,λ</sub>
  830.          */
  831.         public double getCjLambda(final int j) {
  832.             if (j < 1 || j >= jMax) {
  833.                 return 0.;
  834.             }
  835.             return cjlambda[j];
  836.         }

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

  848.     /** Computes the C<sup>j</sup> and S<sup>j</sup> coefficients Danielson 4.2-(15,16)
  849.      * and their derivatives.
  850.      *  <p>
  851.      *  CS Mathematical Report $3.5.3.2
  852.      *  </p>
  853.      */
  854.     private class FieldFourierCjSjCoefficients <T extends CalculusFieldElement<T>> {

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

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

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

  861.         /** The Fourier coefficients C<sup>j</sup> and their derivatives.
  862.          * <p>
  863.          * Each column of the matrix contains the following values: <br/>
  864.          * - C<sup>j</sup> <br/>
  865.          * - dC<sup>j</sup> / da <br/>
  866.          * - dC<sup>j</sup> / dk <br/>
  867.          * - dC<sup>j</sup> / dh <br/>
  868.          * - dC<sup>j</sup> / dα <br/>
  869.          * - dC<sup>j</sup> / dβ <br/>
  870.          * - dC<sup>j</sup> / dγ <br/>
  871.          * </p>
  872.          */
  873.         private final T[][] cj;

  874.         /** The S<sup>j</sup> coefficients and their derivatives.
  875.          * <p>
  876.          * Each column of the matrix contains the following values: <br/>
  877.          * - S<sup>j</sup> <br/>
  878.          * - dS<sup>j</sup> / da <br/>
  879.          * - dS<sup>j</sup> / dk <br/>
  880.          * - dS<sup>j</sup> / dh <br/>
  881.          * - dS<sup>j</sup> / dα <br/>
  882.          * - dS<sup>j</sup> / dβ <br/>
  883.          * - dS<sup>j</sup> / dγ <br/>
  884.          * </p>
  885.          */
  886.         private final T[][] sj;

  887.         /** The Coefficients C<sup>j</sup><sub>,λ</sub>.
  888.          * <p>
  889.          * See Danielson 4.2-21
  890.          * </p>
  891.          */
  892.         private final T[] cjlambda;

  893.         /** The Coefficients S<sup>j</sup><sub>,λ</sub>.
  894.         * <p>
  895.         * See Danielson 4.2-21
  896.         * </p>
  897.         */
  898.         private final T[] sjlambda;

  899.         /** Zero. */
  900.         private final T zero;

  901.         /** Maximum value for n. */
  902.         private final int nMax;

  903.         /** Maximum value for s. */
  904.         private final int sMax;

  905.         /** Maximum value for j. */
  906.         private final int jMax;

  907.         /**
  908.          * Private constructor.
  909.          *
  910.          * @param nMax maximum value for n index
  911.          * @param sMax maximum value for s index
  912.          * @param jMax maximum value for j index
  913.          * @param context container for dynamic force model attributes
  914.          * @param aoR3Pow a / R3 up to power maxAR3Pow
  915.          * @param qns Qns coefficients
  916.          * @param field field used by default
  917.          */
  918.         FieldFourierCjSjCoefficients(final int nMax, final int sMax, final int jMax,
  919.                                      final FieldDSSTThirdBodyDynamicContext<T> context,
  920.                                      final T[] aoR3Pow, final T[][] qns,
  921.                                      final Field<T> field) {
  922.             //Zero
  923.             this.zero = field.getZero();

  924.             //Save parameters
  925.             this.nMax = nMax;
  926.             this.sMax = sMax;
  927.             this.jMax = jMax;

  928.             //Create objects
  929.             wnsjEtomjmsCoefficient = new FieldWnsjEtomjmsCoefficient<>(context, field);
  930.             ABDECoefficients       = new FieldCjSjAlphaBetaKH<>(context, field);
  931.             gns                    = new FieldGnsCoefficients<>(nMax, sMax, context, aoR3Pow, qns, field);

  932.             //create arays
  933.             this.cj = MathArrays.buildArray(field, 7, jMax + 1);
  934.             this.sj = MathArrays.buildArray(field, 7, jMax + 1);
  935.             this.cjlambda = MathArrays.buildArray(field, jMax);
  936.             this.sjlambda = MathArrays.buildArray(field, jMax);

  937.             computeCoefficients(context, field);
  938.         }

  939.         /**
  940.          * Compute all coefficients.
  941.          * @param context container for attributes
  942.          * @param field field used by default
  943.          */
  944.         private void computeCoefficients(final FieldDSSTThirdBodyDynamicContext<T> context,
  945.                                          final Field<T> field) {

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

  947.             for (int j = 1; j <= jMax; j++) {
  948.                 // initialise the coefficients
  949.                 for (int i = 0; i <= 6; i++) {
  950.                     cj[i][j] = zero;
  951.                     sj[i][j] = zero;
  952.                 }
  953.                 if (j < jMax) {
  954.                     // initialise the C<sup>j</sup><sub>,λ</sub> and S<sup>j</sup><sub>,λ</sub> coefficients
  955.                     cjlambda[j] = zero;
  956.                     sjlambda[j] = zero;
  957.                 }
  958.                 for (int s = 0; s <= sMax; s++) {

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

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

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

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

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

  973.                             //Compute C<sup>j</sup>
  974.                             cj[0][j] = cj[0][j].add(gns.getGns(n, s).multiply(coef1));
  975.                             //Compute dC<sup>j</sup> / da
  976.                             cj[1][j] = cj[1][j].add(gns.getdGnsda(n, s).multiply(coef1));
  977.                             //Compute dC<sup>j</sup> / dk
  978.                             cj[2][j] = cj[2][j].add(gns.getGns(n, s).negate().
  979.                                        multiply(
  980.                                             wjnp1semjms[1].multiply(ABDECoefficients.getCoefA()).
  981.                                             add(wjnp1semjms[0].multiply(ABDECoefficients.getdCoefAdk())).
  982.                                             add(wmjnp1semjms[1].multiply(ABDECoefficients.getCoefB())).
  983.                                             add(wmjnp1semjms[0].multiply(ABDECoefficients.getdCoefBdk()))
  984.                                          ));
  985.                             //Compute dC<sup>j</sup> / dh
  986.                             cj[3][j] = cj[3][j].add(gns.getGns(n, s).negate().
  987.                                        multiply(
  988.                                              wjnp1semjms[2].multiply(ABDECoefficients.getCoefA()).
  989.                                              add(wjnp1semjms[0].multiply(ABDECoefficients.getdCoefAdh())).
  990.                                              add(wmjnp1semjms[2].multiply(ABDECoefficients.getCoefB())).
  991.                                              add(wmjnp1semjms[0].multiply(ABDECoefficients.getdCoefBdh()))
  992.                                              ));
  993.                             //Compute dC<sup>j</sup> / dα
  994.                             cj[4][j] = cj[4][j].add(gns.getGns(n, s).negate().
  995.                                        multiply(
  996.                                            wjnp1semjms[0].multiply(ABDECoefficients.getdCoefAdalpha()).
  997.                                            add(wmjnp1semjms[0].multiply(ABDECoefficients.getdCoefBdalpha()))
  998.                                        ));
  999.                             //Compute dC<sup>j</sup> / dβ
  1000.                             cj[5][j] = cj[5][j].add(gns.getGns(n, s).negate().
  1001.                                        multiply(
  1002.                                            wjnp1semjms[0].multiply(ABDECoefficients.getdCoefAdbeta()).
  1003.                                            add(wmjnp1semjms[0].multiply(ABDECoefficients.getdCoefBdbeta()))
  1004.                                        ));
  1005.                             //Compute dC<sup>j</sup> / dγ
  1006.                             cj[6][j] = cj[6][j].add(gns.getdGnsdgamma(n, s).multiply(coef1));

  1007.                             //Compute S<sup>j</sup>
  1008.                             sj[0][j] = sj[0][j].add(gns.getGns(n, s).multiply(coef2));
  1009.                             //Compute dS<sup>j</sup> / da
  1010.                             sj[1][j] = sj[1][j].add(gns.getdGnsda(n, s).multiply(coef2));
  1011.                             //Compute dS<sup>j</sup> / dk
  1012.                             sj[2][j] = sj[2][j].add(gns.getGns(n, s).
  1013.                                        multiply(
  1014.                                            wjnp1semjms[1].multiply(ABDECoefficients.getCoefD()).
  1015.                                            add(wjnp1semjms[0].multiply(ABDECoefficients.getdCoefDdk())).
  1016.                                            add(wmjnp1semjms[1].multiply(ABDECoefficients.getCoefE())).
  1017.                                            add(wmjnp1semjms[0].multiply(ABDECoefficients.getdCoefEdk()))
  1018.                                        ));
  1019.                             //Compute dS<sup>j</sup> / dh
  1020.                             sj[3][j] = sj[3][j].add(gns.getGns(n, s).
  1021.                                        multiply(
  1022.                                            wjnp1semjms[2].multiply(ABDECoefficients.getCoefD()).
  1023.                                            add(wjnp1semjms[0].multiply(ABDECoefficients.getdCoefDdh())).
  1024.                                            add(wmjnp1semjms[2].multiply(ABDECoefficients.getCoefE())).
  1025.                                            add(wmjnp1semjms[0].multiply(ABDECoefficients.getdCoefEdh()))
  1026.                                        ));
  1027.                             //Compute dS<sup>j</sup> / dα
  1028.                             sj[4][j] = sj[4][j].add(gns.getGns(n, s).
  1029.                                        multiply(
  1030.                                             wjnp1semjms[0].multiply(ABDECoefficients.getdCoefDdalpha()).
  1031.                                             add(wmjnp1semjms[0].multiply(ABDECoefficients.getdCoefEdalpha()))
  1032.                                        ));
  1033.                             //Compute dS<sup>j</sup> / dβ
  1034.                             sj[5][j] = sj[5][j].add(gns.getGns(n, s).
  1035.                                         multiply(
  1036.                                             wjnp1semjms[0].multiply(ABDECoefficients.getdCoefDdbeta()).
  1037.                                             add(wmjnp1semjms[0].multiply(ABDECoefficients.getdCoefEdbeta()))
  1038.                                        ));
  1039.                             //Compute dS<sup>j</sup> / dγ
  1040.                             sj[6][j] = sj[6][j].add(gns.getdGnsdgamma(n, s).multiply(coef2));

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

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

  1047.                                 //Compute C<sup>j</sup><sub>,λ</sub>
  1048.                                 cjlambda[j] = cjlambda[j].add(gns.getGns(n, s).multiply(wjnsemjms[0].multiply(ABDECoefficients.getCoefD()).add(wmjnsemjms[0].multiply(ABDECoefficients.getCoefE()))));
  1049.                                 //Compute S<sup>j</sup><sub>,λ</sub>
  1050.                                 sjlambda[j] = sjlambda[j].add(gns.getGns(n, s).multiply(wjnsemjms[0].multiply(ABDECoefficients.getCoefA()).add(wmjnsemjms[0].multiply(ABDECoefficients.getCoefB()))));
  1051.                             }
  1052.                         }
  1053.                     }
  1054.                 }
  1055.                 // Divide by j
  1056.                 for (int i = 0; i <= 6; i++) {
  1057.                     cj[i][j] = cj[i][j].divide(j);
  1058.                     sj[i][j] = sj[i][j].divide(j);
  1059.                 }
  1060.             }
  1061.             //The C⁰ coefficients are not computed here.
  1062.             //They are evaluated at the final point.

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

  1066.         /** Get the Fourier coefficient C<sup>j</sup>.
  1067.          * @param j j index
  1068.          * @return C<sup>j</sup>
  1069.          */
  1070.         public T getCj(final int j) {
  1071.             return cj[0][j];
  1072.         }

  1073.         /** Get the derivative dC<sup>j</sup>/da.
  1074.          * @param j j index
  1075.          * @return dC<sup>j</sup>/da
  1076.          */
  1077.         public T getdCjda(final int j) {
  1078.             return cj[1][j];
  1079.         }

  1080.         /** Get the derivative dC<sup>j</sup>/dk.
  1081.          * @param j j index
  1082.          * @return dC<sup>j</sup>/dk
  1083.          */
  1084.         public T getdCjdk(final int j) {
  1085.             return cj[2][j];
  1086.         }

  1087.         /** Get the derivative dC<sup>j</sup>/dh.
  1088.          * @param j j index
  1089.          * @return dC<sup>j</sup>/dh
  1090.          */
  1091.         public T getdCjdh(final int j) {
  1092.             return cj[3][j];
  1093.         }

  1094.         /** Get the derivative dC<sup>j</sup>/dα.
  1095.          * @param j j index
  1096.          * @return dC<sup>j</sup>/dα
  1097.          */
  1098.         public T getdCjdalpha(final int j) {
  1099.             return cj[4][j];
  1100.         }

  1101.         /** Get the derivative dC<sup>j</sup>/dβ.
  1102.          * @param j j index
  1103.          * @return dC<sup>j</sup>/dβ
  1104.          */
  1105.         public T getdCjdbeta(final int j) {
  1106.             return cj[5][j];
  1107.         }

  1108.         /** Get the derivative dC<sup>j</sup>/dγ.
  1109.          * @param j j index
  1110.          * @return dC<sup>j</sup>/dγ
  1111.          */
  1112.         public T getdCjdgamma(final int j) {
  1113.             return cj[6][j];
  1114.         }

  1115.         /** Get the Fourier coefficient S<sup>j</sup>.
  1116.          * @param j j index
  1117.          * @return S<sup>j</sup>
  1118.          */
  1119.         public T getSj(final int j) {
  1120.             return sj[0][j];
  1121.         }

  1122.         /** Get the derivative dS<sup>j</sup>/da.
  1123.          * @param j j index
  1124.          * @return dS<sup>j</sup>/da
  1125.          */
  1126.         public T getdSjda(final int j) {
  1127.             return sj[1][j];
  1128.         }

  1129.         /** Get the derivative dS<sup>j</sup>/dk.
  1130.          * @param j j index
  1131.          * @return dS<sup>j</sup>/dk
  1132.          */
  1133.         public T getdSjdk(final int j) {
  1134.             return sj[2][j];
  1135.         }

  1136.         /** Get the derivative dS<sup>j</sup>/dh.
  1137.          * @param j j index
  1138.          * @return dS<sup>j</sup>/dh
  1139.          */
  1140.         public T getdSjdh(final int j) {
  1141.             return sj[3][j];
  1142.         }

  1143.         /** Get the derivative dS<sup>j</sup>/dα.
  1144.          * @param j j index
  1145.          * @return dS<sup>j</sup>/dα
  1146.          */
  1147.         public T getdSjdalpha(final int j) {
  1148.             return sj[4][j];
  1149.         }

  1150.         /** Get the derivative dS<sup>j</sup>/dβ.
  1151.          * @param j j index
  1152.          * @return dS<sup>j</sup>/dβ
  1153.          */
  1154.         public T getdSjdbeta(final int j) {
  1155.             return sj[5][j];
  1156.         }

  1157.         /** Get the derivative dS<sup>j</sup>/dγ.
  1158.          * @param j j index
  1159.          * @return dS<sup>j</sup>/dγ
  1160.          */
  1161.         public T getdSjdgamma(final int j) {
  1162.             return sj[6][j];
  1163.         }

  1164.         /** Get the coefficient C⁰<sub>,λ</sub>.
  1165.          * @return C⁰<sub>,λ</sub>
  1166.          */
  1167.         public T getC0Lambda() {
  1168.             return cjlambda[0];
  1169.         }

  1170.         /** Get the coefficient C<sup>j</sup><sub>,λ</sub>.
  1171.          * @param j j index
  1172.          * @return C<sup>j</sup><sub>,λ</sub>
  1173.          */
  1174.         public T getCjLambda(final int j) {
  1175.             if (j < 1 || j >= jMax) {
  1176.                 return zero;
  1177.             }
  1178.             return cjlambda[j];
  1179.         }

  1180.         /** Get the coefficient S<sup>j</sup><sub>,λ</sub>.
  1181.          * @param j j index
  1182.          * @return S<sup>j</sup><sub>,λ</sub>
  1183.          */
  1184.         public T getSjLambda(final int j) {
  1185.             if (j < 1 || j >= jMax) {
  1186.                 return zero;
  1187.             }
  1188.             return sjlambda[j];
  1189.         }
  1190.     }

  1191.     /** 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.
  1192.      *
  1193.      * <p>
  1194.      * Starting from Danielson 4.2-9,10,11 and taking into account that fact that: <br />
  1195.      * c = e / (1 + (1 - e²)<sup>1/2</sup>) = e / (1 + B) = e * b <br/>
  1196.      * the expression e<sup>-|j-s|</sup>*w<sub>j</sub><sup>n, s</sup>
  1197.      * can be written as: <br/ >
  1198.      * - for |s| > |j| <br />
  1199.      * e<sup>-|j-s|</sup>*w<sub>j</sub><sup>n, s</sup> =
  1200.      *          (((n + s)!(n - s)!)/((n + j)!(n - j)!)) *
  1201.      *          (-b)<sup>|j-s|</sup> *
  1202.      *          ((1 - c²)<sup>n-|s|</sup>/(1 + c²)<sup>n</sup>) *
  1203.      *          P<sub>n-|s|</sub><sup>|j-s|, |j+s|</sup>(χ) <br />
  1204.      * <br />
  1205.      * - for |s| <= |j| <br />
  1206.      * e<sup>-|j-s|</sup>*w<sub>j</sub><sup>n, s</sup> =
  1207.      *          (-b)<sup>|j-s|</sup> *
  1208.      *          ((1 - c²)<sup>n-|j|</sup>/(1 + c²)<sup>n</sup>) *
  1209.      *          P<sub>n-|j|</sub><sup>|j-s|, |j+s|</sup>(χ)
  1210.      * </p>
  1211.      *
  1212.      * @author Lucian Barbulescu
  1213.      */
  1214.     private class WnsjEtomjmsCoefficient {

  1215.         /** The value c.
  1216.          * <p>
  1217.          *  c = e / (1 + (1 - e²)<sup>1/2</sup>) = e / (1 + B) = e * b <br/>
  1218.          * </p>
  1219.          *  */
  1220.         private final double c;

  1221.         /** db / dh. */
  1222.         private final double dbdh;

  1223.         /** db / dk. */
  1224.         private final double dbdk;

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

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

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

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

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

  1237.         /**
  1238.          * Standard constructor.
  1239.          * @param context container for attributes
  1240.          */
  1241.         WnsjEtomjmsCoefficient(final DSSTThirdBodyDynamicContext context) {

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

  1243.             //initialise fields
  1244.             c = auxiliaryElements.getEcc() * context.getb();
  1245.             final double c2 = c * c;

  1246.             //b² * χ
  1247.             final double b2Chi = context.getb() * context.getb() * context.getX();
  1248.             //Compute derivatives of b
  1249.             dbdh = auxiliaryElements.getH() * b2Chi;
  1250.             dbdk = auxiliaryElements.getK() * b2Chi;

  1251.             //Compute derivatives of c
  1252.             if (auxiliaryElements.getEcc() == 0.0) {
  1253.                 // we are at a perfectly circular orbit singularity here
  1254.                 // we arbitrarily consider the perigee is along the X axis,
  1255.                 // i.e cos(ω + Ω) = h/ecc 1 and sin(ω + Ω) = k/ecc = 0
  1256.                 dcdh = auxiliaryElements.getEcc() * dbdh + context.getb();
  1257.                 dcdk = auxiliaryElements.getEcc() * dbdk;
  1258.             } else {
  1259.                 dcdh = auxiliaryElements.getEcc() * dbdh + context.getb() * auxiliaryElements.getH() / auxiliaryElements.getEcc();
  1260.                 dcdk = auxiliaryElements.getEcc() * dbdk + context.getb() * auxiliaryElements.getK() / auxiliaryElements.getEcc();
  1261.             }

  1262.             //Compute the powers (1 - c²)<sup>n</sup> and (1 + c²)<sup>n</sup>
  1263.             omc2tn = new double[staticContext.getMaxAR3Pow() + staticContext.getMaxFreqF() + 2];
  1264.             opc2tn = new double[staticContext.getMaxAR3Pow() + staticContext.getMaxFreqF() + 2];
  1265.             final double omc2 = 1. - c2;
  1266.             final double opc2 = 1. + c2;
  1267.             omc2tn[0] = 1.;
  1268.             opc2tn[0] = 1.;
  1269.             for (int i = 1; i <= staticContext.getMaxAR3Pow() + staticContext.getMaxFreqF() + 1; i++) {
  1270.                 omc2tn[i] = omc2tn[i - 1] * omc2;
  1271.                 opc2tn[i] = opc2tn[i - 1] * opc2;
  1272.             }

  1273.             //Compute the powers of b
  1274.             btjms = new double[staticContext.getMaxAR3Pow() + staticContext.getMaxFreqF() + 1];
  1275.             btjms[0] = 1.;
  1276.             for (int i = 1; i <= staticContext.getMaxAR3Pow() + staticContext.getMaxFreqF(); i++) {
  1277.                 btjms[i] = btjms[i - 1] * context.getb();
  1278.             }
  1279.         }

  1280.         /** 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 />
  1281.          *
  1282.          * @param j j index
  1283.          * @param s s index
  1284.          * @param n n index
  1285.          * @param context container for attributes
  1286.          * @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
  1287.          */
  1288.         public double[] computeWjnsEmjmsAndDeriv(final int j, final int s, final int n, final DSSTThirdBodyDynamicContext context) {
  1289.             final double[] wjnsemjms = new double[] {0., 0., 0.};

  1290.             // |j|
  1291.             final int absJ = FastMath.abs(j);
  1292.             // |s|
  1293.             final int absS = FastMath.abs(s);
  1294.             // |j - s|
  1295.             final int absJmS = FastMath.abs(j - s);
  1296.             // |j + s|
  1297.             final int absJpS = FastMath.abs(j + s);

  1298.             //The lower index of P. Also the power of (1 - c²)
  1299.             final int l;
  1300.             // the factorial ratio coefficient or 1. if |s| <= |j|
  1301.             final double factCoef;
  1302.             if (absS > absJ) {
  1303.                 //factCoef = (fact[n + s] / fact[n + j]) * (fact[n - s] / fact[n - j]);
  1304.                 factCoef = (CombinatoricsUtils.factorialDouble(n + s) / CombinatoricsUtils.factorialDouble(n + j)) * (CombinatoricsUtils.factorialDouble(n - s) / CombinatoricsUtils.factorialDouble(n - j));
  1305.                 l = n - absS;
  1306.             } else {
  1307.                 factCoef = 1.;
  1308.                 l = n - absJ;
  1309.             }

  1310.             // (-1)<sup>|j-s|</sup>
  1311.             final double sign = absJmS % 2 != 0 ? -1. : 1.;
  1312.             //(1 - c²)<sup>n-|s|</sup> / (1 + c²)<sup>n</sup>
  1313.             final double coef1 = omc2tn[l] / opc2tn[n];
  1314.             //-b<sup>|j-s|</sup>
  1315.             final double coef2 = sign * btjms[absJmS];
  1316.             // P<sub>l</sub><sup>|j-s|, |j+s|</sup>(χ)
  1317.             // Jacobi polynomial value (0) and first-order derivative (1)
  1318.             final double[] jac = JacobiPolynomials.getValueAndDerivative(l, absJmS, absJpS, context.getX());

  1319.             // the derivative of coef1 by c
  1320.             final double dcoef1dc = -coef1 * 2. * c * (((double) n) / opc2tn[1] + ((double) l) / omc2tn[1]);
  1321.             // the derivative of coef1 by h
  1322.             final double dcoef1dh = dcoef1dc * dcdh;
  1323.             // the derivative of coef1 by k
  1324.             final double dcoef1dk = dcoef1dc * dcdk;

  1325.             // the derivative of coef2 by b
  1326.             final double dcoef2db = absJmS == 0 ? 0 : sign * (double) absJmS * btjms[absJmS - 1];
  1327.             // the derivative of coef2 by h
  1328.             final double dcoef2dh = dcoef2db * dbdh;
  1329.             // the derivative of coef2 by k
  1330.             final double dcoef2dk = dcoef2db * dbdk;

  1331.             // the jacobi polynomial value
  1332.             // final double jacobi = jac.getValue();
  1333.             final double jacobi = jac[0];
  1334.             // the derivative of the Jacobi polynomial by h
  1335.             //final double djacobidh = jac.getGradient()[0] * context.getHXXX();
  1336.             final double djacobidh = jac[1] * context.getHXXX();
  1337.             // the derivative of the Jacobi polynomial by k
  1338.             //final double djacobidk = jac.getGradient()[0] * context.getKXXX();
  1339.             final double djacobidk = jac[1] * context.getKXXX();

  1340.             //group the above coefficients to limit the mathematical operations
  1341.             final double term1 = factCoef * coef1 * coef2;
  1342.             final double term2 = factCoef * coef1 * jacobi;
  1343.             final double term3 = factCoef * coef2 * jacobi;

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

  1348.             return wjnsemjms;
  1349.         }
  1350.     }

  1351.     /** 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.
  1352.     *
  1353.     * <p>
  1354.     * Starting from Danielson 4.2-9,10,11 and taking into account that fact that: <br />
  1355.     * c = e / (1 + (1 - e²)<sup>1/2</sup>) = e / (1 + B) = e * b <br/>
  1356.     * the expression e<sup>-|j-s|</sup>*w<sub>j</sub><sup>n, s</sup>
  1357.     * can be written as: <br/ >
  1358.     * - for |s| > |j| <br />
  1359.     * e<sup>-|j-s|</sup>*w<sub>j</sub><sup>n, s</sup> =
  1360.     *          (((n + s)!(n - s)!)/((n + j)!(n - j)!)) *
  1361.     *          (-b)<sup>|j-s|</sup> *
  1362.     *          ((1 - c²)<sup>n-|s|</sup>/(1 + c²)<sup>n</sup>) *
  1363.     *          P<sub>n-|s|</sub><sup>|j-s|, |j+s|</sup>(χ) <br />
  1364.     * <br />
  1365.     * - for |s| <= |j| <br />
  1366.     * e<sup>-|j-s|</sup>*w<sub>j</sub><sup>n, s</sup> =
  1367.     *          (-b)<sup>|j-s|</sup> *
  1368.     *          ((1 - c²)<sup>n-|j|</sup>/(1 + c²)<sup>n</sup>) *
  1369.     *          P<sub>n-|j|</sub><sup>|j-s|, |j+s|</sup>(χ)
  1370.     * </p>
  1371.     *
  1372.     * @author Lucian Barbulescu
  1373.     */
  1374.     private class FieldWnsjEtomjmsCoefficient <T extends CalculusFieldElement<T>> {

  1375.         /** The value c.
  1376.          * <p>
  1377.          *  c = e / (1 + (1 - e²)<sup>1/2</sup>) = e / (1 + B) = e * b <br/>
  1378.          * </p>
  1379.          *  */
  1380.         private final T c;

  1381.         /** db / dh. */
  1382.         private final T dbdh;

  1383.         /** db / dk. */
  1384.         private final T dbdk;

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

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

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

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

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

  1397.         /**
  1398.          * Standard constructor.
  1399.          * @param context container for attributes
  1400.          * @param field field used by default
  1401.          */
  1402.         FieldWnsjEtomjmsCoefficient(final FieldDSSTThirdBodyDynamicContext<T> context, final Field<T> field) {

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

  1404.             //Zero
  1405.             final T zero = field.getZero();

  1406.             //initialise fields
  1407.             c = auxiliaryElements.getEcc().multiply(context.getb());
  1408.             final T c2 = c.multiply(c);

  1409.             //b² * χ
  1410.             final T b2Chi = context.getb().multiply(context.getb()).multiply(context.getX());
  1411.             //Compute derivatives of b
  1412.             dbdh = auxiliaryElements.getH().multiply(b2Chi);
  1413.             dbdk = auxiliaryElements.getK().multiply(b2Chi);

  1414.             //Compute derivatives of c
  1415.             if (auxiliaryElements.getEcc().getReal() == 0.0) {
  1416.                 // we are at a perfectly circular orbit singularity here
  1417.                 // we arbitrarily consider the perigee is along the X axis,
  1418.                 // i.e cos(ω + Ω) = h/ecc 1 and sin(ω + Ω) = k/ecc = 0
  1419.                 dcdh = auxiliaryElements.getEcc().multiply(dbdh).add(context.getb());
  1420.                 dcdk = auxiliaryElements.getEcc().multiply(dbdk);
  1421.             } else {
  1422.                 dcdh = auxiliaryElements.getEcc().multiply(dbdh).add(context.getb().multiply(auxiliaryElements.getH()).divide(auxiliaryElements.getEcc()));
  1423.                 dcdk = auxiliaryElements.getEcc().multiply(dbdk).add(context.getb().multiply(auxiliaryElements.getK()).divide(auxiliaryElements.getEcc()));
  1424.             }

  1425.             //Compute the powers (1 - c²)<sup>n</sup> and (1 + c²)<sup>n</sup>
  1426.             omc2tn = MathArrays.buildArray(field, staticContext.getMaxAR3Pow() + staticContext.getMaxFreqF() + 2);
  1427.             opc2tn = MathArrays.buildArray(field, staticContext.getMaxAR3Pow() + staticContext.getMaxFreqF() + 2);
  1428.             final T omc2 = c2.negate().add(1.);
  1429.             final T opc2 = c2.add(1.);
  1430.             omc2tn[0] = zero.add(1.);
  1431.             opc2tn[0] = zero.add(1.);
  1432.             for (int i = 1; i <= staticContext.getMaxAR3Pow() + staticContext.getMaxFreqF() + 1; i++) {
  1433.                 omc2tn[i] = omc2tn[i - 1].multiply(omc2);
  1434.                 opc2tn[i] = opc2tn[i - 1].multiply(opc2);
  1435.             }

  1436.             //Compute the powers of b
  1437.             btjms = MathArrays.buildArray(field, staticContext.getMaxAR3Pow() + staticContext.getMaxFreqF() + 1);
  1438.             btjms[0] = zero.add(1.);
  1439.             for (int i = 1; i <= staticContext.getMaxAR3Pow() + staticContext.getMaxFreqF(); i++) {
  1440.                 btjms[i] = btjms[i - 1].multiply(context.getb());
  1441.             }
  1442.         }

  1443.         /** 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 />
  1444.          *
  1445.          * @param j j index
  1446.          * @param s s index
  1447.          * @param n n index
  1448.          * @param context container for attributes
  1449.          * @param field field used by default
  1450.          * @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
  1451.          */
  1452.         public T[] computeWjnsEmjmsAndDeriv(final int j, final int s, final int n,
  1453.                                             final FieldDSSTThirdBodyDynamicContext<T> context,
  1454.                                             final Field<T> field) {
  1455.             //Zero
  1456.             final T zero = field.getZero();

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

  1459.             // |j|
  1460.             final int absJ = FastMath.abs(j);
  1461.             // |s|
  1462.             final int absS = FastMath.abs(s);
  1463.             // |j - s|
  1464.             final int absJmS = FastMath.abs(j - s);
  1465.             // |j + s|
  1466.             final int absJpS = FastMath.abs(j + s);

  1467.             //The lower index of P. Also the power of (1 - c²)
  1468.             final int l;
  1469.             // the factorial ratio coefficient or 1. if |s| <= |j|
  1470.             final T factCoef;
  1471.             if (absS > absJ) {
  1472.                 //factCoef = (fact[n + s] / fact[n + j]) * (fact[n - s] / fact[n - j]);
  1473.                 factCoef = zero.add((CombinatoricsUtils.factorialDouble(n + s) / CombinatoricsUtils.factorialDouble(n + j)) * (CombinatoricsUtils.factorialDouble(n - s) / CombinatoricsUtils.factorialDouble(n - j)));
  1474.                 l = n - absS;
  1475.             } else {
  1476.                 factCoef = zero.add(1.);
  1477.                 l = n - absJ;
  1478.             }

  1479.             // (-1)<sup>|j-s|</sup>
  1480.             final T sign = absJmS % 2 != 0 ? zero.add(-1.) : zero.add(1.);
  1481.             //(1 - c²)<sup>n-|s|</sup> / (1 + c²)<sup>n</sup>
  1482.             final T coef1 = omc2tn[l].divide(opc2tn[n]);
  1483.             //-b<sup>|j-s|</sup>
  1484.             final T coef2 = btjms[absJmS].multiply(sign);
  1485.             // P<sub>l</sub><sup>|j-s|, |j+s|</sup>(χ)
  1486.             final FieldGradient<T> jac =
  1487.                     JacobiPolynomials.getValue(l, absJmS, absJpS, FieldGradient.variable(1, 0, context.getX()));

  1488.             // the derivative of coef1 by c
  1489.             final T dcoef1dc = coef1.negate().multiply(2.).multiply(c).multiply(opc2tn[1].reciprocal().multiply(n).add(omc2tn[1].reciprocal().multiply(l)));
  1490.             // the derivative of coef1 by h
  1491.             final T dcoef1dh = dcoef1dc.multiply(dcdh);
  1492.             // the derivative of coef1 by k
  1493.             final T dcoef1dk = dcoef1dc.multiply(dcdk);

  1494.             // the derivative of coef2 by b
  1495.             final T dcoef2db = absJmS == 0 ? zero : sign.multiply(absJmS).multiply(btjms[absJmS - 1]);
  1496.             // the derivative of coef2 by h
  1497.             final T dcoef2dh = dcoef2db.multiply(dbdh);
  1498.             // the derivative of coef2 by k
  1499.             final T dcoef2dk = dcoef2db.multiply(dbdk);

  1500.             // the jacobi polynomial value
  1501.             final T jacobi = jac.getValue();
  1502.             // the derivative of the Jacobi polynomial by h
  1503.             final T djacobidh = jac.getGradient()[0].multiply(context.getHXXX());
  1504.             // the derivative of the Jacobi polynomial by k
  1505.             final T djacobidk = jac.getGradient()[0].multiply(context.getKXXX());

  1506.             //group the above coefficients to limit the mathematical operations
  1507.             final T term1 = factCoef.multiply(coef1).multiply(coef2);
  1508.             final T term2 = factCoef.multiply(coef1).multiply(jacobi);
  1509.             final T term3 = factCoef.multiply(coef2).multiply(jacobi);

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

  1514.             return wjnsemjms;
  1515.         }
  1516.     }

  1517.     /** The G<sub>n,s</sub> coefficients and their derivatives.
  1518.      * <p>
  1519.      * See Danielson, 4.2-17
  1520.      *
  1521.      * @author Lucian Barbulescu
  1522.      */
  1523.     private class GnsCoefficients {

  1524.         /** Maximum value for n index. */
  1525.         private final int nMax;

  1526.         /** Maximum value for s index. */
  1527.         private final int sMax;

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

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

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

  1534.         /** Standard constructor.
  1535.          *
  1536.          * @param nMax maximum value for n indes
  1537.          * @param sMax maximum value for s index
  1538.          * @param context container for attributes
  1539.          * @param aoR3Pow a / R3 up to power maxAR3Pow
  1540.          * @param qns Qns coefficients
  1541.          */
  1542.         GnsCoefficients(final int nMax, final int sMax,
  1543.                         final DSSTThirdBodyDynamicContext context,
  1544.                         final double[] aoR3Pow, final double[][] qns) {
  1545.             this.nMax = nMax;
  1546.             this.sMax = sMax;

  1547.             final int rows    = nMax + 1;
  1548.             final int columns = sMax + 1;
  1549.             this.gns          = new double[rows][columns];
  1550.             this.dgnsda       = new double[rows][columns];
  1551.             this.dgnsdgamma   = new double[rows][columns];

  1552.             // Generate the coefficients
  1553.             generateCoefficients(context, aoR3Pow, qns);
  1554.         }
  1555.         /**
  1556.          * Compute the coefficient G<sub>n,s</sub> and its derivatives.
  1557.          * <p>
  1558.          * Only the derivatives by a and γ are computed as all others are 0
  1559.          * </p>
  1560.          * @param context container for attributes
  1561.          * @param aoR3Pow a / R3 up to power maxAR3Pow
  1562.          * @param qns Qns coefficients
  1563.          */
  1564.         private void generateCoefficients(final DSSTThirdBodyDynamicContext context, final double[] aoR3Pow, final double[][] qns) {

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

  1566.             for (int s = 0; s <= sMax; s++) {
  1567.                 // The n index is always at least the maximum between 2 and s
  1568.                 final int minN = FastMath.max(2, s);
  1569.                 for (int n = minN; n <= nMax; n++) {
  1570.                     // compute the coefficients only if (n - s) % 2 == 0
  1571.                     if ( (n - s) % 2 == 0 ) {
  1572.                         // Kronecker symbol (2 - delta(0,s))
  1573.                         final double delta0s = (s == 0) ? 1. : 2.;
  1574.                         final double vns   = Vns.get(new NSKey(n, s));
  1575.                         final double coef0 = delta0s * aoR3Pow[n] * vns * context.getMuoR3();
  1576.                         final double coef1 = coef0 * qns[n][s];
  1577.                         // dQns/dGamma = Q(n, s + 1) from Equation 3.1-(8)
  1578.                         // for n = s, Q(n, n + 1) = 0. (Cefola & Broucke, 1975)
  1579.                         final double dqns = (n == s) ? 0. : qns[n][s + 1];

  1580.                         //Compute the coefficient and its derivatives.
  1581.                         this.gns[n][s] = coef1;
  1582.                         this.dgnsda[n][s] = coef1 * n / auxiliaryElements.getSma();
  1583.                         this.dgnsdgamma[n][s] = coef0 * dqns;
  1584.                     } else {
  1585.                         // the coefficient and its derivatives is 0
  1586.                         this.gns[n][s] = 0.;
  1587.                         this.dgnsda[n][s] = 0.;
  1588.                         this.dgnsdgamma[n][s] = 0.;
  1589.                     }
  1590.                 }
  1591.             }
  1592.         }

  1593.         /** Get the coefficient G<sub>n,s</sub>.
  1594.          *
  1595.          * @param n n index
  1596.          * @param s s index
  1597.          * @return the coefficient G<sub>n,s</sub>
  1598.          */
  1599.         public double getGns(final int n, final int s) {
  1600.             return this.gns[n][s];
  1601.         }

  1602.         /** Get the derivative dG<sub>n,s</sub> / da.
  1603.          *
  1604.          * @param n n index
  1605.          * @param s s index
  1606.          * @return the derivative dG<sub>n,s</sub> / da
  1607.          */
  1608.         public double getdGnsda(final int n, final int s) {
  1609.             return this.dgnsda[n][s];
  1610.         }

  1611.         /** Get the derivative dG<sub>n,s</sub> / dγ.
  1612.          *
  1613.          * @param n n index
  1614.          * @param s s index
  1615.          * @return the derivative dG<sub>n,s</sub> / dγ
  1616.          */
  1617.         public double getdGnsdgamma(final int n, final int s) {
  1618.             return this.dgnsdgamma[n][s];
  1619.         }
  1620.     }

  1621.     /** The G<sub>n,s</sub> coefficients and their derivatives.
  1622.      * <p>
  1623.      * See Danielson, 4.2-17
  1624.      *
  1625.      * @author Lucian Barbulescu
  1626.      */
  1627.     private class FieldGnsCoefficients  <T extends CalculusFieldElement<T>> {

  1628.         /** Maximum value for n index. */
  1629.         private final int nMax;

  1630.         /** Maximum value for s index. */
  1631.         private final int sMax;

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

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

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

  1638.         /** Standard constructor.
  1639.          *
  1640.          * @param nMax maximum value for n indes
  1641.          * @param sMax maximum value for s index
  1642.          * @param context container for attributes
  1643.          * @param aoR3Pow a / R3 up to power maxAR3Pow
  1644.          * @param qns Qns coefficients
  1645.          * @param field field used by default
  1646.          */
  1647.         FieldGnsCoefficients(final int nMax, final int sMax,
  1648.                              final FieldDSSTThirdBodyDynamicContext<T> context,
  1649.                              final T[] aoR3Pow, final T[][] qns,
  1650.                              final Field<T> field) {
  1651.             this.nMax = nMax;
  1652.             this.sMax = sMax;

  1653.             final int rows    = nMax + 1;
  1654.             final int columns = sMax + 1;
  1655.             this.gns          = MathArrays.buildArray(field, rows, columns);
  1656.             this.dgnsda       = MathArrays.buildArray(field, rows, columns);
  1657.             this.dgnsdgamma   = MathArrays.buildArray(field, rows, columns);

  1658.             // Generate the coefficients
  1659.             generateCoefficients(context, aoR3Pow, qns, field);
  1660.         }
  1661.         /**
  1662.          * Compute the coefficient G<sub>n,s</sub> and its derivatives.
  1663.          * <p>
  1664.          * Only the derivatives by a and γ are computed as all others are 0
  1665.          * </p>
  1666.          * @param context container for attributes
  1667.          * @param aoR3Pow a / R3 up to power maxAR3Pow
  1668.          * @param qns Qns coefficients
  1669.          * @param field field used by default
  1670.          */
  1671.         private void generateCoefficients(final FieldDSSTThirdBodyDynamicContext<T> context,
  1672.                                           final T[] aoR3Pow, final T[][] qns,
  1673.                                           final Field<T> field) {

  1674.             //Zero
  1675.             final T zero = field.getZero();

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

  1677.             for (int s = 0; s <= sMax; s++) {
  1678.                 // The n index is always at least the maximum between 2 and s
  1679.                 final int minN = FastMath.max(2, s);
  1680.                 for (int n = minN; n <= nMax; n++) {
  1681.                     // compute the coefficients only if (n - s) % 2 == 0
  1682.                     if ( (n - s) % 2 == 0 ) {
  1683.                         // Kronecker symbol (2 - delta(0,s))
  1684.                         final T delta0s = (s == 0) ? zero.add(1.) : zero.add(2.);
  1685.                         final double vns = Vns.get(new NSKey(n, s));
  1686.                         final T coef0 = aoR3Pow[n].multiply(vns).multiply(context.getMuoR3()).multiply(delta0s);
  1687.                         final T coef1 = coef0.multiply(qns[n][s]);
  1688.                         // dQns/dGamma = Q(n, s + 1) from Equation 3.1-(8)
  1689.                         // for n = s, Q(n, n + 1) = 0. (Cefola & Broucke, 1975)
  1690.                         final T dqns = (n == s) ? zero : qns[n][s + 1];

  1691.                         //Compute the coefficient and its derivatives.
  1692.                         this.gns[n][s] = coef1;
  1693.                         this.dgnsda[n][s] = coef1.multiply(n).divide(auxiliaryElements.getSma());
  1694.                         this.dgnsdgamma[n][s] = coef0.multiply(dqns);
  1695.                     } else {
  1696.                         // the coefficient and its derivatives is 0
  1697.                         this.gns[n][s] = zero;
  1698.                         this.dgnsda[n][s] = zero;
  1699.                         this.dgnsdgamma[n][s] = zero;
  1700.                     }
  1701.                 }
  1702.             }
  1703.         }

  1704.         /** Get the coefficient G<sub>n,s</sub>.
  1705.          *
  1706.          * @param n n index
  1707.          * @param s s index
  1708.          * @return the coefficient G<sub>n,s</sub>
  1709.          */
  1710.         public T getGns(final int n, final int s) {
  1711.             return this.gns[n][s];
  1712.         }

  1713.         /** Get the derivative dG<sub>n,s</sub> / da.
  1714.          *
  1715.          * @param n n index
  1716.          * @param s s index
  1717.          * @return the derivative dG<sub>n,s</sub> / da
  1718.          */
  1719.         public T getdGnsda(final int n, final int s) {
  1720.             return this.dgnsda[n][s];
  1721.         }

  1722.         /** Get the derivative dG<sub>n,s</sub> / dγ.
  1723.          *
  1724.          * @param n n index
  1725.          * @param s s index
  1726.          * @return the derivative dG<sub>n,s</sub> / dγ
  1727.          */
  1728.         public T getdGnsdgamma(final int n, final int s) {
  1729.             return this.dgnsdgamma[n][s];
  1730.         }
  1731.     }

  1732.     /** This class computes the terms containing the coefficients C<sub>j</sub> and S<sub>j</sub> of (α, β) or (k, h).
  1733.      *
  1734.      * <p>
  1735.      * The following terms and their derivatives by k, h, alpha and beta are considered: <br/ >
  1736.      * - 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 />
  1737.      * - C<sub>s</sub>(α, β) * S<sub>j+s</sub>(k, h) - S<sub>s</sub>(α, β) * C<sub>j+s</sub>(k, h) <br />
  1738.      * - 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 />
  1739.      * - C<sub>s</sub>(α, β) * C<sub>j+s</sub>(k, h) + S<sub>s</sub>(α, β) * S<sub>j+s</sub>(k, h) <br />
  1740.      * 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 />
  1741.      * See the CS Mathematical report $3.5.3.2 for more details
  1742.      * </p>
  1743.      * @author Lucian Barbulescu
  1744.      */
  1745.     private static class CjSjAlphaBetaKH {

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

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

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

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

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

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

  1762.         /**
  1763.          * Standard constructor.
  1764.          * @param context container for attributes
  1765.          */
  1766.         CjSjAlphaBetaKH(final DSSTThirdBodyDynamicContext context) {

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

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

  1770.             coefAandDeriv = new double[5];
  1771.             coefBandDeriv = new double[5];
  1772.             coefDandDeriv = new double[5];
  1773.             coefEandDeriv = new double[5];
  1774.         }

  1775.         /** Compute the coefficients and their derivatives for a given (j,s) pair.
  1776.          * @param j j index
  1777.          * @param s s index
  1778.          */
  1779.         public void computeCoefficients(final int j, final int s) {
  1780.             // sign of j-s
  1781.             final int sign = j < s ? -1 : 1;

  1782.             //|j-s|
  1783.             final int absJmS = FastMath.abs(j - s);

  1784.             //j+s
  1785.             final int jps = j + s;

  1786.             //Compute the coefficient A and its derivatives
  1787.             coefAandDeriv[0] = sign * cjsjalbe.getCj(s) * cjsjkh.getSj(absJmS) + cjsjalbe.getSj(s) * cjsjkh.getCj(absJmS);
  1788.             coefAandDeriv[1] = sign * cjsjalbe.getCj(s) * cjsjkh.getDsjDk(absJmS) + cjsjalbe.getSj(s) * cjsjkh.getDcjDk(absJmS);
  1789.             coefAandDeriv[2] = sign * cjsjalbe.getCj(s) * cjsjkh.getDsjDh(absJmS) + cjsjalbe.getSj(s) * cjsjkh.getDcjDh(absJmS);
  1790.             coefAandDeriv[3] = sign * cjsjalbe.getDcjDk(s) * cjsjkh.getSj(absJmS) + cjsjalbe.getDsjDk(s) * cjsjkh.getCj(absJmS);
  1791.             coefAandDeriv[4] = sign * cjsjalbe.getDcjDh(s) * cjsjkh.getSj(absJmS) + cjsjalbe.getDsjDh(s) * cjsjkh.getCj(absJmS);

  1792.             //Compute the coefficient B and its derivatives
  1793.             coefBandDeriv[0] = cjsjalbe.getCj(s) * cjsjkh.getSj(jps) - cjsjalbe.getSj(s) * cjsjkh.getCj(jps);
  1794.             coefBandDeriv[1] = cjsjalbe.getCj(s) * cjsjkh.getDsjDk(jps) - cjsjalbe.getSj(s) * cjsjkh.getDcjDk(jps);
  1795.             coefBandDeriv[2] = cjsjalbe.getCj(s) * cjsjkh.getDsjDh(jps) - cjsjalbe.getSj(s) * cjsjkh.getDcjDh(jps);
  1796.             coefBandDeriv[3] = cjsjalbe.getDcjDk(s) * cjsjkh.getSj(jps) - cjsjalbe.getDsjDk(s) * cjsjkh.getCj(jps);
  1797.             coefBandDeriv[4] = cjsjalbe.getDcjDh(s) * cjsjkh.getSj(jps) - cjsjalbe.getDsjDh(s) * cjsjkh.getCj(jps);

  1798.             //Compute the coefficient D and its derivatives
  1799.             coefDandDeriv[0] = cjsjalbe.getCj(s) * cjsjkh.getCj(absJmS) - sign * cjsjalbe.getSj(s) * cjsjkh.getSj(absJmS);
  1800.             coefDandDeriv[1] = cjsjalbe.getCj(s) * cjsjkh.getDcjDk(absJmS) - sign * cjsjalbe.getSj(s) * cjsjkh.getDsjDk(absJmS);
  1801.             coefDandDeriv[2] = cjsjalbe.getCj(s) * cjsjkh.getDcjDh(absJmS) - sign * cjsjalbe.getSj(s) * cjsjkh.getDsjDh(absJmS);
  1802.             coefDandDeriv[3] = cjsjalbe.getDcjDk(s) * cjsjkh.getCj(absJmS) - sign * cjsjalbe.getDsjDk(s) * cjsjkh.getSj(absJmS);
  1803.             coefDandDeriv[4] = cjsjalbe.getDcjDh(s) * cjsjkh.getCj(absJmS) - sign * cjsjalbe.getDsjDh(s) * cjsjkh.getSj(absJmS);

  1804.             //Compute the coefficient E and its derivatives
  1805.             coefEandDeriv[0] = cjsjalbe.getCj(s) * cjsjkh.getCj(jps) + cjsjalbe.getSj(s) * cjsjkh.getSj(jps);
  1806.             coefEandDeriv[1] = cjsjalbe.getCj(s) * cjsjkh.getDcjDk(jps) + cjsjalbe.getSj(s) * cjsjkh.getDsjDk(jps);
  1807.             coefEandDeriv[2] = cjsjalbe.getCj(s) * cjsjkh.getDcjDh(jps) + cjsjalbe.getSj(s) * cjsjkh.getDsjDh(jps);
  1808.             coefEandDeriv[3] = cjsjalbe.getDcjDk(s) * cjsjkh.getCj(jps) + cjsjalbe.getDsjDk(s) * cjsjkh.getSj(jps);
  1809.             coefEandDeriv[4] = cjsjalbe.getDcjDh(s) * cjsjkh.getCj(jps) + cjsjalbe.getDsjDh(s) * cjsjkh.getSj(jps);
  1810.         }

  1811.         /** Get the value of coefficient A<sub>j,s</sub>.
  1812.          *
  1813.          * @return the coefficient A<sub>j,s</sub>
  1814.          */
  1815.         public double getCoefA() {
  1816.             return coefAandDeriv[0];
  1817.         }

  1818.         /** Get the value of coefficient dA<sub>j,s</sub>/dk.
  1819.          *
  1820.          * @return the coefficient dA<sub>j,s</sub>/dk
  1821.          */
  1822.         public double getdCoefAdk() {
  1823.             return coefAandDeriv[1];
  1824.         }

  1825.         /** Get the value of coefficient dA<sub>j,s</sub>/dh.
  1826.          *
  1827.          * @return the coefficient dA<sub>j,s</sub>/dh
  1828.          */
  1829.         public double getdCoefAdh() {
  1830.             return coefAandDeriv[2];
  1831.         }

  1832.         /** Get the value of coefficient dA<sub>j,s</sub>/dα.
  1833.          *
  1834.          * @return the coefficient dA<sub>j,s</sub>/dα
  1835.          */
  1836.         public double getdCoefAdalpha() {
  1837.             return coefAandDeriv[3];
  1838.         }

  1839.         /** Get the value of coefficient dA<sub>j,s</sub>/dβ.
  1840.          *
  1841.          * @return the coefficient dA<sub>j,s</sub>/dβ
  1842.          */
  1843.         public double getdCoefAdbeta() {
  1844.             return coefAandDeriv[4];
  1845.         }

  1846.         /** Get the value of coefficient B<sub>j,s</sub>.
  1847.          *
  1848.          * @return the coefficient B<sub>j,s</sub>
  1849.          */
  1850.         public double getCoefB() {
  1851.             return coefBandDeriv[0];
  1852.         }

  1853.         /** Get the value of coefficient dB<sub>j,s</sub>/dk.
  1854.          *
  1855.          * @return the coefficient dB<sub>j,s</sub>/dk
  1856.          */
  1857.         public double getdCoefBdk() {
  1858.             return coefBandDeriv[1];
  1859.         }

  1860.         /** Get the value of coefficient dB<sub>j,s</sub>/dh.
  1861.          *
  1862.          * @return the coefficient dB<sub>j,s</sub>/dh
  1863.          */
  1864.         public double getdCoefBdh() {
  1865.             return coefBandDeriv[2];
  1866.         }

  1867.         /** Get the value of coefficient dB<sub>j,s</sub>/dα.
  1868.          *
  1869.          * @return the coefficient dB<sub>j,s</sub>/dα
  1870.          */
  1871.         public double getdCoefBdalpha() {
  1872.             return coefBandDeriv[3];
  1873.         }

  1874.         /** Get the value of coefficient dB<sub>j,s</sub>/dβ.
  1875.          *
  1876.          * @return the coefficient dB<sub>j,s</sub>/dβ
  1877.          */
  1878.         public double getdCoefBdbeta() {
  1879.             return coefBandDeriv[4];
  1880.         }

  1881.         /** Get the value of coefficient D<sub>j,s</sub>.
  1882.          *
  1883.          * @return the coefficient D<sub>j,s</sub>
  1884.          */
  1885.         public double getCoefD() {
  1886.             return coefDandDeriv[0];
  1887.         }

  1888.         /** Get the value of coefficient dD<sub>j,s</sub>/dk.
  1889.          *
  1890.          * @return the coefficient dD<sub>j,s</sub>/dk
  1891.          */
  1892.         public double getdCoefDdk() {
  1893.             return coefDandDeriv[1];
  1894.         }

  1895.         /** Get the value of coefficient dD<sub>j,s</sub>/dh.
  1896.          *
  1897.          * @return the coefficient dD<sub>j,s</sub>/dh
  1898.          */
  1899.         public double getdCoefDdh() {
  1900.             return coefDandDeriv[2];
  1901.         }

  1902.         /** Get the value of coefficient dD<sub>j,s</sub>/dα.
  1903.          *
  1904.          * @return the coefficient dD<sub>j,s</sub>/dα
  1905.          */
  1906.         public double getdCoefDdalpha() {
  1907.             return coefDandDeriv[3];
  1908.         }

  1909.         /** Get the value of coefficient dD<sub>j,s</sub>/dβ.
  1910.          *
  1911.          * @return the coefficient dD<sub>j,s</sub>/dβ
  1912.          */
  1913.         public double getdCoefDdbeta() {
  1914.             return coefDandDeriv[4];
  1915.         }

  1916.         /** Get the value of coefficient E<sub>j,s</sub>.
  1917.          *
  1918.          * @return the coefficient E<sub>j,s</sub>
  1919.          */
  1920.         public double getCoefE() {
  1921.             return coefEandDeriv[0];
  1922.         }

  1923.         /** Get the value of coefficient dE<sub>j,s</sub>/dk.
  1924.          *
  1925.          * @return the coefficient dE<sub>j,s</sub>/dk
  1926.          */
  1927.         public double getdCoefEdk() {
  1928.             return coefEandDeriv[1];
  1929.         }

  1930.         /** Get the value of coefficient dE<sub>j,s</sub>/dh.
  1931.          *
  1932.          * @return the coefficient dE<sub>j,s</sub>/dh
  1933.          */
  1934.         public double getdCoefEdh() {
  1935.             return coefEandDeriv[2];
  1936.         }

  1937.         /** Get the value of coefficient dE<sub>j,s</sub>/dα.
  1938.          *
  1939.          * @return the coefficient dE<sub>j,s</sub>/dα
  1940.          */
  1941.         public double getdCoefEdalpha() {
  1942.             return coefEandDeriv[3];
  1943.         }

  1944.         /** Get the value of coefficient dE<sub>j,s</sub>/dβ.
  1945.          *
  1946.          * @return the coefficient dE<sub>j,s</sub>/dβ
  1947.          */
  1948.         public double getdCoefEdbeta() {
  1949.             return coefEandDeriv[4];
  1950.         }
  1951.     }

  1952.      /** This class computes the terms containing the coefficients C<sub>j</sub> and S<sub>j</sub> of (α, β) or (k, h).
  1953.      *
  1954.      * <p>
  1955.      * The following terms and their derivatives by k, h, alpha and beta are considered: <br/ >
  1956.      * - 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 />
  1957.      * - C<sub>s</sub>(α, β) * S<sub>j+s</sub>(k, h) - S<sub>s</sub>(α, β) * C<sub>j+s</sub>(k, h) <br />
  1958.      * - 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 />
  1959.      * - C<sub>s</sub>(α, β) * C<sub>j+s</sub>(k, h) + S<sub>s</sub>(α, β) * S<sub>j+s</sub>(k, h) <br />
  1960.      * 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 />
  1961.      * See the CS Mathematical report $3.5.3.2 for more details
  1962.      * </p>
  1963.      * @author Lucian Barbulescu
  1964.      */
  1965.     private static class FieldCjSjAlphaBetaKH <T extends CalculusFieldElement<T>> {

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

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

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

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

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

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

  1982.         /**
  1983.          * Standard constructor.
  1984.          * @param context container for attributes
  1985.          * @param field field used by default
  1986.          */
  1987.         FieldCjSjAlphaBetaKH(final FieldDSSTThirdBodyDynamicContext<T> context, final Field<T> field) {

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

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

  1991.             coefAandDeriv = MathArrays.buildArray(field, 5);
  1992.             coefBandDeriv = MathArrays.buildArray(field, 5);
  1993.             coefDandDeriv = MathArrays.buildArray(field, 5);
  1994.             coefEandDeriv = MathArrays.buildArray(field, 5);
  1995.         }

  1996.         /** Compute the coefficients and their derivatives for a given (j,s) pair.
  1997.          * @param j j index
  1998.          * @param s s index
  1999.          */
  2000.         public void computeCoefficients(final int j, final int s) {
  2001.             // sign of j-s
  2002.             final int sign = j < s ? -1 : 1;

  2003.             //|j-s|
  2004.             final int absJmS = FastMath.abs(j - s);

  2005.             //j+s
  2006.             final int jps = j + s;

  2007.             //Compute the coefficient A and its derivatives
  2008.             coefAandDeriv[0] = cjsjalbe.getCj(s).multiply(cjsjkh.getSj(absJmS)).multiply(sign).add(cjsjalbe.getSj(s).multiply(cjsjkh.getCj(absJmS)));
  2009.             coefAandDeriv[1] = cjsjalbe.getCj(s).multiply(cjsjkh.getDsjDk(absJmS)).multiply(sign).add(cjsjalbe.getSj(s).multiply(cjsjkh.getDcjDk(absJmS)));
  2010.             coefAandDeriv[2] = cjsjalbe.getCj(s).multiply(cjsjkh.getDsjDh(absJmS)).multiply(sign).add(cjsjalbe.getSj(s).multiply(cjsjkh.getDcjDh(absJmS)));
  2011.             coefAandDeriv[3] = cjsjalbe.getDcjDk(s).multiply(cjsjkh.getSj(absJmS)).multiply(sign).add(cjsjalbe.getDsjDk(s).multiply(cjsjkh.getCj(absJmS)));
  2012.             coefAandDeriv[4] = cjsjalbe.getDcjDh(s).multiply(cjsjkh.getSj(absJmS)).multiply(sign).add(cjsjalbe.getDsjDh(s).multiply(cjsjkh.getCj(absJmS)));

  2013.             //Compute the coefficient B and its derivatives
  2014.             coefBandDeriv[0] = cjsjalbe.getCj(s).multiply(cjsjkh.getSj(jps)).subtract(cjsjalbe.getSj(s).multiply(cjsjkh.getCj(jps)));
  2015.             coefBandDeriv[1] = cjsjalbe.getCj(s).multiply(cjsjkh.getDsjDk(jps)).subtract(cjsjalbe.getSj(s).multiply(cjsjkh.getDcjDk(jps)));
  2016.             coefBandDeriv[2] = cjsjalbe.getCj(s).multiply(cjsjkh.getDsjDh(jps)).subtract(cjsjalbe.getSj(s).multiply(cjsjkh.getDcjDh(jps)));
  2017.             coefBandDeriv[3] = cjsjalbe.getDcjDk(s).multiply(cjsjkh.getSj(jps)).subtract(cjsjalbe.getDsjDk(s).multiply(cjsjkh.getCj(jps)));
  2018.             coefBandDeriv[4] = cjsjalbe.getDcjDh(s).multiply(cjsjkh.getSj(jps)).subtract(cjsjalbe.getDsjDh(s).multiply(cjsjkh.getCj(jps)));

  2019.             //Compute the coefficient D and its derivatives
  2020.             coefDandDeriv[0] = cjsjalbe.getCj(s).multiply(cjsjkh.getCj(absJmS)).subtract(cjsjalbe.getSj(s).multiply(cjsjkh.getSj(absJmS)).multiply(sign));
  2021.             coefDandDeriv[1] = cjsjalbe.getCj(s).multiply(cjsjkh.getDcjDk(absJmS)).subtract(cjsjalbe.getSj(s).multiply(cjsjkh.getDsjDk(absJmS)).multiply(sign));
  2022.             coefDandDeriv[2] = cjsjalbe.getCj(s).multiply(cjsjkh.getDcjDh(absJmS)).subtract(cjsjalbe.getSj(s).multiply(cjsjkh.getDsjDh(absJmS)).multiply(sign));
  2023.             coefDandDeriv[3] = cjsjalbe.getDcjDk(s).multiply(cjsjkh.getCj(absJmS)).subtract(cjsjalbe.getDsjDk(s).multiply(cjsjkh.getSj(absJmS)).multiply(sign));
  2024.             coefDandDeriv[4] = cjsjalbe.getDcjDh(s).multiply(cjsjkh.getCj(absJmS)).subtract(cjsjalbe.getDsjDh(s).multiply(cjsjkh.getSj(absJmS)).multiply(sign));

  2025.             //Compute the coefficient E and its derivatives
  2026.             coefEandDeriv[0] = cjsjalbe.getCj(s).multiply(cjsjkh.getCj(jps)).add(cjsjalbe.getSj(s).multiply(cjsjkh.getSj(jps)));
  2027.             coefEandDeriv[1] = cjsjalbe.getCj(s).multiply(cjsjkh.getDcjDk(jps)).add(cjsjalbe.getSj(s).multiply(cjsjkh.getDsjDk(jps)));
  2028.             coefEandDeriv[2] = cjsjalbe.getCj(s).multiply(cjsjkh.getDcjDh(jps)).add(cjsjalbe.getSj(s).multiply(cjsjkh.getDsjDh(jps)));
  2029.             coefEandDeriv[3] = cjsjalbe.getDcjDk(s).multiply(cjsjkh.getCj(jps)).add(cjsjalbe.getDsjDk(s).multiply(cjsjkh.getSj(jps)));
  2030.             coefEandDeriv[4] = cjsjalbe.getDcjDh(s).multiply(cjsjkh.getCj(jps)).add(cjsjalbe.getDsjDh(s).multiply(cjsjkh.getSj(jps)));
  2031.         }

  2032.         /** Get the value of coefficient A<sub>j,s</sub>.
  2033.          *
  2034.          * @return the coefficient A<sub>j,s</sub>
  2035.          */
  2036.         public T getCoefA() {
  2037.             return coefAandDeriv[0];
  2038.         }

  2039.         /** Get the value of coefficient dA<sub>j,s</sub>/dk.
  2040.          *
  2041.          * @return the coefficient dA<sub>j,s</sub>/dk
  2042.          */
  2043.         public T getdCoefAdk() {
  2044.             return coefAandDeriv[1];
  2045.         }

  2046.         /** Get the value of coefficient dA<sub>j,s</sub>/dh.
  2047.          *
  2048.          * @return the coefficient dA<sub>j,s</sub>/dh
  2049.          */
  2050.         public T getdCoefAdh() {
  2051.             return coefAandDeriv[2];
  2052.         }

  2053.         /** Get the value of coefficient dA<sub>j,s</sub>/dα.
  2054.          *
  2055.          * @return the coefficient dA<sub>j,s</sub>/dα
  2056.          */
  2057.         public T getdCoefAdalpha() {
  2058.             return coefAandDeriv[3];
  2059.         }

  2060.         /** Get the value of coefficient dA<sub>j,s</sub>/dβ.
  2061.          *
  2062.          * @return the coefficient dA<sub>j,s</sub>/dβ
  2063.          */
  2064.         public T getdCoefAdbeta() {
  2065.             return coefAandDeriv[4];
  2066.         }

  2067.        /** Get the value of coefficient B<sub>j,s</sub>.
  2068.         *
  2069.         * @return the coefficient B<sub>j,s</sub>
  2070.         */
  2071.         public T getCoefB() {
  2072.             return coefBandDeriv[0];
  2073.         }

  2074.         /** Get the value of coefficient dB<sub>j,s</sub>/dk.
  2075.          *
  2076.          * @return the coefficient dB<sub>j,s</sub>/dk
  2077.          */
  2078.         public T getdCoefBdk() {
  2079.             return coefBandDeriv[1];
  2080.         }

  2081.         /** Get the value of coefficient dB<sub>j,s</sub>/dh.
  2082.          *
  2083.          * @return the coefficient dB<sub>j,s</sub>/dh
  2084.          */
  2085.         public T getdCoefBdh() {
  2086.             return coefBandDeriv[2];
  2087.         }

  2088.         /** Get the value of coefficient dB<sub>j,s</sub>/dα.
  2089.          *
  2090.          * @return the coefficient dB<sub>j,s</sub>/dα
  2091.          */
  2092.         public T getdCoefBdalpha() {
  2093.             return coefBandDeriv[3];
  2094.         }

  2095.         /** Get the value of coefficient dB<sub>j,s</sub>/dβ.
  2096.          *
  2097.          * @return the coefficient dB<sub>j,s</sub>/dβ
  2098.          */
  2099.         public T getdCoefBdbeta() {
  2100.             return coefBandDeriv[4];
  2101.         }

  2102.         /** Get the value of coefficient D<sub>j,s</sub>.
  2103.          *
  2104.          * @return the coefficient D<sub>j,s</sub>
  2105.          */
  2106.         public T getCoefD() {
  2107.             return coefDandDeriv[0];
  2108.         }

  2109.         /** Get the value of coefficient dD<sub>j,s</sub>/dk.
  2110.          *
  2111.          * @return the coefficient dD<sub>j,s</sub>/dk
  2112.          */
  2113.         public T getdCoefDdk() {
  2114.             return coefDandDeriv[1];
  2115.         }

  2116.         /** Get the value of coefficient dD<sub>j,s</sub>/dh.
  2117.          *
  2118.          * @return the coefficient dD<sub>j,s</sub>/dh
  2119.          */
  2120.         public T getdCoefDdh() {
  2121.             return coefDandDeriv[2];
  2122.         }

  2123.         /** Get the value of coefficient dD<sub>j,s</sub>/dα.
  2124.          *
  2125.          * @return the coefficient dD<sub>j,s</sub>/dα
  2126.          */
  2127.         public T getdCoefDdalpha() {
  2128.             return coefDandDeriv[3];
  2129.         }

  2130.         /** Get the value of coefficient dD<sub>j,s</sub>/dβ.
  2131.          *
  2132.          * @return the coefficient dD<sub>j,s</sub>/dβ
  2133.          */
  2134.         public T getdCoefDdbeta() {
  2135.             return coefDandDeriv[4];
  2136.         }

  2137.         /** Get the value of coefficient E<sub>j,s</sub>.
  2138.          *
  2139.          * @return the coefficient E<sub>j,s</sub>
  2140.          */
  2141.         public T getCoefE() {
  2142.             return coefEandDeriv[0];
  2143.         }

  2144.         /** Get the value of coefficient dE<sub>j,s</sub>/dk.
  2145.          *
  2146.          * @return the coefficient dE<sub>j,s</sub>/dk
  2147.          */
  2148.         public T getdCoefEdk() {
  2149.             return coefEandDeriv[1];
  2150.         }

  2151.         /** Get the value of coefficient dE<sub>j,s</sub>/dh.
  2152.          *
  2153.          * @return the coefficient dE<sub>j,s</sub>/dh
  2154.          */
  2155.         public T getdCoefEdh() {
  2156.             return coefEandDeriv[2];
  2157.         }

  2158.         /** Get the value of coefficient dE<sub>j,s</sub>/dα.
  2159.          *
  2160.          * @return the coefficient dE<sub>j,s</sub>/dα
  2161.          */
  2162.         public T getdCoefEdalpha() {
  2163.             return coefEandDeriv[3];
  2164.         }

  2165.         /** Get the value of coefficient dE<sub>j,s</sub>/dβ.
  2166.          *
  2167.          * @return the coefficient dE<sub>j,s</sub>/dβ
  2168.          */
  2169.         public T getdCoefEdbeta() {
  2170.             return coefEandDeriv[4];
  2171.         }
  2172.     }

  2173.     /** This class computes the coefficients for the generating function S and its derivatives.
  2174.      * <p>
  2175.      * The form of the generating functions is: <br>
  2176.      *  S = C⁰ + &Sigma;<sub>j=1</sub><sup>N+1</sup>(C<sup>j</sup> * cos(jF) + S<sup>j</sup> * sin(jF)) <br>
  2177.      *  The coefficients C⁰, C<sup>j</sup>, S<sup>j</sup> are the Fourrier coefficients
  2178.      *  presented in Danielson 4.2-14,15 except for the case j=1 where
  2179.      *  C¹ = C¹<sub>Fourier</sub> - hU and
  2180.      *  S¹ = S¹<sub>Fourier</sub> + kU <br>
  2181.      *  Also the coefficients of the derivatives of S by a, k, h, α, β, γ and λ
  2182.      *  are computed end expressed in a similar manner. The formulas used are 4.2-19, 20, 23, 24
  2183.      * </p>
  2184.      * @author Lucian Barbulescu
  2185.      */
  2186.     private class GeneratingFunctionCoefficients {

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

  2189.         /** Maximum value of j index. */
  2190.         private final int jMax;

  2191.         /** The coefficients C<sup>j</sup> of the function S and its derivatives.
  2192.          * <p>
  2193.          * The index j belongs to the interval [0,jMax]. The coefficient C⁰ is the free coefficient.<br>
  2194.          * Each column of the matrix contains the coefficient corresponding to the following functions: <br/>
  2195.          * - S <br/>
  2196.          * - dS / da <br/>
  2197.          * - dS / dk <br/>
  2198.          * - dS / dh <br/>
  2199.          * - dS / dα <br/>
  2200.          * - dS / dβ <br/>
  2201.          * - dS / dγ <br/>
  2202.          * - dS / dλ
  2203.          * </p>
  2204.          */
  2205.         private final double[][] cjCoefs;

  2206.         /** The coefficients S<sup>j</sup> of the function S and its derivatives.
  2207.          * <p>
  2208.          * The index j belongs to the interval [0,jMax].<br>
  2209.          * Each column of the matrix contains the coefficient corresponding to the following functions: <br/>
  2210.          * - S <br/>
  2211.          * - dS / da <br/>
  2212.          * - dS / dk <br/>
  2213.          * - dS / dh <br/>
  2214.          * - dS / dα <br/>
  2215.          * - dS / dβ <br/>
  2216.          * - dS / dγ <br/>
  2217.          * - dS / dλ
  2218.          * </p>
  2219.          */
  2220.         private final double[][] sjCoefs;

  2221.         /**
  2222.          * Standard constructor.
  2223.          *
  2224.          * @param nMax maximum value of n index
  2225.          * @param sMax maximum value of s index
  2226.          * @param jMax maximum value of j index
  2227.          * @param context container for attributes
  2228.          * @param hansen hansen objects
  2229.          * @param aoR3Pow a / R3 up to power maxAR3Pow
  2230.          * @param qns Qns coefficients
  2231.          */
  2232.         GeneratingFunctionCoefficients(final int nMax, final int sMax, final int jMax,
  2233.                                        final DSSTThirdBodyDynamicContext context,
  2234.                                        final HansenObjects hansen,
  2235.                                        final double[] aoR3Pow, final double[][] qns) {
  2236.             this.jMax = jMax;
  2237.             this.cjsjFourier = new FourierCjSjCoefficients(nMax, sMax, jMax, context, aoR3Pow, qns);
  2238.             this.cjCoefs = new double[8][jMax + 1];
  2239.             this.sjCoefs = new double[8][jMax + 1];

  2240.             computeGeneratingFunctionCoefficients(context, hansen, aoR3Pow, qns);
  2241.         }

  2242.         /**
  2243.          * Compute the coefficients for the generating function S and its derivatives.
  2244.          * @param context container for attributes
  2245.          * @param hansenObjects hansen objects
  2246.          * @param aoR3Pow a / R3 up to power maxAR3Pow
  2247.          * @param qns Qns coefficients
  2248.          */
  2249.         private void computeGeneratingFunctionCoefficients(final DSSTThirdBodyDynamicContext context, final HansenObjects hansenObjects,
  2250.                                                            final double[] aoR3Pow, final double[][] qns) {

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

  2252.             // Access to potential U derivatives
  2253.             final UAnddU udu = new UAnddU(context, hansenObjects, aoR3Pow, qns);

  2254.             //Compute the C<sup>j</sup> coefficients
  2255.             for (int j = 1; j <= jMax; j++) {
  2256.                 //Compute the C<sup>j</sup> coefficients
  2257.                 cjCoefs[0][j] = cjsjFourier.getCj(j);
  2258.                 cjCoefs[1][j] = cjsjFourier.getdCjda(j);
  2259.                 cjCoefs[2][j] = cjsjFourier.getdCjdk(j) - (cjsjFourier.getSjLambda(j - 1) - cjsjFourier.getSjLambda(j + 1)) / 2;
  2260.                 cjCoefs[3][j] = cjsjFourier.getdCjdh(j) - (cjsjFourier.getCjLambda(j - 1) + cjsjFourier.getCjLambda(j + 1)) / 2;
  2261.                 cjCoefs[4][j] = cjsjFourier.getdCjdalpha(j);
  2262.                 cjCoefs[5][j] = cjsjFourier.getdCjdbeta(j);
  2263.                 cjCoefs[6][j] = cjsjFourier.getdCjdgamma(j);
  2264.                 cjCoefs[7][j] = cjsjFourier.getCjLambda(j);

  2265.                 //Compute the S<sup>j</sup> coefficients
  2266.                 sjCoefs[0][j] = cjsjFourier.getSj(j);
  2267.                 sjCoefs[1][j] = cjsjFourier.getdSjda(j);
  2268.                 sjCoefs[2][j] = cjsjFourier.getdSjdk(j) + (cjsjFourier.getCjLambda(j - 1) - cjsjFourier.getCjLambda(j + 1)) / 2;
  2269.                 sjCoefs[3][j] = cjsjFourier.getdSjdh(j) - (cjsjFourier.getSjLambda(j - 1) + cjsjFourier.getSjLambda(j + 1)) / 2;
  2270.                 sjCoefs[4][j] = cjsjFourier.getdSjdalpha(j);
  2271.                 sjCoefs[5][j] = cjsjFourier.getdSjdbeta(j);
  2272.                 sjCoefs[6][j] = cjsjFourier.getdSjdgamma(j);
  2273.                 sjCoefs[7][j] = cjsjFourier.getSjLambda(j);

  2274.                 //In the special case j == 1 there are some additional terms to be added
  2275.                 if (j == 1) {
  2276.                     //Additional terms for C<sup>j</sup> coefficients
  2277.                     cjCoefs[0][j] += -auxiliaryElements.getH() * udu.getU();
  2278.                     cjCoefs[1][j] += -auxiliaryElements.getH() * udu.getdUda();
  2279.                     cjCoefs[2][j] += -auxiliaryElements.getH() * udu.getdUdk();
  2280.                     cjCoefs[3][j] += -(auxiliaryElements.getH() * udu.getdUdh() + udu.getU() + cjsjFourier.getC0Lambda());
  2281.                     cjCoefs[4][j] += -auxiliaryElements.getH() * udu.getdUdAl();
  2282.                     cjCoefs[5][j] += -auxiliaryElements.getH() * udu.getdUdBe();
  2283.                     cjCoefs[6][j] += -auxiliaryElements.getH() * udu.getdUdGa();

  2284.                     //Additional terms for S<sup>j</sup> coefficients
  2285.                     sjCoefs[0][j] += auxiliaryElements.getK() * udu.getU();
  2286.                     sjCoefs[1][j] += auxiliaryElements.getK() * udu.getdUda();
  2287.                     sjCoefs[2][j] += auxiliaryElements.getK() * udu.getdUdk() + udu.getU() + cjsjFourier.getC0Lambda();
  2288.                     sjCoefs[3][j] += auxiliaryElements.getK() * udu.getdUdh();
  2289.                     sjCoefs[4][j] += auxiliaryElements.getK() * udu.getdUdAl();
  2290.                     sjCoefs[5][j] += auxiliaryElements.getK() * udu.getdUdBe();
  2291.                     sjCoefs[6][j] += auxiliaryElements.getK() * udu.getdUdGa();
  2292.                 }
  2293.             }
  2294.         }

  2295.         /** Get the coefficient C<sup>j</sup> for the function S.
  2296.          * <br>
  2297.          * Possible values for j are within the interval [0,jMax].
  2298.          * The value 0 is used to obtain the free coefficient C⁰
  2299.          * @param j j index
  2300.          * @return C<sup>j</sup> for the function S
  2301.          */
  2302.         public double getSCj(final int j) {
  2303.             return cjCoefs[0][j];
  2304.         }

  2305.         /** Get the coefficient S<sup>j</sup> for the function S.
  2306.          * <br>
  2307.          * Possible values for j are within the interval [1,jMax].
  2308.          * @param j j index
  2309.          * @return S<sup>j</sup> for the function S
  2310.          */
  2311.         public double getSSj(final int j) {
  2312.             return sjCoefs[0][j];
  2313.         }

  2314.         /** Get the coefficient C<sup>j</sup> for the derivative dS/da.
  2315.          * <br>
  2316.          * Possible values for j are within the interval [0,jMax].
  2317.          * The value 0 is used to obtain the free coefficient C⁰
  2318.          * @param j j index
  2319.          * @return C<sup>j</sup> for the function dS/da
  2320.          */
  2321.         public double getdSdaCj(final int j) {
  2322.             return cjCoefs[1][j];
  2323.         }

  2324.         /** Get the coefficient S<sup>j</sup> for the derivative dS/da.
  2325.          * <br>
  2326.          * Possible values for j are within the interval [1,jMax].
  2327.          * @param j j index
  2328.          * @return S<sup>j</sup> for the derivative dS/da
  2329.          */
  2330.         public double getdSdaSj(final int j) {
  2331.             return sjCoefs[1][j];
  2332.         }

  2333.         /** Get the coefficient C<sup>j</sup> for the derivative dS/dk
  2334.          * <br>
  2335.          * Possible values for j are within the interval [0,jMax].
  2336.          * The value 0 is used to obtain the free coefficient C⁰
  2337.          * @param j j index
  2338.          * @return C<sup>j</sup> for the function dS/dk
  2339.          */
  2340.         public double getdSdkCj(final int j) {
  2341.             return cjCoefs[2][j];
  2342.         }

  2343.         /** Get the coefficient S<sup>j</sup> for the derivative dS/dk.
  2344.          * <br>
  2345.          * Possible values for j are within the interval [1,jMax].
  2346.          * @param j j index
  2347.          * @return S<sup>j</sup> for the derivative dS/dk
  2348.          */
  2349.         public double getdSdkSj(final int j) {
  2350.             return sjCoefs[2][j];
  2351.         }

  2352.         /** Get the coefficient C<sup>j</sup> for the derivative dS/dh
  2353.          * <br>
  2354.          * Possible values for j are within the interval [0,jMax].
  2355.          * The value 0 is used to obtain the free coefficient C⁰
  2356.          * @param j j index
  2357.          * @return C<sup>j</sup> for the function dS/dh
  2358.          */
  2359.         public double getdSdhCj(final int j) {
  2360.             return cjCoefs[3][j];
  2361.         }

  2362.         /** Get the coefficient S<sup>j</sup> for the derivative dS/dh.
  2363.          * <br>
  2364.          * Possible values for j are within the interval [1,jMax].
  2365.          * @param j j index
  2366.          * @return S<sup>j</sup> for the derivative dS/dh
  2367.          */
  2368.         public double getdSdhSj(final int j) {
  2369.             return sjCoefs[3][j];
  2370.         }

  2371.         /** Get the coefficient C<sup>j</sup> for the derivative dS/dα
  2372.          * <br>
  2373.          * Possible values for j are within the interval [0,jMax].
  2374.          * The value 0 is used to obtain the free coefficient C⁰
  2375.          * @param j j index
  2376.          * @return C<sup>j</sup> for the function dS/dα
  2377.          */
  2378.         public double getdSdalphaCj(final int j) {
  2379.             return cjCoefs[4][j];
  2380.         }

  2381.         /** Get the coefficient S<sup>j</sup> for the derivative dS/dα.
  2382.          * <br>
  2383.          * Possible values for j are within the interval [1,jMax].
  2384.          * @param j j index
  2385.          * @return S<sup>j</sup> for the derivative dS/dα
  2386.          */
  2387.         public double getdSdalphaSj(final int j) {
  2388.             return sjCoefs[4][j];
  2389.         }

  2390.         /** Get the coefficient C<sup>j</sup> for the derivative dS/dβ
  2391.          * <br>
  2392.          * Possible values for j are within the interval [0,jMax].
  2393.          * The value 0 is used to obtain the free coefficient C⁰
  2394.          * @param j j index
  2395.          * @return C<sup>j</sup> for the function dS/dβ
  2396.          */
  2397.         public double getdSdbetaCj(final int j) {
  2398.             return cjCoefs[5][j];
  2399.         }

  2400.         /** Get the coefficient S<sup>j</sup> for the derivative dS/dβ.
  2401.          * <br>
  2402.          * Possible values for j are within the interval [1,jMax].
  2403.          * @param j j index
  2404.          * @return S<sup>j</sup> for the derivative dS/dβ
  2405.          */
  2406.         public double getdSdbetaSj(final int j) {
  2407.             return sjCoefs[5][j];
  2408.         }

  2409.         /** Get the coefficient C<sup>j</sup> for the derivative dS/dγ
  2410.          * <br>
  2411.          * Possible values for j are within the interval [0,jMax].
  2412.          * The value 0 is used to obtain the free coefficient C⁰
  2413.          * @param j j index
  2414.          * @return C<sup>j</sup> for the function dS/dγ
  2415.          */
  2416.         public double getdSdgammaCj(final int j) {
  2417.             return cjCoefs[6][j];
  2418.         }

  2419.         /** Get the coefficient S<sup>j</sup> for the derivative dS/dγ.
  2420.          * <br>
  2421.          * Possible values for j are within the interval [1,jMax].
  2422.          * @param j j index
  2423.          * @return S<sup>j</sup> for the derivative dS/dγ
  2424.          */
  2425.         public double getdSdgammaSj(final int j) {
  2426.             return sjCoefs[6][j];
  2427.         }

  2428.         /** Get the coefficient C<sup>j</sup> for the derivative dS/dλ
  2429.          * <br>
  2430.          * Possible values for j are within the interval [0,jMax].
  2431.          * The value 0 is used to obtain the free coefficient C⁰
  2432.          * @param j j index
  2433.          * @return C<sup>j</sup> for the function dS/dλ
  2434.          */
  2435.         public double getdSdlambdaCj(final int j) {
  2436.             return cjCoefs[7][j];
  2437.         }

  2438.         /** Get the coefficient S<sup>j</sup> for the derivative dS/dλ.
  2439.          * <br>
  2440.          * Possible values for j are within the interval [1,jMax].
  2441.          * @param j j index
  2442.          * @return S<sup>j</sup> for the derivative dS/dλ
  2443.          */
  2444.         public double getdSdlambdaSj(final int j) {
  2445.             return sjCoefs[7][j];
  2446.         }
  2447.     }

  2448.     /** This class computes the coefficients for the generating function S and its derivatives.
  2449.      * <p>
  2450.      * The form of the generating functions is: <br>
  2451.      *  S = C⁰ + &Sigma;<sub>j=1</sub><sup>N+1</sup>(C<sup>j</sup> * cos(jF) + S<sup>j</sup> * sin(jF)) <br>
  2452.      *  The coefficients C⁰, C<sup>j</sup>, S<sup>j</sup> are the Fourrier coefficients
  2453.      *  presented in Danielson 4.2-14,15 except for the case j=1 where
  2454.      *  C¹ = C¹<sub>Fourier</sub> - hU and
  2455.      *  S¹ = S¹<sub>Fourier</sub> + kU <br>
  2456.      *  Also the coefficients of the derivatives of S by a, k, h, α, β, γ and λ
  2457.      *  are computed end expressed in a similar manner. The formulas used are 4.2-19, 20, 23, 24
  2458.      * </p>
  2459.      * @author Lucian Barbulescu
  2460.      */
  2461.     private class FieldGeneratingFunctionCoefficients <T extends CalculusFieldElement<T>> {

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

  2464.         /** Maximum value of j index. */
  2465.         private final int jMax;

  2466.         /** The coefficients C<sup>j</sup> of the function S and its derivatives.
  2467.          * <p>
  2468.          * The index j belongs to the interval [0,jMax]. The coefficient C⁰ is the free coefficient.<br>
  2469.          * Each column of the matrix contains the coefficient corresponding to the following functions: <br/>
  2470.          * - S <br/>
  2471.          * - dS / da <br/>
  2472.          * - dS / dk <br/>
  2473.          * - dS / dh <br/>
  2474.          * - dS / dα <br/>
  2475.          * - dS / dβ <br/>
  2476.          * - dS / dγ <br/>
  2477.          * - dS / dλ
  2478.          * </p>
  2479.          */
  2480.         private final T[][] cjCoefs;

  2481.         /** The coefficients S<sup>j</sup> of the function S and its derivatives.
  2482.          * <p>
  2483.          * The index j belongs to the interval [0,jMax].<br>
  2484.          * Each column of the matrix contains the coefficient corresponding to the following functions: <br/>
  2485.          * - S <br/>
  2486.          * - dS / da <br/>
  2487.          * - dS / dk <br/>
  2488.          * - dS / dh <br/>
  2489.          * - dS / dα <br/>
  2490.          * - dS / dβ <br/>
  2491.          * - dS / dγ <br/>
  2492.          * - dS / dλ
  2493.          * </p>
  2494.          */
  2495.         private final T[][] sjCoefs;

  2496.         /**
  2497.          * Standard constructor.
  2498.          *
  2499.          * @param nMax maximum value of n index
  2500.          * @param sMax maximum value of s index
  2501.          * @param jMax maximum value of j index
  2502.          * @param context container for attributes
  2503.          * @param hansen hansen objects
  2504.          * @param field field used by default
  2505.          * @param aoR3Pow a / R3 up to power maxAR3Pow
  2506.          * @param qns Qns coefficients
  2507.          */
  2508.         FieldGeneratingFunctionCoefficients(final int nMax, final int sMax, final int jMax,
  2509.                                             final FieldDSSTThirdBodyDynamicContext<T> context,
  2510.                                             final FieldHansenObjects<T> hansen, final Field<T> field,
  2511.                                             final T[] aoR3Pow, final T[][] qns) {
  2512.             this.jMax = jMax;
  2513.             this.cjsjFourier = new FieldFourierCjSjCoefficients<>(nMax, sMax, jMax, context, aoR3Pow, qns, field);
  2514.             this.cjCoefs     = MathArrays.buildArray(field, 8, jMax + 1);
  2515.             this.sjCoefs     = MathArrays.buildArray(field, 8, jMax + 1);

  2516.             computeGeneratingFunctionCoefficients(context, hansen, aoR3Pow, qns);
  2517.         }

  2518.         /**
  2519.          * Compute the coefficients for the generating function S and its derivatives.
  2520.          * @param context container for attributes
  2521.          * @param hansenObjects hansen objects
  2522.          * @param aoR3Pow a / R3 up to power maxAR3Pow
  2523.          * @param qns Qns coefficients
  2524.          */
  2525.         private void computeGeneratingFunctionCoefficients(final FieldDSSTThirdBodyDynamicContext<T> context,
  2526.                                                            final FieldHansenObjects<T> hansenObjects,
  2527.                                                            final T[] aoR3Pow, final T[][] qns) {

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

  2529.             // Access to potential U derivatives
  2530.             final FieldUAnddU<T> udu = new FieldUAnddU<>(context, hansenObjects, aoR3Pow, qns);

  2531.             //Compute the C<sup>j</sup> coefficients
  2532.             for (int j = 1; j <= jMax; j++) {
  2533.                 //Compute the C<sup>j</sup> coefficients
  2534.                 cjCoefs[0][j] = cjsjFourier.getCj(j);
  2535.                 cjCoefs[1][j] = cjsjFourier.getdCjda(j);
  2536.                 cjCoefs[2][j] = cjsjFourier.getdCjdk(j).subtract((cjsjFourier.getSjLambda(j - 1).subtract(cjsjFourier.getSjLambda(j + 1))).divide(2.));
  2537.                 cjCoefs[3][j] = cjsjFourier.getdCjdh(j).subtract((cjsjFourier.getCjLambda(j - 1).add(cjsjFourier.getCjLambda(j + 1))).divide(2.));
  2538.                 cjCoefs[4][j] = cjsjFourier.getdCjdalpha(j);
  2539.                 cjCoefs[5][j] = cjsjFourier.getdCjdbeta(j);
  2540.                 cjCoefs[6][j] = cjsjFourier.getdCjdgamma(j);
  2541.                 cjCoefs[7][j] = cjsjFourier.getCjLambda(j);

  2542.                 //Compute the S<sup>j</sup> coefficients
  2543.                 sjCoefs[0][j] = cjsjFourier.getSj(j);
  2544.                 sjCoefs[1][j] = cjsjFourier.getdSjda(j);
  2545.                 sjCoefs[2][j] = cjsjFourier.getdSjdk(j).add((cjsjFourier.getCjLambda(j - 1).subtract(cjsjFourier.getCjLambda(j + 1))).divide(2.));
  2546.                 sjCoefs[3][j] = cjsjFourier.getdSjdh(j).subtract((cjsjFourier.getSjLambda(j - 1).add(cjsjFourier.getSjLambda(j + 1))).divide(2.));
  2547.                 sjCoefs[4][j] = cjsjFourier.getdSjdalpha(j);
  2548.                 sjCoefs[5][j] = cjsjFourier.getdSjdbeta(j);
  2549.                 sjCoefs[6][j] = cjsjFourier.getdSjdgamma(j);
  2550.                 sjCoefs[7][j] = cjsjFourier.getSjLambda(j);

  2551.                 //In the special case j == 1 there are some additional terms to be added
  2552.                 if (j == 1) {
  2553.                     //Additional terms for C<sup>j</sup> coefficients
  2554.                     cjCoefs[0][j] = cjCoefs[0][j].add(auxiliaryElements.getH().negate().multiply(udu.getU()));
  2555.                     cjCoefs[1][j] = cjCoefs[1][j].add(auxiliaryElements.getH().negate().multiply(udu.getdUda()));
  2556.                     cjCoefs[2][j] = cjCoefs[2][j].add(auxiliaryElements.getH().negate().multiply(udu.getdUdk()));
  2557.                     cjCoefs[3][j] = cjCoefs[3][j].add(auxiliaryElements.getH().multiply(udu.getdUdh()).add(udu.getU()).add(cjsjFourier.getC0Lambda()).negate());
  2558.                     cjCoefs[4][j] = cjCoefs[4][j].add(auxiliaryElements.getH().negate().multiply(udu.getdUdAl()));
  2559.                     cjCoefs[5][j] = cjCoefs[5][j].add(auxiliaryElements.getH().negate().multiply(udu.getdUdBe()));
  2560.                     cjCoefs[6][j] = cjCoefs[6][j].add(auxiliaryElements.getH().negate().multiply(udu.getdUdGa()));

  2561.                     //Additional terms for S<sup>j</sup> coefficients
  2562.                     sjCoefs[0][j] = sjCoefs[0][j].add(auxiliaryElements.getK().multiply(udu.getU()));
  2563.                     sjCoefs[1][j] = sjCoefs[1][j].add(auxiliaryElements.getK().multiply(udu.getdUda()));
  2564.                     sjCoefs[2][j] = sjCoefs[2][j].add(auxiliaryElements.getK().multiply(udu.getdUdk()).add(udu.getU()).add(cjsjFourier.getC0Lambda()));
  2565.                     sjCoefs[3][j] = sjCoefs[3][j].add(auxiliaryElements.getK().multiply(udu.getdUdh()));
  2566.                     sjCoefs[4][j] = sjCoefs[4][j].add(auxiliaryElements.getK().multiply(udu.getdUdAl()));
  2567.                     sjCoefs[5][j] = sjCoefs[5][j].add(auxiliaryElements.getK().multiply(udu.getdUdBe()));
  2568.                     sjCoefs[6][j] = sjCoefs[6][j].add(auxiliaryElements.getK().multiply(udu.getdUdGa()));
  2569.                 }
  2570.             }
  2571.         }

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

  2582.         /** Get the coefficient S<sup>j</sup> for the function S.
  2583.          * <br>
  2584.          * Possible values for j are within the interval [1,jMax].
  2585.          * @param j j index
  2586.          * @return S<sup>j</sup> for the function S
  2587.          */
  2588.         public T getSSj(final int j) {
  2589.             return sjCoefs[0][j];
  2590.         }

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

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

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

  2620.         /** Get the coefficient S<sup>j</sup> for the derivative dS/dk.
  2621.          * <br>
  2622.          * Possible values for j are within the interval [1,jMax].
  2623.          * @param j j index
  2624.          * @return S<sup>j</sup> for the derivative dS/dk
  2625.          */
  2626.         public T getdSdkSj(final int j) {
  2627.             return sjCoefs[2][j];
  2628.         }

  2629.         /** Get the coefficient C<sup>j</sup> for the derivative dS/dh
  2630.          * <br>
  2631.          * Possible values for j are within the interval [0,jMax].
  2632.          * The value 0 is used to obtain the free coefficient C⁰
  2633.          * @param j j index
  2634.          * @return C<sup>j</sup> for the function dS/dh
  2635.          */
  2636.         public T getdSdhCj(final int j) {
  2637.             return cjCoefs[3][j];
  2638.         }

  2639.         /** Get the coefficient S<sup>j</sup> for the derivative dS/dh.
  2640.          * <br>
  2641.          * Possible values for j are within the interval [1,jMax].
  2642.          * @param j j index
  2643.          * @return S<sup>j</sup> for the derivative dS/dh
  2644.          */
  2645.         public T getdSdhSj(final int j) {
  2646.             return sjCoefs[3][j];
  2647.         }

  2648.         /** Get the coefficient C<sup>j</sup> for the derivative dS/dα
  2649.          * <br>
  2650.          * Possible values for j are within the interval [0,jMax].
  2651.          * The value 0 is used to obtain the free coefficient C⁰
  2652.          * @param j j index
  2653.          * @return C<sup>j</sup> for the function dS/dα
  2654.          */
  2655.         public T getdSdalphaCj(final int j) {
  2656.             return cjCoefs[4][j];
  2657.         }

  2658.         /** Get the coefficient S<sup>j</sup> for the derivative dS/dα.
  2659.          * <br>
  2660.          * Possible values for j are within the interval [1,jMax].
  2661.          * @param j j index
  2662.          * @return S<sup>j</sup> for the derivative dS/dα
  2663.          */
  2664.         public T getdSdalphaSj(final int j) {
  2665.             return sjCoefs[4][j];
  2666.         }

  2667.         /** Get the coefficient C<sup>j</sup> for the derivative dS/dβ
  2668.          * <br>
  2669.          * Possible values for j are within the interval [0,jMax].
  2670.          * The value 0 is used to obtain the free coefficient C⁰
  2671.          * @param j j index
  2672.          * @return C<sup>j</sup> for the function dS/dβ
  2673.          */
  2674.         public T getdSdbetaCj(final int j) {
  2675.             return cjCoefs[5][j];
  2676.         }

  2677.         /** Get the coefficient S<sup>j</sup> for the derivative dS/dβ.
  2678.          * <br>
  2679.          * Possible values for j are within the interval [1,jMax].
  2680.          * @param j j index
  2681.          * @return S<sup>j</sup> for the derivative dS/dβ
  2682.          */
  2683.         public T getdSdbetaSj(final int j) {
  2684.             return sjCoefs[5][j];
  2685.         }

  2686.         /** Get the coefficient C<sup>j</sup> for the derivative dS/dγ
  2687.          * <br>
  2688.          * Possible values for j are within the interval [0,jMax].
  2689.          * The value 0 is used to obtain the free coefficient C⁰
  2690.          * @param j j index
  2691.          * @return C<sup>j</sup> for the function dS/dγ
  2692.          */
  2693.         public T getdSdgammaCj(final int j) {
  2694.             return cjCoefs[6][j];
  2695.         }

  2696.         /** Get the coefficient S<sup>j</sup> for the derivative dS/dγ.
  2697.          * <br>
  2698.          * Possible values for j are within the interval [1,jMax].
  2699.          * @param j j index
  2700.          * @return S<sup>j</sup> for the derivative dS/dγ
  2701.          */
  2702.         public T getdSdgammaSj(final int j) {
  2703.             return sjCoefs[6][j];
  2704.         }

  2705.         /** Get the coefficient C<sup>j</sup> for the derivative dS/dλ
  2706.          * <br>
  2707.          * Possible values for j are within the interval [0,jMax].
  2708.          * The value 0 is used to obtain the free coefficient C⁰
  2709.          * @param j j index
  2710.          * @return C<sup>j</sup> for the function dS/dλ
  2711.          */
  2712.         public T getdSdlambdaCj(final int j) {
  2713.             return cjCoefs[7][j];
  2714.         }

  2715.         /** Get the coefficient S<sup>j</sup> for the derivative dS/dλ.
  2716.          * <br>
  2717.          * Possible values for j are within the interval [1,jMax].
  2718.          * @param j j index
  2719.          * @return S<sup>j</sup> for the derivative dS/dλ
  2720.          */
  2721.         public T getdSdlambdaSj(final int j) {
  2722.             return sjCoefs[7][j];
  2723.         }
  2724.     }

  2725.     /**
  2726.      * The coefficients used to compute the short periodic contribution for the Third body perturbation.
  2727.      * <p>
  2728.      * The short periodic contribution for the Third Body is expressed in Danielson 4.2-25.<br>
  2729.      * The coefficients C<sub>i</sub>⁰, C<sub>i</sub><sup>j</sup>, S<sub>i</sub><sup>j</sup>
  2730.      * are computed by replacing the corresponding values in formula 2.5.5-10.
  2731.      * </p>
  2732.      * @author Lucian Barbulescu
  2733.      */
  2734.     private static class ThirdBodyShortPeriodicCoefficients implements ShortPeriodTerms {

  2735.         /** Maximal value for j. */
  2736.         private final int jMax;

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

  2739.         /** Max frequency of F. */
  2740.         private final int    maxFreqF;

  2741.         /** Coefficients prefix. */
  2742.         private final String prefix;

  2743.         /** All coefficients slots. */
  2744.         private final transient TimeSpanMap<Slot> slots;

  2745.         /**
  2746.          * Standard constructor.
  2747.          *  @param interpolationPoints number of points used in the interpolation process
  2748.          * @param jMax maximal value for j
  2749.          * @param maxFreqF Max frequency of F
  2750.          * @param bodyName third body name
  2751.          * @param slots all coefficients slots
  2752.          */
  2753.         ThirdBodyShortPeriodicCoefficients(final int jMax, final int interpolationPoints,
  2754.                                            final int maxFreqF, final String bodyName,
  2755.                                            final TimeSpanMap<Slot> slots) {
  2756.             this.jMax                = jMax;
  2757.             this.interpolationPoints = interpolationPoints;
  2758.             this.maxFreqF            = maxFreqF;
  2759.             this.prefix              = DSSTThirdBody.SHORT_PERIOD_PREFIX + bodyName + "-";
  2760.             this.slots               = slots;
  2761.         }

  2762.         /** Get the slot valid for some date.
  2763.          * @param meanStates mean states defining the slot
  2764.          * @return slot valid at the specified date
  2765.          */
  2766.         public Slot createSlot(final SpacecraftState... meanStates) {
  2767.             final Slot         slot  = new Slot(jMax, interpolationPoints);
  2768.             final AbsoluteDate first = meanStates[0].getDate();
  2769.             final AbsoluteDate last  = meanStates[meanStates.length - 1].getDate();
  2770.             final int compare = first.compareTo(last);
  2771.             if (compare < 0) {
  2772.                 slots.addValidAfter(slot, first, false);
  2773.             } else if (compare > 0) {
  2774.                 slots.addValidBefore(slot, first, false);
  2775.             } else {
  2776.                 // single date, valid for all time
  2777.                 slots.addValidAfter(slot, AbsoluteDate.PAST_INFINITY, false);
  2778.             }
  2779.             return slot;
  2780.         }

  2781.         /** {@inheritDoc} */
  2782.         @Override
  2783.         public double[] value(final Orbit meanOrbit) {

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

  2786.             // the current eccentric longitude
  2787.             final double F = meanOrbit.getLE();

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

  2790.             // Add the cos and sin dependent terms
  2791.             for (int j = 1; j <= maxFreqF; j++) {
  2792.                 //compute cos and sin
  2793.                 final SinCos scjF  = FastMath.sinCos(j * F);

  2794.                 final double[] c = slot.cij[j].value(meanOrbit.getDate());
  2795.                 final double[] s = slot.sij[j].value(meanOrbit.getDate());
  2796.                 for (int i = 0; i < 6; i++) {
  2797.                     shortPeriodic[i] += c[i] * scjF.cos() + s[i] * scjF.sin();
  2798.                 }
  2799.             }

  2800.             return shortPeriodic;

  2801.         }

  2802.         /** {@inheritDoc} */
  2803.         @Override
  2804.         public String getCoefficientsKeyPrefix() {
  2805.             return prefix;
  2806.         }

  2807.         /** {@inheritDoc}
  2808.          * <p>
  2809.          * For third body attraction forces,there are maxFreqF + 1 cj coefficients,
  2810.          * maxFreqF sj coefficients where maxFreqF depends on the orbit.
  2811.          * The j index is the integer multiplier for the eccentric longitude argument
  2812.          * in the cj and sj coefficients.
  2813.          * </p>
  2814.          */
  2815.         @Override
  2816.         public Map<String, double[]> getCoefficients(final AbsoluteDate date, final Set<String> selected) {

  2817.             // select the coefficients slot
  2818.             final Slot slot = slots.get(date);

  2819.             final Map<String, double[]> coefficients = new HashMap<String, double[]>(2 * maxFreqF + 1);
  2820.             storeIfSelected(coefficients, selected, slot.cij[0].value(date), "c", 0);
  2821.             for (int j = 1; j <= maxFreqF; j++) {
  2822.                 storeIfSelected(coefficients, selected, slot.cij[j].value(date), "c", j);
  2823.                 storeIfSelected(coefficients, selected, slot.sij[j].value(date), "s", j);
  2824.             }
  2825.             return coefficients;

  2826.         }

  2827.         /** Put a coefficient in a map if selected.
  2828.          * @param map map to populate
  2829.          * @param selected set of coefficients that should be put in the map
  2830.          * (empty set means all coefficients are selected)
  2831.          * @param value coefficient value
  2832.          * @param id coefficient identifier
  2833.          * @param indices list of coefficient indices
  2834.          */
  2835.         private void storeIfSelected(final Map<String, double[]> map, final Set<String> selected,
  2836.                                      final double[] value, final String id, final int... indices) {
  2837.             final StringBuilder keyBuilder = new StringBuilder(getCoefficientsKeyPrefix());
  2838.             keyBuilder.append(id);
  2839.             for (int index : indices) {
  2840.                 keyBuilder.append('[').append(index).append(']');
  2841.             }
  2842.             final String key = keyBuilder.toString();
  2843.             if (selected.isEmpty() || selected.contains(key)) {
  2844.                 map.put(key, value);
  2845.             }
  2846.         }

  2847.     }

  2848.     /**
  2849.      * The coefficients used to compute the short periodic contribution for the Third body perturbation.
  2850.      * <p>
  2851.      * The short periodic contribution for the Third Body is expressed in Danielson 4.2-25.<br>
  2852.      * The coefficients C<sub>i</sub>⁰, C<sub>i</sub><sup>j</sup>, S<sub>i</sub><sup>j</sup>
  2853.      * are computed by replacing the corresponding values in formula 2.5.5-10.
  2854.      * </p>
  2855.      * @author Lucian Barbulescu
  2856.      */
  2857.     private static class FieldThirdBodyShortPeriodicCoefficients <T extends CalculusFieldElement<T>> implements FieldShortPeriodTerms<T> {

  2858.         /** Maximal value for j. */
  2859.         private final int jMax;

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

  2862.         /** Max frequency of F. */
  2863.         private final int    maxFreqF;

  2864.         /** Coefficients prefix. */
  2865.         private final String prefix;

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

  2868.         /**
  2869.          * Standard constructor.
  2870.          * @param interpolationPoints number of points used in the interpolation process
  2871.          * @param jMax maximal value for j
  2872.          * @param maxFreqF Max frequency of F
  2873.          * @param bodyName third body name
  2874.          * @param slots all coefficients slots
  2875.          */
  2876.         FieldThirdBodyShortPeriodicCoefficients(final int jMax, final int interpolationPoints,
  2877.                                                 final int maxFreqF, final String bodyName,
  2878.                                                 final FieldTimeSpanMap<FieldSlot<T>, T> slots) {
  2879.             this.jMax                = jMax;
  2880.             this.interpolationPoints = interpolationPoints;
  2881.             this.maxFreqF            = maxFreqF;
  2882.             this.prefix              = DSSTThirdBody.SHORT_PERIOD_PREFIX + bodyName + "-";
  2883.             this.slots               = slots;
  2884.         }

  2885.         /** Get the slot valid for some date.
  2886.          * @param meanStates mean states defining the slot
  2887.          * @return slot valid at the specified date
  2888.          */
  2889.         @SuppressWarnings("unchecked")
  2890.         public FieldSlot<T> createSlot(final FieldSpacecraftState<T>... meanStates) {
  2891.             final FieldSlot<T>         slot  = new FieldSlot<>(jMax, interpolationPoints);
  2892.             final FieldAbsoluteDate<T> first = meanStates[0].getDate();
  2893.             final FieldAbsoluteDate<T> last  = meanStates[meanStates.length - 1].getDate();
  2894.             if (first.compareTo(last) <= 0) {
  2895.                 slots.addValidAfter(slot, first);
  2896.             } else {
  2897.                 slots.addValidBefore(slot, first);
  2898.             }
  2899.             return slot;
  2900.         }

  2901.         /** {@inheritDoc} */
  2902.         @Override
  2903.         public T[] value(final FieldOrbit<T> meanOrbit) {

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

  2906.             // the current eccentric longitude
  2907.             final T F = meanOrbit.getLE();

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

  2910.             // Add the cos and sin dependent terms
  2911.             for (int j = 1; j <= maxFreqF; j++) {
  2912.                 //compute cos and sin
  2913.                 final FieldSinCos<T> scjF = FastMath.sinCos(F.multiply(j));

  2914.                 final T[] c = (T[]) slot.cij[j].value(meanOrbit.getDate());
  2915.                 final T[] s = (T[]) slot.sij[j].value(meanOrbit.getDate());
  2916.                 for (int i = 0; i < 6; i++) {
  2917.                     shortPeriodic[i] = shortPeriodic[i].add(c[i].multiply(scjF.cos()).add(s[i].multiply(scjF.sin())));
  2918.                 }
  2919.             }

  2920.             return shortPeriodic;

  2921.         }

  2922.         /** {@inheritDoc} */
  2923.         @Override
  2924.         public String getCoefficientsKeyPrefix() {
  2925.             return prefix;
  2926.         }

  2927.         /** {@inheritDoc}
  2928.          * <p>
  2929.          * For third body attraction forces,there are maxFreqF + 1 cj coefficients,
  2930.          * maxFreqF sj coefficients where maxFreqF depends on the orbit.
  2931.          * The j index is the integer multiplier for the eccentric longitude argument
  2932.          * in the cj and sj coefficients.
  2933.          * </p>
  2934.          */
  2935.         @Override
  2936.         public Map<String, T[]> getCoefficients(final FieldAbsoluteDate<T> date, final Set<String> selected) {

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

  2939.             final Map<String, T[]> coefficients = new HashMap<String, T[]>(2 * maxFreqF + 1);
  2940.             storeIfSelected(coefficients, selected, slot.cij[0].value(date), "c", 0);
  2941.             for (int j = 1; j <= maxFreqF; j++) {
  2942.                 storeIfSelected(coefficients, selected, slot.cij[j].value(date), "c", j);
  2943.                 storeIfSelected(coefficients, selected, slot.sij[j].value(date), "s", j);
  2944.             }
  2945.             return coefficients;

  2946.         }

  2947.         /** Put a coefficient in a map if selected.
  2948.          * @param map map to populate
  2949.          * @param selected set of coefficients that should be put in the map
  2950.          * (empty set means all coefficients are selected)
  2951.          * @param value coefficient value
  2952.          * @param id coefficient identifier
  2953.          * @param indices list of coefficient indices
  2954.          */
  2955.         private void storeIfSelected(final Map<String, T[]> map, final Set<String> selected,
  2956.                                      final T[] value, final String id, final int... indices) {
  2957.             final StringBuilder keyBuilder = new StringBuilder(getCoefficientsKeyPrefix());
  2958.             keyBuilder.append(id);
  2959.             for (int index : indices) {
  2960.                 keyBuilder.append('[').append(index).append(']');
  2961.             }
  2962.             final String key = keyBuilder.toString();
  2963.             if (selected.isEmpty() || selected.contains(key)) {
  2964.                 map.put(key, value);
  2965.             }
  2966.         }

  2967.     }

  2968.     /** Coefficients valid for one time slot. */
  2969.     private static class Slot {

  2970.         /** The coefficients C<sub>i</sub><sup>j</sup>.
  2971.          * <p>
  2972.          * The index order is cij[j][i] <br/>
  2973.          * i corresponds to the equinoctial element, as follows: <br/>
  2974.          * - i=0 for a <br/>
  2975.          * - i=1 for k <br/>
  2976.          * - i=2 for h <br/>
  2977.          * - i=3 for q <br/>
  2978.          * - i=4 for p <br/>
  2979.          * - i=5 for λ <br/>
  2980.          * </p>
  2981.          */
  2982.         private final ShortPeriodicsInterpolatedCoefficient[] cij;

  2983.         /** The coefficients S<sub>i</sub><sup>j</sup>.
  2984.          * <p>
  2985.          * The index order is sij[j][i] <br/>
  2986.          * i corresponds to the equinoctial element, as follows: <br/>
  2987.          * - i=0 for a <br/>
  2988.          * - i=1 for k <br/>
  2989.          * - i=2 for h <br/>
  2990.          * - i=3 for q <br/>
  2991.          * - i=4 for p <br/>
  2992.          * - i=5 for λ <br/>
  2993.          * </p>
  2994.          */
  2995.         private final ShortPeriodicsInterpolatedCoefficient[] sij;

  2996.         /** Simple constructor.
  2997.          *  @param jMax maximum value for j index
  2998.          *  @param interpolationPoints number of points used in the interpolation process
  2999.          */
  3000.         Slot(final int jMax, final int interpolationPoints) {
  3001.             // allocate the coefficients arrays
  3002.             cij = new ShortPeriodicsInterpolatedCoefficient[jMax + 1];
  3003.             sij = new ShortPeriodicsInterpolatedCoefficient[jMax + 1];
  3004.             for (int j = 0; j <= jMax; j++) {
  3005.                 cij[j] = new ShortPeriodicsInterpolatedCoefficient(interpolationPoints);
  3006.                 sij[j] = new ShortPeriodicsInterpolatedCoefficient(interpolationPoints);
  3007.             }


  3008.         }
  3009.     }

  3010.     /** Coefficients valid for one time slot. */
  3011.     private static class FieldSlot <T extends CalculusFieldElement<T>> {

  3012.         /** The coefficients C<sub>i</sub><sup>j</sup>.
  3013.          * <p>
  3014.          * The index order is cij[j][i] <br/>
  3015.          * i corresponds to the equinoctial element, as follows: <br/>
  3016.          * - i=0 for a <br/>
  3017.          * - i=1 for k <br/>
  3018.          * - i=2 for h <br/>
  3019.          * - i=3 for q <br/>
  3020.          * - i=4 for p <br/>
  3021.          * - i=5 for λ <br/>
  3022.          * </p>
  3023.          */
  3024.         private final FieldShortPeriodicsInterpolatedCoefficient<T>[] cij;

  3025.         /** The coefficients S<sub>i</sub><sup>j</sup>.
  3026.          * <p>
  3027.          * The index order is sij[j][i] <br/>
  3028.          * i corresponds to the equinoctial element, as follows: <br/>
  3029.          * - i=0 for a <br/>
  3030.          * - i=1 for k <br/>
  3031.          * - i=2 for h <br/>
  3032.          * - i=3 for q <br/>
  3033.          * - i=4 for p <br/>
  3034.          * - i=5 for λ <br/>
  3035.          * </p>
  3036.          */
  3037.         private final FieldShortPeriodicsInterpolatedCoefficient<T>[] sij;

  3038.         /** Simple constructor.
  3039.          *  @param jMax maximum value for j index
  3040.          *  @param interpolationPoints number of points used in the interpolation process
  3041.          */
  3042.         @SuppressWarnings("unchecked")
  3043.         FieldSlot(final int jMax, final int interpolationPoints) {
  3044.             // allocate the coefficients arrays
  3045.             cij = (FieldShortPeriodicsInterpolatedCoefficient<T>[]) Array.newInstance(FieldShortPeriodicsInterpolatedCoefficient.class, jMax + 1);
  3046.             sij = (FieldShortPeriodicsInterpolatedCoefficient<T>[]) Array.newInstance(FieldShortPeriodicsInterpolatedCoefficient.class, jMax + 1);
  3047.             for (int j = 0; j <= jMax; j++) {
  3048.                 cij[j] = new FieldShortPeriodicsInterpolatedCoefficient<>(interpolationPoints);
  3049.                 sij[j] = new FieldShortPeriodicsInterpolatedCoefficient<>(interpolationPoints);
  3050.             }


  3051.         }
  3052.     }

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

  3055.         /** The current value of the U function. <br/>
  3056.          * Needed for the short periodic contribution */
  3057.         private double U;

  3058.         /** dU / da. */
  3059.         private  double dUda;

  3060.         /** dU / dk. */
  3061.         private double dUdk;

  3062.         /** dU / dh. */
  3063.         private double dUdh;

  3064.         /** dU / dAlpha. */
  3065.         private double dUdAl;

  3066.         /** dU / dBeta. */
  3067.         private double dUdBe;

  3068.         /** dU / dGamma. */
  3069.         private double dUdGa;

  3070.         /** Simple constuctor.
  3071.          * @param context container for attributes
  3072.          * @param hansen hansen objects
  3073.          * @param aoR3Pow a / R3 up to power maxAR3Pow
  3074.          * @param qns Qns coefficients
  3075.          */
  3076.         UAnddU(final DSSTThirdBodyDynamicContext context, final HansenObjects hansen,
  3077.                final double[] aoR3Pow, final double[][] qns) {
  3078.             // Auxiliary elements related to the current orbit
  3079.             final AuxiliaryElements auxiliaryElements = context.getAuxiliaryElements();

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

  3082.             // Initialise U.
  3083.             U = 0.;

  3084.             // Potential derivatives
  3085.             dUda  = 0.;
  3086.             dUdk  = 0.;
  3087.             dUdh  = 0.;
  3088.             dUdAl = 0.;
  3089.             dUdBe = 0.;
  3090.             dUdGa = 0.;

  3091.             for (int s = 0; s <= staticContext.getMaxEccPow(); s++) {

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

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

  3096.                 // Compute Gs partial derivatives from 3.1-(9)
  3097.                 double dGsdh  = 0.;
  3098.                 double dGsdk  = 0.;
  3099.                 double dGsdAl = 0.;
  3100.                 double dGsdBe = 0.;
  3101.                 if (s > 0) {
  3102.                     // First get the G(s-1) and the H(s-1) coefficients
  3103.                     final double sxGsm1 = s * GsHs[0][s - 1];
  3104.                     final double sxHsm1 = s * GsHs[1][s - 1];
  3105.                     // Then compute derivatives
  3106.                     dGsdh  = context.getBeta()  * sxGsm1 - context.getAlpha() * sxHsm1;
  3107.                     dGsdk  = context.getAlpha() * sxGsm1 + context.getBeta()  * sxHsm1;
  3108.                     dGsdAl = auxiliaryElements.getK() * sxGsm1 - auxiliaryElements.getH() * sxHsm1;
  3109.                     dGsdBe = auxiliaryElements.getH() * sxGsm1 + auxiliaryElements.getK() * sxHsm1;
  3110.                 }

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

  3113.                 for (int n = FastMath.max(2, s); n <= staticContext.getMaxAR3Pow(); n++) {
  3114.                     // (n - s) must be even
  3115.                     if ((n - s) % 2 == 0) {
  3116.                         // Extract data from previous computation :
  3117.                         final double kns   = hansen.getHansenObjects()[s].getValue(n, auxiliaryElements.getB());
  3118.                         final double dkns  = hansen.getHansenObjects()[s].getDerivative(n, auxiliaryElements.getB());

  3119.                         final double vns   = Vns.get(new NSKey(n, s));
  3120.                         final double coef0 = delta0s * aoR3Pow[n] * vns;
  3121.                         final double coef1 = coef0 * qns[n][s];
  3122.                         final double coef2 = coef1 * kns;
  3123.                         // dQns/dGamma = Q(n, s + 1) from Equation 3.1-(8)
  3124.                         // for n = s, Q(n, n + 1) = 0. (Cefola & Broucke, 1975)
  3125.                         final double dqns = (n == s) ? 0. : qns[n][s + 1];

  3126.                         //Compute U:
  3127.                         U += coef2 * gs;

  3128.                         // Compute dU / da :
  3129.                         dUda  += coef2 * n * gs;
  3130.                         // Compute dU / dh
  3131.                         dUdh  += coef1 * (kns * dGsdh + context.getHXXX() * gs * dkns);
  3132.                         // Compute dU / dk
  3133.                         dUdk  += coef1 * (kns * dGsdk + context.getKXXX() * gs * dkns);
  3134.                         // Compute dU / dAlpha
  3135.                         dUdAl += coef2 * dGsdAl;
  3136.                         // Compute dU / dBeta
  3137.                         dUdBe += coef2 * dGsdBe;
  3138.                         // Compute dU / dGamma
  3139.                         dUdGa += coef0 * kns * dqns * gs;
  3140.                     }
  3141.                 }
  3142.             }

  3143.             // multiply by mu3 / R3
  3144.             this.U = U * context.getMuoR3();

  3145.             this.dUda  = dUda  * context.getMuoR3() / auxiliaryElements.getSma();
  3146.             this.dUdk  = dUdk  * context.getMuoR3();
  3147.             this.dUdh  = dUdh  * context.getMuoR3();
  3148.             this.dUdAl = dUdAl * context.getMuoR3();
  3149.             this.dUdBe = dUdBe * context.getMuoR3();
  3150.             this.dUdGa = dUdGa * context.getMuoR3();

  3151.         }

  3152.         /** Return value of U.
  3153.          * @return U
  3154.          */
  3155.         public double getU() {
  3156.             return U;
  3157.         }

  3158.         /** Return value of dU / da.
  3159.          * @return dUda
  3160.          */
  3161.         public double getdUda() {
  3162.             return dUda;
  3163.         }

  3164.         /** Return value of dU / dk.
  3165.          * @return dUdk
  3166.          */
  3167.         public double getdUdk() {
  3168.             return dUdk;
  3169.         }

  3170.         /** Return value of dU / dh.
  3171.          * @return dUdh
  3172.          */
  3173.         public double getdUdh() {
  3174.             return dUdh;
  3175.         }

  3176.         /** Return value of dU / dAlpha.
  3177.          * @return dUdAl
  3178.          */
  3179.         public double getdUdAl() {
  3180.             return dUdAl;
  3181.         }

  3182.         /** Return value of dU / dBeta.
  3183.          * @return dUdBe
  3184.          */
  3185.         public double getdUdBe() {
  3186.             return dUdBe;
  3187.         }

  3188.         /** Return value of dU / dGamma.
  3189.          * @return dUdGa
  3190.          */
  3191.         public double getdUdGa() {
  3192.             return dUdGa;
  3193.         }

  3194.     }

  3195.     /** Compute potential and potential derivatives with respect to orbital parameters. */
  3196.     private class FieldUAnddU <T extends CalculusFieldElement<T>> {

  3197.         /** The current value of the U function. <br/>
  3198.          * Needed for the short periodic contribution */
  3199.         private T U;

  3200.         /** dU / da. */
  3201.         private T dUda;

  3202.         /** dU / dk. */
  3203.         private T dUdk;

  3204.         /** dU / dh. */
  3205.         private T dUdh;

  3206.         /** dU / dAlpha. */
  3207.         private T dUdAl;

  3208.         /** dU / dBeta. */
  3209.         private T dUdBe;

  3210.         /** dU / dGamma. */
  3211.         private T dUdGa;

  3212.         /** Simple constuctor.
  3213.          * @param context container for attributes
  3214.          * @param hansen hansen objects
  3215.          * @param aoR3Pow a / R3 up to power maxAR3Pow
  3216.          * @param qns Qns coefficients
  3217.          */
  3218.         FieldUAnddU(final FieldDSSTThirdBodyDynamicContext<T> context, final FieldHansenObjects<T> hansen,
  3219.                     final T[] aoR3Pow, final T[][] qns) {

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

  3222.             // Field for array building
  3223.             final Field<T> field = auxiliaryElements.getDate().getField();
  3224.             // Zero for initialization
  3225.             final T zero = field.getZero();

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

  3228.             // Initialise U.
  3229.             U = zero;

  3230.             // Potential derivatives
  3231.             dUda  = zero;
  3232.             dUdk  = zero;
  3233.             dUdh  = zero;
  3234.             dUdAl = zero;
  3235.             dUdBe = zero;
  3236.             dUdGa = zero;

  3237.             for (int s = 0; s <= staticContext.getMaxEccPow(); s++) {
  3238.                 // initialise the Hansen roots
  3239.                 hansen.computeHansenObjectsInitValues(context, auxiliaryElements.getB(), s);

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

  3242.                 // Compute Gs partial derivatives from 3.1-(9)
  3243.                 T dGsdh  = zero;
  3244.                 T dGsdk  = zero;
  3245.                 T dGsdAl = zero;
  3246.                 T dGsdBe = zero;
  3247.                 if (s > 0) {
  3248.                     // First get the G(s-1) and the H(s-1) coefficients
  3249.                     final T sxGsm1 = GsHs[0][s - 1].multiply(s);
  3250.                     final T sxHsm1 = GsHs[1][s - 1].multiply(s);
  3251.                     // Then compute derivatives
  3252.                     dGsdh  = sxGsm1.multiply(context.getBeta()).subtract(sxHsm1.multiply(context.getAlpha()));
  3253.                     dGsdk  = sxGsm1.multiply(context.getAlpha()).add(sxHsm1.multiply(context.getBeta()));
  3254.                     dGsdAl = sxGsm1.multiply(auxiliaryElements.getK()).subtract(sxHsm1.multiply(auxiliaryElements.getH()));
  3255.                     dGsdBe = sxGsm1.multiply(auxiliaryElements.getH()).add(sxHsm1.multiply(auxiliaryElements.getK()));
  3256.                 }

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

  3259.                 for (int n = FastMath.max(2, s); n <= staticContext.getMaxAR3Pow(); n++) {
  3260.                     // (n - s) must be even
  3261.                     if ((n - s) % 2 == 0) {
  3262.                         // Extract data from previous computation :
  3263.                         final T kns   = (T) hansen.getHansenObjects()[s].getValue(n, auxiliaryElements.getB());
  3264.                         final T dkns  = (T) hansen.getHansenObjects()[s].getDerivative(n, auxiliaryElements.getB());

  3265.                         final double vns = Vns.get(new NSKey(n, s));
  3266.                         final T coef0 = delta0s.multiply(vns).multiply(aoR3Pow[n]);
  3267.                         final T coef1 = coef0.multiply(qns[n][s]);
  3268.                         final T coef2 = coef1.multiply(kns);
  3269.                         // dQns/dGamma = Q(n, s + 1) from Equation 3.1-(8)
  3270.                         // for n = s, Q(n, n + 1) = 0. (Cefola & Broucke, 1975)
  3271.                         final T dqns = (n == s) ? zero : qns[n][s + 1];

  3272.                         //Compute U:
  3273.                         U = U.add(coef2.multiply(gs));

  3274.                         // Compute dU / da :
  3275.                         dUda  = dUda.add(coef2.multiply(n).multiply(gs));
  3276.                         // Compute dU / dh
  3277.                         dUdh  = dUdh.add(coef1.multiply(dGsdh.multiply(kns).add(context.getHXXX().multiply(gs).multiply(dkns))));
  3278.                         // Compute dU / dk
  3279.                         dUdk  = dUdk.add(coef1.multiply(dGsdk.multiply(kns).add(context.getKXXX().multiply(gs).multiply(dkns))));
  3280.                         // Compute dU / dAlpha
  3281.                         dUdAl = dUdAl.add(coef2.multiply(dGsdAl));
  3282.                         // Compute dU / dBeta
  3283.                         dUdBe = dUdBe.add(coef2.multiply(dGsdBe));
  3284.                         // Compute dU / dGamma
  3285.                         dUdGa = dUdGa.add(coef0.multiply(kns).multiply(dqns).multiply(gs));
  3286.                     }
  3287.                 }
  3288.             }

  3289.             // multiply by mu3 / R3
  3290.             this.U = U.multiply(context.getMuoR3());

  3291.             this.dUda  = dUda.multiply(context.getMuoR3().divide(auxiliaryElements.getSma()));
  3292.             this.dUdk  = dUdk.multiply(context.getMuoR3());
  3293.             this.dUdh  = dUdh.multiply(context.getMuoR3());
  3294.             this.dUdAl = dUdAl.multiply(context.getMuoR3());
  3295.             this.dUdBe = dUdBe.multiply(context.getMuoR3());
  3296.             this.dUdGa = dUdGa.multiply(context.getMuoR3());

  3297.         }

  3298.         /** Return value of U.
  3299.          * @return U
  3300.          */
  3301.         public T getU() {
  3302.             return U;
  3303.         }

  3304.         /** Return value of dU / da.
  3305.          * @return dUda
  3306.          */
  3307.         public T getdUda() {
  3308.             return dUda;
  3309.         }

  3310.         /** Return value of dU / dk.
  3311.          * @return dUdk
  3312.          */
  3313.         public T getdUdk() {
  3314.             return dUdk;
  3315.         }

  3316.         /** Return value of dU / dh.
  3317.          * @return dUdh
  3318.          */
  3319.         public T getdUdh() {
  3320.             return dUdh;
  3321.         }

  3322.         /** Return value of dU / dAlpha.
  3323.          * @return dUdAl
  3324.          */
  3325.         public T getdUdAl() {
  3326.             return dUdAl;
  3327.         }

  3328.         /** Return value of dU / dBeta.
  3329.          * @return dUdBe
  3330.          */
  3331.         public T getdUdBe() {
  3332.             return dUdBe;
  3333.         }

  3334.         /** Return value of dU / dGamma.
  3335.          * @return dUdGa
  3336.          */
  3337.         public T getdUdGa() {
  3338.             return dUdGa;
  3339.         }

  3340.     }

  3341.     /** Computes init values of the Hansen Objects. */
  3342.     private static class HansenObjects {

  3343.         /** Max power for summation. */
  3344.         private static final int    MAX_POWER = 22;

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

  3348.         /** Simple constructor. */
  3349.         HansenObjects() {
  3350.             this.hansenObjects = new HansenThirdBodyLinear[MAX_POWER + 1];
  3351.             for (int s = 0; s <= MAX_POWER; s++) {
  3352.                 this.hansenObjects[s] = new HansenThirdBodyLinear(MAX_POWER, s);
  3353.             }
  3354.         }

  3355.         /** Compute init values for hansen objects.
  3356.          * @param context container for attributes
  3357.          * @param B = sqrt(1 - e²).
  3358.          * @param element element of the array to compute the init values
  3359.          */
  3360.         public void computeHansenObjectsInitValues(final DSSTThirdBodyDynamicContext context, final double B, final int element) {
  3361.             hansenObjects[element].computeInitValues(B, context.getBB(), context.getBBB());
  3362.         }

  3363.         /** Get the Hansen Objects.
  3364.          * @return hansenObjects
  3365.          */
  3366.         public HansenThirdBodyLinear[] getHansenObjects() {
  3367.             return hansenObjects;
  3368.         }

  3369.     }

  3370.     /** Computes init values of the Hansen Objects. */
  3371.     private static class FieldHansenObjects<T extends CalculusFieldElement<T>> {

  3372.         /** Max power for summation. */
  3373.         private static final int    MAX_POWER = 22;

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

  3377.         /** Simple constructor.
  3378.          * @param field field used by default
  3379.          */
  3380.         @SuppressWarnings("unchecked")
  3381.         FieldHansenObjects(final Field<T> field) {
  3382.             this.hansenObjects = (FieldHansenThirdBodyLinear<T>[]) Array.newInstance(FieldHansenThirdBodyLinear.class, MAX_POWER + 1);
  3383.             for (int s = 0; s <= MAX_POWER; s++) {
  3384.                 this.hansenObjects[s] = new FieldHansenThirdBodyLinear<>(MAX_POWER, s, field);
  3385.             }
  3386.         }

  3387.         /** Initialise the Hansen roots for third body problem.
  3388.          * @param context container for attributes
  3389.          * @param B = sqrt(1 - e²).
  3390.          * @param element element of the array to compute the init values
  3391.          */
  3392.         public void computeHansenObjectsInitValues(final FieldDSSTThirdBodyDynamicContext<T> context,
  3393.                                                    final T B, final int element) {
  3394.             hansenObjects[element].computeInitValues(B, context.getBB(), context.getBBB());
  3395.         }

  3396.         /** Get the Hansen Objects.
  3397.          * @return hansenObjects
  3398.          */
  3399.         public FieldHansenThirdBodyLinear<T>[] getHansenObjects() {
  3400.             return hansenObjects;
  3401.         }

  3402.     }

  3403. }