AbstractGaussianContribution.java

  1. /* Copyright 2002-2020 CS GROUP
  2.  * Licensed to CS GROUP (CS) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * CS licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *   http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.orekit.propagation.semianalytical.dsst.forces;

  18. import java.lang.reflect.Array;
  19. import java.util.ArrayList;
  20. import java.util.Collections;
  21. import java.util.HashMap;
  22. import java.util.List;
  23. import java.util.Map;
  24. import java.util.Set;

  25. import org.hipparchus.Field;
  26. import org.hipparchus.RealFieldElement;
  27. import org.hipparchus.analysis.RealFieldUnivariateVectorFunction;
  28. import org.hipparchus.analysis.UnivariateVectorFunction;
  29. import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
  30. import org.hipparchus.geometry.euclidean.threed.Vector3D;
  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.Attitude;
  36. import org.orekit.attitudes.AttitudeProvider;
  37. import org.orekit.attitudes.FieldAttitude;
  38. import org.orekit.forces.ForceModel;
  39. import org.orekit.orbits.EquinoctialOrbit;
  40. import org.orekit.orbits.FieldEquinoctialOrbit;
  41. import org.orekit.orbits.FieldOrbit;
  42. import org.orekit.orbits.Orbit;
  43. import org.orekit.orbits.OrbitType;
  44. import org.orekit.orbits.PositionAngle;
  45. import org.orekit.propagation.FieldSpacecraftState;
  46. import org.orekit.propagation.PropagationType;
  47. import org.orekit.propagation.SpacecraftState;
  48. import org.orekit.propagation.semianalytical.dsst.utilities.AuxiliaryElements;
  49. import org.orekit.propagation.semianalytical.dsst.utilities.CjSjCoefficient;
  50. import org.orekit.propagation.semianalytical.dsst.utilities.FieldAuxiliaryElements;
  51. import org.orekit.propagation.semianalytical.dsst.utilities.FieldCjSjCoefficient;
  52. import org.orekit.propagation.semianalytical.dsst.utilities.FieldShortPeriodicsInterpolatedCoefficient;
  53. import org.orekit.propagation.semianalytical.dsst.utilities.ShortPeriodicsInterpolatedCoefficient;
  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. /** Common handling of {@link DSSTForceModel} methods for Gaussian contributions to DSST propagation.
  60.  * <p>
  61.  * This abstract class allows to provide easily a subset of {@link DSSTForceModel} methods
  62.  * for specific Gaussian contributions.
  63.  * </p><p>
  64.  * This class implements the notion of numerical averaging of the DSST theory.
  65.  * Numerical averaging is mainly used for non-conservative disturbing forces such as
  66.  * atmospheric drag and solar radiation pressure.
  67.  * </p><p>
  68.  * Gaussian contributions can be expressed as: da<sub>i</sub>/dt = δa<sub>i</sub>/δv . q<br>
  69.  * where:
  70.  * <ul>
  71.  * <li>a<sub>i</sub> are the six equinoctial elements</li>
  72.  * <li>v is the velocity vector</li>
  73.  * <li>q is the perturbing acceleration due to the considered force</li>
  74.  * </ul>
  75.  *
  76.  * <p> The averaging process and other considerations lead to integrate this contribution
  77.  * over the true longitude L possibly taking into account some limits.
  78.  *
  79.  * <p> To create a numerically averaged contribution, one needs only to provide a
  80.  * {@link ForceModel} and to implement in the derived class the methods:
  81.  * {@link #getLLimits(SpacecraftState, AuxiliaryElements)}
  82.  * and {@link #getParametersDriversWithoutMu()}.
  83.  * </p>
  84.  * @author Pascal Parraud
  85.  * @author Bryan Cazabonne (field translation)
  86.  */
  87. public abstract class AbstractGaussianContribution implements DSSTForceModel {

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

  102.     /** Central attraction scaling factor.
  103.      * <p>
  104.      * We use a power of 2 to avoid numeric noise introduction
  105.      * in the multiplications/divisions sequences.
  106.      * </p>
  107.      */
  108.     private static final double MU_SCALE = FastMath.scalb(1.0, 32);

  109.     /** Available orders for Gauss quadrature. */
  110.     private static final int[] GAUSS_ORDER = {12, 16, 20, 24, 32, 40, 48};

  111.     /** Max rank in Gauss quadrature orders array. */
  112.     private static final int MAX_ORDER_RANK = GAUSS_ORDER.length - 1;

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

  115.     /** Maximum value for j index. */
  116.     private static final int JMAX = 12;

  117.     /** Contribution to be numerically averaged. */
  118.     private final ForceModel contribution;

  119.     /** Gauss integrator. */
  120.     private final double threshold;

  121.     /** Gauss integrator. */
  122.     private GaussQuadrature integrator;

  123.     /** Flag for Gauss order computation. */
  124.     private boolean isDirty;

  125.     /** Attitude provider. */
  126.     private AttitudeProvider attitudeProvider;

  127.     /** Prefix for coefficients keys. */
  128.     private final String coefficientsKeyPrefix;

  129.     /** Short period terms. */
  130.     private GaussianShortPeriodicCoefficients gaussianSPCoefs;

  131.     /** Short period terms. */
  132.     private Map<Field<?>, FieldGaussianShortPeriodicCoefficients<?>> gaussianFieldSPCoefs;

  133.     /** Driver for gravitational parameter. */
  134.     private final ParameterDriver gmParameterDriver;

  135.     /** Build a new instance.
  136.      *  @param coefficientsKeyPrefix prefix for coefficients keys
  137.      *  @param threshold tolerance for the choice of the Gauss quadrature order
  138.      *  @param contribution the {@link ForceModel} to be numerically averaged
  139.      *  @param mu central attraction coefficient
  140.      */
  141.     protected AbstractGaussianContribution(final String coefficientsKeyPrefix,
  142.                                            final double threshold,
  143.                                            final ForceModel contribution,
  144.                                            final double mu) {

  145.         gmParameterDriver = new ParameterDriver(DSSTNewtonianAttraction.CENTRAL_ATTRACTION_COEFFICIENT,
  146.                                                 mu, MU_SCALE,
  147.                                                 0.0, Double.POSITIVE_INFINITY);

  148.         this.coefficientsKeyPrefix = coefficientsKeyPrefix;
  149.         this.contribution          = contribution;
  150.         this.threshold             = threshold;
  151.         this.integrator            = new GaussQuadrature(GAUSS_ORDER[MAX_ORDER_RANK]);
  152.         this.isDirty               = true;

  153.         gaussianFieldSPCoefs       = new HashMap<>();
  154.     }

  155.     /** {@inheritDoc} */
  156.     @Override
  157.     public ParameterDriver[] getParametersDrivers() {

  158.         final ParameterDriver[] driversWithoutMu = getParametersDriversWithoutMu();

  159.         // + 1 for central attraction coefficient driver
  160.         final ParameterDriver[] drivers = new ParameterDriver[driversWithoutMu.length + 1];

  161.         int index = 0;
  162.         for (final ParameterDriver driver : driversWithoutMu) {
  163.             drivers[index] = driver;
  164.             index++;
  165.         }

  166.         // We put central attraction coefficient driver at the end of the array
  167.         drivers[driversWithoutMu.length] = gmParameterDriver;
  168.         return drivers;

  169.     }

  170.     /** Get the drivers for force model parameters except the one for the central attraction coefficient.
  171.      * <p>
  172.      * The driver for central attraction coefficient is automatically
  173.      * added at the last element of the {@link ParameterDriver} array
  174.      * into {@link #getParametersDrivers()} method.
  175.      * </p>
  176.      * @return drivers for force model parameters
  177.      */
  178.     protected abstract ParameterDriver[] getParametersDriversWithoutMu();

  179.     /** {@inheritDoc} */
  180.     @Override
  181.     public List<ShortPeriodTerms> initialize(final AuxiliaryElements auxiliaryElements,
  182.                                              final PropagationType type,
  183.                                              final double[] parameters) {

  184.         final List<ShortPeriodTerms> list = new ArrayList<ShortPeriodTerms>();
  185.         gaussianSPCoefs = new GaussianShortPeriodicCoefficients(coefficientsKeyPrefix,
  186.                                                                 JMAX, INTERPOLATION_POINTS,
  187.                                                                 new TimeSpanMap<Slot>(new Slot(JMAX, INTERPOLATION_POINTS)));
  188.         list.add(gaussianSPCoefs);
  189.         return list;

  190.     }

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

  196.         final Field<T> field = auxiliaryElements.getDate().getField();

  197.         final FieldGaussianShortPeriodicCoefficients<T> fgspc =
  198.                         new FieldGaussianShortPeriodicCoefficients<>(coefficientsKeyPrefix,
  199.                                                                      JMAX, INTERPOLATION_POINTS,
  200.                                                                      new FieldTimeSpanMap<>(new FieldSlot<>(JMAX,
  201.                                                                                                             INTERPOLATION_POINTS),
  202.                                                                                             field));
  203.         gaussianFieldSPCoefs.put(field, fgspc);
  204.         return Collections.singletonList(fgspc);
  205.     }

  206.     /** Performs initialization at each integration step for the current force model.
  207.      *  <p>
  208.      *  This method aims at being called before mean elements rates computation.
  209.      *  </p>
  210.      *  @param auxiliaryElements auxiliary elements related to the current orbit
  211.      *  @param parameters parameters values of the force model parameters
  212.      *  @return new force model context
  213.      */
  214.     private AbstractGaussianContributionContext initializeStep(final AuxiliaryElements auxiliaryElements, final double[] parameters) {
  215.         return new AbstractGaussianContributionContext(auxiliaryElements, parameters);
  216.     }

  217.     /** Performs initialization at each integration step for the current force model.
  218.      *  <p>
  219.      *  This method aims at being called before mean elements rates computation.
  220.      *  </p>
  221.      *  @param <T> type of the elements
  222.      *  @param auxiliaryElements auxiliary elements related to the current orbit
  223.      *  @param parameters parameters values of the force model parameters
  224.      *  @return new force model context
  225.      */
  226.     private <T extends RealFieldElement<T>> FieldAbstractGaussianContributionContext<T> initializeStep(final FieldAuxiliaryElements<T> auxiliaryElements,
  227.                                                                                                        final T[] parameters) {
  228.         return new FieldAbstractGaussianContributionContext<>(auxiliaryElements, parameters);
  229.     }

  230.     /** {@inheritDoc} */
  231.     @Override
  232.     public double[] getMeanElementRate(final SpacecraftState state,
  233.                                        final AuxiliaryElements auxiliaryElements, final double[] parameters) {

  234.         // Container for attributes
  235.         final AbstractGaussianContributionContext context = initializeStep(auxiliaryElements, parameters);
  236.         double[] meanElementRate = new double[6];
  237.         // Computes the limits for the integral
  238.         final double[] ll = getLLimits(state, auxiliaryElements);
  239.         // Computes integrated mean element rates if Llow < Lhigh
  240.         if (ll[0] < ll[1]) {
  241.             meanElementRate = getMeanElementRate(state, integrator, ll[0], ll[1], context, parameters);
  242.             if (isDirty) {
  243.                 boolean next = true;
  244.                 for (int i = 0; i < MAX_ORDER_RANK && next; i++) {
  245.                     final double[] meanRates = getMeanElementRate(state, new GaussQuadrature(GAUSS_ORDER[i]), ll[0], ll[1], context, parameters);
  246.                     if (getRatesDiff(meanElementRate, meanRates, context) < threshold) {
  247.                         integrator = new GaussQuadrature(GAUSS_ORDER[i]);
  248.                         next = false;
  249.                     }
  250.                 }
  251.                 isDirty = false;
  252.             }
  253.         }
  254.         return meanElementRate;
  255.     }

  256.     /** {@inheritDoc} */
  257.     @Override
  258.     public <T extends RealFieldElement<T>> T[] getMeanElementRate(final FieldSpacecraftState<T> state,
  259.                                                                   final FieldAuxiliaryElements<T> auxiliaryElements,
  260.                                                                   final T[] parameters) {

  261.         // Container for attributes
  262.         final FieldAbstractGaussianContributionContext<T> context = initializeStep(auxiliaryElements, parameters);
  263.         final Field<T> field = state.getDate().getField();

  264.         T[] meanElementRate = MathArrays.buildArray(field, 6);
  265.         // Computes the limits for the integral
  266.         final T[] ll = getLLimits(state, auxiliaryElements);
  267.         // Computes integrated mean element rates if Llow < Lhigh
  268.         if (ll[0].getReal() < ll[1].getReal()) {
  269.             meanElementRate = getMeanElementRate(state, integrator, ll[0], ll[1], context, parameters);
  270.             if (isDirty) {
  271.                 boolean next = true;
  272.                 for (int i = 0; i < MAX_ORDER_RANK && next; i++) {
  273.                     final T[] meanRates = getMeanElementRate(state, new GaussQuadrature(GAUSS_ORDER[i]), ll[0], ll[1], context, parameters);
  274.                     if (getRatesDiff(meanElementRate, meanRates, context).getReal() < threshold) {
  275.                         integrator = new GaussQuadrature(GAUSS_ORDER[i]);
  276.                         next = false;
  277.                     }
  278.                 }
  279.                 isDirty = false;
  280.             }
  281.         }

  282.         return meanElementRate;
  283.     }

  284.     /** Compute the limits in L, the true longitude, for integration.
  285.      *
  286.      *  @param  state current state information: date, kinematics, attitude
  287.      *  @param auxiliaryElements auxiliary elements related to the current orbit
  288.      *  @return the integration limits in L
  289.      */
  290.     protected abstract double[] getLLimits(SpacecraftState state, AuxiliaryElements auxiliaryElements);

  291.     /** Compute the limits in L, the true longitude, for integration.
  292.      *
  293.      *  @param <T> type of the elements
  294.      *  @param state current state information: date, kinematics, attitude
  295.      *  @param auxiliaryElements auxiliary elements related to the current orbit
  296.      *  @return the integration limits in L
  297.      */
  298.     protected abstract <T extends RealFieldElement<T>> T[] getLLimits(FieldSpacecraftState<T> state,
  299.                                                                       FieldAuxiliaryElements<T> auxiliaryElements);

  300.     /** Computes the mean equinoctial elements rates da<sub>i</sub> / dt.
  301.     *
  302.     *  @param state current state
  303.     *  @param gauss Gauss quadrature
  304.     *  @param low lower bound of the integral interval
  305.     *  @param high upper bound of the integral interval
  306.     *  @param context container for attributes
  307.     *  @param parameters values of the force model parameters
  308.     *  @return the mean element rates
  309.     */
  310.     private double[] getMeanElementRate(final SpacecraftState state,
  311.                                         final GaussQuadrature gauss,
  312.                                         final double low,
  313.                                         final double high,
  314.                                         final AbstractGaussianContributionContext context,
  315.                                         final double[] parameters) {

  316.         // Auxiliary elements related to the current orbit
  317.         final AuxiliaryElements auxiliaryElements = context.getAuxiliaryElements();

  318.         final double[] meanElementRate = gauss.integrate(new IntegrableFunction(state, true, 0, parameters), low, high);

  319.         // Constant multiplier for integral
  320.         final double coef = 1. / (2. * FastMath.PI * auxiliaryElements.getB());
  321.         // Corrects mean element rates
  322.         for (int i = 0; i < 6; i++) {
  323.             meanElementRate[i] *= coef;
  324.         }
  325.         return meanElementRate;
  326.     }

  327.     /** Computes the mean equinoctial elements rates da<sub>i</sub> / dt.
  328.     *
  329.     *  @param <T> type of the elements
  330.     *  @param state current state
  331.     *  @param gauss Gauss quadrature
  332.     *  @param low lower bound of the integral interval
  333.     *  @param high upper bound of the integral interval
  334.     *  @param context container for attributes
  335.     *  @param parameters values of the force model parameters
  336.     *  @return the mean element rates
  337.     */
  338.     private <T extends RealFieldElement<T>> T[] getMeanElementRate(final FieldSpacecraftState<T> state,
  339.                                                                    final GaussQuadrature gauss,
  340.                                                                    final T low,
  341.                                                                    final T high,
  342.                                                                    final FieldAbstractGaussianContributionContext<T> context,
  343.                                                                    final T[] parameters) {

  344.         // Field
  345.         final Field<T> field = context.getA().getField();

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

  348.         final T[] meanElementRate = gauss.integrate(new FieldIntegrableFunction<>(state, true, 0, parameters, field), low, high, field);
  349.         // Constant multiplier for integral
  350.         final T coef = auxiliaryElements.getB().multiply(FastMath.PI).multiply(2.).reciprocal();
  351.         // Corrects mean element rates
  352.         for (int i = 0; i < 6; i++) {
  353.             meanElementRate[i] = meanElementRate[i].multiply(coef);
  354.         }
  355.         return meanElementRate;
  356.     }

  357.     /** Estimates the weighted magnitude of the difference between 2 sets of equinoctial elements rates.
  358.      *
  359.      *  @param meanRef reference rates
  360.      *  @param meanCur current rates
  361.      *  @param context container for attributes
  362.      *  @return estimated magnitude of weighted differences
  363.      */
  364.     private double getRatesDiff(final double[] meanRef, final double[] meanCur, final AbstractGaussianContributionContext context) {

  365.         // Auxiliary elements related to the current orbit
  366.         final AuxiliaryElements auxiliaryElements = context.getAuxiliaryElements();

  367.         double maxDiff = FastMath.abs(meanRef[0] - meanCur[0]) / auxiliaryElements.getSma();
  368.         // Corrects mean element rates
  369.         for (int i = 1; i < meanRef.length; i++) {
  370.             maxDiff = FastMath.max(maxDiff, FastMath.abs(meanRef[i] - meanCur[i]));
  371.         }
  372.         return maxDiff;
  373.     }

  374.     /** Estimates the weighted magnitude of the difference between 2 sets of equinoctial elements rates.
  375.     *
  376.     *  @param <T> type of the elements
  377.     *  @param meanRef reference rates
  378.     *  @param meanCur current rates
  379.     *  @param context container for attributes
  380.     *  @return estimated magnitude of weighted differences
  381.     */
  382.     private <T extends RealFieldElement<T>> T getRatesDiff(final T[] meanRef, final T[] meanCur, final FieldAbstractGaussianContributionContext<T> context) {

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

  385.         T maxDiff = FastMath.abs(meanRef[0].subtract(meanCur[0])).divide(auxiliaryElements.getSma());;
  386.         // Corrects mean element rates
  387.         for (int i = 1; i < meanRef.length; i++) {
  388.             maxDiff = FastMath.max(maxDiff, FastMath.abs(meanRef[i].subtract(meanCur[i])));
  389.         }
  390.         return maxDiff;
  391.     }

  392.     /** {@inheritDoc} */
  393.     @Override
  394.     public void registerAttitudeProvider(final AttitudeProvider provider) {
  395.         this.attitudeProvider = provider;
  396.     }

  397.     /** {@inheritDoc} */
  398.     @Override
  399.     public void updateShortPeriodTerms(final double[] parameters, final SpacecraftState... meanStates) {

  400.         final Slot slot = gaussianSPCoefs.createSlot(meanStates);
  401.         for (final SpacecraftState meanState : meanStates) {

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

  404.             // Container of attributes
  405.             final AbstractGaussianContributionContext context = initializeStep(auxiliaryElements, parameters);

  406.             // Compute rhoj and sigmaj
  407.             final double[][] currentRhoSigmaj = computeRhoSigmaCoefficients(meanState.getDate(), auxiliaryElements);

  408.             // Generate the Cij and Sij coefficients
  409.             final FourierCjSjCoefficients fourierCjSj = new FourierCjSjCoefficients(meanState, JMAX, auxiliaryElements, parameters);

  410.             // Generate the Uij and Vij coefficients
  411.             final UijVijCoefficients uijvij = new UijVijCoefficients(currentRhoSigmaj, fourierCjSj, JMAX);

  412.             gaussianSPCoefs.computeCoefficients(meanState, slot, fourierCjSj, uijvij, context.getMeanMotion(), auxiliaryElements.getSma());

  413.         }

  414.     }

  415.     /** {@inheritDoc} */
  416.     @Override
  417.     @SuppressWarnings("unchecked")
  418.     public <T extends RealFieldElement<T>> void updateShortPeriodTerms(final T[] parameters,
  419.                                                                        final FieldSpacecraftState<T>... meanStates) {

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

  422.         final FieldGaussianShortPeriodicCoefficients<T> fgspc = (FieldGaussianShortPeriodicCoefficients<T>) gaussianFieldSPCoefs.get(field);
  423.         final FieldSlot<T> slot = fgspc.createSlot(meanStates);
  424.         for (final FieldSpacecraftState<T> meanState : meanStates) {

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

  427.             // Container of attributes
  428.             final FieldAbstractGaussianContributionContext<T> context = initializeStep(auxiliaryElements, parameters);

  429.             // Compute rhoj and sigmaj
  430.             final T[][] currentRhoSigmaj = computeRhoSigmaCoefficients(meanState.getDate(), context, field);

  431.             // Generate the Cij and Sij coefficients
  432.             final FieldFourierCjSjCoefficients<T> fourierCjSj = new FieldFourierCjSjCoefficients<>(meanState, JMAX, auxiliaryElements, parameters, field);

  433.             // Generate the Uij and Vij coefficients
  434.             final FieldUijVijCoefficients<T> uijvij = new FieldUijVijCoefficients<>(currentRhoSigmaj, fourierCjSj, JMAX, field);

  435.             fgspc.computeCoefficients(meanState, slot, fourierCjSj, uijvij, context.getMeanMotion(), auxiliaryElements.getSma(), field);

  436.         }

  437.     }

  438.     /**
  439.      * Compute the auxiliary quantities ρ<sub>j</sub> and σ<sub>j</sub>.
  440.      * <p>
  441.      * The expressions used are equations 2.5.3-(4) from the Danielson paper. <br/>
  442.      *  ρ<sub>j</sub> = (1+jB)(-b)<sup>j</sup>C<sub>j</sub>(k, h) <br/>
  443.      *  σ<sub>j</sub> = (1+jB)(-b)<sup>j</sup>S<sub>j</sub>(k, h) <br/>
  444.      * </p>
  445.      * @param date current date
  446.      * @param auxiliaryElements auxiliary elements related to the current orbit
  447.      * @return computed coefficients
  448.      */
  449.     private double[][] computeRhoSigmaCoefficients(final AbsoluteDate date, final AuxiliaryElements auxiliaryElements) {
  450.         final double[][] currentRhoSigmaj = new double[2][3 * JMAX + 1];
  451.         final CjSjCoefficient cjsjKH = new CjSjCoefficient(auxiliaryElements.getK(), auxiliaryElements.getH());
  452.         final double b = 1. / (1 + auxiliaryElements.getB());

  453.         // (-b)<sup>j</sup>
  454.         double mbtj = 1;

  455.         for (int j = 1; j <= 3 * JMAX; j++) {

  456.             //Compute current rho and sigma;
  457.             mbtj *= -b;
  458.             final double coef = (1 + j * auxiliaryElements.getB()) * mbtj;
  459.             currentRhoSigmaj[0][j] = coef * cjsjKH.getCj(j);
  460.             currentRhoSigmaj[1][j] = coef * cjsjKH.getSj(j);
  461.         }
  462.         return currentRhoSigmaj;
  463.     }

  464.     /**
  465.      * Compute the auxiliary quantities ρ<sub>j</sub> and σ<sub>j</sub>.
  466.      * <p>
  467.      * The expressions used are equations 2.5.3-(4) from the Danielson paper. <br/>
  468.      *  ρ<sub>j</sub> = (1+jB)(-b)<sup>j</sup>C<sub>j</sub>(k, h) <br/>
  469.      *  σ<sub>j</sub> = (1+jB)(-b)<sup>j</sup>S<sub>j</sub>(k, h) <br/>
  470.      * </p>
  471.      * @param <T> type of the elements
  472.      * @param date current date
  473.      * @param context container for attributes
  474.      * @param field field used by default
  475.      * @return computed coefficients
  476.      */
  477.     private <T extends RealFieldElement<T>> T[][] computeRhoSigmaCoefficients(final FieldAbsoluteDate<T> date,
  478.                                                                               final FieldAbstractGaussianContributionContext<T> context,
  479.                                                                               final Field<T> field) {
  480.         // zero
  481.         final T zero = field.getZero();

  482.         final FieldAuxiliaryElements<T> auxiliaryElements = context.getFieldAuxiliaryElements();
  483.         final T[][] currentRhoSigmaj = MathArrays.buildArray(field, 2, 3 * JMAX + 1);
  484.         final FieldCjSjCoefficient<T> cjsjKH = new FieldCjSjCoefficient<>(auxiliaryElements.getK(), auxiliaryElements.getH(), field);
  485.         final T b = auxiliaryElements.getB().add(1.).reciprocal();

  486.         // (-b)<sup>j</sup>
  487.         T mbtj = zero.add(1.);

  488.         for (int j = 1; j <= 3 * JMAX; j++) {

  489.             //Compute current rho and sigma;
  490.             mbtj = mbtj.multiply(b.negate());
  491.             final T coef = mbtj.multiply(auxiliaryElements.getB().multiply(j).add(1.));
  492.             currentRhoSigmaj[0][j] = coef.multiply(cjsjKH.getCj(j));
  493.             currentRhoSigmaj[1][j] = coef.multiply(cjsjKH.getSj(j));
  494.         }
  495.         return currentRhoSigmaj;
  496.     }

  497.     /** Internal class for numerical quadrature.
  498.      *  <p>
  499.      *  This class is a rewrite of {@link IntegrableFunction} for field elements
  500.      *  </p>
  501.      */
  502.     private class FieldIntegrableFunction <T extends RealFieldElement<T>> implements RealFieldUnivariateVectorFunction<T> {

  503.         /** Current state. */
  504.         private final FieldSpacecraftState<T> state;

  505.         /** Signal that this class is used to compute the values required by the mean element variations
  506.          * or by the short periodic element variations. */
  507.         private final boolean meanMode;

  508.         /** The j index.
  509.          * <p>
  510.          * Used only for short periodic variation. Ignored for mean elements variation.
  511.          * </p> */
  512.         private final int j;

  513.         /** Container for attributes. */
  514.         private final FieldAbstractGaussianContributionContext<T> context;

  515.         /** Auxiliary Elements. */
  516.         private final FieldAuxiliaryElements<T> auxiliaryElements;

  517.         /** Drivers for solar radiation and atmospheric drag forces. */
  518.         private final T[] parameters;

  519.         /** Build a new instance with a new field.
  520.          *  @param  state current state information: date, kinematics, attitude
  521.          *  @param meanMode if true return the value associated to the mean elements variation,
  522.          *                  if false return the values associated to the short periodic elements variation
  523.          *  @param j the j index. used only for short periodic variation. Ignored for mean elements variation.
  524.          *  @param parameters values of the force model parameters
  525.          *  @param field field utilized by default
  526.          */
  527.         FieldIntegrableFunction(final FieldSpacecraftState<T> state,
  528.                                 final boolean meanMode,
  529.                                 final int j,
  530.                                 final T[] parameters,
  531.                                 final Field<T> field) {

  532.             this.meanMode = meanMode;
  533.             this.j = j;
  534.             this.parameters = parameters;
  535.             this.auxiliaryElements = new FieldAuxiliaryElements<>(state.getOrbit(), I);
  536.             this.context = new FieldAbstractGaussianContributionContext<>(auxiliaryElements, parameters);
  537.             // remove derivatives from state
  538.             final T[] stateVector = MathArrays.buildArray(field, 6);
  539.             OrbitType.EQUINOCTIAL.mapOrbitToArray(state.getOrbit(), PositionAngle.TRUE, stateVector, null);
  540.             final FieldOrbit<T> fixedOrbit = OrbitType.EQUINOCTIAL.mapArrayToOrbit(stateVector, null, PositionAngle.TRUE,
  541.                                                                            state.getDate(),
  542.                                                                            context.getMu(),
  543.                                                                            state.getFrame());
  544.             this.state = new FieldSpacecraftState<>(fixedOrbit, state.getAttitude(), state.getMass());
  545.         }

  546.         /** {@inheritDoc} */
  547.         @Override
  548.         public T[] value(final T x) {

  549.             // Parameters for array building
  550.             final Field<T> field = auxiliaryElements.getDate().getField();
  551.             final int dimension = 6;

  552.             //Compute the time difference from the true longitude difference
  553.             final T shiftedLm = trueToMean(x);
  554.             final T dLm = shiftedLm.subtract(auxiliaryElements.getLM());
  555.             final T dt = dLm.divide(context.getMeanMotion());

  556.             final FieldSinCos<T> scL = FastMath.sinCos(x);
  557.             final T cosL = scL.cos();
  558.             final T sinL = scL.sin();
  559.             final T roa  = auxiliaryElements.getB().multiply(auxiliaryElements.getB()).divide(auxiliaryElements.getH().multiply(sinL).add(auxiliaryElements.getK().multiply(cosL)).add(1.));
  560.             final T roa2 = roa.multiply(roa);
  561.             final T r    = auxiliaryElements.getSma().multiply(roa);
  562.             final T X    = r.multiply(cosL);
  563.             final T Y    = r.multiply(sinL);
  564.             final T naob = context.getMeanMotion().multiply(auxiliaryElements.getSma()).divide(auxiliaryElements.getB());
  565.             final T Xdot = naob.multiply(auxiliaryElements.getH().add(sinL)).negate();
  566.             final T Ydot = naob.multiply(auxiliaryElements.getK().add(cosL));
  567.             final FieldVector3D<T> vel = new FieldVector3D<>(Xdot, auxiliaryElements.getVectorF(), Ydot, auxiliaryElements.getVectorG());

  568.             // Compute acceleration
  569.             FieldVector3D<T> acc = FieldVector3D.getZero(field);

  570.             // shift the orbit to dt
  571.             final FieldOrbit<T> shiftedOrbit = state.getOrbit().shiftedBy(dt);

  572.             // Recompose an orbit with time held fixed to be compliant with DSST theory
  573.             final FieldOrbit<T> recomposedOrbit =
  574.                             new FieldEquinoctialOrbit<>(shiftedOrbit.getA(),
  575.                                             shiftedOrbit.getEquinoctialEx(),
  576.                                             shiftedOrbit.getEquinoctialEy(),
  577.                                             shiftedOrbit.getHx(),
  578.                                             shiftedOrbit.getHy(),
  579.                                             shiftedOrbit.getLv(),
  580.                                             PositionAngle.TRUE,
  581.                                             shiftedOrbit.getFrame(),
  582.                                             state.getDate(),
  583.                                             context.getMu());

  584.             // Get the corresponding attitude
  585.             final FieldAttitude<T> recomposedAttitude =
  586.                             attitudeProvider.getAttitude(recomposedOrbit,
  587.                                                          recomposedOrbit.getDate(),
  588.                                                          recomposedOrbit.getFrame());

  589.             // create shifted SpacecraftState with attitude at specified time
  590.             final FieldSpacecraftState<T> shiftedState =
  591.                             new FieldSpacecraftState<>(recomposedOrbit, recomposedAttitude, state.getMass());

  592.             acc = contribution.acceleration(shiftedState, parameters);

  593.             //Compute the derivatives of the elements by the speed
  594.             final T[] deriv = MathArrays.buildArray(field, dimension);
  595.             // da/dv
  596.             deriv[0] = getAoV(vel).dotProduct(acc);
  597.             // dex/dv
  598.             deriv[1] = getKoV(X, Y, Xdot, Ydot).dotProduct(acc);
  599.             // dey/dv
  600.             deriv[2] = getHoV(X, Y, Xdot, Ydot).dotProduct(acc);
  601.             // dhx/dv
  602.             deriv[3] = getQoV(X).dotProduct(acc);
  603.             // dhy/dv
  604.             deriv[4] = getPoV(Y).dotProduct(acc);
  605.             // dλ/dv
  606.             deriv[5] = getLoV(X, Y, Xdot, Ydot).dotProduct(acc);

  607.             // Compute mean elements rates
  608.             T[] val = null;
  609.             if (meanMode) {
  610.                 val = MathArrays.buildArray(field, dimension);
  611.                 for (int i = 0; i < 6; i++) {
  612.                     // da<sub>i</sub>/dt
  613.                     val[i] = deriv[i].multiply(roa2);
  614.                 }
  615.             } else {
  616.                 val = MathArrays.buildArray(field, dimension * 2);
  617.                 //Compute cos(j*L) and sin(j*L);
  618.                 final FieldSinCos<T> scjL = FastMath.sinCos(x.multiply(j));
  619.                 final T cosjL = j == 1 ? cosL : scjL.cos();
  620.                 final T sinjL = j == 1 ? sinL : scjL.sin();

  621.                 for (int i = 0; i < 6; i++) {
  622.                     // da<sub>i</sub>/dv * cos(jL)
  623.                     val[i] = deriv[i].multiply(cosjL);
  624.                     // da<sub>i</sub>/dv * sin(jL)
  625.                     val[i + 6] = deriv[i].multiply(sinjL);
  626.                 }
  627.             }

  628.             return val;
  629.         }

  630.         /** Converts true longitude to mean longitude.
  631.          * @param x True longitude
  632.          * @return Eccentric longitude
  633.          */
  634.         private T trueToMean (final T x) {
  635.             return eccentricToMean(trueToEccentric(x));
  636.         }

  637.         /** Converts true longitude to eccentric longitude.
  638.          * @param lv True longitude
  639.          * @return Eccentric longitude
  640.          */
  641.         private T trueToEccentric (final T lv) {
  642.             final FieldSinCos<T> sclV = FastMath.sinCos(lv);
  643.             final T cosLv   = sclV.cos();
  644.             final T sinLv   = sclV.sin();
  645.             final T num     = auxiliaryElements.getH().multiply(cosLv).subtract(auxiliaryElements.getK().multiply(sinLv));
  646.             final T den     = auxiliaryElements.getB().add(auxiliaryElements.getK().multiply(cosLv)).add(auxiliaryElements.getH().multiply(sinLv)).add(1.);
  647.             return FastMath.atan(num.divide(den)).multiply(2.).add(lv);
  648.         }

  649.         /** Converts eccentric longitude to mean longitude.
  650.          * @param le Eccentric longitude
  651.          * @return Mean longitude
  652.          */
  653.         private T eccentricToMean (final T le) {
  654.             final FieldSinCos<T> scle = FastMath.sinCos(le);
  655.             return le.subtract(auxiliaryElements.getK().multiply(scle.sin())).add(auxiliaryElements.getH().multiply(scle.cos()));
  656.         }

  657.         /** Compute δa/δv.
  658.          *  @param vel satellite velocity
  659.          *  @return δa/δv
  660.          */
  661.         private FieldVector3D<T> getAoV(final FieldVector3D<T> vel) {
  662.             return new FieldVector3D<>(context.getTon2a(), vel);
  663.         }

  664.         /** Compute δh/δv.
  665.          *  @param X satellite position component along f, equinoctial reference frame 1st vector
  666.          *  @param Y satellite position component along g, equinoctial reference frame 2nd vector
  667.          *  @param Xdot satellite velocity component along f, equinoctial reference frame 1st vector
  668.          *  @param Ydot satellite velocity component along g, equinoctial reference frame 2nd vector
  669.          *  @return δh/δv
  670.          */
  671.         private FieldVector3D<T> getHoV(final T X, final T Y, final T Xdot, final T Ydot) {
  672.             final T kf = (Xdot.multiply(Y).multiply(2.).subtract(X.multiply(Ydot))).multiply(context.getOoMU());
  673.             final T kg = X.multiply(Xdot).multiply(context.getOoMU());
  674.             final T kw = auxiliaryElements.getK().multiply(auxiliaryElements.getQ().multiply(Y).multiply(I).subtract(auxiliaryElements.getP().multiply(X))).multiply(context.getOOAB());
  675.             return new FieldVector3D<>(kf, auxiliaryElements.getVectorF(), kg.negate(), auxiliaryElements.getVectorG(), kw, auxiliaryElements.getVectorW());
  676.         }

  677.         /** Compute δk/δv.
  678.          *  @param X satellite position component along f, equinoctial reference frame 1st vector
  679.          *  @param Y satellite position component along g, equinoctial reference frame 2nd vector
  680.          *  @param Xdot satellite velocity component along f, equinoctial reference frame 1st vector
  681.          *  @param Ydot satellite velocity component along g, equinoctial reference frame 2nd vector
  682.          *  @return δk/δv
  683.          */
  684.         private FieldVector3D<T> getKoV(final T X, final T Y, final T Xdot, final T Ydot) {
  685.             final T kf = Y.multiply(Ydot).multiply(context.getOoMU());
  686.             final T kg = (X.multiply(Ydot).multiply(2.).subtract(Xdot.multiply(Y))).multiply(context.getOoMU());
  687.             final T kw = auxiliaryElements.getH().multiply(auxiliaryElements.getQ().multiply(Y).multiply(I).subtract(auxiliaryElements.getP().multiply(X))).multiply(context.getOOAB());
  688.             return new FieldVector3D<>(kf.negate(), auxiliaryElements.getVectorF(), kg, auxiliaryElements.getVectorG(), kw.negate(), auxiliaryElements.getVectorW());
  689.         }

  690.         /** Compute δp/δv.
  691.          *  @param Y satellite position component along g, equinoctial reference frame 2nd vector
  692.          *  @return δp/δv
  693.          */
  694.         private FieldVector3D<T> getPoV(final T Y) {
  695.             return new FieldVector3D<>(context.getCo2AB().multiply(Y), auxiliaryElements.getVectorW());
  696.         }

  697.         /** Compute δq/δv.
  698.          *  @param X satellite position component along f, equinoctial reference frame 1st vector
  699.          *  @return δq/δv
  700.          */
  701.         private FieldVector3D<T> getQoV(final T X) {
  702.             return new FieldVector3D<>(context.getCo2AB().multiply(X).multiply(I), auxiliaryElements.getVectorW());
  703.         }

  704.         /** Compute δλ/δv.
  705.          *  @param X satellite position component along f, equinoctial reference frame 1st vector
  706.          *  @param Y satellite position component along g, equinoctial reference frame 2nd vector
  707.          *  @param Xdot satellite velocity component along f, equinoctial reference frame 1st vector
  708.          *  @param Ydot satellite velocity component along g, equinoctial reference frame 2nd vector
  709.          *  @return δλ/δv
  710.          */
  711.         private FieldVector3D<T> getLoV(final T X, final T Y, final T Xdot, final T Ydot) {
  712.             final FieldVector3D<T> pos = new FieldVector3D<>(X, auxiliaryElements.getVectorF(), Y, auxiliaryElements.getVectorG());
  713.             final FieldVector3D<T> v2  = new FieldVector3D<>(auxiliaryElements.getK(), getHoV(X, Y, Xdot, Ydot), auxiliaryElements.getH().negate(), getKoV(X, Y, Xdot, Ydot));
  714.             return new FieldVector3D<>(context.getOOA().multiply(-2.), pos, context.getOoBpo(), v2, context.getOOA().multiply(auxiliaryElements.getQ().multiply(Y).multiply(I).subtract(auxiliaryElements.getP().multiply(X))), auxiliaryElements.getVectorW());
  715.         }

  716.     }

  717.     /** Internal class for numerical quadrature. */
  718.     private class IntegrableFunction implements UnivariateVectorFunction {

  719.         /** Current state. */
  720.         private final SpacecraftState state;

  721.         /** Signal that this class is used to compute the values required by the mean element variations
  722.          * or by the short periodic element variations. */
  723.         private final boolean meanMode;

  724.         /** The j index.
  725.          * <p>
  726.          * Used only for short periodic variation. Ignored for mean elements variation.
  727.          * </p> */
  728.         private final int j;

  729.         /** Container for attributes. */
  730.         private final AbstractGaussianContributionContext context;

  731.         /** Auxiliary Elements. */
  732.         private final AuxiliaryElements auxiliaryElements;

  733.         /** Drivers for solar radiation and atmospheric drag forces. */
  734.         private final double[] parameters;

  735.         /** Build a new instance.
  736.          *  @param  state current state information: date, kinematics, attitude
  737.          *  @param meanMode if true return the value associated to the mean elements variation,
  738.          *                  if false return the values associated to the short periodic elements variation
  739.          *  @param j the j index. used only for short periodic variation. Ignored for mean elements variation.
  740.          *  @param parameters values of the force model parameters
  741.          */
  742.         IntegrableFunction(final SpacecraftState state,
  743.                            final boolean meanMode,
  744.                            final int j,
  745.                            final double[] parameters) {

  746.             this.meanMode = meanMode;
  747.             this.j = j;
  748.             this.parameters = parameters;
  749.             this.auxiliaryElements = new AuxiliaryElements(state.getOrbit(), I);
  750.             this.context = new AbstractGaussianContributionContext(auxiliaryElements, parameters);
  751.             // remove derivatives from state
  752.             final double[] stateVector = new double[6];
  753.             OrbitType.EQUINOCTIAL.mapOrbitToArray(state.getOrbit(), PositionAngle.TRUE, stateVector, null);
  754.             final Orbit fixedOrbit = OrbitType.EQUINOCTIAL.mapArrayToOrbit(stateVector, null, PositionAngle.TRUE,
  755.                                                                            state.getDate(),
  756.                                                                            context.getMu(),
  757.                                                                            state.getFrame());
  758.             this.state = new SpacecraftState(fixedOrbit, state.getAttitude(), state.getMass());
  759.         }

  760.         /** {@inheritDoc} */
  761.         @Override
  762.         public double[] value(final double x) {

  763.             //Compute the time difference from the true longitude difference
  764.             final double shiftedLm = trueToMean(x);
  765.             final double dLm = shiftedLm - auxiliaryElements.getLM();
  766.             final double dt = dLm / context.getMeanMotion();

  767.             final SinCos scL  = FastMath.sinCos(x);
  768.             final double cosL = scL.cos();
  769.             final double sinL = scL.sin();
  770.             final double roa  = auxiliaryElements.getB() * auxiliaryElements.getB() / (1. + auxiliaryElements.getH() * sinL + auxiliaryElements.getK() * cosL);
  771.             final double roa2 = roa * roa;
  772.             final double r    = auxiliaryElements.getSma() * roa;
  773.             final double X    = r * cosL;
  774.             final double Y    = r * sinL;
  775.             final double naob = context.getMeanMotion() * auxiliaryElements.getSma() / auxiliaryElements.getB();
  776.             final double Xdot = -naob * (auxiliaryElements.getH() + sinL);
  777.             final double Ydot =  naob * (auxiliaryElements.getK() + cosL);
  778.             final Vector3D vel = new Vector3D(Xdot, auxiliaryElements.getVectorF(), Ydot, auxiliaryElements.getVectorG());

  779.             // Compute acceleration
  780.             Vector3D acc = Vector3D.ZERO;

  781.             // shift the orbit to dt
  782.             final Orbit shiftedOrbit = state.getOrbit().shiftedBy(dt);

  783.             // Recompose an orbit with time held fixed to be compliant with DSST theory
  784.             final Orbit recomposedOrbit =
  785.                             new EquinoctialOrbit(shiftedOrbit.getA(),
  786.                                                  shiftedOrbit.getEquinoctialEx(),
  787.                                                  shiftedOrbit.getEquinoctialEy(),
  788.                                                  shiftedOrbit.getHx(),
  789.                                                  shiftedOrbit.getHy(),
  790.                                                  shiftedOrbit.getLv(),
  791.                                                  PositionAngle.TRUE,
  792.                                                  shiftedOrbit.getFrame(),
  793.                                                  state.getDate(),
  794.                                                  context.getMu());

  795.             // Get the corresponding attitude
  796.             final Attitude recomposedAttitude =
  797.                     attitudeProvider.getAttitude(recomposedOrbit,
  798.                                                  recomposedOrbit.getDate(),
  799.                                                  recomposedOrbit.getFrame());

  800.             // create shifted SpacecraftState with attitude at specified time
  801.             final SpacecraftState shiftedState =
  802.                     new SpacecraftState(recomposedOrbit, recomposedAttitude, state.getMass());

  803.             acc = contribution.acceleration(shiftedState, parameters);

  804.             //Compute the derivatives of the elements by the speed
  805.             final double[] deriv = new double[6];
  806.             // da/dv
  807.             deriv[0] = getAoV(vel).dotProduct(acc);
  808.             // dex/dv
  809.             deriv[1] = getKoV(X, Y, Xdot, Ydot).dotProduct(acc);
  810.             // dey/dv
  811.             deriv[2] = getHoV(X, Y, Xdot, Ydot).dotProduct(acc);
  812.             // dhx/dv
  813.             deriv[3] = getQoV(X).dotProduct(acc);
  814.             // dhy/dv
  815.             deriv[4] = getPoV(Y).dotProduct(acc);
  816.             // dλ/dv
  817.             deriv[5] = getLoV(X, Y, Xdot, Ydot).dotProduct(acc);

  818.             // Compute mean elements rates
  819.             double[] val = null;
  820.             if (meanMode) {
  821.                 val = new double[6];
  822.                 for (int i = 0; i < 6; i++) {
  823.                     // da<sub>i</sub>/dt
  824.                     val[i] = roa2 * deriv[i];
  825.                 }
  826.             } else {
  827.                 val = new double[12];
  828.                 //Compute cos(j*L) and sin(j*L);
  829.                 final SinCos scjL  = FastMath.sinCos(j * x);
  830.                 final double cosjL = j == 1 ? cosL : scjL.cos();
  831.                 final double sinjL = j == 1 ? sinL : scjL.sin();

  832.                 for (int i = 0; i < 6; i++) {
  833.                     // da<sub>i</sub>/dv * cos(jL)
  834.                     val[i] = cosjL * deriv[i];
  835.                     // da<sub>i</sub>/dv * sin(jL)
  836.                     val[i + 6] = sinjL * deriv[i];
  837.                 }
  838.             }
  839.             return val;
  840.         }

  841.         /** Converts true longitude to eccentric longitude.
  842.          * @param lv True longitude
  843.          * @return Eccentric longitude
  844.          */
  845.         private double trueToEccentric (final double lv) {
  846.             final SinCos scLv    = FastMath.sinCos(lv);
  847.             final double num     = auxiliaryElements.getH() * scLv.cos() - auxiliaryElements.getK() * scLv.sin();
  848.             final double den     = auxiliaryElements.getB() + 1. + auxiliaryElements.getK() * scLv.cos() + auxiliaryElements.getH() * scLv.sin();
  849.             return lv + 2. * FastMath.atan(num / den);
  850.         }

  851.         /** Converts eccentric longitude to mean longitude.
  852.          * @param le Eccentric longitude
  853.          * @return Mean longitude
  854.          */
  855.         private double eccentricToMean (final double le) {
  856.             final SinCos scLe = FastMath.sinCos(le);
  857.             return le - auxiliaryElements.getK() * scLe.sin() + auxiliaryElements.getH() * scLe.cos();
  858.         }

  859.         /** Converts true longitude to mean longitude.
  860.          * @param lv True longitude
  861.          * @return Eccentric longitude
  862.          */
  863.         private double trueToMean (final double lv) {
  864.             return eccentricToMean(trueToEccentric(lv));
  865.         }

  866.         /** Compute δa/δv.
  867.          *  @param vel satellite velocity
  868.          *  @return δa/δv
  869.          */
  870.         private Vector3D getAoV(final Vector3D vel) {
  871.             return new Vector3D(context.getTon2a(), vel);
  872.         }

  873.         /** Compute δh/δv.
  874.          *  @param X satellite position component along f, equinoctial reference frame 1st vector
  875.          *  @param Y satellite position component along g, equinoctial reference frame 2nd vector
  876.          *  @param Xdot satellite velocity component along f, equinoctial reference frame 1st vector
  877.          *  @param Ydot satellite velocity component along g, equinoctial reference frame 2nd vector
  878.          *  @return δh/δv
  879.          */
  880.         private Vector3D getHoV(final double X, final double Y, final double Xdot, final double Ydot) {
  881.             final double kf = (2. * Xdot * Y - X * Ydot) * context.getOoMU();
  882.             final double kg = X * Xdot * context.getOoMU();
  883.             final double kw = auxiliaryElements.getK() * (I * auxiliaryElements.getQ() * Y - auxiliaryElements.getP() * X) * context.getOOAB();
  884.             return new Vector3D(kf, auxiliaryElements.getVectorF(), -kg, auxiliaryElements.getVectorG(), kw, auxiliaryElements.getVectorW());
  885.         }

  886.         /** Compute δk/δv.
  887.          *  @param X satellite position component along f, equinoctial reference frame 1st vector
  888.          *  @param Y satellite position component along g, equinoctial reference frame 2nd vector
  889.          *  @param Xdot satellite velocity component along f, equinoctial reference frame 1st vector
  890.          *  @param Ydot satellite velocity component along g, equinoctial reference frame 2nd vector
  891.          *  @return δk/δv
  892.          */
  893.         private Vector3D getKoV(final double X, final double Y, final double Xdot, final double Ydot) {
  894.             final double kf = Y * Ydot * context.getOoMU();
  895.             final double kg = (2. * X * Ydot - Xdot * Y) * context.getOoMU();
  896.             final double kw = auxiliaryElements.getH() * (I * auxiliaryElements.getQ() * Y - auxiliaryElements.getP() * X) * context.getOOAB();
  897.             return new Vector3D(-kf, auxiliaryElements.getVectorF(), kg, auxiliaryElements.getVectorG(), -kw, auxiliaryElements.getVectorW());
  898.         }

  899.         /** Compute δp/δv.
  900.          *  @param Y satellite position component along g, equinoctial reference frame 2nd vector
  901.          *  @return δp/δv
  902.          */
  903.         private Vector3D getPoV(final double Y) {
  904.             return new Vector3D(context.getCo2AB() * Y, auxiliaryElements.getVectorW());
  905.         }

  906.         /** Compute δq/δv.
  907.          *  @param X satellite position component along f, equinoctial reference frame 1st vector
  908.          *  @return δq/δv
  909.          */
  910.         private Vector3D getQoV(final double X) {
  911.             return new Vector3D(I * context.getCo2AB() * X, auxiliaryElements.getVectorW());
  912.         }

  913.         /** Compute δλ/δv.
  914.          *  @param X satellite position component along f, equinoctial reference frame 1st vector
  915.          *  @param Y satellite position component along g, equinoctial reference frame 2nd vector
  916.          *  @param Xdot satellite velocity component along f, equinoctial reference frame 1st vector
  917.          *  @param Ydot satellite velocity component along g, equinoctial reference frame 2nd vector
  918.          *  @return δλ/δv
  919.          */
  920.         private Vector3D getLoV(final double X, final double Y, final double Xdot, final double Ydot) {
  921.             final Vector3D pos = new Vector3D(X, auxiliaryElements.getVectorF(), Y, auxiliaryElements.getVectorG());
  922.             final Vector3D v2  = new Vector3D(auxiliaryElements.getK(), getHoV(X, Y, Xdot, Ydot), -auxiliaryElements.getH(), getKoV(X, Y, Xdot, Ydot));
  923.             return new Vector3D(-2. * context.getOOA(), pos, context.getOoBpo(), v2, (I * auxiliaryElements.getQ() * Y - auxiliaryElements.getP() * X) * context.getOOA(), auxiliaryElements.getVectorW());
  924.         }

  925.     }

  926.     /** Class used to {@link #integrate(UnivariateVectorFunction, double, double) integrate}
  927.      *  a {@link org.hipparchus.analysis.UnivariateVectorFunction function}
  928.      *  of the orbital elements using the Gaussian quadrature rule to get the acceleration.
  929.      */
  930.     private static class GaussQuadrature {

  931.         // Points and weights for the available quadrature orders

  932.         /** Points for quadrature of order 12. */
  933.         private static final double[] P_12 = {
  934.             -0.98156063424671910000,
  935.             -0.90411725637047490000,
  936.             -0.76990267419430470000,
  937.             -0.58731795428661740000,
  938.             -0.36783149899818024000,
  939.             -0.12523340851146890000,
  940.             0.12523340851146890000,
  941.             0.36783149899818024000,
  942.             0.58731795428661740000,
  943.             0.76990267419430470000,
  944.             0.90411725637047490000,
  945.             0.98156063424671910000
  946.         };

  947.         /** Weights for quadrature of order 12. */
  948.         private static final double[] W_12 = {
  949.             0.04717533638651220000,
  950.             0.10693932599531830000,
  951.             0.16007832854334633000,
  952.             0.20316742672306584000,
  953.             0.23349253653835478000,
  954.             0.24914704581340286000,
  955.             0.24914704581340286000,
  956.             0.23349253653835478000,
  957.             0.20316742672306584000,
  958.             0.16007832854334633000,
  959.             0.10693932599531830000,
  960.             0.04717533638651220000
  961.         };

  962.         /** Points for quadrature of order 16. */
  963.         private static final double[] P_16 = {
  964.             -0.98940093499164990000,
  965.             -0.94457502307323260000,
  966.             -0.86563120238783160000,
  967.             -0.75540440835500310000,
  968.             -0.61787624440264380000,
  969.             -0.45801677765722737000,
  970.             -0.28160355077925890000,
  971.             -0.09501250983763745000,
  972.             0.09501250983763745000,
  973.             0.28160355077925890000,
  974.             0.45801677765722737000,
  975.             0.61787624440264380000,
  976.             0.75540440835500310000,
  977.             0.86563120238783160000,
  978.             0.94457502307323260000,
  979.             0.98940093499164990000
  980.         };

  981.         /** Weights for quadrature of order 16. */
  982.         private static final double[] W_16 = {
  983.             0.02715245941175405800,
  984.             0.06225352393864777000,
  985.             0.09515851168249283000,
  986.             0.12462897125553388000,
  987.             0.14959598881657685000,
  988.             0.16915651939500256000,
  989.             0.18260341504492360000,
  990.             0.18945061045506847000,
  991.             0.18945061045506847000,
  992.             0.18260341504492360000,
  993.             0.16915651939500256000,
  994.             0.14959598881657685000,
  995.             0.12462897125553388000,
  996.             0.09515851168249283000,
  997.             0.06225352393864777000,
  998.             0.02715245941175405800
  999.         };

  1000.         /** Points for quadrature of order 20. */
  1001.         private static final double[] P_20 = {
  1002.             -0.99312859918509490000,
  1003.             -0.96397192727791390000,
  1004.             -0.91223442825132600000,
  1005.             -0.83911697182221890000,
  1006.             -0.74633190646015080000,
  1007.             -0.63605368072651510000,
  1008.             -0.51086700195082700000,
  1009.             -0.37370608871541955000,
  1010.             -0.22778585114164507000,
  1011.             -0.07652652113349734000,
  1012.             0.07652652113349734000,
  1013.             0.22778585114164507000,
  1014.             0.37370608871541955000,
  1015.             0.51086700195082700000,
  1016.             0.63605368072651510000,
  1017.             0.74633190646015080000,
  1018.             0.83911697182221890000,
  1019.             0.91223442825132600000,
  1020.             0.96397192727791390000,
  1021.             0.99312859918509490000
  1022.         };

  1023.         /** Weights for quadrature of order 20. */
  1024.         private static final double[] W_20 = {
  1025.             0.01761400713915226400,
  1026.             0.04060142980038684000,
  1027.             0.06267204833410904000,
  1028.             0.08327674157670477000,
  1029.             0.10193011981724048000,
  1030.             0.11819453196151844000,
  1031.             0.13168863844917678000,
  1032.             0.14209610931838212000,
  1033.             0.14917298647260380000,
  1034.             0.15275338713072600000,
  1035.             0.15275338713072600000,
  1036.             0.14917298647260380000,
  1037.             0.14209610931838212000,
  1038.             0.13168863844917678000,
  1039.             0.11819453196151844000,
  1040.             0.10193011981724048000,
  1041.             0.08327674157670477000,
  1042.             0.06267204833410904000,
  1043.             0.04060142980038684000,
  1044.             0.01761400713915226400
  1045.         };

  1046.         /** Points for quadrature of order 24. */
  1047.         private static final double[] P_24 = {
  1048.             -0.99518721999702130000,
  1049.             -0.97472855597130950000,
  1050.             -0.93827455200273270000,
  1051.             -0.88641552700440100000,
  1052.             -0.82000198597390300000,
  1053.             -0.74012419157855440000,
  1054.             -0.64809365193697550000,
  1055.             -0.54542147138883950000,
  1056.             -0.43379350762604520000,
  1057.             -0.31504267969616340000,
  1058.             -0.19111886747361634000,
  1059.             -0.06405689286260563000,
  1060.             0.06405689286260563000,
  1061.             0.19111886747361634000,
  1062.             0.31504267969616340000,
  1063.             0.43379350762604520000,
  1064.             0.54542147138883950000,
  1065.             0.64809365193697550000,
  1066.             0.74012419157855440000,
  1067.             0.82000198597390300000,
  1068.             0.88641552700440100000,
  1069.             0.93827455200273270000,
  1070.             0.97472855597130950000,
  1071.             0.99518721999702130000
  1072.         };

  1073.         /** Weights for quadrature of order 24. */
  1074.         private static final double[] W_24 = {
  1075.             0.01234122979998733500,
  1076.             0.02853138862893380600,
  1077.             0.04427743881741981000,
  1078.             0.05929858491543691500,
  1079.             0.07334648141108027000,
  1080.             0.08619016153195320000,
  1081.             0.09761865210411391000,
  1082.             0.10744427011596558000,
  1083.             0.11550566805372553000,
  1084.             0.12167047292780335000,
  1085.             0.12583745634682825000,
  1086.             0.12793819534675221000,
  1087.             0.12793819534675221000,
  1088.             0.12583745634682825000,
  1089.             0.12167047292780335000,
  1090.             0.11550566805372553000,
  1091.             0.10744427011596558000,
  1092.             0.09761865210411391000,
  1093.             0.08619016153195320000,
  1094.             0.07334648141108027000,
  1095.             0.05929858491543691500,
  1096.             0.04427743881741981000,
  1097.             0.02853138862893380600,
  1098.             0.01234122979998733500
  1099.         };

  1100.         /** Points for quadrature of order 32. */
  1101.         private static final double[] P_32 = {
  1102.             -0.99726386184948160000,
  1103.             -0.98561151154526840000,
  1104.             -0.96476225558750640000,
  1105.             -0.93490607593773970000,
  1106.             -0.89632115576605220000,
  1107.             -0.84936761373256990000,
  1108.             -0.79448379596794250000,
  1109.             -0.73218211874028970000,
  1110.             -0.66304426693021520000,
  1111.             -0.58771575724076230000,
  1112.             -0.50689990893222950000,
  1113.             -0.42135127613063540000,
  1114.             -0.33186860228212767000,
  1115.             -0.23928736225213710000,
  1116.             -0.14447196158279646000,
  1117.             -0.04830766568773831000,
  1118.             0.04830766568773831000,
  1119.             0.14447196158279646000,
  1120.             0.23928736225213710000,
  1121.             0.33186860228212767000,
  1122.             0.42135127613063540000,
  1123.             0.50689990893222950000,
  1124.             0.58771575724076230000,
  1125.             0.66304426693021520000,
  1126.             0.73218211874028970000,
  1127.             0.79448379596794250000,
  1128.             0.84936761373256990000,
  1129.             0.89632115576605220000,
  1130.             0.93490607593773970000,
  1131.             0.96476225558750640000,
  1132.             0.98561151154526840000,
  1133.             0.99726386184948160000
  1134.         };

  1135.         /** Weights for quadrature of order 32. */
  1136.         private static final double[] W_32 = {
  1137.             0.00701861000947013600,
  1138.             0.01627439473090571200,
  1139.             0.02539206530926214200,
  1140.             0.03427386291302141000,
  1141.             0.04283589802222658600,
  1142.             0.05099805926237621600,
  1143.             0.05868409347853559000,
  1144.             0.06582222277636193000,
  1145.             0.07234579410884862000,
  1146.             0.07819389578707042000,
  1147.             0.08331192422694673000,
  1148.             0.08765209300440380000,
  1149.             0.09117387869576390000,
  1150.             0.09384439908080441000,
  1151.             0.09563872007927487000,
  1152.             0.09654008851472784000,
  1153.             0.09654008851472784000,
  1154.             0.09563872007927487000,
  1155.             0.09384439908080441000,
  1156.             0.09117387869576390000,
  1157.             0.08765209300440380000,
  1158.             0.08331192422694673000,
  1159.             0.07819389578707042000,
  1160.             0.07234579410884862000,
  1161.             0.06582222277636193000,
  1162.             0.05868409347853559000,
  1163.             0.05099805926237621600,
  1164.             0.04283589802222658600,
  1165.             0.03427386291302141000,
  1166.             0.02539206530926214200,
  1167.             0.01627439473090571200,
  1168.             0.00701861000947013600
  1169.         };

  1170.         /** Points for quadrature of order 40. */
  1171.         private static final double[] P_40 = {
  1172.             -0.99823770971055930000,
  1173.             -0.99072623869945710000,
  1174.             -0.97725994998377420000,
  1175.             -0.95791681921379170000,
  1176.             -0.93281280827867660000,
  1177.             -0.90209880696887420000,
  1178.             -0.86595950321225960000,
  1179.             -0.82461223083331170000,
  1180.             -0.77830565142651940000,
  1181.             -0.72731825518992710000,
  1182.             -0.67195668461417960000,
  1183.             -0.61255388966798030000,
  1184.             -0.54946712509512820000,
  1185.             -0.48307580168617870000,
  1186.             -0.41377920437160500000,
  1187.             -0.34199409082575850000,
  1188.             -0.26815218500725370000,
  1189.             -0.19269758070137110000,
  1190.             -0.11608407067525522000,
  1191.             -0.03877241750605081600,
  1192.             0.03877241750605081600,
  1193.             0.11608407067525522000,
  1194.             0.19269758070137110000,
  1195.             0.26815218500725370000,
  1196.             0.34199409082575850000,
  1197.             0.41377920437160500000,
  1198.             0.48307580168617870000,
  1199.             0.54946712509512820000,
  1200.             0.61255388966798030000,
  1201.             0.67195668461417960000,
  1202.             0.72731825518992710000,
  1203.             0.77830565142651940000,
  1204.             0.82461223083331170000,
  1205.             0.86595950321225960000,
  1206.             0.90209880696887420000,
  1207.             0.93281280827867660000,
  1208.             0.95791681921379170000,
  1209.             0.97725994998377420000,
  1210.             0.99072623869945710000,
  1211.             0.99823770971055930000
  1212.         };

  1213.         /** Weights for quadrature of order 40. */
  1214.         private static final double[] W_40 = {
  1215.             0.00452127709853309800,
  1216.             0.01049828453115270400,
  1217.             0.01642105838190797300,
  1218.             0.02224584919416689000,
  1219.             0.02793700698002338000,
  1220.             0.03346019528254786500,
  1221.             0.03878216797447199000,
  1222.             0.04387090818567333000,
  1223.             0.04869580763507221000,
  1224.             0.05322784698393679000,
  1225.             0.05743976909939157000,
  1226.             0.06130624249292891000,
  1227.             0.06480401345660108000,
  1228.             0.06791204581523394000,
  1229.             0.07061164739128681000,
  1230.             0.07288658239580408000,
  1231.             0.07472316905796833000,
  1232.             0.07611036190062619000,
  1233.             0.07703981816424793000,
  1234.             0.07750594797842482000,
  1235.             0.07750594797842482000,
  1236.             0.07703981816424793000,
  1237.             0.07611036190062619000,
  1238.             0.07472316905796833000,
  1239.             0.07288658239580408000,
  1240.             0.07061164739128681000,
  1241.             0.06791204581523394000,
  1242.             0.06480401345660108000,
  1243.             0.06130624249292891000,
  1244.             0.05743976909939157000,
  1245.             0.05322784698393679000,
  1246.             0.04869580763507221000,
  1247.             0.04387090818567333000,
  1248.             0.03878216797447199000,
  1249.             0.03346019528254786500,
  1250.             0.02793700698002338000,
  1251.             0.02224584919416689000,
  1252.             0.01642105838190797300,
  1253.             0.01049828453115270400,
  1254.             0.00452127709853309800
  1255.         };

  1256.         /** Points for quadrature of order 48. */
  1257.         private static final double[] P_48 = {
  1258.             -0.99877100725242610000,
  1259.             -0.99353017226635080000,
  1260.             -0.98412458372282700000,
  1261.             -0.97059159254624720000,
  1262.             -0.95298770316043080000,
  1263.             -0.93138669070655440000,
  1264.             -0.90587913671556960000,
  1265.             -0.87657202027424800000,
  1266.             -0.84358826162439350000,
  1267.             -0.80706620402944250000,
  1268.             -0.76715903251574020000,
  1269.             -0.72403413092381470000,
  1270.             -0.67787237963266400000,
  1271.             -0.62886739677651370000,
  1272.             -0.57722472608397270000,
  1273.             -0.52316097472223300000,
  1274.             -0.46690290475095840000,
  1275.             -0.40868648199071680000,
  1276.             -0.34875588629216070000,
  1277.             -0.28736248735545555000,
  1278.             -0.22476379039468908000,
  1279.             -0.16122235606889174000,
  1280.             -0.09700469920946270000,
  1281.             -0.03238017096286937000,
  1282.             0.03238017096286937000,
  1283.             0.09700469920946270000,
  1284.             0.16122235606889174000,
  1285.             0.22476379039468908000,
  1286.             0.28736248735545555000,
  1287.             0.34875588629216070000,
  1288.             0.40868648199071680000,
  1289.             0.46690290475095840000,
  1290.             0.52316097472223300000,
  1291.             0.57722472608397270000,
  1292.             0.62886739677651370000,
  1293.             0.67787237963266400000,
  1294.             0.72403413092381470000,
  1295.             0.76715903251574020000,
  1296.             0.80706620402944250000,
  1297.             0.84358826162439350000,
  1298.             0.87657202027424800000,
  1299.             0.90587913671556960000,
  1300.             0.93138669070655440000,
  1301.             0.95298770316043080000,
  1302.             0.97059159254624720000,
  1303.             0.98412458372282700000,
  1304.             0.99353017226635080000,
  1305.             0.99877100725242610000
  1306.         };

  1307.         /** Weights for quadrature of order 48. */
  1308.         private static final double[] W_48 = {
  1309.             0.00315334605230596250,
  1310.             0.00732755390127620800,
  1311.             0.01147723457923446900,
  1312.             0.01557931572294386600,
  1313.             0.01961616045735556700,
  1314.             0.02357076083932435600,
  1315.             0.02742650970835688000,
  1316.             0.03116722783279807000,
  1317.             0.03477722256477045000,
  1318.             0.03824135106583080600,
  1319.             0.04154508294346483000,
  1320.             0.04467456085669424000,
  1321.             0.04761665849249054000,
  1322.             0.05035903555385448000,
  1323.             0.05289018948519365000,
  1324.             0.05519950369998416500,
  1325.             0.05727729210040315000,
  1326.             0.05911483969839566000,
  1327.             0.06070443916589384000,
  1328.             0.06203942315989268000,
  1329.             0.06311419228625403000,
  1330.             0.06392423858464817000,
  1331.             0.06446616443595010000,
  1332.             0.06473769681268386000,
  1333.             0.06473769681268386000,
  1334.             0.06446616443595010000,
  1335.             0.06392423858464817000,
  1336.             0.06311419228625403000,
  1337.             0.06203942315989268000,
  1338.             0.06070443916589384000,
  1339.             0.05911483969839566000,
  1340.             0.05727729210040315000,
  1341.             0.05519950369998416500,
  1342.             0.05289018948519365000,
  1343.             0.05035903555385448000,
  1344.             0.04761665849249054000,
  1345.             0.04467456085669424000,
  1346.             0.04154508294346483000,
  1347.             0.03824135106583080600,
  1348.             0.03477722256477045000,
  1349.             0.03116722783279807000,
  1350.             0.02742650970835688000,
  1351.             0.02357076083932435600,
  1352.             0.01961616045735556700,
  1353.             0.01557931572294386600,
  1354.             0.01147723457923446900,
  1355.             0.00732755390127620800,
  1356.             0.00315334605230596250
  1357.         };

  1358.         /** Node points. */
  1359.         private final double[] nodePoints;

  1360.         /** Node weights. */
  1361.         private final double[] nodeWeights;

  1362.         /** Number of points. */
  1363.         private final int numberOfPoints;

  1364.         /** Creates a Gauss integrator of the given order.
  1365.          *
  1366.          *  @param numberOfPoints Order of the integration rule.
  1367.          */
  1368.         GaussQuadrature(final int numberOfPoints) {

  1369.             this.numberOfPoints = numberOfPoints;

  1370.             switch(numberOfPoints) {
  1371.                 case 12 :
  1372.                     this.nodePoints  = P_12.clone();
  1373.                     this.nodeWeights = W_12.clone();
  1374.                     break;
  1375.                 case 16 :
  1376.                     this.nodePoints  = P_16.clone();
  1377.                     this.nodeWeights = W_16.clone();
  1378.                     break;
  1379.                 case 20 :
  1380.                     this.nodePoints  = P_20.clone();
  1381.                     this.nodeWeights = W_20.clone();
  1382.                     break;
  1383.                 case 24 :
  1384.                     this.nodePoints  = P_24.clone();
  1385.                     this.nodeWeights = W_24.clone();
  1386.                     break;
  1387.                 case 32 :
  1388.                     this.nodePoints  = P_32.clone();
  1389.                     this.nodeWeights = W_32.clone();
  1390.                     break;
  1391.                 case 40 :
  1392.                     this.nodePoints  = P_40.clone();
  1393.                     this.nodeWeights = W_40.clone();
  1394.                     break;
  1395.                 case 48 :
  1396.                 default :
  1397.                     this.nodePoints  = P_48.clone();
  1398.                     this.nodeWeights = W_48.clone();
  1399.                     break;
  1400.             }

  1401.         }

  1402.         /** Integrates a given function on the given interval.
  1403.         *
  1404.         *  @param f Function to integrate.
  1405.         *  @param lowerBound Lower bound of the integration interval.
  1406.         *  @param upperBound Upper bound of the integration interval.
  1407.         *  @return the integral of the weighted function.
  1408.         */
  1409.         public double[] integrate(final UnivariateVectorFunction f,
  1410.                final double lowerBound, final double upperBound) {

  1411.             final double[] adaptedPoints  = nodePoints.clone();
  1412.             final double[] adaptedWeights = nodeWeights.clone();
  1413.             transform(adaptedPoints, adaptedWeights, lowerBound, upperBound);
  1414.             return basicIntegrate(f, adaptedPoints, adaptedWeights);
  1415.         }

  1416.        /** Integrates a given function on the given interval.
  1417.        *
  1418.        *  @param <T> the type of the field elements
  1419.        *  @param f Function to integrate.
  1420.        *  @param lowerBound Lower bound of the integration interval.
  1421.        *  @param upperBound Upper bound of the integration interval.
  1422.        *  @param field field utilized by default
  1423.        *  @return the integral of the weighted function.
  1424.        */
  1425.         public <T extends RealFieldElement<T>> T[] integrate(final RealFieldUnivariateVectorFunction<T> f,
  1426.                                                              final T lowerBound, final T upperBound,
  1427.                                                              final Field<T> field) {

  1428.             final T zero = field.getZero();

  1429.             final T[] adaptedPoints  = MathArrays.buildArray(field, numberOfPoints);
  1430.             final T[] adaptedWeights = MathArrays.buildArray(field, numberOfPoints);;

  1431.             for (int i = 0; i < numberOfPoints; i++) {
  1432.                 adaptedPoints[i]  = zero.add(nodePoints[i]);
  1433.                 adaptedWeights[i] = zero.add(nodeWeights[i]);
  1434.             }

  1435.             transform(adaptedPoints, adaptedWeights, lowerBound, upperBound);
  1436.             return basicIntegrate(f, adaptedPoints, adaptedWeights, field);
  1437.         }

  1438.         /** Performs a change of variable so that the integration
  1439.          *  can be performed on an arbitrary interval {@code [a, b]}.
  1440.          *  <p>
  1441.          *  It is assumed that the natural interval is {@code [-1, 1]}.
  1442.          *  </p>
  1443.          *
  1444.          * @param points  Points to adapt to the new interval.
  1445.          * @param weights Weights to adapt to the new interval.
  1446.          * @param a Lower bound of the integration interval.
  1447.          * @param b Lower bound of the integration interval.
  1448.          */
  1449.         private void transform(final double[] points, final double[] weights,
  1450.                 final double a, final double b) {
  1451.             // Scaling
  1452.             final double scale = (b - a) / 2;
  1453.             final double shift = a + scale;
  1454.             for (int i = 0; i < points.length; i++) {
  1455.                 points[i]   = points[i] * scale + shift;
  1456.                 weights[i] *= scale;
  1457.             }
  1458.         }

  1459.         /** Performs a change of variable so that the integration
  1460.          *  can be performed on an arbitrary interval {@code [a, b]}.
  1461.          *  <p>
  1462.          *  It is assumed that the natural interval is {@code [-1, 1]}.
  1463.          *  </p>
  1464.          * @param <T> the type of the field elements
  1465.          * @param points  Points to adapt to the new interval.
  1466.          * @param weights Weights to adapt to the new interval.
  1467.          * @param a Lower bound of the integration interval.
  1468.          * @param b Lower bound of the integration interval
  1469.          */
  1470.         private <T extends RealFieldElement<T>> void transform(final T[] points, final T[] weights,
  1471.                 final T a, final T b) {
  1472.             // Scaling
  1473.             final T scale = (b.subtract(a)).divide(2.);
  1474.             final T shift = a.add(scale);
  1475.             for (int i = 0; i < points.length; i++) {
  1476.                 points[i]   = scale.multiply(points[i]).add(shift);
  1477.                 weights[i]  = scale.multiply(weights[i]);
  1478.             }
  1479.         }

  1480.         /** Returns an estimate of the integral of {@code f(x) * w(x)},
  1481.          *  where {@code w} is a weight function that depends on the actual
  1482.          *  flavor of the Gauss integration scheme.
  1483.          *
  1484.          * @param f Function to integrate.
  1485.          * @param points  Nodes.
  1486.          * @param weights Nodes weights.
  1487.          * @return the integral of the weighted function.
  1488.          */
  1489.         private double[] basicIntegrate(final UnivariateVectorFunction f,
  1490.                 final double[] points,
  1491.                 final double[] weights) {
  1492.             double x = points[0];
  1493.             double w = weights[0];
  1494.             double[] v = f.value(x);
  1495.             final double[] y = new double[v.length];
  1496.             for (int j = 0; j < v.length; j++) {
  1497.                 y[j] = w * v[j];
  1498.             }
  1499.             final double[] t = y.clone();
  1500.             final double[] c = new double[v.length];
  1501.             final double[] s = t.clone();
  1502.             for (int i = 1; i < points.length; i++) {
  1503.                 x = points[i];
  1504.                 w = weights[i];
  1505.                 v = f.value(x);
  1506.                 for (int j = 0; j < v.length; j++) {
  1507.                     y[j] = w * v[j] - c[j];
  1508.                     t[j] =  s[j] + y[j];
  1509.                     c[j] = (t[j] - s[j]) - y[j];
  1510.                     s[j] = t[j];
  1511.                 }
  1512.             }
  1513.             return s;
  1514.         }

  1515.         /** Returns an estimate of the integral of {@code f(x) * w(x)},
  1516.          *  where {@code w} is a weight function that depends on the actual
  1517.          *  flavor of the Gauss integration scheme.
  1518.          *
  1519.          * @param <T> the type of the field elements.
  1520.          * @param f Function to integrate.
  1521.          * @param points  Nodes.
  1522.          * @param weights Nodes weight
  1523.          * @param field field utilized by default
  1524.          * @return the integral of the weighted function.
  1525.          */
  1526.         private <T extends RealFieldElement<T>> T[] basicIntegrate(final RealFieldUnivariateVectorFunction<T> f,
  1527.                 final T[] points,
  1528.                 final T[] weights,
  1529.                 final Field<T> field) {

  1530.             T x = points[0];
  1531.             T w = weights[0];
  1532.             T[] v = f.value(x);

  1533.             final T[] y = MathArrays.buildArray(field, v.length);
  1534.             for (int j = 0; j < v.length; j++) {
  1535.                 y[j] = v[j].multiply(w);
  1536.             }
  1537.             final T[] t = y.clone();
  1538.             final T[] c = MathArrays.buildArray(field, v.length);;
  1539.             final T[] s = t.clone();
  1540.             for (int i = 1; i < points.length; i++) {
  1541.                 x = points[i];
  1542.                 w = weights[i];
  1543.                 v = f.value(x);
  1544.                 for (int j = 0; j < v.length; j++) {
  1545.                     y[j] = v[j].multiply(w).subtract(c[j]);
  1546.                     t[j] = y[j].add(s[j]);
  1547.                     c[j] = (t[j].subtract(s[j])).subtract(y[j]);
  1548.                     s[j] = t[j];
  1549.                 }
  1550.             }
  1551.             return s;
  1552.         }

  1553.     }

  1554.     /** Compute the C<sub>i</sub><sup>j</sup> and the S<sub>i</sub><sup>j</sup> coefficients.
  1555.      *  <p>
  1556.      *  Those coefficients are given in Danielson paper by expression 4.4-(6)
  1557.      *  </p>
  1558.      *  @author Petre Bazavan
  1559.      *  @author Lucian Barbulescu
  1560.      */
  1561.     private class FourierCjSjCoefficients {

  1562.         /** Maximum possible value for j. */
  1563.         private final int jMax;

  1564.         /** The C<sub>i</sub><sup>j</sup> coefficients.
  1565.          * <p>
  1566.          * the index i corresponds to the following elements: <br/>
  1567.          * - 0 for a <br>
  1568.          * - 1 for k <br>
  1569.          * - 2 for h <br>
  1570.          * - 3 for q <br>
  1571.          * - 4 for p <br>
  1572.          * - 5 for λ <br>
  1573.          * </p>
  1574.          */
  1575.         private final double[][] cCoef;

  1576.         /** The C<sub>i</sub><sup>j</sup> coefficients.
  1577.          * <p>
  1578.          * the index i corresponds to the following elements: <br/>
  1579.          * - 0 for a <br>
  1580.          * - 1 for k <br>
  1581.          * - 2 for h <br>
  1582.          * - 3 for q <br>
  1583.          * - 4 for p <br>
  1584.          * - 5 for λ <br>
  1585.          * </p>
  1586.          */
  1587.         private final double[][] sCoef;

  1588.         /** Standard constructor.
  1589.          * @param state the current state
  1590.          * @param jMax maximum value for j
  1591.          * @param auxiliaryElements auxiliary elements related to the current orbit
  1592.          * @param parameters values of the force model parameters
  1593.          */
  1594.         FourierCjSjCoefficients(final SpacecraftState state, final int jMax,
  1595.                                 final AuxiliaryElements auxiliaryElements, final double[] parameters) {

  1596.             //Initialise the fields
  1597.             this.jMax = jMax;

  1598.             //Allocate the arrays
  1599.             final int rows = jMax + 1;
  1600.             cCoef = new double[rows][6];
  1601.             sCoef = new double[rows][6];

  1602.             //Compute the coefficients
  1603.             computeCoefficients(state, auxiliaryElements, parameters);
  1604.         }

  1605.         /**
  1606.          * Compute the Fourrier coefficients.
  1607.          * <p>
  1608.          * Only the C<sub>i</sub><sup>j</sup> and S<sub>i</sub><sup>j</sup> coefficients need to be computed
  1609.          * as D<sub>i</sub><sup>m</sup> is always 0.
  1610.          * </p>
  1611.          * @param state the current state
  1612.          * @param auxiliaryElements auxiliary elements related to the current orbit
  1613.          * @param parameters values of the force model parameters
  1614.          */
  1615.         private void computeCoefficients(final SpacecraftState state,
  1616.                                          final AuxiliaryElements auxiliaryElements,
  1617.                                          final double[] parameters) {

  1618.             // Computes the limits for the integral
  1619.             final double[] ll = getLLimits(state, auxiliaryElements);
  1620.             // Computes integrated mean element rates if Llow < Lhigh
  1621.             if (ll[0] < ll[1]) {
  1622.                 //Compute 1 / PI
  1623.                 final double ooPI = 1 / FastMath.PI;

  1624.                 // loop through all values of j
  1625.                 for (int j = 0; j <= jMax; j++) {
  1626.                     final double[] curentCoefficients =
  1627.                             integrator.integrate(new IntegrableFunction(state, false, j, parameters), ll[0], ll[1]);

  1628.                     //divide by PI and set the values for the coefficients
  1629.                     for (int i = 0; i < 6; i++) {
  1630.                         cCoef[j][i] = ooPI * curentCoefficients[i];
  1631.                         sCoef[j][i] = ooPI * curentCoefficients[i + 6];
  1632.                     }
  1633.                 }
  1634.             }
  1635.         }

  1636.         /** Get the coefficient C<sub>i</sub><sup>j</sup>.
  1637.          * @param i i index - corresponds to the required variation
  1638.          * @param j j index
  1639.          * @return the coefficient C<sub>i</sub><sup>j</sup>
  1640.          */
  1641.         public double getCij(final int i, final int j) {
  1642.             return cCoef[j][i];
  1643.         }

  1644.         /** Get the coefficient S<sub>i</sub><sup>j</sup>.
  1645.          * @param i i index - corresponds to the required variation
  1646.          * @param j j index
  1647.          * @return the coefficient S<sub>i</sub><sup>j</sup>
  1648.          */
  1649.         public double getSij(final int i, final int j) {
  1650.             return sCoef[j][i];
  1651.         }
  1652.     }

  1653.     /** Compute the C<sub>i</sub><sup>j</sup> and the S<sub>i</sub><sup>j</sup> coefficients with field elements.
  1654.      *  <p>
  1655.      *  Those coefficients are given in Danielson paper by expression 4.4-(6)
  1656.      *  </p>
  1657.      *  @author Petre Bazavan
  1658.      *  @author Lucian Barbulescu
  1659.      */
  1660.     private class FieldFourierCjSjCoefficients <T extends RealFieldElement<T>> {

  1661.         /** Maximum possible value for j. */
  1662.         private final int jMax;

  1663.         /** The C<sub>i</sub><sup>j</sup> coefficients.
  1664.          * <p>
  1665.          * the index i corresponds to the following elements: <br/>
  1666.          * - 0 for a <br>
  1667.          * - 1 for k <br>
  1668.          * - 2 for h <br>
  1669.          * - 3 for q <br>
  1670.          * - 4 for p <br>
  1671.          * - 5 for λ <br>
  1672.          * </p>
  1673.          */
  1674.         private final T[][] cCoef;

  1675.         /** The C<sub>i</sub><sup>j</sup> coefficients.
  1676.          * <p>
  1677.          * the index i corresponds to the following elements: <br/>
  1678.          * - 0 for a <br>
  1679.          * - 1 for k <br>
  1680.          * - 2 for h <br>
  1681.          * - 3 for q <br>
  1682.          * - 4 for p <br>
  1683.          * - 5 for λ <br>
  1684.          * </p>
  1685.          */
  1686.         private final T[][] sCoef;

  1687.         /** Standard constructor.
  1688.          * @param state the current state
  1689.          * @param jMax maximum value for j
  1690.          * @param auxiliaryElements auxiliary elements related to the current orbit
  1691.          * @param parameters values of the force model parameters
  1692.          * @param field field used by default
  1693.          */
  1694.         FieldFourierCjSjCoefficients(final FieldSpacecraftState<T> state, final int jMax,
  1695.                                      final FieldAuxiliaryElements<T> auxiliaryElements,
  1696.                                      final T[] parameters,
  1697.                                      final Field<T> field) {
  1698.             //Initialise the fields
  1699.             this.jMax = jMax;

  1700.             //Allocate the arrays
  1701.             final int rows = jMax + 1;
  1702.             cCoef = MathArrays.buildArray(field, rows, 6);
  1703.             sCoef = MathArrays.buildArray(field, rows, 6);

  1704.             //Compute the coefficients
  1705.             computeCoefficients(state, auxiliaryElements, parameters, field);
  1706.         }

  1707.         /**
  1708.          * Compute the Fourrier coefficients.
  1709.          * <p>
  1710.          * Only the C<sub>i</sub><sup>j</sup> and S<sub>i</sub><sup>j</sup> coefficients need to be computed
  1711.          * as D<sub>i</sub><sup>m</sup> is always 0.
  1712.          * </p>
  1713.          * @param state the current state
  1714.          * @param auxiliaryElements auxiliary elements related to the current orbit
  1715.          * @param parameters values of the force model parameters
  1716.          * @param field field used by default
  1717.          */
  1718.         private void computeCoefficients(final FieldSpacecraftState<T> state,
  1719.                                          final FieldAuxiliaryElements<T> auxiliaryElements,
  1720.                                          final T[] parameters,
  1721.                                          final Field<T> field) {
  1722.             // Zero
  1723.             final T zero = field.getZero();
  1724.             // Computes the limits for the integral
  1725.             final T[] ll = getLLimits(state, auxiliaryElements);
  1726.             // Computes integrated mean element rates if Llow < Lhigh
  1727.             if (ll[0].getReal() < ll[1].getReal()) {
  1728.                 //Compute 1 / PI
  1729.                 final T ooPI = zero.add(FastMath.PI).reciprocal();

  1730.                 // loop through all values of j
  1731.                 for (int j = 0; j <= jMax; j++) {
  1732.                     final T[] curentCoefficients =
  1733.                             integrator.integrate(new FieldIntegrableFunction<>(state, false, j, parameters, field), ll[0], ll[1], field);

  1734.                     //divide by PI and set the values for the coefficients
  1735.                     for (int i = 0; i < 6; i++) {
  1736.                         cCoef[j][i] = curentCoefficients[i].multiply(ooPI);
  1737.                         sCoef[j][i] = curentCoefficients[i + 6].multiply(ooPI);
  1738.                     }
  1739.                 }
  1740.             }
  1741.         }

  1742.         /** Get the coefficient C<sub>i</sub><sup>j</sup>.
  1743.          * @param i i index - corresponds to the required variation
  1744.          * @param j j index
  1745.          * @return the coefficient C<sub>i</sub><sup>j</sup>
  1746.          */
  1747.         public T getCij(final int i, final int j) {
  1748.             return cCoef[j][i];
  1749.         }

  1750.         /** Get the coefficient S<sub>i</sub><sup>j</sup>.
  1751.          * @param i i index - corresponds to the required variation
  1752.          * @param j j index
  1753.          * @return the coefficient S<sub>i</sub><sup>j</sup>
  1754.          */
  1755.         public T getSij(final int i, final int j) {
  1756.             return sCoef[j][i];
  1757.         }
  1758.     }

  1759.     /** This class handles the short periodic coefficients described in Danielson 2.5.3-26.
  1760.      *
  1761.      * <p>
  1762.      * The value of M is 0. Also, since the values of the Fourier coefficient D<sub>i</sub><sup>m</sup> is 0
  1763.      * then the values of the coefficients D<sub>i</sub><sup>m</sup> for m &gt; 2 are also 0.
  1764.      * </p>
  1765.      * @author Petre Bazavan
  1766.      * @author Lucian Barbulescu
  1767.      *
  1768.      */
  1769.     private static class GaussianShortPeriodicCoefficients implements ShortPeriodTerms {

  1770.         /** Maximum value for j index. */
  1771.         private final int jMax;

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

  1774.         /** Prefix for coefficients keys. */
  1775.         private final String coefficientsKeyPrefix;

  1776.         /** All coefficients slots. */
  1777.         private final transient TimeSpanMap<Slot> slots;

  1778.         /** Constructor.
  1779.          *  @param coefficientsKeyPrefix prefix for coefficients keys
  1780.          *  @param jMax maximum value for j index
  1781.          *  @param interpolationPoints number of points used in the interpolation process
  1782.          *  @param slots all coefficients slots
  1783.          */
  1784.         GaussianShortPeriodicCoefficients(final String coefficientsKeyPrefix,
  1785.                                           final int jMax, final int interpolationPoints,
  1786.                                           final TimeSpanMap<Slot> slots) {
  1787.             //Initialize fields
  1788.             this.jMax                  = jMax;
  1789.             this.interpolationPoints   = interpolationPoints;
  1790.             this.coefficientsKeyPrefix = coefficientsKeyPrefix;
  1791.             this.slots                 = slots;
  1792.         }

  1793.         /** Get the slot valid for some date.
  1794.          * @param meanStates mean states defining the slot
  1795.          * @return slot valid at the specified date
  1796.          */
  1797.         public Slot createSlot(final SpacecraftState... meanStates) {
  1798.             final Slot         slot  = new Slot(jMax, interpolationPoints);
  1799.             final AbsoluteDate first = meanStates[0].getDate();
  1800.             final AbsoluteDate last  = meanStates[meanStates.length - 1].getDate();
  1801.             if (first.compareTo(last) <= 0) {
  1802.                 slots.addValidAfter(slot, first);
  1803.             } else {
  1804.                 slots.addValidBefore(slot, first);
  1805.             }
  1806.             return slot;
  1807.         }

  1808.         /** Compute the short periodic coefficients.
  1809.          *
  1810.          * @param state current state information: date, kinematics, attitude
  1811.          * @param slot coefficients slot
  1812.          * @param fourierCjSj Fourier coefficients
  1813.          * @param uijvij U and V coefficients
  1814.          * @param n Keplerian mean motion
  1815.          * @param a semi major axis
  1816.          */
  1817.         private void computeCoefficients(final SpacecraftState state, final Slot slot,
  1818.                                          final FourierCjSjCoefficients fourierCjSj,
  1819.                                          final UijVijCoefficients uijvij,
  1820.                                          final double n, final double a) {

  1821.             // get the current date
  1822.             final AbsoluteDate date = state.getDate();

  1823.             // compute the k₂⁰ coefficient
  1824.             final double k20 = computeK20(jMax, uijvij.currentRhoSigmaj);

  1825.             // 1. / n
  1826.             final double oon = 1. / n;
  1827.             // 3. / (2 * a * n)
  1828.             final double to2an = 1.5 * oon / a;
  1829.             // 3. / (4 * a * n)
  1830.             final double to4an = to2an / 2;

  1831.             // Compute the coefficients for each element
  1832.             final int size = jMax + 1;
  1833.             final double[]   di1        = new double[6];
  1834.             final double[]   di2        = new double[6];
  1835.             final double[][] currentCij = new double[size][6];
  1836.             final double[][] currentSij = new double[size][6];
  1837.             for (int i = 0; i < 6; i++) {

  1838.                 // compute D<sub>i</sub>¹ and D<sub>i</sub>² (all others are 0)
  1839.                 di1[i] = -oon * fourierCjSj.getCij(i, 0);
  1840.                 if (i == 5) {
  1841.                     di1[i] += to2an * uijvij.getU1(0, 0);
  1842.                 }
  1843.                 di2[i] = 0.;
  1844.                 if (i == 5) {
  1845.                     di2[i] += -to4an * fourierCjSj.getCij(0, 0);
  1846.                 }

  1847.                 //the C<sub>i</sub>⁰ is computed based on all others
  1848.                 currentCij[0][i] = -di2[i] * k20;

  1849.                 for (int j = 1; j <= jMax; j++) {
  1850.                     // compute the current C<sub>i</sub><sup>j</sup> and S<sub>i</sub><sup>j</sup>
  1851.                     currentCij[j][i] = oon * uijvij.getU1(j, i);
  1852.                     if (i == 5) {
  1853.                         currentCij[j][i] += -to2an * uijvij.getU2(j);
  1854.                     }
  1855.                     currentSij[j][i] = oon * uijvij.getV1(j, i);
  1856.                     if (i == 5) {
  1857.                         currentSij[j][i] += -to2an * uijvij.getV2(j);
  1858.                     }

  1859.                     // add the computed coefficients to C<sub>i</sub>⁰
  1860.                     currentCij[0][i] += -(currentCij[j][i] * uijvij.currentRhoSigmaj[0][j] + currentSij[j][i] * uijvij.currentRhoSigmaj[1][j]);
  1861.                 }

  1862.             }

  1863.             // add the values to the interpolators
  1864.             slot.cij[0].addGridPoint(date, currentCij[0]);
  1865.             slot.dij[1].addGridPoint(date, di1);
  1866.             slot.dij[2].addGridPoint(date, di2);
  1867.             for (int j = 1; j <= jMax; j++) {
  1868.                 slot.cij[j].addGridPoint(date, currentCij[j]);
  1869.                 slot.sij[j].addGridPoint(date, currentSij[j]);
  1870.             }

  1871.         }

  1872.         /** Compute the coefficient k₂⁰ by using the equation
  1873.          * 2.5.3-(9a) from Danielson.
  1874.          * <p>
  1875.          * After inserting 2.5.3-(8) into 2.5.3-(9a) the result becomes:<br>
  1876.          * k₂⁰ = &Sigma;<sub>k=1</sub><sup>kMax</sup>[(2 / k²) * (σ<sub>k</sub>² + ρ<sub>k</sub>²)]
  1877.          * </p>
  1878.          * @param kMax max value fot k index
  1879.          * @param currentRhoSigmaj the current computed values for the ρ<sub>j</sub> and σ<sub>j</sub> coefficients
  1880.          * @return the coefficient k₂⁰
  1881.          */
  1882.         private double computeK20(final int kMax, final double[][] currentRhoSigmaj) {
  1883.             double k20 = 0.;

  1884.             for (int kIndex = 1; kIndex <= kMax; kIndex++) {
  1885.                 // After inserting 2.5.3-(8) into 2.5.3-(9a) the result becomes:
  1886.                 //k₂⁰ = &Sigma;<sub>k=1</sub><sup>kMax</sup>[(2 / k²) * (σ<sub>k</sub>² + ρ<sub>k</sub>²)]
  1887.                 double currentTerm = currentRhoSigmaj[1][kIndex] * currentRhoSigmaj[1][kIndex] +
  1888.                                      currentRhoSigmaj[0][kIndex] * currentRhoSigmaj[0][kIndex];

  1889.                 //multiply by 2 / k²
  1890.                 currentTerm *= 2. / (kIndex * kIndex);

  1891.                 // add the term to the result
  1892.                 k20 += currentTerm;
  1893.             }

  1894.             return k20;
  1895.         }

  1896.         /** {@inheritDoc} */
  1897.         @Override
  1898.         public double[] value(final Orbit meanOrbit) {

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

  1901.             // Get the True longitude L
  1902.             final double L = meanOrbit.getLv();

  1903.             // Compute the center (l - λ)
  1904.             final double center =  L - meanOrbit.getLM();
  1905.             // Compute (l - λ)²
  1906.             final double center2 = center * center;

  1907.             // Initialize short periodic variations
  1908.             final double[] shortPeriodicVariation = slot.cij[0].value(meanOrbit.getDate());
  1909.             final double[] d1 = slot.dij[1].value(meanOrbit.getDate());
  1910.             final double[] d2 = slot.dij[2].value(meanOrbit.getDate());
  1911.             for (int i = 0; i < 6; i++) {
  1912.                 shortPeriodicVariation[i] += center * d1[i] + center2 * d2[i];
  1913.             }

  1914.             for (int j = 1; j <= JMAX; j++) {
  1915.                 final double[] c = slot.cij[j].value(meanOrbit.getDate());
  1916.                 final double[] s = slot.sij[j].value(meanOrbit.getDate());
  1917.                 final SinCos sc  = FastMath.sinCos(j * L);
  1918.                 final double cos = sc.cos();
  1919.                 final double sin = sc.sin();
  1920.                 for (int i = 0; i < 6; i++) {
  1921.                     // add corresponding term to the short periodic variation
  1922.                     shortPeriodicVariation[i] += c[i] * cos;
  1923.                     shortPeriodicVariation[i] += s[i] * sin;
  1924.                 }
  1925.             }

  1926.             return shortPeriodicVariation;

  1927.         }

  1928.         /** {@inheritDoc} */
  1929.         public String getCoefficientsKeyPrefix() {
  1930.             return coefficientsKeyPrefix;
  1931.         }

  1932.         /** {@inheritDoc}
  1933.          * <p>
  1934.          * For Gaussian forces, there are JMAX cj coefficients,
  1935.          * JMAX sj coefficients and 3 dj coefficients. As JMAX = 12,
  1936.          * this sums up to 27 coefficients. The j index is the integer
  1937.          * multiplier for the true longitude argument in the cj and sj
  1938.          * coefficients and to the degree in  the polynomial dj coefficients.
  1939.          * </p>
  1940.          */
  1941.         @Override
  1942.         public Map<String, double[]> getCoefficients(final AbsoluteDate date, final Set<String> selected) {

  1943.             // select the coefficients slot
  1944.             final Slot slot = slots.get(date);

  1945.             final Map<String, double[]> coefficients = new HashMap<String, double[]>(2 * JMAX + 3);
  1946.             storeIfSelected(coefficients, selected, slot.cij[0].value(date), "d", 0);
  1947.             storeIfSelected(coefficients, selected, slot.dij[1].value(date), "d", 1);
  1948.             storeIfSelected(coefficients, selected, slot.dij[2].value(date), "d", 2);
  1949.             for (int j = 1; j <= JMAX; j++) {
  1950.                 storeIfSelected(coefficients, selected, slot.cij[j].value(date), "c", j);
  1951.                 storeIfSelected(coefficients, selected, slot.sij[j].value(date), "s", j);
  1952.             }

  1953.             return coefficients;

  1954.         }

  1955.         /** Put a coefficient in a map if selected.
  1956.          * @param map map to populate
  1957.          * @param selected set of coefficients that should be put in the map
  1958.          * (empty set means all coefficients are selected)
  1959.          * @param value coefficient value
  1960.          * @param id coefficient identifier
  1961.          * @param indices list of coefficient indices
  1962.          */
  1963.         private void storeIfSelected(final Map<String, double[]> map, final Set<String> selected,
  1964.                                      final double[] value, final String id, final int... indices) {
  1965.             final StringBuilder keyBuilder = new StringBuilder(getCoefficientsKeyPrefix());
  1966.             keyBuilder.append(id);
  1967.             for (int index : indices) {
  1968.                 keyBuilder.append('[').append(index).append(']');
  1969.             }
  1970.             final String key = keyBuilder.toString();
  1971.             if (selected.isEmpty() || selected.contains(key)) {
  1972.                 map.put(key, value);
  1973.             }
  1974.         }

  1975.     }

  1976.      /** This class handles the short periodic coefficients described in Danielson 2.5.3-26.
  1977.      *
  1978.      * <p>
  1979.      * The value of M is 0. Also, since the values of the Fourier coefficient D<sub>i</sub><sup>m</sup> is 0
  1980.      * then the values of the coefficients D<sub>i</sub><sup>m</sup> for m &gt; 2 are also 0.
  1981.      * </p>
  1982.      * @author Petre Bazavan
  1983.      * @author Lucian Barbulescu
  1984.      *
  1985.      */
  1986.     private static class FieldGaussianShortPeriodicCoefficients <T extends RealFieldElement<T>> implements FieldShortPeriodTerms<T> {

  1987.         /** Maximum value for j index. */
  1988.         private final int jMax;

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

  1991.         /** Prefix for coefficients keys. */
  1992.         private final String coefficientsKeyPrefix;

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

  1995.         /** Constructor.
  1996.          *  @param coefficientsKeyPrefix prefix for coefficients keys
  1997.          *  @param jMax maximum value for j index
  1998.          *  @param interpolationPoints number of points used in the interpolation process
  1999.          *  @param slots all coefficients slots
  2000.          */
  2001.         FieldGaussianShortPeriodicCoefficients(final String coefficientsKeyPrefix,
  2002.                                                final int jMax, final int interpolationPoints,
  2003.                                                final FieldTimeSpanMap<FieldSlot<T>, T> slots) {
  2004.             //Initialize fields
  2005.             this.jMax                  = jMax;
  2006.             this.interpolationPoints   = interpolationPoints;
  2007.             this.coefficientsKeyPrefix = coefficientsKeyPrefix;
  2008.             this.slots                 = slots;
  2009.         }

  2010.         /** Get the slot valid for some date.
  2011.          * @param meanStates mean states defining the slot
  2012.          * @return slot valid at the specified date
  2013.          */
  2014.         @SuppressWarnings("unchecked")
  2015.         public FieldSlot<T> createSlot(final FieldSpacecraftState<T>... meanStates) {
  2016.             final FieldSlot<T>         slot  = new FieldSlot<>(jMax, interpolationPoints);
  2017.             final FieldAbsoluteDate<T> first = meanStates[0].getDate();
  2018.             final FieldAbsoluteDate<T> last  = meanStates[meanStates.length - 1].getDate();
  2019.             if (first.compareTo(last) <= 0) {
  2020.                 slots.addValidAfter(slot, first);
  2021.             } else {
  2022.                 slots.addValidBefore(slot, first);
  2023.             }
  2024.             return slot;
  2025.         }

  2026.         /** Compute the short periodic coefficients.
  2027.          *
  2028.          * @param state current state information: date, kinematics, attitude
  2029.          * @param slot coefficients slot
  2030.          * @param fourierCjSj Fourier coefficients
  2031.          * @param uijvij U and V coefficients
  2032.          * @param n Keplerian mean motion
  2033.          * @param a semi major axis
  2034.          * @param field field used by default
  2035.          */
  2036.         private void computeCoefficients(final FieldSpacecraftState<T> state, final FieldSlot<T> slot,
  2037.                                          final FieldFourierCjSjCoefficients<T> fourierCjSj,
  2038.                                          final FieldUijVijCoefficients<T> uijvij,
  2039.                                          final T n, final T a,
  2040.                                          final Field<T> field) {

  2041.             // Zero
  2042.             final T zero = field.getZero();

  2043.             // get the current date
  2044.             final FieldAbsoluteDate<T> date = state.getDate();

  2045.             // compute the k₂⁰ coefficient
  2046.             final T k20 = computeK20(jMax, uijvij.currentRhoSigmaj, field);

  2047.             // 1. / n
  2048.             final T oon = n.reciprocal();
  2049.             // 3. / (2 * a * n)
  2050.             final T to2an = oon.multiply(1.5).divide(a);
  2051.             // 3. / (4 * a * n)
  2052.             final T to4an = to2an.divide(2.);

  2053.             // Compute the coefficients for each element
  2054.             final int size = jMax + 1;
  2055.             final T[]   di1        = MathArrays.buildArray(field, 6);
  2056.             final T[]   di2        = MathArrays.buildArray(field, 6);
  2057.             final T[][] currentCij = MathArrays.buildArray(field, size, 6);
  2058.             final T[][] currentSij = MathArrays.buildArray(field, size, 6);
  2059.             for (int i = 0; i < 6; i++) {

  2060.                 // compute D<sub>i</sub>¹ and D<sub>i</sub>² (all others are 0)
  2061.                 di1[i] = oon.negate().multiply(fourierCjSj.getCij(i, 0));
  2062.                 if (i == 5) {
  2063.                     di1[i] = di1[i].add(to2an.multiply(uijvij.getU1(0, 0)));
  2064.                 }
  2065.                 di2[i] = zero;
  2066.                 if (i == 5) {
  2067.                     di2[i] = di2[i].add(to4an.negate().multiply(fourierCjSj.getCij(0, 0)));
  2068.                 }

  2069.                 //the C<sub>i</sub>⁰ is computed based on all others
  2070.                 currentCij[0][i] = di2[i].negate().multiply(k20);

  2071.                 for (int j = 1; j <= jMax; j++) {
  2072.                     // compute the current C<sub>i</sub><sup>j</sup> and S<sub>i</sub><sup>j</sup>
  2073.                     currentCij[j][i] = oon.multiply(uijvij.getU1(j, i));
  2074.                     if (i == 5) {
  2075.                         currentCij[j][i] = currentCij[j][i].add(to2an.negate().multiply(uijvij.getU2(j)));
  2076.                     }
  2077.                     currentSij[j][i] = oon.multiply(uijvij.getV1(j, i));
  2078.                     if (i == 5) {
  2079.                         currentSij[j][i] = currentSij[j][i].add(to2an.negate().multiply(uijvij.getV2(j)));
  2080.                     }

  2081.                     // add the computed coefficients to C<sub>i</sub>⁰
  2082.                     currentCij[0][i] = currentCij[0][i].add(currentCij[j][i].multiply(uijvij.currentRhoSigmaj[0][j]).add(currentSij[j][i].multiply(uijvij.currentRhoSigmaj[1][j])).negate());
  2083.                 }

  2084.             }

  2085.             // add the values to the interpolators
  2086.             slot.cij[0].addGridPoint(date, currentCij[0]);
  2087.             slot.dij[1].addGridPoint(date, di1);
  2088.             slot.dij[2].addGridPoint(date, di2);
  2089.             for (int j = 1; j <= jMax; j++) {
  2090.                 slot.cij[j].addGridPoint(date, currentCij[j]);
  2091.                 slot.sij[j].addGridPoint(date, currentSij[j]);
  2092.             }

  2093.         }

  2094.         /** Compute the coefficient k₂⁰ by using the equation
  2095.          * 2.5.3-(9a) from Danielson.
  2096.          * <p>
  2097.          * After inserting 2.5.3-(8) into 2.5.3-(9a) the result becomes:<br>
  2098.          * k₂⁰ = &Sigma;<sub>k=1</sub><sup>kMax</sup>[(2 / k²) * (σ<sub>k</sub>² + ρ<sub>k</sub>²)]
  2099.          * </p>
  2100.          * @param kMax max value fot k index
  2101.          * @param currentRhoSigmaj the current computed values for the ρ<sub>j</sub> and σ<sub>j</sub> coefficients
  2102.          * @param field field used by default
  2103.          * @return the coefficient k₂⁰
  2104.          */
  2105.         private T computeK20(final int kMax, final T[][] currentRhoSigmaj, final Field<T> field) {
  2106.             final T zero = field.getZero();
  2107.             T k20 = zero;

  2108.             for (int kIndex = 1; kIndex <= kMax; kIndex++) {
  2109.                 // After inserting 2.5.3-(8) into 2.5.3-(9a) the result becomes:
  2110.                 //k₂⁰ = &Sigma;<sub>k=1</sub><sup>kMax</sup>[(2 / k²) * (σ<sub>k</sub>² + ρ<sub>k</sub>²)]
  2111.                 T currentTerm = currentRhoSigmaj[1][kIndex].multiply(currentRhoSigmaj[1][kIndex]).
  2112.                                 add(currentRhoSigmaj[0][kIndex].multiply(currentRhoSigmaj[0][kIndex]));

  2113.                 //multiply by 2 / k²
  2114.                 currentTerm = currentTerm.multiply(2. / (kIndex * kIndex));

  2115.                 // add the term to the result
  2116.                 k20 = k20.add(currentTerm);
  2117.             }

  2118.             return k20;
  2119.         }

  2120.         /** {@inheritDoc} */
  2121.         @Override
  2122.         public T[] value(final FieldOrbit<T> meanOrbit) {

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

  2125.             // Get the True longitude L
  2126.             final T L = meanOrbit.getLv();

  2127.             // Compute the center (l - λ)
  2128.             final T center =  L.subtract(meanOrbit.getLM());
  2129.             // Compute (l - λ)²
  2130.             final T center2 = center.multiply(center);

  2131.             // Initialize short periodic variations
  2132.             final T[] shortPeriodicVariation = slot.cij[0].value(meanOrbit.getDate());
  2133.             final T[] d1 = slot.dij[1].value(meanOrbit.getDate());
  2134.             final T[] d2 = slot.dij[2].value(meanOrbit.getDate());
  2135.             for (int i = 0; i < 6; i++) {
  2136.                 shortPeriodicVariation[i] = shortPeriodicVariation[i].add(center.multiply(d1[i]).add(center2.multiply(d2[i])));
  2137.             }

  2138.             for (int j = 1; j <= JMAX; j++) {
  2139.                 final T[] c = slot.cij[j].value(meanOrbit.getDate());
  2140.                 final T[] s = slot.sij[j].value(meanOrbit.getDate());
  2141.                 final FieldSinCos<T> sc = FastMath.sinCos(L.multiply(j));
  2142.                 final T cos = sc.cos();
  2143.                 final T sin = sc.sin();
  2144.                 for (int i = 0; i < 6; i++) {
  2145.                     // add corresponding term to the short periodic variation
  2146.                     shortPeriodicVariation[i] = shortPeriodicVariation[i].add(c[i].multiply(cos));
  2147.                     shortPeriodicVariation[i] = shortPeriodicVariation[i].add(s[i].multiply(sin));
  2148.                 }
  2149.             }

  2150.             return shortPeriodicVariation;

  2151.         }

  2152.         /** {@inheritDoc} */
  2153.         public String getCoefficientsKeyPrefix() {
  2154.             return coefficientsKeyPrefix;
  2155.         }

  2156.         /** {@inheritDoc}
  2157.          * <p>
  2158.          * For Gaussian forces, there are JMAX cj coefficients,
  2159.          * JMAX sj coefficients and 3 dj coefficients. As JMAX = 12,
  2160.          * this sums up to 27 coefficients. The j index is the integer
  2161.          * multiplier for the true longitude argument in the cj and sj
  2162.          * coefficients and to the degree in  the polynomial dj coefficients.
  2163.          * </p>
  2164.          */
  2165.         @Override
  2166.         public Map<String, T[]> getCoefficients(final FieldAbsoluteDate<T> date, final Set<String> selected) {

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

  2169.             final Map<String, T[]> coefficients = new HashMap<String, T[]>(2 * JMAX + 3);
  2170.             storeIfSelected(coefficients, selected, slot.cij[0].value(date), "d", 0);
  2171.             storeIfSelected(coefficients, selected, slot.dij[1].value(date), "d", 1);
  2172.             storeIfSelected(coefficients, selected, slot.dij[2].value(date), "d", 2);
  2173.             for (int j = 1; j <= JMAX; j++) {
  2174.                 storeIfSelected(coefficients, selected, slot.cij[j].value(date), "c", j);
  2175.                 storeIfSelected(coefficients, selected, slot.sij[j].value(date), "s", j);
  2176.             }

  2177.             return coefficients;

  2178.         }

  2179.         /** Put a coefficient in a map if selected.
  2180.          * @param map map to populate
  2181.          * @param selected set of coefficients that should be put in the map
  2182.          * (empty set means all coefficients are selected)
  2183.          * @param value coefficient value
  2184.          * @param id coefficient identifier
  2185.          * @param indices list of coefficient indices
  2186.          */
  2187.         private void storeIfSelected(final Map<String, T[]> map, final Set<String> selected,
  2188.                                      final T[] value, final String id, final int... indices) {
  2189.             final StringBuilder keyBuilder = new StringBuilder(getCoefficientsKeyPrefix());
  2190.             keyBuilder.append(id);
  2191.             for (int index : indices) {
  2192.                 keyBuilder.append('[').append(index).append(']');
  2193.             }
  2194.             final String key = keyBuilder.toString();
  2195.             if (selected.isEmpty() || selected.contains(key)) {
  2196.                 map.put(key, value);
  2197.             }
  2198.         }

  2199.     }


  2200.     /** The U<sub>i</sub><sup>j</sup> and V<sub>i</sub><sup>j</sup> coefficients described by
  2201.      * equations 2.5.3-(21) and 2.5.3-(22) from Danielson.
  2202.      * <p>
  2203.      * The index i takes only the values 1 and 2<br>
  2204.      * For U only the index 0 for j is used.
  2205.      * </p>
  2206.      *
  2207.      * @author Petre Bazavan
  2208.      * @author Lucian Barbulescu
  2209.      */
  2210.     private static class UijVijCoefficients {

  2211.         /** The U₁<sup>j</sup> coefficients.
  2212.          * <p>
  2213.          * The first index identifies the Fourier coefficients used<br>
  2214.          * Those coefficients are computed for all Fourier C<sub>i</sub><sup>j</sup> and S<sub>i</sub><sup>j</sup><br>
  2215.          * The only exception is when j = 0 when only the coefficient for fourier index = 1 (i == 0) is needed.<br>
  2216.          * Also, for fourier index = 1 (i == 0), the coefficients up to 2 * jMax are computed, because are required
  2217.          * to compute the coefficients U₂<sup>j</sup>
  2218.          * </p>
  2219.          */
  2220.         private final double[][] u1ij;

  2221.         /** The V₁<sup>j</sup> coefficients.
  2222.          * <p>
  2223.          * The first index identifies the Fourier coefficients used<br>
  2224.          * Those coefficients are computed for all Fourier C<sub>i</sub><sup>j</sup> and S<sub>i</sub><sup>j</sup><br>
  2225.          * for fourier index = 1 (i == 0), the coefficients up to 2 * jMax are computed, because are required
  2226.          * to compute the coefficients V₂<sup>j</sup>
  2227.          * </p>
  2228.          */
  2229.         private final double[][] v1ij;

  2230.         /** The U₂<sup>j</sup> coefficients.
  2231.          * <p>
  2232.          * Only the coefficients that use the Fourier index = 1 (i == 0) are computed as they are the only ones required.
  2233.          * </p>
  2234.          */
  2235.         private final double[] u2ij;

  2236.         /** The V₂<sup>j</sup> coefficients.
  2237.          * <p>
  2238.          * Only the coefficients that use the Fourier index = 1 (i == 0) are computed as they are the only ones required.
  2239.          * </p>
  2240.          */
  2241.         private final double[] v2ij;

  2242.         /** The current computed values for the ρ<sub>j</sub> and σ<sub>j</sub> coefficients. */
  2243.         private final double[][] currentRhoSigmaj;

  2244.         /** The C<sub>i</sub><sup>j</sup> and the S<sub>i</sub><sup>j</sup> Fourier coefficients. */
  2245.         private final FourierCjSjCoefficients fourierCjSj;

  2246.         /** The maximum value for j index. */
  2247.         private final int jMax;

  2248.         /** Constructor.
  2249.          * @param currentRhoSigmaj the current computed values for the ρ<sub>j</sub> and σ<sub>j</sub> coefficients
  2250.          * @param fourierCjSj the fourier coefficients C<sub>i</sub><sup>j</sup> and the S<sub>i</sub><sup>j</sup>
  2251.          * @param jMax maximum value for j index
  2252.          */
  2253.         UijVijCoefficients(final double[][] currentRhoSigmaj, final FourierCjSjCoefficients fourierCjSj, final int jMax) {
  2254.             this.currentRhoSigmaj = currentRhoSigmaj;
  2255.             this.fourierCjSj = fourierCjSj;
  2256.             this.jMax = jMax;

  2257.             // initialize the internal arrays.
  2258.             this.u1ij = new double[6][2 * jMax + 1];
  2259.             this.v1ij = new double[6][2 * jMax + 1];
  2260.             this.u2ij = new double[jMax + 1];
  2261.             this.v2ij = new double[jMax + 1];

  2262.             //compute the coefficients
  2263.             computeU1V1Coefficients();
  2264.             computeU2V2Coefficients();
  2265.         }

  2266.         /** Build the U₁<sup>j</sup> and V₁<sup>j</sup> coefficients. */
  2267.         private void computeU1V1Coefficients() {
  2268.             // generate the U₁<sup>j</sup> and V₁<sup>j</sup> coefficients
  2269.             // for j >= 1
  2270.             // also the U₁⁰ for Fourier index = 1 (i == 0) coefficient will be computed
  2271.             u1ij[0][0] = 0;
  2272.             for (int j = 1; j <= jMax; j++) {
  2273.                 // compute 1 / j
  2274.                 final double ooj = 1. / j;

  2275.                 for (int i = 0; i < 6; i++) {
  2276.                     //j is aready between 1 and J
  2277.                     u1ij[i][j] = fourierCjSj.getSij(i, j);
  2278.                     v1ij[i][j] = fourierCjSj.getCij(i, j);

  2279.                     // 1 - δ<sub>1j</sub> is 1 for all j > 1
  2280.                     if (j > 1) {
  2281.                         // k starts with 1 because j-J is less than or equal to 0
  2282.                         for (int kIndex = 1; kIndex <= j - 1; kIndex++) {
  2283.                             // C<sub>i</sub><sup>j-k</sup> * σ<sub>k</sub> +
  2284.                             // S<sub>i</sub><sup>j-k</sup> * ρ<sub>k</sub>
  2285.                             u1ij[i][j] +=   fourierCjSj.getCij(i, j - kIndex) * currentRhoSigmaj[1][kIndex] +
  2286.                                             fourierCjSj.getSij(i, j - kIndex) * currentRhoSigmaj[0][kIndex];

  2287.                             // C<sub>i</sub><sup>j-k</sup> * ρ<sub>k</sub> -
  2288.                             // S<sub>i</sub><sup>j-k</sup> * σ<sub>k</sub>
  2289.                             v1ij[i][j] +=   fourierCjSj.getCij(i, j - kIndex) * currentRhoSigmaj[0][kIndex] -
  2290.                                             fourierCjSj.getSij(i, j - kIndex) * currentRhoSigmaj[1][kIndex];
  2291.                         }
  2292.                     }

  2293.                     // since j must be between 1 and J-1 and is already between 1 and J
  2294.                     // the following sum is skiped only for j = jMax
  2295.                     if (j != jMax) {
  2296.                         for (int kIndex = 1; kIndex <= jMax - j; kIndex++) {
  2297.                             // -C<sub>i</sub><sup>j+k</sup> * σ<sub>k</sub> +
  2298.                             // S<sub>i</sub><sup>j+k</sup> * ρ<sub>k</sub>
  2299.                             u1ij[i][j] +=   -fourierCjSj.getCij(i, j + kIndex) * currentRhoSigmaj[1][kIndex] +
  2300.                                             fourierCjSj.getSij(i, j + kIndex) * currentRhoSigmaj[0][kIndex];

  2301.                             // C<sub>i</sub><sup>j+k</sup> * ρ<sub>k</sub> +
  2302.                             // S<sub>i</sub><sup>j+k</sup> * σ<sub>k</sub>
  2303.                             v1ij[i][j] +=   fourierCjSj.getCij(i, j + kIndex) * currentRhoSigmaj[0][kIndex] +
  2304.                                             fourierCjSj.getSij(i, j + kIndex) * currentRhoSigmaj[1][kIndex];
  2305.                         }
  2306.                     }

  2307.                     for (int kIndex = 1; kIndex <= jMax; kIndex++) {
  2308.                         // C<sub>i</sub><sup>k</sup> * σ<sub>j+k</sub> -
  2309.                         // S<sub>i</sub><sup>k</sup> * ρ<sub>j+k</sub>
  2310.                         u1ij[i][j] +=   -fourierCjSj.getCij(i, kIndex) * currentRhoSigmaj[1][j + kIndex] -
  2311.                                         fourierCjSj.getSij(i, kIndex) * currentRhoSigmaj[0][j + kIndex];

  2312.                         // C<sub>i</sub><sup>k</sup> * ρ<sub>j+k</sub> +
  2313.                         // S<sub>i</sub><sup>k</sup> * σ<sub>j+k</sub>
  2314.                         v1ij[i][j] +=   fourierCjSj.getCij(i, kIndex) * currentRhoSigmaj[0][j + kIndex] +
  2315.                                         fourierCjSj.getSij(i, kIndex) * currentRhoSigmaj[1][j + kIndex];
  2316.                     }

  2317.                     // divide by 1 / j
  2318.                     u1ij[i][j] *= -ooj;
  2319.                     v1ij[i][j] *= ooj;

  2320.                     // if index = 1 (i == 0) add the computed terms to U₁⁰
  2321.                     if (i == 0) {
  2322.                         //- (U₁<sup>j</sup> * ρ<sub>j</sub> + V₁<sup>j</sup> * σ<sub>j</sub>
  2323.                         u1ij[0][0] += -u1ij[0][j] * currentRhoSigmaj[0][j] - v1ij[0][j] * currentRhoSigmaj[1][j];
  2324.                     }
  2325.                 }
  2326.             }

  2327.             // Terms with j > jMax are required only when computing the coefficients
  2328.             // U₂<sup>j</sup> and V₂<sup>j</sup>
  2329.             // and those coefficients are only required for Fourier index = 1 (i == 0).
  2330.             for (int j = jMax + 1; j <= 2 * jMax; j++) {
  2331.                 // compute 1 / j
  2332.                 final double ooj = 1. / j;
  2333.                 //the value of i is 0
  2334.                 u1ij[0][j] = 0.;
  2335.                 v1ij[0][j] = 0.;

  2336.                 //k starts from j-J as it is always greater than or equal to 1
  2337.                 for (int kIndex = j - jMax; kIndex <= j - 1; kIndex++) {
  2338.                     // C<sub>i</sub><sup>j-k</sup> * σ<sub>k</sub> +
  2339.                     // S<sub>i</sub><sup>j-k</sup> * ρ<sub>k</sub>
  2340.                     u1ij[0][j] +=   fourierCjSj.getCij(0, j - kIndex) * currentRhoSigmaj[1][kIndex] +
  2341.                                     fourierCjSj.getSij(0, j - kIndex) * currentRhoSigmaj[0][kIndex];

  2342.                     // C<sub>i</sub><sup>j-k</sup> * ρ<sub>k</sub> -
  2343.                     // S<sub>i</sub><sup>j-k</sup> * σ<sub>k</sub>
  2344.                     v1ij[0][j] +=   fourierCjSj.getCij(0, j - kIndex) * currentRhoSigmaj[0][kIndex] -
  2345.                                     fourierCjSj.getSij(0, j - kIndex) * currentRhoSigmaj[1][kIndex];
  2346.                 }
  2347.                 for (int kIndex = 1; kIndex <= jMax; kIndex++) {
  2348.                     // C<sub>i</sub><sup>k</sup> * σ<sub>j+k</sub> -
  2349.                     // S<sub>i</sub><sup>k</sup> * ρ<sub>j+k</sub>
  2350.                     u1ij[0][j] +=   -fourierCjSj.getCij(0, kIndex) * currentRhoSigmaj[1][j + kIndex] -
  2351.                                     fourierCjSj.getSij(0, kIndex) * currentRhoSigmaj[0][j + kIndex];

  2352.                     // C<sub>i</sub><sup>k</sup> * ρ<sub>j+k</sub> +
  2353.                     // S<sub>i</sub><sup>k</sup> * σ<sub>j+k</sub>
  2354.                     v1ij[0][j] +=   fourierCjSj.getCij(0, kIndex) * currentRhoSigmaj[0][j + kIndex] +
  2355.                                     fourierCjSj.getSij(0, kIndex) * currentRhoSigmaj[1][j + kIndex];
  2356.                 }

  2357.                 // divide by 1 / j
  2358.                 u1ij[0][j] *= -ooj;
  2359.                 v1ij[0][j] *= ooj;
  2360.             }
  2361.         }

  2362.         /** Build the U₁<sup>j</sup> and V₁<sup>j</sup> coefficients.
  2363.          * <p>
  2364.          * Only the coefficients for Fourier index = 1 (i == 0) are required.
  2365.          * </p>
  2366.          */
  2367.         private void computeU2V2Coefficients() {
  2368.             for (int j = 1; j <= jMax; j++) {
  2369.                 // compute 1 / j
  2370.                 final double ooj = 1. / j;

  2371.                 // only the values for i == 0 are computed
  2372.                 u2ij[j] = v1ij[0][j];
  2373.                 v2ij[j] = u1ij[0][j];

  2374.                 // 1 - δ<sub>1j</sub> is 1 for all j > 1
  2375.                 if (j > 1) {
  2376.                     for (int l = 1; l <= j - 1; l++) {
  2377.                         // U₁<sup>j-l</sup> * σ<sub>l</sub> +
  2378.                         // V₁<sup>j-l</sup> * ρ<sub>l</sub>
  2379.                         u2ij[j] +=   u1ij[0][j - l] * currentRhoSigmaj[1][l] +
  2380.                                      v1ij[0][j - l] * currentRhoSigmaj[0][l];

  2381.                         // U₁<sup>j-l</sup> * ρ<sub>l</sub> -
  2382.                         // V₁<sup>j-l</sup> * σ<sub>l</sub>
  2383.                         v2ij[j] +=   u1ij[0][j - l] * currentRhoSigmaj[0][l] -
  2384.                                      v1ij[0][j - l] * currentRhoSigmaj[1][l];
  2385.                     }
  2386.                 }

  2387.                 for (int l = 1; l <= jMax; l++) {
  2388.                     // -U₁<sup>j+l</sup> * σ<sub>l</sub> +
  2389.                     // U₁<sup>l</sup> * σ<sub>j+l</sub> +
  2390.                     // V₁<sup>j+l</sup> * ρ<sub>l</sub> -
  2391.                     // V₁<sup>l</sup> * ρ<sub>j+l</sub>
  2392.                     u2ij[j] +=   -u1ij[0][j + l] * currentRhoSigmaj[1][l] +
  2393.                                   u1ij[0][l] * currentRhoSigmaj[1][j + l] +
  2394.                                   v1ij[0][j + l] * currentRhoSigmaj[0][l] -
  2395.                                   v1ij[0][l] * currentRhoSigmaj[0][j + l];

  2396.                     // U₁<sup>j+l</sup> * ρ<sub>l</sub> +
  2397.                     // U₁<sup>l</sup> * ρ<sub>j+l</sub> +
  2398.                     // V₁<sup>j+l</sup> * σ<sub>l</sub> +
  2399.                     // V₁<sup>l</sup> * σ<sub>j+l</sub>
  2400.                     u2ij[j] +=   u1ij[0][j + l] * currentRhoSigmaj[0][l] +
  2401.                                  u1ij[0][l] * currentRhoSigmaj[0][j + l] +
  2402.                                  v1ij[0][j + l] * currentRhoSigmaj[1][l] +
  2403.                                  v1ij[0][l] * currentRhoSigmaj[1][j + l];
  2404.                 }

  2405.                 // divide by 1 / j
  2406.                 u2ij[j] *= -ooj;
  2407.                 v2ij[j] *= ooj;
  2408.             }
  2409.         }

  2410.         /** Get the coefficient U₁<sup>j</sup> for Fourier index i.
  2411.          *
  2412.          * @param j j index
  2413.          * @param i Fourier index (starts at 0)
  2414.          * @return the coefficient U₁<sup>j</sup> for the given Fourier index i
  2415.          */
  2416.         public double getU1(final int j, final int i) {
  2417.             return u1ij[i][j];
  2418.         }

  2419.         /** Get the coefficient V₁<sup>j</sup> for Fourier index i.
  2420.          *
  2421.          * @param j j index
  2422.          * @param i Fourier index (starts at 0)
  2423.          * @return the coefficient V₁<sup>j</sup> for the given Fourier index i
  2424.          */
  2425.         public double getV1(final int j, final int i) {
  2426.             return v1ij[i][j];
  2427.         }

  2428.         /** Get the coefficient U₂<sup>j</sup> for Fourier index = 1 (i == 0).
  2429.          *
  2430.          * @param j j index
  2431.          * @return the coefficient U₂<sup>j</sup> for Fourier index = 1 (i == 0)
  2432.          */
  2433.         public double getU2(final int j) {
  2434.             return u2ij[j];
  2435.         }

  2436.         /** Get the coefficient V₂<sup>j</sup> for Fourier index = 1 (i == 0).
  2437.          *
  2438.          * @param j j index
  2439.          * @return the coefficient V₂<sup>j</sup> for Fourier index = 1 (i == 0)
  2440.          */
  2441.         public double getV2(final int j) {
  2442.             return v2ij[j];
  2443.         }
  2444.     }

  2445.     /** The U<sub>i</sub><sup>j</sup> and V<sub>i</sub><sup>j</sup> coefficients described by
  2446.      * equations 2.5.3-(21) and 2.5.3-(22) from Danielson.
  2447.      * <p>
  2448.      * The index i takes only the values 1 and 2<br>
  2449.      * For U only the index 0 for j is used.
  2450.      * </p>
  2451.      *
  2452.      * @author Petre Bazavan
  2453.      * @author Lucian Barbulescu
  2454.      */
  2455.     private static class FieldUijVijCoefficients <T extends RealFieldElement<T>> {

  2456.         /** The U₁<sup>j</sup> coefficients.
  2457.          * <p>
  2458.          * The first index identifies the Fourier coefficients used<br>
  2459.          * Those coefficients are computed for all Fourier C<sub>i</sub><sup>j</sup> and S<sub>i</sub><sup>j</sup><br>
  2460.          * The only exception is when j = 0 when only the coefficient for fourier index = 1 (i == 0) is needed.<br>
  2461.          * Also, for fourier index = 1 (i == 0), the coefficients up to 2 * jMax are computed, because are required
  2462.          * to compute the coefficients U₂<sup>j</sup>
  2463.          * </p>
  2464.          */
  2465.         private final T[][] u1ij;

  2466.         /** The V₁<sup>j</sup> coefficients.
  2467.          * <p>
  2468.          * The first index identifies the Fourier coefficients used<br>
  2469.          * Those coefficients are computed for all Fourier C<sub>i</sub><sup>j</sup> and S<sub>i</sub><sup>j</sup><br>
  2470.          * for fourier index = 1 (i == 0), the coefficients up to 2 * jMax are computed, because are required
  2471.          * to compute the coefficients V₂<sup>j</sup>
  2472.          * </p>
  2473.          */
  2474.         private final T[][] v1ij;

  2475.         /** The U₂<sup>j</sup> coefficients.
  2476.          * <p>
  2477.          * Only the coefficients that use the Fourier index = 1 (i == 0) are computed as they are the only ones required.
  2478.          * </p>
  2479.          */
  2480.         private final T[] u2ij;

  2481.         /** The V₂<sup>j</sup> coefficients.
  2482.          * <p>
  2483.          * Only the coefficients that use the Fourier index = 1 (i == 0) are computed as they are the only ones required.
  2484.          * </p>
  2485.          */
  2486.         private final T[] v2ij;

  2487.         /** The current computed values for the ρ<sub>j</sub> and σ<sub>j</sub> coefficients. */
  2488.         private final T[][] currentRhoSigmaj;

  2489.         /** The C<sub>i</sub><sup>j</sup> and the S<sub>i</sub><sup>j</sup> Fourier coefficients. */
  2490.         private final FieldFourierCjSjCoefficients<T> fourierCjSj;

  2491.         /** The maximum value for j index. */
  2492.         private final int jMax;

  2493.         /** Constructor.
  2494.          * @param currentRhoSigmaj the current computed values for the ρ<sub>j</sub> and σ<sub>j</sub> coefficients
  2495.          * @param fourierCjSj the fourier coefficients C<sub>i</sub><sup>j</sup> and the S<sub>i</sub><sup>j</sup>
  2496.          * @param jMax maximum value for j index
  2497.          * @param field field used by default
  2498.          */
  2499.         FieldUijVijCoefficients(final T[][] currentRhoSigmaj,
  2500.                                 final FieldFourierCjSjCoefficients<T> fourierCjSj,
  2501.                                 final int jMax,
  2502.                                 final Field<T> field) {
  2503.             this.currentRhoSigmaj = currentRhoSigmaj;
  2504.             this.fourierCjSj = fourierCjSj;
  2505.             this.jMax = jMax;

  2506.             // initialize the internal arrays.
  2507.             this.u1ij = MathArrays.buildArray(field, 6, 2 * jMax + 1);
  2508.             this.v1ij = MathArrays.buildArray(field, 6, 2 * jMax + 1);
  2509.             this.u2ij = MathArrays.buildArray(field, jMax + 1);
  2510.             this.v2ij = MathArrays.buildArray(field, jMax + 1);

  2511.             //compute the coefficients
  2512.             computeU1V1Coefficients(field);
  2513.             computeU2V2Coefficients(field);
  2514.         }

  2515.         /** Build the U₁<sup>j</sup> and V₁<sup>j</sup> coefficients.
  2516.          * @param field field used by default
  2517.          */
  2518.         private void computeU1V1Coefficients(final Field<T> field) {
  2519.             // Zero
  2520.             final T zero = field.getZero();

  2521.             // generate the U₁<sup>j</sup> and V₁<sup>j</sup> coefficients
  2522.             // for j >= 1
  2523.             // also the U₁⁰ for Fourier index = 1 (i == 0) coefficient will be computed
  2524.             u1ij[0][0] = zero;
  2525.             for (int j = 1; j <= jMax; j++) {
  2526.                 // compute 1 / j
  2527.                 final double ooj = 1. / j;

  2528.                 for (int i = 0; i < 6; i++) {
  2529.                     //j is aready between 1 and J
  2530.                     u1ij[i][j] = fourierCjSj.getSij(i, j);
  2531.                     v1ij[i][j] = fourierCjSj.getCij(i, j);

  2532.                     // 1 - δ<sub>1j</sub> is 1 for all j > 1
  2533.                     if (j > 1) {
  2534.                         // k starts with 1 because j-J is less than or equal to 0
  2535.                         for (int kIndex = 1; kIndex <= j - 1; kIndex++) {
  2536.                             // C<sub>i</sub><sup>j-k</sup> * σ<sub>k</sub> +
  2537.                             // S<sub>i</sub><sup>j-k</sup> * ρ<sub>k</sub>
  2538.                             u1ij[i][j] = u1ij[i][j].add(fourierCjSj.getCij(i, j - kIndex).multiply(currentRhoSigmaj[1][kIndex]).
  2539.                                          add(fourierCjSj.getSij(i, j - kIndex).multiply(currentRhoSigmaj[0][kIndex])));

  2540.                             // C<sub>i</sub><sup>j-k</sup> * ρ<sub>k</sub> -
  2541.                             // S<sub>i</sub><sup>j-k</sup> * σ<sub>k</sub>
  2542.                             v1ij[i][j] = v1ij[i][j].add(fourierCjSj.getCij(i, j - kIndex).multiply(currentRhoSigmaj[0][kIndex]).
  2543.                                          subtract(fourierCjSj.getSij(i, j - kIndex).multiply(currentRhoSigmaj[1][kIndex])));
  2544.                         }
  2545.                     }

  2546.                     // since j must be between 1 and J-1 and is already between 1 and J
  2547.                     // the following sum is skiped only for j = jMax
  2548.                     if (j != jMax) {
  2549.                         for (int kIndex = 1; kIndex <= jMax - j; kIndex++) {
  2550.                             // -C<sub>i</sub><sup>j+k</sup> * σ<sub>k</sub> +
  2551.                             // S<sub>i</sub><sup>j+k</sup> * ρ<sub>k</sub>
  2552.                             u1ij[i][j] = u1ij[i][j].add(fourierCjSj.getCij(i, j + kIndex).negate().multiply(currentRhoSigmaj[1][kIndex]).
  2553.                                          add(fourierCjSj.getSij(i, j + kIndex).multiply(currentRhoSigmaj[0][kIndex])));

  2554.                             // C<sub>i</sub><sup>j+k</sup> * ρ<sub>k</sub> +
  2555.                             // S<sub>i</sub><sup>j+k</sup> * σ<sub>k</sub>
  2556.                             v1ij[i][j] = v1ij[i][j].add(fourierCjSj.getCij(i, j + kIndex).multiply(currentRhoSigmaj[0][kIndex]).
  2557.                                          add(fourierCjSj.getSij(i, j + kIndex).multiply(currentRhoSigmaj[1][kIndex])));
  2558.                         }
  2559.                     }

  2560.                     for (int kIndex = 1; kIndex <= jMax; kIndex++) {
  2561.                         // C<sub>i</sub><sup>k</sup> * σ<sub>j+k</sub> -
  2562.                         // S<sub>i</sub><sup>k</sup> * ρ<sub>j+k</sub>
  2563.                         u1ij[i][j] = u1ij[i][j].add(fourierCjSj.getCij(i, kIndex).negate().multiply(currentRhoSigmaj[1][j + kIndex]).
  2564.                                      subtract(fourierCjSj.getSij(i, kIndex).multiply(currentRhoSigmaj[0][j + kIndex])));

  2565.                         // C<sub>i</sub><sup>k</sup> * ρ<sub>j+k</sub> +
  2566.                         // S<sub>i</sub><sup>k</sup> * σ<sub>j+k</sub>
  2567.                         v1ij[i][j] = v1ij[i][j].add(fourierCjSj.getCij(i, kIndex).multiply(currentRhoSigmaj[0][j + kIndex]).
  2568.                                      add(fourierCjSj.getSij(i, kIndex).multiply(currentRhoSigmaj[1][j + kIndex])));
  2569.                     }

  2570.                     // divide by 1 / j
  2571.                     u1ij[i][j] = u1ij[i][j].multiply(-ooj);
  2572.                     v1ij[i][j] = v1ij[i][j].multiply(ooj);

  2573.                     // if index = 1 (i == 0) add the computed terms to U₁⁰
  2574.                     if (i == 0) {
  2575.                         //- (U₁<sup>j</sup> * ρ<sub>j</sub> + V₁<sup>j</sup> * σ<sub>j</sub>
  2576.                         u1ij[0][0] = u1ij[0][0].add(u1ij[0][j].negate().multiply(currentRhoSigmaj[0][j]).subtract(v1ij[0][j].multiply(currentRhoSigmaj[1][j])));
  2577.                     }
  2578.                 }
  2579.             }

  2580.             // Terms with j > jMax are required only when computing the coefficients
  2581.             // U₂<sup>j</sup> and V₂<sup>j</sup>
  2582.             // and those coefficients are only required for Fourier index = 1 (i == 0).
  2583.             for (int j = jMax + 1; j <= 2 * jMax; j++) {
  2584.                 // compute 1 / j
  2585.                 final double ooj = 1. / j;
  2586.                 //the value of i is 0
  2587.                 u1ij[0][j] = zero;
  2588.                 v1ij[0][j] = zero;

  2589.                 //k starts from j-J as it is always greater than or equal to 1
  2590.                 for (int kIndex = j - jMax; kIndex <= j - 1; kIndex++) {
  2591.                     // C<sub>i</sub><sup>j-k</sup> * σ<sub>k</sub> +
  2592.                     // S<sub>i</sub><sup>j-k</sup> * ρ<sub>k</sub>
  2593.                     u1ij[0][j] = u1ij[0][j].add(fourierCjSj.getCij(0, j - kIndex).multiply(currentRhoSigmaj[1][kIndex]).
  2594.                                  add(fourierCjSj.getSij(0, j - kIndex).multiply(currentRhoSigmaj[0][kIndex])));

  2595.                     // C<sub>i</sub><sup>j-k</sup> * ρ<sub>k</sub> -
  2596.                     // S<sub>i</sub><sup>j-k</sup> * σ<sub>k</sub>
  2597.                     v1ij[0][j] = v1ij[0][j].add(fourierCjSj.getCij(0, j - kIndex).multiply(currentRhoSigmaj[0][kIndex]).
  2598.                                  subtract(fourierCjSj.getSij(0, j - kIndex).multiply(currentRhoSigmaj[1][kIndex])));
  2599.                 }
  2600.                 for (int kIndex = 1; kIndex <= jMax; kIndex++) {
  2601.                     // C<sub>i</sub><sup>k</sup> * σ<sub>j+k</sub> -
  2602.                     // S<sub>i</sub><sup>k</sup> * ρ<sub>j+k</sub>
  2603.                     u1ij[0][j] = u1ij[0][j].add(fourierCjSj.getCij(0, kIndex).negate().multiply(currentRhoSigmaj[1][j + kIndex]).
  2604.                                  subtract(fourierCjSj.getSij(0, kIndex).multiply(currentRhoSigmaj[0][j + kIndex])));

  2605.                     // C<sub>i</sub><sup>k</sup> * ρ<sub>j+k</sub> +
  2606.                     // S<sub>i</sub><sup>k</sup> * σ<sub>j+k</sub>
  2607.                     v1ij[0][j] = v1ij[0][j].add(fourierCjSj.getCij(0, kIndex).multiply(currentRhoSigmaj[0][j + kIndex]).add(
  2608.                                  fourierCjSj.getSij(0, kIndex).multiply(currentRhoSigmaj[1][j + kIndex])));
  2609.                 }

  2610.                 // divide by 1 / j
  2611.                 u1ij[0][j] = u1ij[0][j].multiply(-ooj);
  2612.                 v1ij[0][j] = v1ij[0][j].multiply(ooj);
  2613.             }
  2614.         }

  2615.         /** Build the U₁<sup>j</sup> and V₁<sup>j</sup> coefficients.
  2616.          * <p>
  2617.          * Only the coefficients for Fourier index = 1 (i == 0) are required.
  2618.          * </p>
  2619.          * @param field field used by default
  2620.          */
  2621.         private void computeU2V2Coefficients(final Field<T> field) {
  2622.             for (int j = 1; j <= jMax; j++) {
  2623.                 // compute 1 / j
  2624.                 final double ooj = 1. / j;

  2625.                 // only the values for i == 0 are computed
  2626.                 u2ij[j] = v1ij[0][j];
  2627.                 v2ij[j] = u1ij[0][j];

  2628.                 // 1 - δ<sub>1j</sub> is 1 for all j > 1
  2629.                 if (j > 1) {
  2630.                     for (int l = 1; l <= j - 1; l++) {
  2631.                         // U₁<sup>j-l</sup> * σ<sub>l</sub> +
  2632.                         // V₁<sup>j-l</sup> * ρ<sub>l</sub>
  2633.                         u2ij[j] = u2ij[j].add(u1ij[0][j - l].multiply(currentRhoSigmaj[1][l]).
  2634.                                   add(v1ij[0][j - l].multiply(currentRhoSigmaj[0][l])));

  2635.                         // U₁<sup>j-l</sup> * ρ<sub>l</sub> -
  2636.                         // V₁<sup>j-l</sup> * σ<sub>l</sub>
  2637.                         v2ij[j] = v2ij[j].add(u1ij[0][j - l].multiply(currentRhoSigmaj[0][l]).
  2638.                                   subtract(v1ij[0][j - l].multiply(currentRhoSigmaj[1][l])));
  2639.                     }
  2640.                 }

  2641.                 for (int l = 1; l <= jMax; l++) {
  2642.                     // -U₁<sup>j+l</sup> * σ<sub>l</sub> +
  2643.                     // U₁<sup>l</sup> * σ<sub>j+l</sub> +
  2644.                     // V₁<sup>j+l</sup> * ρ<sub>l</sub> -
  2645.                     // V₁<sup>l</sup> * ρ<sub>j+l</sub>
  2646.                     u2ij[j] = u2ij[j].add(u1ij[0][j + l].negate().multiply(currentRhoSigmaj[1][l]).
  2647.                               add(u1ij[0][l].multiply(currentRhoSigmaj[1][j + l])).
  2648.                               add(v1ij[0][j + l].multiply(currentRhoSigmaj[0][l])).
  2649.                               subtract(v1ij[0][l].multiply(currentRhoSigmaj[0][j + l])));

  2650.                     // U₁<sup>j+l</sup> * ρ<sub>l</sub> +
  2651.                     // U₁<sup>l</sup> * ρ<sub>j+l</sub> +
  2652.                     // V₁<sup>j+l</sup> * σ<sub>l</sub> +
  2653.                     // V₁<sup>l</sup> * σ<sub>j+l</sub>
  2654.                     u2ij[j] = u2ij[j].add(u1ij[0][j + l].multiply(currentRhoSigmaj[0][l]).
  2655.                               add(u1ij[0][l].multiply(currentRhoSigmaj[0][j + l])).
  2656.                               add(v1ij[0][j + l].multiply(currentRhoSigmaj[1][l])).
  2657.                               add(v1ij[0][l].multiply(currentRhoSigmaj[1][j + l])));
  2658.                 }

  2659.                 // divide by 1 / j
  2660.                 u2ij[j] = u2ij[j].multiply(-ooj);
  2661.                 v2ij[j] = v2ij[j].multiply(ooj);
  2662.             }
  2663.         }

  2664.         /** Get the coefficient U₁<sup>j</sup> for Fourier index i.
  2665.          *
  2666.          * @param j j index
  2667.          * @param i Fourier index (starts at 0)
  2668.          * @return the coefficient U₁<sup>j</sup> for the given Fourier index i
  2669.          */
  2670.         public T getU1(final int j, final int i) {
  2671.             return u1ij[i][j];
  2672.         }

  2673.         /** Get the coefficient V₁<sup>j</sup> for Fourier index i.
  2674.          *
  2675.          * @param j j index
  2676.          * @param i Fourier index (starts at 0)
  2677.          * @return the coefficient V₁<sup>j</sup> for the given Fourier index i
  2678.          */
  2679.         public T getV1(final int j, final int i) {
  2680.             return v1ij[i][j];
  2681.         }

  2682.         /** Get the coefficient U₂<sup>j</sup> for Fourier index = 1 (i == 0).
  2683.          *
  2684.          * @param j j index
  2685.          * @return the coefficient U₂<sup>j</sup> for Fourier index = 1 (i == 0)
  2686.          */
  2687.         public T getU2(final int j) {
  2688.             return u2ij[j];
  2689.         }

  2690.         /** Get the coefficient V₂<sup>j</sup> for Fourier index = 1 (i == 0).
  2691.          *
  2692.          * @param j j index
  2693.          * @return the coefficient V₂<sup>j</sup> for Fourier index = 1 (i == 0)
  2694.          */
  2695.         public T getV2(final int j) {
  2696.             return v2ij[j];
  2697.         }
  2698.     }

  2699.     /** Coefficients valid for one time slot. */
  2700.     private static class Slot {

  2701.         /**The coefficients D<sub>i</sub><sup>j</sup>.
  2702.          * <p>
  2703.          * Only for j = 1 and j = 2 the coefficients are not 0. <br>
  2704.          * i corresponds to the equinoctial element, as follows:
  2705.          * - i=0 for a <br/>
  2706.          * - i=1 for k <br/>
  2707.          * - i=2 for h <br/>
  2708.          * - i=3 for q <br/>
  2709.          * - i=4 for p <br/>
  2710.          * - i=5 for λ <br/>
  2711.          * </p>
  2712.          */
  2713.         private final ShortPeriodicsInterpolatedCoefficient[] dij;

  2714.         /** The coefficients C<sub>i</sub><sup>j</sup>.
  2715.          * <p>
  2716.          * The index order is cij[j][i] <br/>
  2717.          * i corresponds to the equinoctial element, as follows: <br/>
  2718.          * - i=0 for a <br/>
  2719.          * - i=1 for k <br/>
  2720.          * - i=2 for h <br/>
  2721.          * - i=3 for q <br/>
  2722.          * - i=4 for p <br/>
  2723.          * - i=5 for λ <br/>
  2724.          * </p>
  2725.          */
  2726.         private final ShortPeriodicsInterpolatedCoefficient[] cij;

  2727.         /** The coefficients S<sub>i</sub><sup>j</sup>.
  2728.          * <p>
  2729.          * The index order is sij[j][i] <br/>
  2730.          * i corresponds to the equinoctial element, as follows: <br/>
  2731.          * - i=0 for a <br/>
  2732.          * - i=1 for k <br/>
  2733.          * - i=2 for h <br/>
  2734.          * - i=3 for q <br/>
  2735.          * - i=4 for p <br/>
  2736.          * - i=5 for λ <br/>
  2737.          * </p>
  2738.          */
  2739.         private final ShortPeriodicsInterpolatedCoefficient[] sij;

  2740.         /** Simple constructor.
  2741.          *  @param jMax maximum value for j index
  2742.          *  @param interpolationPoints number of points used in the interpolation process
  2743.          */
  2744.         Slot(final int jMax, final int interpolationPoints) {

  2745.             dij = new ShortPeriodicsInterpolatedCoefficient[3];
  2746.             cij = new ShortPeriodicsInterpolatedCoefficient[jMax + 1];
  2747.             sij = new ShortPeriodicsInterpolatedCoefficient[jMax + 1];

  2748.             // Initialize the C<sub>i</sub><sup>j</sup>, S<sub>i</sub><sup>j</sup> and D<sub>i</sub><sup>j</sup> coefficients
  2749.             for (int j = 0; j <= jMax; j++) {
  2750.                 cij[j] = new ShortPeriodicsInterpolatedCoefficient(interpolationPoints);
  2751.                 if (j > 0) {
  2752.                     sij[j] = new ShortPeriodicsInterpolatedCoefficient(interpolationPoints);
  2753.                 }
  2754.                 // Initialize only the non-zero D<sub>i</sub><sup>j</sup> coefficients
  2755.                 if (j == 1 || j == 2) {
  2756.                     dij[j] = new ShortPeriodicsInterpolatedCoefficient(interpolationPoints);
  2757.                 }
  2758.             }

  2759.         }

  2760.     }

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

  2763.         /**The coefficients D<sub>i</sub><sup>j</sup>.
  2764.          * <p>
  2765.          * Only for j = 1 and j = 2 the coefficients are not 0. <br>
  2766.          * i corresponds to the equinoctial element, as follows:
  2767.          * - i=0 for a <br/>
  2768.          * - i=1 for k <br/>
  2769.          * - i=2 for h <br/>
  2770.          * - i=3 for q <br/>
  2771.          * - i=4 for p <br/>
  2772.          * - i=5 for λ <br/>
  2773.          * </p>
  2774.          */
  2775.         private final FieldShortPeriodicsInterpolatedCoefficient<T>[] dij;

  2776.         /** The coefficients C<sub>i</sub><sup>j</sup>.
  2777.          * <p>
  2778.          * The index order is cij[j][i] <br/>
  2779.          * i corresponds to the equinoctial element, as follows: <br/>
  2780.          * - i=0 for a <br/>
  2781.          * - i=1 for k <br/>
  2782.          * - i=2 for h <br/>
  2783.          * - i=3 for q <br/>
  2784.          * - i=4 for p <br/>
  2785.          * - i=5 for λ <br/>
  2786.          * </p>
  2787.          */
  2788.         private final FieldShortPeriodicsInterpolatedCoefficient<T>[] cij;

  2789.         /** The coefficients S<sub>i</sub><sup>j</sup>.
  2790.          * <p>
  2791.          * The index order is sij[j][i] <br/>
  2792.          * i corresponds to the equinoctial element, as follows: <br/>
  2793.          * - i=0 for a <br/>
  2794.          * - i=1 for k <br/>
  2795.          * - i=2 for h <br/>
  2796.          * - i=3 for q <br/>
  2797.          * - i=4 for p <br/>
  2798.          * - i=5 for λ <br/>
  2799.          * </p>
  2800.          */
  2801.         private final FieldShortPeriodicsInterpolatedCoefficient<T>[] sij;

  2802.         /** Simple constructor.
  2803.          *  @param jMax maximum value for j index
  2804.          *  @param interpolationPoints number of points used in the interpolation process
  2805.          */
  2806.         @SuppressWarnings("unchecked")
  2807.         FieldSlot(final int jMax, final int interpolationPoints) {

  2808.             dij = (FieldShortPeriodicsInterpolatedCoefficient<T>[]) Array.newInstance(FieldShortPeriodicsInterpolatedCoefficient.class, 3);
  2809.             cij = (FieldShortPeriodicsInterpolatedCoefficient<T>[]) Array.newInstance(FieldShortPeriodicsInterpolatedCoefficient.class, jMax + 1);
  2810.             sij = (FieldShortPeriodicsInterpolatedCoefficient<T>[]) Array.newInstance(FieldShortPeriodicsInterpolatedCoefficient.class, jMax + 1);

  2811.             // Initialize the C<sub>i</sub><sup>j</sup>, S<sub>i</sub><sup>j</sup> and D<sub>i</sub><sup>j</sup> coefficients
  2812.             for (int j = 0; j <= jMax; j++) {
  2813.                 cij[j] = new FieldShortPeriodicsInterpolatedCoefficient<>(interpolationPoints);
  2814.                 if (j > 0) {
  2815.                     sij[j] = new FieldShortPeriodicsInterpolatedCoefficient<>(interpolationPoints);
  2816.                 }
  2817.                 // Initialize only the non-zero D<sub>i</sub><sup>j</sup> coefficients
  2818.                 if (j == 1 || j == 2) {
  2819.                     dij[j] = new FieldShortPeriodicsInterpolatedCoefficient<>(interpolationPoints);
  2820.                 }
  2821.             }

  2822.         }

  2823.     }

  2824. }