AbstractGaussianContribution.java

  1. /* Copyright 2002-2025 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 org.hipparchus.CalculusFieldElement;
  19. import org.hipparchus.Field;
  20. import org.hipparchus.analysis.CalculusFieldUnivariateVectorFunction;
  21. import org.hipparchus.analysis.UnivariateVectorFunction;
  22. import org.hipparchus.geometry.euclidean.threed.FieldRotation;
  23. import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
  24. import org.hipparchus.geometry.euclidean.threed.Rotation;
  25. import org.hipparchus.geometry.euclidean.threed.Vector3D;
  26. import org.hipparchus.util.FastMath;
  27. import org.hipparchus.util.FieldSinCos;
  28. import org.hipparchus.util.MathArrays;
  29. import org.hipparchus.util.SinCos;
  30. import org.orekit.attitudes.Attitude;
  31. import org.orekit.attitudes.AttitudeProvider;
  32. import org.orekit.attitudes.FieldAttitude;
  33. import org.orekit.forces.ForceModel;
  34. import org.orekit.orbits.EquinoctialOrbit;
  35. import org.orekit.orbits.FieldEquinoctialOrbit;
  36. import org.orekit.orbits.FieldOrbit;
  37. import org.orekit.orbits.Orbit;
  38. import org.orekit.orbits.OrbitType;
  39. import org.orekit.orbits.PositionAngleType;
  40. import org.orekit.propagation.FieldSpacecraftState;
  41. import org.orekit.propagation.PropagationType;
  42. import org.orekit.propagation.SpacecraftState;
  43. import org.orekit.propagation.semianalytical.dsst.utilities.AuxiliaryElements;
  44. import org.orekit.propagation.semianalytical.dsst.utilities.CjSjCoefficient;
  45. import org.orekit.propagation.semianalytical.dsst.utilities.FieldAuxiliaryElements;
  46. import org.orekit.propagation.semianalytical.dsst.utilities.FieldCjSjCoefficient;
  47. import org.orekit.propagation.semianalytical.dsst.utilities.FieldShortPeriodicsInterpolatedCoefficient;
  48. import org.orekit.propagation.semianalytical.dsst.utilities.ShortPeriodicsInterpolatedCoefficient;
  49. import org.orekit.time.AbsoluteDate;
  50. import org.orekit.time.FieldAbsoluteDate;
  51. import org.orekit.utils.FieldTimeSpanMap;
  52. import org.orekit.utils.ParameterDriver;
  53. import org.orekit.utils.TimeSpanMap;

  54. import java.lang.reflect.Array;
  55. import java.util.ArrayList;
  56. import java.util.Collections;
  57. import java.util.HashMap;
  58. import java.util.List;
  59. import java.util.Map;
  60. import java.util.Set;

  61. /**
  62.  * Common handling of {@link DSSTForceModel} methods for Gaussian contributions
  63.  * to DSST propagation.
  64.  * <p>
  65.  * This abstract class allows to provide easily a subset of
  66.  * {@link DSSTForceModel} methods for specific Gaussian contributions.
  67.  * </p>
  68.  * <p>
  69.  * This class implements the notion of numerical averaging of the DSST theory.
  70.  * Numerical averaging is mainly used for non-conservative disturbing forces
  71.  * such as atmospheric drag and solar radiation pressure.
  72.  * </p>
  73.  * <p>
  74.  * Gaussian contributions can be expressed as: da<sub>i</sub>/dt =
  75.  * δa<sub>i</sub>/δv . q<br>
  76.  * where:
  77.  * <ul>
  78.  * <li>a<sub>i</sub> are the six equinoctial elements</li>
  79.  * <li>v is the velocity vector</li>
  80.  * <li>q is the perturbing acceleration due to the considered force</li>
  81.  * </ul>
  82.  *
  83.  * <p>
  84.  * The averaging process and other considerations lead to integrate this
  85.  * contribution over the true longitude L possibly taking into account some
  86.  * limits.
  87.  *
  88.  * <p>
  89.  * To create a numerically averaged contribution, one needs only to provide a
  90.  * {@link ForceModel} and to implement in the derived class the methods:
  91.  * {@link #getLLimits(SpacecraftState, AuxiliaryElements)} and
  92.  * {@link #getParametersDriversWithoutMu()}.
  93.  * </p>
  94.  * @author Pascal Parraud
  95.  * @author Bryan Cazabonne (field translation)
  96.  */
  97. public abstract class AbstractGaussianContribution implements DSSTForceModel {

  98.     /**
  99.      * Retrograde factor I.
  100.      * <p>
  101.      * DSST model needs equinoctial orbit as internal representation. Classical
  102.      * equinoctial elements have discontinuities when inclination is close to zero.
  103.      * In this representation, I = +1. <br>
  104.      * To avoid this discontinuity, another representation exists and equinoctial
  105.      * elements can be expressed in a different way, called "retrograde" orbit. This
  106.      * implies I = -1. <br>
  107.      * As Orekit doesn't implement the retrograde orbit, I is always set to +1. But
  108.      * for the sake of consistency with the theory, the retrograde factor has been
  109.      * kept in the formulas.
  110.      * </p>
  111.      */
  112.     private static final int I = 1;

  113.     /**
  114.      * Central attraction scaling factor.
  115.      * <p>
  116.      * We use a power of 2 to avoid numeric noise introduction in the
  117.      * multiplications/divisions sequences.
  118.      * </p>
  119.      */
  120.     private static final double MU_SCALE = FastMath.scalb(1.0, 32);

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

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

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

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

  129.     /** Contribution to be numerically averaged. */
  130.     private final ForceModel contribution;

  131.     /** Gauss integrator. */
  132.     private final double threshold;

  133.     /** Gauss integrator. */
  134.     private GaussQuadrature integrator;

  135.     /** Flag for Gauss order computation. */
  136.     private boolean isDirty;

  137.     /** Attitude provider. */
  138.     private AttitudeProvider attitudeProvider;

  139.     /** Prefix for coefficients keys. */
  140.     private final String coefficientsKeyPrefix;

  141.     /** Short period terms. */
  142.     private GaussianShortPeriodicCoefficients gaussianSPCoefs;

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

  145.     /** Driver for gravitational parameter. */
  146.     private final ParameterDriver gmParameterDriver;

  147.     /**
  148.      * Build a new instance.
  149.      * @param coefficientsKeyPrefix prefix for coefficients keys
  150.      * @param threshold             tolerance for the choice of the Gauss quadrature
  151.      *                              order
  152.      * @param contribution          the {@link ForceModel} to be numerically
  153.      *                              averaged
  154.      * @param mu                    central attraction coefficient
  155.      */
  156.     protected AbstractGaussianContribution(final String coefficientsKeyPrefix, final double threshold,
  157.             final ForceModel contribution, final double mu) {

  158.         gmParameterDriver = new ParameterDriver(DSSTNewtonianAttraction.CENTRAL_ATTRACTION_COEFFICIENT, mu, MU_SCALE,
  159.                 0.0, Double.POSITIVE_INFINITY);

  160.         this.coefficientsKeyPrefix = coefficientsKeyPrefix;
  161.         this.contribution = contribution;
  162.         this.threshold = threshold;
  163.         this.integrator = new GaussQuadrature(GAUSS_ORDER[MAX_ORDER_RANK]);
  164.         this.isDirty = true;

  165.         gaussianFieldSPCoefs = new HashMap<>();
  166.     }

  167.     /** {@inheritDoc} */
  168.     @Override
  169.     public void init(final SpacecraftState initialState, final AbsoluteDate target) {
  170.         // Initialize the numerical force model
  171.         contribution.init(initialState, target);
  172.     }

  173.     /** {@inheritDoc} */
  174.     @Override
  175.     public <T extends CalculusFieldElement<T>> void init(final FieldSpacecraftState<T> initialState, final FieldAbsoluteDate<T> target) {
  176.         // Initialize the numerical force model
  177.         contribution.init(initialState, target);
  178.     }

  179.     /** {@inheritDoc} */
  180.     @Override
  181.     public List<ParameterDriver> getParametersDrivers() {
  182.         // Initialize drivers (without central attraction coefficient driver)
  183.         final List<ParameterDriver> drivers = new ArrayList<>(getParametersDriversWithoutMu());
  184.         // We put central attraction coefficient driver at the end of the array
  185.         drivers.add(gmParameterDriver);
  186.         return drivers;
  187.     }

  188.     /**
  189.      * Get the drivers for force model parameters except the one for the central
  190.      * attraction coefficient.
  191.      * <p>
  192.      * The driver for central attraction coefficient is automatically added at the
  193.      * last element of the {@link ParameterDriver} array into
  194.      * {@link #getParametersDrivers()} method.
  195.      * </p>
  196.      * @return drivers for force model parameters
  197.      */
  198.     protected abstract List<ParameterDriver> getParametersDriversWithoutMu();

  199.     /** {@inheritDoc} */
  200.     @Override
  201.     public List<ShortPeriodTerms> initializeShortPeriodTerms(final AuxiliaryElements auxiliaryElements, final PropagationType type,
  202.             final double[] parameters) {

  203.         final List<ShortPeriodTerms> list = new ArrayList<>();
  204.         gaussianSPCoefs = new GaussianShortPeriodicCoefficients(coefficientsKeyPrefix, JMAX, INTERPOLATION_POINTS,
  205.                 new TimeSpanMap<>(new Slot(JMAX, INTERPOLATION_POINTS)));
  206.         list.add(gaussianSPCoefs);
  207.         return list;

  208.     }

  209.     /** {@inheritDoc} */
  210.     @Override
  211.     public <T extends CalculusFieldElement<T>> List<FieldShortPeriodTerms<T>> initializeShortPeriodTerms(
  212.             final FieldAuxiliaryElements<T> auxiliaryElements, final PropagationType type, final T[] parameters) {

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

  214.         final FieldGaussianShortPeriodicCoefficients<T> fgspc = new FieldGaussianShortPeriodicCoefficients<>(
  215.                 coefficientsKeyPrefix, JMAX, INTERPOLATION_POINTS,
  216.                 new FieldTimeSpanMap<>(new FieldSlot<>(JMAX, INTERPOLATION_POINTS), field));
  217.         gaussianFieldSPCoefs.put(field, fgspc);
  218.         return Collections.singletonList(fgspc);
  219.     }

  220.     /**
  221.      * Performs initialization at each integration step for the current force model.
  222.      * <p>
  223.      * This method aims at being called before mean elements rates computation.
  224.      * </p>
  225.      * @param auxiliaryElements auxiliary elements related to the current orbit
  226.      * @param parameters        parameters values of the force model parameters
  227.      *                          only 1 value for each parameterDriver
  228.      * @return new force model context
  229.      */
  230.     private AbstractGaussianContributionContext initializeStep(final AuxiliaryElements auxiliaryElements,
  231.             final double[] parameters) {
  232.         return new AbstractGaussianContributionContext(auxiliaryElements, parameters);
  233.     }

  234.     /**
  235.      * Performs initialization at each integration step for the current force model.
  236.      * <p>
  237.      * This method aims at being called before mean elements rates computation.
  238.      * </p>
  239.      * @param <T>               type of the elements
  240.      * @param auxiliaryElements auxiliary elements related to the current orbit
  241.      * @param parameters        parameters values of the force model parameters
  242.      *                          (only 1 values for each parameters corresponding
  243.      *                          to state date) by getting the parameters for a specific date.
  244.      * @return new force model context
  245.      */
  246.     private <T extends CalculusFieldElement<T>> FieldAbstractGaussianContributionContext<T> initializeStep(
  247.             final FieldAuxiliaryElements<T> auxiliaryElements, final T[] parameters) {
  248.         return new FieldAbstractGaussianContributionContext<>(auxiliaryElements, parameters);
  249.     }

  250.     /** {@inheritDoc} */
  251.     @Override
  252.     public double[] getMeanElementRate(final SpacecraftState state, final AuxiliaryElements auxiliaryElements,
  253.             final double[] parameters) {

  254.         // Container for attributes

  255.         final AbstractGaussianContributionContext context = initializeStep(auxiliaryElements, parameters);
  256.         double[] meanElementRate = new double[6];
  257.         // Computes the limits for the integral
  258.         final double[] ll = getLLimits(state, auxiliaryElements);
  259.         // Computes integrated mean element rates if Llow < Lhigh
  260.         if (ll[0] < ll[1]) {
  261.             meanElementRate = getMeanElementRate(state, integrator, ll[0], ll[1], context, parameters);
  262.             if (isDirty) {
  263.                 boolean next = true;
  264.                 for (int i = 0; i < MAX_ORDER_RANK && next; i++) {
  265.                     final double[] meanRates = getMeanElementRate(state, new GaussQuadrature(GAUSS_ORDER[i]), ll[0],
  266.                             ll[1], context, parameters);
  267.                     if (getRatesDiff(meanElementRate, meanRates, context) < threshold) {
  268.                         integrator = new GaussQuadrature(GAUSS_ORDER[i]);
  269.                         next = false;
  270.                     }
  271.                 }
  272.                 isDirty = false;
  273.             }
  274.         }
  275.         return meanElementRate;
  276.     }

  277.     /** {@inheritDoc} */
  278.     @Override
  279.     public <T extends CalculusFieldElement<T>> T[] getMeanElementRate(final FieldSpacecraftState<T> state,
  280.             final FieldAuxiliaryElements<T> auxiliaryElements, final T[] parameters) {

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

  284.         T[] meanElementRate = MathArrays.buildArray(field, 6);
  285.         // Computes the limits for the integral
  286.         final T[] ll = getLLimits(state, auxiliaryElements);
  287.         // Computes integrated mean element rates if Llow < Lhigh
  288.         if (ll[0].getReal() < ll[1].getReal()) {
  289.             meanElementRate = getMeanElementRate(state, integrator, ll[0], ll[1], context, parameters);
  290.             if (isDirty) {
  291.                 boolean next = true;
  292.                 for (int i = 0; i < MAX_ORDER_RANK && next; i++) {
  293.                     final T[] meanRates = getMeanElementRate(state, new GaussQuadrature(GAUSS_ORDER[i]), ll[0], ll[1],
  294.                             context, parameters);
  295.                     if (getRatesDiff(meanElementRate, meanRates, context).getReal() < threshold) {
  296.                         integrator = new GaussQuadrature(GAUSS_ORDER[i]);
  297.                         next = false;
  298.                     }
  299.                 }
  300.                 isDirty = false;
  301.             }
  302.         }

  303.         return meanElementRate;
  304.     }

  305.     /**
  306.      * Compute the limits in L, the true longitude, for integration.
  307.      *
  308.      * @param state             current state information: date, kinematics,
  309.      *                          attitude
  310.      * @param auxiliaryElements auxiliary elements related to the current orbit
  311.      * @return the integration limits in L
  312.      */
  313.     protected abstract double[] getLLimits(SpacecraftState state, AuxiliaryElements auxiliaryElements);

  314.     /**
  315.      * Compute the limits in L, the true longitude, for integration.
  316.      *
  317.      * @param <T>               type of the elements
  318.      * @param state             current state information: date, kinematics,
  319.      *                          attitude
  320.      * @param auxiliaryElements auxiliary elements related to the current orbit
  321.      * @return the integration limits in L
  322.      */
  323.     protected abstract <T extends CalculusFieldElement<T>> T[] getLLimits(FieldSpacecraftState<T> state,
  324.             FieldAuxiliaryElements<T> auxiliaryElements);

  325.     /**
  326.      * Computes the mean equinoctial elements rates da<sub>i</sub> / dt.
  327.      *
  328.      * @param state      current state
  329.      * @param gauss      Gauss quadrature
  330.      * @param low        lower bound of the integral interval
  331.      * @param high       upper bound of the integral interval
  332.      * @param context    container for attributes
  333.      * @param parameters values of the force model parameters
  334.      * at state date (1 values for each parameters)
  335.      * @return the mean element rates
  336.      */
  337.     protected double[] getMeanElementRate(final SpacecraftState state, final GaussQuadrature gauss, final double low,
  338.             final double high, final AbstractGaussianContributionContext context, final double[] parameters) {

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

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

  342.         // Constant multiplier for integral
  343.         final double coef = 1. / (2. * FastMath.PI * auxiliaryElements.getB());
  344.         // Corrects mean element rates
  345.         for (int i = 0; i < 6; i++) {
  346.             meanElementRate[i] *= coef;
  347.         }
  348.         return meanElementRate;
  349.     }

  350.     /**
  351.      * Computes the mean equinoctial elements rates da<sub>i</sub> / dt.
  352.      *
  353.      * @param <T>        type of the elements
  354.      * @param state      current state
  355.      * @param gauss      Gauss quadrature
  356.      * @param low        lower bound of the integral interval
  357.      * @param high       upper bound of the integral interval
  358.      * @param context    container for attributes
  359.      * @param parameters values of the force model parameters(1 values for each parameters)
  360.      * @return the mean element rates
  361.      */
  362.     protected <T extends CalculusFieldElement<T>> T[] getMeanElementRate(final FieldSpacecraftState<T> state,
  363.             final GaussQuadrature gauss, final T low, final T high,
  364.             final FieldAbstractGaussianContributionContext<T> context, final T[] parameters) {

  365.         // Field
  366.         final Field<T> field = context.getA().getField();

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

  369.         final T[] meanElementRate = gauss.integrate(new FieldIntegrableFunction<>(state, true, 0, parameters, field),
  370.                 low, high, field);
  371.         // Constant multiplier for integral
  372.         final T coef = auxiliaryElements.getB().multiply(low.getPi()).multiply(2.).reciprocal();
  373.         // Corrects mean element rates
  374.         for (int i = 0; i < 6; i++) {
  375.             meanElementRate[i] = meanElementRate[i].multiply(coef);
  376.         }
  377.         return meanElementRate;
  378.     }

  379.     /**
  380.      * Estimates the weighted magnitude of the difference between 2 sets of
  381.      * equinoctial elements rates.
  382.      *
  383.      * @param meanRef reference rates
  384.      * @param meanCur current rates
  385.      * @param context container for attributes
  386.      * @return estimated magnitude of weighted differences
  387.      */
  388.     private double getRatesDiff(final double[] meanRef, final double[] meanCur,
  389.             final AbstractGaussianContributionContext context) {

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

  392.         double maxDiff = FastMath.abs(meanRef[0] - meanCur[0]) / auxiliaryElements.getSma();
  393.         // Corrects mean element rates
  394.         for (int i = 1; i < meanRef.length; i++) {
  395.             maxDiff = FastMath.max(maxDiff, FastMath.abs(meanRef[i] - meanCur[i]));
  396.         }
  397.         return maxDiff;
  398.     }

  399.     /**
  400.      * Estimates the weighted magnitude of the difference between 2 sets of
  401.      * equinoctial elements rates.
  402.      *
  403.      * @param <T>     type of the elements
  404.      * @param meanRef reference rates
  405.      * @param meanCur current rates
  406.      * @param context container for attributes
  407.      * @return estimated magnitude of weighted differences
  408.      */
  409.     private <T extends CalculusFieldElement<T>> T getRatesDiff(final T[] meanRef, final T[] meanCur,
  410.             final FieldAbstractGaussianContributionContext<T> context) {

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

  413.         T maxDiff = FastMath.abs(meanRef[0].subtract(meanCur[0])).divide(auxiliaryElements.getSma());

  414.         // Corrects mean element rates
  415.         for (int i = 1; i < meanRef.length; i++) {
  416.             maxDiff = FastMath.max(maxDiff, FastMath.abs(meanRef[i].subtract(meanCur[i])));
  417.         }
  418.         return maxDiff;
  419.     }

  420.     /** {@inheritDoc} */
  421.     @Override
  422.     public void registerAttitudeProvider(final AttitudeProvider provider) {
  423.         this.attitudeProvider = provider;
  424.     }

  425.     /** {@inheritDoc} */
  426.     @Override
  427.     public void updateShortPeriodTerms(final double[] parameters, final SpacecraftState... meanStates) {

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

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

  432.             // Container of attributes
  433.             // Extract the proper parameters valid for the corresponding meanState date from the input array
  434.             final double[] extractedParameters = this.extractParameters(parameters, auxiliaryElements.getDate());
  435.             final AbstractGaussianContributionContext context = initializeStep(auxiliaryElements, extractedParameters);

  436.             // Compute rhoj and sigmaj
  437.             final double[][] currentRhoSigmaj = computeRhoSigmaCoefficients(auxiliaryElements);

  438.             // Generate the Cij and Sij coefficients
  439.             final FourierCjSjCoefficients fourierCjSj = new FourierCjSjCoefficients(meanState, JMAX, auxiliaryElements,
  440.                                                                                     extractedParameters);

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

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

  445.         }

  446.     }

  447.     /** {@inheritDoc} */
  448.     @Override
  449.     @SuppressWarnings("unchecked")
  450.     public <T extends CalculusFieldElement<T>> void updateShortPeriodTerms(final T[] parameters,
  451.             final FieldSpacecraftState<T>... meanStates) {

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

  454.         final FieldGaussianShortPeriodicCoefficients<T> fgspc = (FieldGaussianShortPeriodicCoefficients<T>) gaussianFieldSPCoefs
  455.                 .get(field);
  456.         final FieldSlot<T> slot = fgspc.createSlot(meanStates);
  457.         for (final FieldSpacecraftState<T> meanState : meanStates) {

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

  460.             // Container of attributes
  461.             // Extract the proper parameters valid for the corresponding meanState date from the input array
  462.             final T[] extractedParameters = this.extractParameters(parameters, auxiliaryElements.getDate());
  463.             final FieldAbstractGaussianContributionContext<T> context = initializeStep(auxiliaryElements, extractedParameters);

  464.             // Compute rhoj and sigmaj
  465.             final T[][] currentRhoSigmaj = computeRhoSigmaCoefficients(context, field);

  466.             // Generate the Cij and Sij coefficients
  467.             final FieldFourierCjSjCoefficients<T> fourierCjSj = new FieldFourierCjSjCoefficients<>(meanState, JMAX,
  468.                     auxiliaryElements, extractedParameters, field);

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

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

  474.         }

  475.     }

  476.     /**
  477.      * Compute the auxiliary quantities ρ<sub>j</sub> and σ<sub>j</sub>.
  478.      * <p>
  479.      * The expressions used are equations 2.5.3-(4) from the Danielson paper. <br/>
  480.      * ρ<sub>j</sub> = (1+jB)(-b)<sup>j</sup>C<sub>j</sub>(k, h) <br/>
  481.      * σ<sub>j</sub> = (1+jB)(-b)<sup>j</sup>S<sub>j</sub>(k, h) <br/>
  482.      * </p>
  483.      * @param auxiliaryElements auxiliary elements related to the current orbit
  484.      * @return computed coefficients
  485.      */
  486.     private double[][] computeRhoSigmaCoefficients(final AuxiliaryElements auxiliaryElements) {
  487.         final double[][] currentRhoSigmaj = new double[2][3 * JMAX + 1];
  488.         final CjSjCoefficient cjsjKH = new CjSjCoefficient(auxiliaryElements.getK(), auxiliaryElements.getH());
  489.         final double b = 1. / (1 + auxiliaryElements.getB());

  490.         // (-b)<sup>j</sup>
  491.         double mbtj = 1;

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

  493.             // Compute current rho and sigma;
  494.             mbtj *= -b;
  495.             final double coef = (1 + j * auxiliaryElements.getB()) * mbtj;
  496.             currentRhoSigmaj[0][j] = coef * cjsjKH.getCj(j);
  497.             currentRhoSigmaj[1][j] = coef * cjsjKH.getSj(j);
  498.         }
  499.         return currentRhoSigmaj;
  500.     }

  501.     /**
  502.      * Compute the auxiliary quantities ρ<sub>j</sub> and σ<sub>j</sub>.
  503.      * <p>
  504.      * The expressions used are equations 2.5.3-(4) from the Danielson paper. <br/>
  505.      * ρ<sub>j</sub> = (1+jB)(-b)<sup>j</sup>C<sub>j</sub>(k, h) <br/>
  506.      * σ<sub>j</sub> = (1+jB)(-b)<sup>j</sup>S<sub>j</sub>(k, h) <br/>
  507.      * </p>
  508.      * @param <T>     type of the elements
  509.      * @param context container for attributes
  510.      * @param field   field used by default
  511.      * @return computed coefficients
  512.      */
  513.     private <T extends CalculusFieldElement<T>> T[][] computeRhoSigmaCoefficients(final FieldAbstractGaussianContributionContext<T> context, final Field<T> field) {
  514.         // zero
  515.         final T zero = field.getZero();

  516.         final FieldAuxiliaryElements<T> auxiliaryElements = context.getFieldAuxiliaryElements();
  517.         final T[][] currentRhoSigmaj = MathArrays.buildArray(field, 2, 3 * JMAX + 1);
  518.         final FieldCjSjCoefficient<T> cjsjKH = new FieldCjSjCoefficient<>(auxiliaryElements.getK(),
  519.                 auxiliaryElements.getH(), field);
  520.         final T b = auxiliaryElements.getB().add(1.).reciprocal();

  521.         // (-b)<sup>j</sup>
  522.         T mbtj = zero.newInstance(1.);

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

  524.             // Compute current rho and sigma;
  525.             mbtj = mbtj.multiply(b.negate());
  526.             final T coef = mbtj.multiply(auxiliaryElements.getB().multiply(j).add(1.));
  527.             currentRhoSigmaj[0][j] = coef.multiply(cjsjKH.getCj(j));
  528.             currentRhoSigmaj[1][j] = coef.multiply(cjsjKH.getSj(j));
  529.         }
  530.         return currentRhoSigmaj;
  531.     }

  532.     /**
  533.      * Internal class for numerical quadrature.
  534.      * <p>
  535.      * This class is a rewrite of {@link IntegrableFunction} for field elements
  536.      * </p>
  537.      * @param <T> type of the field elements
  538.      */
  539.     protected class FieldIntegrableFunction<T extends CalculusFieldElement<T>>
  540.             implements CalculusFieldUnivariateVectorFunction<T> {

  541.         /** Current state. */
  542.         private final FieldSpacecraftState<T> state;

  543.         /**
  544.          * Signal that this class is used to compute the values required by the mean
  545.          * element variations or by the short periodic element variations.
  546.          */
  547.         private final boolean meanMode;

  548.         /**
  549.          * The j index.
  550.          * <p>
  551.          * Used only for short periodic variation. Ignored for mean elements variation.
  552.          * </p>
  553.          */
  554.         private final int j;

  555.         /** Container for attributes. */
  556.         private final FieldAbstractGaussianContributionContext<T> context;

  557.         /** Auxiliary Elements. */
  558.         private final FieldAuxiliaryElements<T> auxiliaryElements;

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

  561.         /**
  562.          * Build a new instance with a new field.
  563.          * @param state      current state information: date, kinematics, attitude
  564.          * @param meanMode   if true return the value associated to the mean elements
  565.          *                   variation, if false return the values associated to the
  566.          *                   short periodic elements variation
  567.          * @param j          the j index. used only for short periodic variation.
  568.          *                   Ignored for mean elements variation.
  569.          * @param parameters values of the force model parameters (only 1 values
  570.          *                   for each parameters corresponding to state date) obtained by
  571.          *                   calling the extract parameter method {@link #extractParameters(double[], AbsoluteDate)}
  572.          *                   to selected the right value for state date or by getting the parameters for a specific date
  573.          * @param field      field utilized by default
  574.          */
  575.         public FieldIntegrableFunction(final FieldSpacecraftState<T> state, final boolean meanMode, final int j,
  576.                 final T[] parameters, final Field<T> field) {

  577.             this.meanMode = meanMode;
  578.             this.j = j;
  579.             this.parameters = parameters.clone();
  580.             this.auxiliaryElements = new FieldAuxiliaryElements<>(state.getOrbit(), I);
  581.             this.context = new FieldAbstractGaussianContributionContext<>(auxiliaryElements, this.parameters);
  582.             // remove derivatives from state
  583.             final T[] stateVector = MathArrays.buildArray(field, 6);
  584.             final PositionAngleType positionAngleType = PositionAngleType.MEAN;
  585.             OrbitType.EQUINOCTIAL.mapOrbitToArray(state.getOrbit(), positionAngleType, stateVector, null);
  586.             final FieldOrbit<T> fixedOrbit = OrbitType.EQUINOCTIAL.mapArrayToOrbit(stateVector, null,
  587.                     positionAngleType, state.getDate(), context.getMu(), state.getFrame());
  588.             this.state = new FieldSpacecraftState<>(fixedOrbit, state.getAttitude()).withMass(state.getMass());
  589.         }

  590.         /** {@inheritDoc} */
  591.         @Override
  592.         public T[] value(final T x) {

  593.             // Parameters for array building
  594.             final Field<T> field = auxiliaryElements.getDate().getField();
  595.             final int dimension = 6;

  596.             // Compute the time difference from the true longitude difference
  597.             final T shiftedLm = trueToMean(x);
  598.             final T dLm = shiftedLm.subtract(auxiliaryElements.getLM());
  599.             final T dt = dLm.divide(context.getMeanMotion());

  600.             final FieldSinCos<T> scL = FastMath.sinCos(x);
  601.             final T cosL = scL.cos();
  602.             final T sinL = scL.sin();
  603.             final T roa  = auxiliaryElements.getB().multiply(auxiliaryElements.getB()).divide(auxiliaryElements.getH().multiply(sinL).add(auxiliaryElements.getK().multiply(cosL)).add(1.));
  604.             final T roa2 = roa.multiply(roa);
  605.             final T r = auxiliaryElements.getSma().multiply(roa);
  606.             final T X = r.multiply(cosL);
  607.             final T Y = r.multiply(sinL);
  608.             final T naob = context.getMeanMotion().multiply(auxiliaryElements.getSma())
  609.                     .divide(auxiliaryElements.getB());
  610.             final T Xdot = naob.multiply(auxiliaryElements.getH().add(sinL)).negate();
  611.             final T Ydot = naob.multiply(auxiliaryElements.getK().add(cosL));
  612.             final FieldVector3D<T> vel = new FieldVector3D<>(Xdot, auxiliaryElements.getVectorF(), Ydot,
  613.                     auxiliaryElements.getVectorG());

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

  616.             // Recompose an orbit with time held fixed to be compliant with DSST theory
  617.             final FieldOrbit<T> recomposedOrbit = new FieldEquinoctialOrbit<>(shiftedOrbit.getA(),
  618.                     shiftedOrbit.getEquinoctialEx(), shiftedOrbit.getEquinoctialEy(), shiftedOrbit.getHx(),
  619.                     shiftedOrbit.getHy(), shiftedOrbit.getLM(), PositionAngleType.MEAN, shiftedOrbit.getFrame(),
  620.                     state.getDate(), context.getMu());

  621.             // Get the corresponding attitude
  622.             final FieldAttitude<T> recomposedAttitude;
  623.             if (contribution.dependsOnAttitudeRate()) {
  624.                 recomposedAttitude = attitudeProvider.getAttitude(recomposedOrbit,
  625.                         recomposedOrbit.getDate(), recomposedOrbit.getFrame());
  626.             } else {
  627.                 final FieldRotation<T> rotation = attitudeProvider.getAttitudeRotation(recomposedOrbit,
  628.                         recomposedOrbit.getDate(), recomposedOrbit.getFrame());
  629.                 final FieldVector3D<T> zeroVector = FieldVector3D.getZero(recomposedOrbit.getA().getField());
  630.                 recomposedAttitude = new FieldAttitude<>(recomposedOrbit.getDate(), recomposedOrbit.getFrame(),
  631.                         rotation, zeroVector, zeroVector);
  632.             }

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

  635.             final FieldVector3D<T> acc = contribution.acceleration(shiftedState, parameters);

  636.             // Compute the derivatives of the elements by the speed
  637.             final T[] deriv = MathArrays.buildArray(field, dimension);
  638.             // da/dv
  639.             deriv[0] = getAoV(vel).dotProduct(acc);
  640.             // dex/dv
  641.             deriv[1] = getKoV(X, Y, Xdot, Ydot).dotProduct(acc);
  642.             // dey/dv
  643.             deriv[2] = getHoV(X, Y, Xdot, Ydot).dotProduct(acc);
  644.             // dhx/dv
  645.             deriv[3] = getQoV(X).dotProduct(acc);
  646.             // dhy/dv
  647.             deriv[4] = getPoV(Y).dotProduct(acc);
  648.             // dλ/dv
  649.             deriv[5] = getLoV(X, Y, Xdot, Ydot).dotProduct(acc);

  650.             // Compute mean elements rates
  651.             final T[] val;
  652.             if (meanMode) {
  653.                 val = MathArrays.buildArray(field, dimension);
  654.                 for (int i = 0; i < 6; i++) {
  655.                     // da<sub>i</sub>/dt
  656.                     val[i] = deriv[i].multiply(roa2);
  657.                 }
  658.             } else {
  659.                 val = MathArrays.buildArray(field, dimension * 2);
  660.                 //Compute cos(j*L) and sin(j*L);
  661.                 final FieldSinCos<T> scjL = FastMath.sinCos(x.multiply(j));
  662.                 final T cosjL = j == 1 ? cosL : scjL.cos();
  663.                 final T sinjL = j == 1 ? sinL : scjL.sin();

  664.                 for (int i = 0; i < 6; i++) {
  665.                     // da<sub>i</sub>/dv * cos(jL)
  666.                     val[i] = deriv[i].multiply(cosjL);
  667.                     // da<sub>i</sub>/dv * sin(jL)
  668.                     val[i + 6] = deriv[i].multiply(sinjL);
  669.                 }
  670.             }

  671.             return val;
  672.         }

  673.         /**
  674.          * Converts true longitude to mean longitude.
  675.          * @param x True longitude
  676.          * @return Eccentric longitude
  677.          */
  678.         private T trueToMean(final T x) {
  679.             return eccentricToMean(trueToEccentric(x));
  680.         }

  681.         /**
  682.          * Converts true longitude to eccentric longitude.
  683.          * @param lv True longitude
  684.          * @return Eccentric longitude
  685.          */
  686.         private T trueToEccentric (final T lv) {
  687.             final FieldSinCos<T> sclV = FastMath.sinCos(lv);
  688.             final T cosLv   = sclV.cos();
  689.             final T sinLv   = sclV.sin();
  690.             final T num     = auxiliaryElements.getH().multiply(cosLv).subtract(auxiliaryElements.getK().multiply(sinLv));
  691.             final T den     = auxiliaryElements.getB().add(auxiliaryElements.getK().multiply(cosLv)).add(auxiliaryElements.getH().multiply(sinLv)).add(1.);
  692.             return FastMath.atan(num.divide(den)).multiply(2.).add(lv);
  693.         }

  694.         /**
  695.          * Converts eccentric longitude to mean longitude.
  696.          * @param le Eccentric longitude
  697.          * @return Mean longitude
  698.          */
  699.         private T eccentricToMean (final T le) {
  700.             final FieldSinCos<T> scle = FastMath.sinCos(le);
  701.             return le.subtract(auxiliaryElements.getK().multiply(scle.sin())).add(auxiliaryElements.getH().multiply(scle.cos()));
  702.         }

  703.         /**
  704.          * Compute δa/δv.
  705.          * @param vel satellite velocity
  706.          * @return δa/δv
  707.          */
  708.         private FieldVector3D<T> getAoV(final FieldVector3D<T> vel) {
  709.             return new FieldVector3D<>(context.getTon2a(), vel);
  710.         }

  711.         /**
  712.          * Compute δh/δv.
  713.          * @param X    satellite position component along f, equinoctial reference frame
  714.          *             1st vector
  715.          * @param Y    satellite position component along g, equinoctial reference frame
  716.          *             2nd vector
  717.          * @param Xdot satellite velocity component along f, equinoctial reference frame
  718.          *             1st vector
  719.          * @param Ydot satellite velocity component along g, equinoctial reference frame
  720.          *             2nd vector
  721.          * @return δh/δv
  722.          */
  723.         private FieldVector3D<T> getHoV(final T X, final T Y, final T Xdot, final T Ydot) {
  724.             final T kf = (Xdot.multiply(Y).multiply(2.).subtract(X.multiply(Ydot))).multiply(context.getOoMU());
  725.             final T kg = X.multiply(Xdot).multiply(context.getOoMU());
  726.             final T kw = auxiliaryElements.getK().multiply(
  727.                     auxiliaryElements.getQ().multiply(Y).multiply(I).subtract(auxiliaryElements.getP().multiply(X)))
  728.                     .multiply(context.getOOAB());
  729.             return new FieldVector3D<>(kf, auxiliaryElements.getVectorF(), kg.negate(), auxiliaryElements.getVectorG(),
  730.                     kw, auxiliaryElements.getVectorW());
  731.         }

  732.         /**
  733.          * Compute δk/δv.
  734.          * @param X    satellite position component along f, equinoctial reference frame
  735.          *             1st vector
  736.          * @param Y    satellite position component along g, equinoctial reference frame
  737.          *             2nd vector
  738.          * @param Xdot satellite velocity component along f, equinoctial reference frame
  739.          *             1st vector
  740.          * @param Ydot satellite velocity component along g, equinoctial reference frame
  741.          *             2nd vector
  742.          * @return δk/δv
  743.          */
  744.         private FieldVector3D<T> getKoV(final T X, final T Y, final T Xdot, final T Ydot) {
  745.             final T kf = Y.multiply(Ydot).multiply(context.getOoMU());
  746.             final T kg = (X.multiply(Ydot).multiply(2.).subtract(Xdot.multiply(Y))).multiply(context.getOoMU());
  747.             final T kw = auxiliaryElements.getH().multiply(
  748.                     auxiliaryElements.getQ().multiply(Y).multiply(I).subtract(auxiliaryElements.getP().multiply(X)))
  749.                     .multiply(context.getOOAB());
  750.             return new FieldVector3D<>(kf.negate(), auxiliaryElements.getVectorF(), kg, auxiliaryElements.getVectorG(),
  751.                     kw.negate(), auxiliaryElements.getVectorW());
  752.         }

  753.         /**
  754.          * Compute δp/δv.
  755.          * @param Y satellite position component along g, equinoctial reference frame
  756.          *          2nd vector
  757.          * @return δp/δv
  758.          */
  759.         private FieldVector3D<T> getPoV(final T Y) {
  760.             return new FieldVector3D<>(context.getCo2AB().multiply(Y), auxiliaryElements.getVectorW());
  761.         }

  762.         /**
  763.          * Compute δq/δv.
  764.          * @param X satellite position component along f, equinoctial reference frame
  765.          *          1st vector
  766.          * @return δq/δv
  767.          */
  768.         private FieldVector3D<T> getQoV(final T X) {
  769.             return new FieldVector3D<>(context.getCo2AB().multiply(X).multiply(I), auxiliaryElements.getVectorW());
  770.         }

  771.         /**
  772.          * Compute δλ/δv.
  773.          * @param X    satellite position component along f, equinoctial reference frame
  774.          *             1st vector
  775.          * @param Y    satellite position component along g, equinoctial reference frame
  776.          *             2nd vector
  777.          * @param Xdot satellite velocity component along f, equinoctial reference frame
  778.          *             1st vector
  779.          * @param Ydot satellite velocity component along g, equinoctial reference frame
  780.          *             2nd vector
  781.          * @return δλ/δv
  782.          */
  783.         private FieldVector3D<T> getLoV(final T X, final T Y, final T Xdot, final T Ydot) {
  784.             final FieldVector3D<T> pos = new FieldVector3D<>(X, auxiliaryElements.getVectorF(), Y,
  785.                     auxiliaryElements.getVectorG());
  786.             final FieldVector3D<T> v2 = new FieldVector3D<>(auxiliaryElements.getK(), getHoV(X, Y, Xdot, Ydot),
  787.                     auxiliaryElements.getH().negate(), getKoV(X, Y, Xdot, Ydot));
  788.             return new FieldVector3D<>(context.getOOA().multiply(-2.), pos, context.getOoBpo(), v2,
  789.                     context.getOOA().multiply(auxiliaryElements.getQ().multiply(Y).multiply(I)
  790.                             .subtract(auxiliaryElements.getP().multiply(X))),
  791.                     auxiliaryElements.getVectorW());
  792.         }

  793.     }

  794.     /** Internal class for numerical quadrature. */
  795.     protected class IntegrableFunction implements UnivariateVectorFunction {

  796.         /** Current state. */
  797.         private final SpacecraftState state;

  798.         /**
  799.          * Signal that this class is used to compute the values required by the mean
  800.          * element variations or by the short periodic element variations.
  801.          */
  802.         private final boolean meanMode;

  803.         /**
  804.          * The j index.
  805.          * <p>
  806.          * Used only for short periodic variation. Ignored for mean elements variation.
  807.          * </p>
  808.          */
  809.         private final int j;

  810.         /** Container for attributes. */
  811.         private final AbstractGaussianContributionContext context;

  812.         /** Auxiliary Elements. */
  813.         private final AuxiliaryElements auxiliaryElements;

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

  816.         /**
  817.          * Build a new instance.
  818.          * @param state      current state information: date, kinematics, attitude
  819.          * @param meanMode   if true return the value associated to the mean elements
  820.          *                   variation, if false return the values associated to the
  821.          *                   short periodic elements variation
  822.          * @param j          the j index. used only for short periodic variation.
  823.          *                   Ignored for mean elements variation.
  824.          * @param parameters list of the estimated values for each driver at state date of the force model parameters
  825.          *                   only 1 value for each parameter
  826.          */
  827.         IntegrableFunction(final SpacecraftState state, final boolean meanMode, final int j,
  828.                 final double[] parameters) {

  829.             this.meanMode = meanMode;
  830.             this.j = j;
  831.             this.parameters = parameters.clone();
  832.             this.auxiliaryElements = new AuxiliaryElements(state.getOrbit(), I);
  833.             this.context = new AbstractGaussianContributionContext(auxiliaryElements, this.parameters);
  834.             // remove derivatives from state
  835.             final double[] stateVector = new double[6];
  836.             final PositionAngleType positionAngleType = PositionAngleType.MEAN;
  837.             OrbitType.EQUINOCTIAL.mapOrbitToArray(state.getOrbit(), positionAngleType, stateVector, null);
  838.             final Orbit fixedOrbit = OrbitType.EQUINOCTIAL.mapArrayToOrbit(stateVector, null, positionAngleType,
  839.                     state.getDate(), context.getMu(), state.getFrame());
  840.             this.state = new SpacecraftState(fixedOrbit, state.getAttitude()).withMass(state.getMass());
  841.         }

  842.         /** {@inheritDoc} */
  843.         @SuppressWarnings("checkstyle:FinalLocalVariable")
  844.         @Override
  845.         public double[] value(final double x) {

  846.             // Compute the time difference from the true longitude difference
  847.             final double shiftedLm = trueToMean(x);
  848.             final double dLm = shiftedLm - auxiliaryElements.getLM();
  849.             final double dt = dLm / context.getMeanMotion();

  850.             final SinCos scL  = FastMath.sinCos(x);
  851.             final double cosL = scL.cos();
  852.             final double sinL = scL.sin();
  853.             final double roa  = auxiliaryElements.getB() * auxiliaryElements.getB() / (1. + auxiliaryElements.getH() * sinL + auxiliaryElements.getK() * cosL);
  854.             final double roa2 = roa * roa;
  855.             final double r = auxiliaryElements.getSma() * roa;
  856.             final double X = r * cosL;
  857.             final double Y = r * sinL;
  858.             final double naob = context.getMeanMotion() * auxiliaryElements.getSma() / auxiliaryElements.getB();
  859.             final double Xdot = -naob * (auxiliaryElements.getH() + sinL);
  860.             final double Ydot = naob * (auxiliaryElements.getK() + cosL);
  861.             final Vector3D vel = new Vector3D(Xdot, auxiliaryElements.getVectorF(), Ydot,
  862.                     auxiliaryElements.getVectorG());

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

  865.             // Recompose an orbit with time held fixed to be compliant with DSST theory
  866.             final Orbit recomposedOrbit = new EquinoctialOrbit(shiftedOrbit.getA(), shiftedOrbit.getEquinoctialEx(),
  867.                     shiftedOrbit.getEquinoctialEy(), shiftedOrbit.getHx(), shiftedOrbit.getHy(), shiftedOrbit.getLM(),
  868.                     PositionAngleType.MEAN, shiftedOrbit.getFrame(), state.getDate(), context.getMu());

  869.             // Get the corresponding attitude
  870.             final Attitude recomposedAttitude;
  871.             if (contribution.dependsOnAttitudeRate()) {
  872.                 recomposedAttitude = attitudeProvider.getAttitude(recomposedOrbit,
  873.                         recomposedOrbit.getDate(), recomposedOrbit.getFrame());
  874.             } else {
  875.                 final Rotation rotation = attitudeProvider.getAttitudeRotation(recomposedOrbit,
  876.                         recomposedOrbit.getDate(), recomposedOrbit.getFrame());
  877.                 final Vector3D zeroVector = Vector3D.ZERO;
  878.                 recomposedAttitude = new Attitude(recomposedOrbit.getDate(), recomposedOrbit.getFrame(),
  879.                         rotation, zeroVector, zeroVector);
  880.             }

  881.             // create shifted SpacecraftState with attitude at specified time
  882.             final SpacecraftState shiftedState = new SpacecraftState(recomposedOrbit, recomposedAttitude).withMass(state.getMass());

  883.             // here parameters is a list of all span values of each parameter driver
  884.             final Vector3D acc = contribution.acceleration(shiftedState, parameters);

  885.             // Compute the derivatives of the elements by the speed
  886.             final double[] deriv = new double[6];
  887.             // da/dv
  888.             deriv[0] = getAoV(vel).dotProduct(acc);
  889.             // dex/dv
  890.             deriv[1] = getKoV(X, Y, Xdot, Ydot).dotProduct(acc);
  891.             // dey/dv
  892.             deriv[2] = getHoV(X, Y, Xdot, Ydot).dotProduct(acc);
  893.             // dhx/dv
  894.             deriv[3] = getQoV(X).dotProduct(acc);
  895.             // dhy/dv
  896.             deriv[4] = getPoV(Y).dotProduct(acc);
  897.             // dλ/dv
  898.             deriv[5] = getLoV(X, Y, Xdot, Ydot).dotProduct(acc);

  899.             // Compute mean elements rates
  900.             final double[] val;
  901.             if (meanMode) {
  902.                 val = new double[6];
  903.                 for (int i = 0; i < 6; i++) {
  904.                     // da<sub>i</sub>/dt
  905.                     val[i] = roa2 * deriv[i];
  906.                 }
  907.             } else {
  908.                 val = new double[12];
  909.                 //Compute cos(j*L) and sin(j*L);
  910.                 final SinCos scjL  = FastMath.sinCos(j * x);
  911.                 final double cosjL = j == 1 ? cosL : scjL.cos();
  912.                 final double sinjL = j == 1 ? sinL : scjL.sin();

  913.                 for (int i = 0; i < 6; i++) {
  914.                     // da<sub>i</sub>/dv * cos(jL)
  915.                     val[i] = cosjL * deriv[i];
  916.                     // da<sub>i</sub>/dv * sin(jL)
  917.                     val[i + 6] = sinjL * deriv[i];
  918.                 }
  919.             }
  920.             return val;
  921.         }

  922.         /**
  923.          * Converts true longitude to eccentric longitude.
  924.          * @param lv True longitude
  925.          * @return Eccentric longitude
  926.          */
  927.         private double trueToEccentric (final double lv) {
  928.             final SinCos scLv    = FastMath.sinCos(lv);
  929.             final double num     = auxiliaryElements.getH() * scLv.cos() - auxiliaryElements.getK() * scLv.sin();
  930.             final double den     = auxiliaryElements.getB() + 1. + auxiliaryElements.getK() * scLv.cos() + auxiliaryElements.getH() * scLv.sin();
  931.             return lv + 2. * FastMath.atan(num / den);
  932.         }

  933.         /**
  934.          * Converts eccentric longitude to mean longitude.
  935.          * @param le Eccentric longitude
  936.          * @return Mean longitude
  937.          */
  938.         private double eccentricToMean (final double le) {
  939.             final SinCos scLe = FastMath.sinCos(le);
  940.             return le - auxiliaryElements.getK() * scLe.sin() + auxiliaryElements.getH() * scLe.cos();
  941.         }

  942.         /**
  943.          * Converts true longitude to mean longitude.
  944.          * @param lv True longitude
  945.          * @return Eccentric longitude
  946.          */
  947.         private double trueToMean(final double lv) {
  948.             return eccentricToMean(trueToEccentric(lv));
  949.         }

  950.         /**
  951.          * Compute δa/δv.
  952.          * @param vel satellite velocity
  953.          * @return δa/δv
  954.          */
  955.         private Vector3D getAoV(final Vector3D vel) {
  956.             return new Vector3D(context.getTon2a(), vel);
  957.         }

  958.         /**
  959.          * Compute δh/δv.
  960.          * @param X    satellite position component along f, equinoctial reference frame
  961.          *             1st vector
  962.          * @param Y    satellite position component along g, equinoctial reference frame
  963.          *             2nd vector
  964.          * @param Xdot satellite velocity component along f, equinoctial reference frame
  965.          *             1st vector
  966.          * @param Ydot satellite velocity component along g, equinoctial reference frame
  967.          *             2nd vector
  968.          * @return δh/δv
  969.          */
  970.         private Vector3D getHoV(final double X, final double Y, final double Xdot, final double Ydot) {
  971.             final double kf = (2. * Xdot * Y - X * Ydot) * context.getOoMU();
  972.             final double kg = X * Xdot * context.getOoMU();
  973.             final double kw = auxiliaryElements.getK() *
  974.                     (I * auxiliaryElements.getQ() * Y - auxiliaryElements.getP() * X) * context.getOOAB();
  975.             return new Vector3D(kf, auxiliaryElements.getVectorF(), -kg, auxiliaryElements.getVectorG(), kw,
  976.                     auxiliaryElements.getVectorW());
  977.         }

  978.         /**
  979.          * Compute δk/δv.
  980.          * @param X    satellite position component along f, equinoctial reference frame
  981.          *             1st vector
  982.          * @param Y    satellite position component along g, equinoctial reference frame
  983.          *             2nd vector
  984.          * @param Xdot satellite velocity component along f, equinoctial reference frame
  985.          *             1st vector
  986.          * @param Ydot satellite velocity component along g, equinoctial reference frame
  987.          *             2nd vector
  988.          * @return δk/δv
  989.          */
  990.         private Vector3D getKoV(final double X, final double Y, final double Xdot, final double Ydot) {
  991.             final double kf = Y * Ydot * context.getOoMU();
  992.             final double kg = (2. * X * Ydot - Xdot * Y) * context.getOoMU();
  993.             final double kw = auxiliaryElements.getH() *
  994.                     (I * auxiliaryElements.getQ() * Y - auxiliaryElements.getP() * X) * context.getOOAB();
  995.             return new Vector3D(-kf, auxiliaryElements.getVectorF(), kg, auxiliaryElements.getVectorG(), -kw,
  996.                     auxiliaryElements.getVectorW());
  997.         }

  998.         /**
  999.          * Compute δp/δv.
  1000.          * @param Y satellite position component along g, equinoctial reference frame
  1001.          *          2nd vector
  1002.          * @return δp/δv
  1003.          */
  1004.         private Vector3D getPoV(final double Y) {
  1005.             return new Vector3D(context.getCo2AB() * Y, auxiliaryElements.getVectorW());
  1006.         }

  1007.         /**
  1008.          * Compute δq/δv.
  1009.          * @param X satellite position component along f, equinoctial reference frame
  1010.          *          1st vector
  1011.          * @return δq/δv
  1012.          */
  1013.         private Vector3D getQoV(final double X) {
  1014.             return new Vector3D(I * context.getCo2AB() * X, auxiliaryElements.getVectorW());
  1015.         }

  1016.         /**
  1017.          * Compute δλ/δv.
  1018.          * @param X    satellite position component along f, equinoctial reference frame
  1019.          *             1st vector
  1020.          * @param Y    satellite position component along g, equinoctial reference frame
  1021.          *             2nd vector
  1022.          * @param Xdot satellite velocity component along f, equinoctial reference frame
  1023.          *             1st vector
  1024.          * @param Ydot satellite velocity component along g, equinoctial reference frame
  1025.          *             2nd vector
  1026.          * @return δλ/δv
  1027.          */
  1028.         private Vector3D getLoV(final double X, final double Y, final double Xdot, final double Ydot) {
  1029.             final Vector3D pos = new Vector3D(X, auxiliaryElements.getVectorF(), Y, auxiliaryElements.getVectorG());
  1030.             final Vector3D v2 = new Vector3D(auxiliaryElements.getK(), getHoV(X, Y, Xdot, Ydot),
  1031.                     -auxiliaryElements.getH(), getKoV(X, Y, Xdot, Ydot));
  1032.             return new Vector3D(-2. * context.getOOA(), pos, context.getOoBpo(), v2,
  1033.                     (I * auxiliaryElements.getQ() * Y - auxiliaryElements.getP() * X) * context.getOOA(),
  1034.                     auxiliaryElements.getVectorW());
  1035.         }

  1036.     }

  1037.     /**
  1038.      * Class used to {@link #integrate(UnivariateVectorFunction, double, double)
  1039.      * integrate} a {@link org.hipparchus.analysis.UnivariateVectorFunction
  1040.      * function} of the orbital elements using the Gaussian quadrature rule to get
  1041.      * the acceleration.
  1042.      */
  1043.     protected static class GaussQuadrature {

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

  1045.         /** Points for quadrature of order 12. */
  1046.         private static final double[] P_12 = { -0.98156063424671910000, -0.90411725637047490000,
  1047.             -0.76990267419430470000, -0.58731795428661740000, -0.36783149899818024000, -0.12523340851146890000,
  1048.             0.12523340851146890000, 0.36783149899818024000, 0.58731795428661740000, 0.76990267419430470000,
  1049.             0.90411725637047490000, 0.98156063424671910000 };

  1050.         /** Weights for quadrature of order 12. */
  1051.         private static final double[] W_12 = { 0.04717533638651220000, 0.10693932599531830000, 0.16007832854334633000,
  1052.             0.20316742672306584000, 0.23349253653835478000, 0.24914704581340286000, 0.24914704581340286000,
  1053.             0.23349253653835478000, 0.20316742672306584000, 0.16007832854334633000, 0.10693932599531830000,
  1054.             0.04717533638651220000 };

  1055.         /** Points for quadrature of order 16. */
  1056.         private static final double[] P_16 = { -0.98940093499164990000, -0.94457502307323260000,
  1057.             -0.86563120238783160000, -0.75540440835500310000, -0.61787624440264380000, -0.45801677765722737000,
  1058.             -0.28160355077925890000, -0.09501250983763745000, 0.09501250983763745000, 0.28160355077925890000,
  1059.             0.45801677765722737000, 0.61787624440264380000, 0.75540440835500310000, 0.86563120238783160000,
  1060.             0.94457502307323260000, 0.98940093499164990000 };

  1061.         /** Weights for quadrature of order 16. */
  1062.         private static final double[] W_16 = { 0.02715245941175405800, 0.06225352393864777000, 0.09515851168249283000,
  1063.             0.12462897125553388000, 0.14959598881657685000, 0.16915651939500256000, 0.18260341504492360000,
  1064.             0.18945061045506847000, 0.18945061045506847000, 0.18260341504492360000, 0.16915651939500256000,
  1065.             0.14959598881657685000, 0.12462897125553388000, 0.09515851168249283000, 0.06225352393864777000,
  1066.             0.02715245941175405800 };

  1067.         /** Points for quadrature of order 20. */
  1068.         private static final double[] P_20 = { -0.99312859918509490000, -0.96397192727791390000,
  1069.             -0.91223442825132600000, -0.83911697182221890000, -0.74633190646015080000, -0.63605368072651510000,
  1070.             -0.51086700195082700000, -0.37370608871541955000, -0.22778585114164507000, -0.07652652113349734000,
  1071.             0.07652652113349734000, 0.22778585114164507000, 0.37370608871541955000, 0.51086700195082700000,
  1072.             0.63605368072651510000, 0.74633190646015080000, 0.83911697182221890000, 0.91223442825132600000,
  1073.             0.96397192727791390000, 0.99312859918509490000 };

  1074.         /** Weights for quadrature of order 20. */
  1075.         private static final double[] W_20 = { 0.01761400713915226400, 0.04060142980038684000, 0.06267204833410904000,
  1076.             0.08327674157670477000, 0.10193011981724048000, 0.11819453196151844000, 0.13168863844917678000,
  1077.             0.14209610931838212000, 0.14917298647260380000, 0.15275338713072600000, 0.15275338713072600000,
  1078.             0.14917298647260380000, 0.14209610931838212000, 0.13168863844917678000, 0.11819453196151844000,
  1079.             0.10193011981724048000, 0.08327674157670477000, 0.06267204833410904000, 0.04060142980038684000,
  1080.             0.01761400713915226400 };

  1081.         /** Points for quadrature of order 24. */
  1082.         private static final double[] P_24 = { -0.99518721999702130000, -0.97472855597130950000,
  1083.             -0.93827455200273270000, -0.88641552700440100000, -0.82000198597390300000, -0.74012419157855440000,
  1084.             -0.64809365193697550000, -0.54542147138883950000, -0.43379350762604520000, -0.31504267969616340000,
  1085.             -0.19111886747361634000, -0.06405689286260563000, 0.06405689286260563000, 0.19111886747361634000,
  1086.             0.31504267969616340000, 0.43379350762604520000, 0.54542147138883950000, 0.64809365193697550000,
  1087.             0.74012419157855440000, 0.82000198597390300000, 0.88641552700440100000, 0.93827455200273270000,
  1088.             0.97472855597130950000, 0.99518721999702130000 };

  1089.         /** Weights for quadrature of order 24. */
  1090.         private static final double[] W_24 = { 0.01234122979998733500, 0.02853138862893380600, 0.04427743881741981000,
  1091.             0.05929858491543691500, 0.07334648141108027000, 0.08619016153195320000, 0.09761865210411391000,
  1092.             0.10744427011596558000, 0.11550566805372553000, 0.12167047292780335000, 0.12583745634682825000,
  1093.             0.12793819534675221000, 0.12793819534675221000, 0.12583745634682825000, 0.12167047292780335000,
  1094.             0.11550566805372553000, 0.10744427011596558000, 0.09761865210411391000, 0.08619016153195320000,
  1095.             0.07334648141108027000, 0.05929858491543691500, 0.04427743881741981000, 0.02853138862893380600,
  1096.             0.01234122979998733500 };

  1097.         /** Points for quadrature of order 32. */
  1098.         private static final double[] P_32 = { -0.99726386184948160000, -0.98561151154526840000,
  1099.             -0.96476225558750640000, -0.93490607593773970000, -0.89632115576605220000, -0.84936761373256990000,
  1100.             -0.79448379596794250000, -0.73218211874028970000, -0.66304426693021520000, -0.58771575724076230000,
  1101.             -0.50689990893222950000, -0.42135127613063540000, -0.33186860228212767000, -0.23928736225213710000,
  1102.             -0.14447196158279646000, -0.04830766568773831000, 0.04830766568773831000, 0.14447196158279646000,
  1103.             0.23928736225213710000, 0.33186860228212767000, 0.42135127613063540000, 0.50689990893222950000,
  1104.             0.58771575724076230000, 0.66304426693021520000, 0.73218211874028970000, 0.79448379596794250000,
  1105.             0.84936761373256990000, 0.89632115576605220000, 0.93490607593773970000, 0.96476225558750640000,
  1106.             0.98561151154526840000, 0.99726386184948160000 };

  1107.         /** Weights for quadrature of order 32. */
  1108.         private static final double[] W_32 = { 0.00701861000947013600, 0.01627439473090571200, 0.02539206530926214200,
  1109.             0.03427386291302141000, 0.04283589802222658600, 0.05099805926237621600, 0.05868409347853559000,
  1110.             0.06582222277636193000, 0.07234579410884862000, 0.07819389578707042000, 0.08331192422694673000,
  1111.             0.08765209300440380000, 0.09117387869576390000, 0.09384439908080441000, 0.09563872007927487000,
  1112.             0.09654008851472784000, 0.09654008851472784000, 0.09563872007927487000, 0.09384439908080441000,
  1113.             0.09117387869576390000, 0.08765209300440380000, 0.08331192422694673000, 0.07819389578707042000,
  1114.             0.07234579410884862000, 0.06582222277636193000, 0.05868409347853559000, 0.05099805926237621600,
  1115.             0.04283589802222658600, 0.03427386291302141000, 0.02539206530926214200, 0.01627439473090571200,
  1116.             0.00701861000947013600 };

  1117.         /** Points for quadrature of order 40. */
  1118.         private static final double[] P_40 = { -0.99823770971055930000, -0.99072623869945710000,
  1119.             -0.97725994998377420000, -0.95791681921379170000, -0.93281280827867660000, -0.90209880696887420000,
  1120.             -0.86595950321225960000, -0.82461223083331170000, -0.77830565142651940000, -0.72731825518992710000,
  1121.             -0.67195668461417960000, -0.61255388966798030000, -0.54946712509512820000, -0.48307580168617870000,
  1122.             -0.41377920437160500000, -0.34199409082575850000, -0.26815218500725370000, -0.19269758070137110000,
  1123.             -0.11608407067525522000, -0.03877241750605081600, 0.03877241750605081600, 0.11608407067525522000,
  1124.             0.19269758070137110000, 0.26815218500725370000, 0.34199409082575850000, 0.41377920437160500000,
  1125.             0.48307580168617870000, 0.54946712509512820000, 0.61255388966798030000, 0.67195668461417960000,
  1126.             0.72731825518992710000, 0.77830565142651940000, 0.82461223083331170000, 0.86595950321225960000,
  1127.             0.90209880696887420000, 0.93281280827867660000, 0.95791681921379170000, 0.97725994998377420000,
  1128.             0.99072623869945710000, 0.99823770971055930000 };

  1129.         /** Weights for quadrature of order 40. */
  1130.         private static final double[] W_40 = { 0.00452127709853309800, 0.01049828453115270400, 0.01642105838190797300,
  1131.             0.02224584919416689000, 0.02793700698002338000, 0.03346019528254786500, 0.03878216797447199000,
  1132.             0.04387090818567333000, 0.04869580763507221000, 0.05322784698393679000, 0.05743976909939157000,
  1133.             0.06130624249292891000, 0.06480401345660108000, 0.06791204581523394000, 0.07061164739128681000,
  1134.             0.07288658239580408000, 0.07472316905796833000, 0.07611036190062619000, 0.07703981816424793000,
  1135.             0.07750594797842482000, 0.07750594797842482000, 0.07703981816424793000, 0.07611036190062619000,
  1136.             0.07472316905796833000, 0.07288658239580408000, 0.07061164739128681000, 0.06791204581523394000,
  1137.             0.06480401345660108000, 0.06130624249292891000, 0.05743976909939157000, 0.05322784698393679000,
  1138.             0.04869580763507221000, 0.04387090818567333000, 0.03878216797447199000, 0.03346019528254786500,
  1139.             0.02793700698002338000, 0.02224584919416689000, 0.01642105838190797300, 0.01049828453115270400,
  1140.             0.00452127709853309800 };

  1141.         /** Points for quadrature of order 48. */
  1142.         private static final double[] P_48 = { -0.99877100725242610000, -0.99353017226635080000,
  1143.             -0.98412458372282700000, -0.97059159254624720000, -0.95298770316043080000, -0.93138669070655440000,
  1144.             -0.90587913671556960000, -0.87657202027424800000, -0.84358826162439350000, -0.80706620402944250000,
  1145.             -0.76715903251574020000, -0.72403413092381470000, -0.67787237963266400000, -0.62886739677651370000,
  1146.             -0.57722472608397270000, -0.52316097472223300000, -0.46690290475095840000, -0.40868648199071680000,
  1147.             -0.34875588629216070000, -0.28736248735545555000, -0.22476379039468908000, -0.16122235606889174000,
  1148.             -0.09700469920946270000, -0.03238017096286937000, 0.03238017096286937000, 0.09700469920946270000,
  1149.             0.16122235606889174000, 0.22476379039468908000, 0.28736248735545555000, 0.34875588629216070000,
  1150.             0.40868648199071680000, 0.46690290475095840000, 0.52316097472223300000, 0.57722472608397270000,
  1151.             0.62886739677651370000, 0.67787237963266400000, 0.72403413092381470000, 0.76715903251574020000,
  1152.             0.80706620402944250000, 0.84358826162439350000, 0.87657202027424800000, 0.90587913671556960000,
  1153.             0.93138669070655440000, 0.95298770316043080000, 0.97059159254624720000, 0.98412458372282700000,
  1154.             0.99353017226635080000, 0.99877100725242610000 };

  1155.         /** Weights for quadrature of order 48. */
  1156.         private static final double[] W_48 = { 0.00315334605230596250, 0.00732755390127620800, 0.01147723457923446900,
  1157.             0.01557931572294386600, 0.01961616045735556700, 0.02357076083932435600, 0.02742650970835688000,
  1158.             0.03116722783279807000, 0.03477722256477045000, 0.03824135106583080600, 0.04154508294346483000,
  1159.             0.04467456085669424000, 0.04761665849249054000, 0.05035903555385448000, 0.05289018948519365000,
  1160.             0.05519950369998416500, 0.05727729210040315000, 0.05911483969839566000, 0.06070443916589384000,
  1161.             0.06203942315989268000, 0.06311419228625403000, 0.06392423858464817000, 0.06446616443595010000,
  1162.             0.06473769681268386000, 0.06473769681268386000, 0.06446616443595010000, 0.06392423858464817000,
  1163.             0.06311419228625403000, 0.06203942315989268000, 0.06070443916589384000, 0.05911483969839566000,
  1164.             0.05727729210040315000, 0.05519950369998416500, 0.05289018948519365000, 0.05035903555385448000,
  1165.             0.04761665849249054000, 0.04467456085669424000, 0.04154508294346483000, 0.03824135106583080600,
  1166.             0.03477722256477045000, 0.03116722783279807000, 0.02742650970835688000, 0.02357076083932435600,
  1167.             0.01961616045735556700, 0.01557931572294386600, 0.01147723457923446900, 0.00732755390127620800,
  1168.             0.00315334605230596250 };

  1169.         /** Node points. */
  1170.         private final double[] nodePoints;

  1171.         /** Node weights. */
  1172.         private final double[] nodeWeights;

  1173.         /** Number of points. */
  1174.         private final int numberOfPoints;

  1175.         /**
  1176.          * Creates a Gauss integrator of the given order.
  1177.          *
  1178.          * @param numberOfPoints Order of the integration rule.
  1179.          */
  1180.         GaussQuadrature(final int numberOfPoints) {

  1181.             this.numberOfPoints = numberOfPoints;

  1182.             switch (numberOfPoints) {
  1183.                 case 12:
  1184.                     this.nodePoints = P_12.clone();
  1185.                     this.nodeWeights = W_12.clone();
  1186.                     break;
  1187.                 case 16:
  1188.                     this.nodePoints = P_16.clone();
  1189.                     this.nodeWeights = W_16.clone();
  1190.                     break;
  1191.                 case 20:
  1192.                     this.nodePoints = P_20.clone();
  1193.                     this.nodeWeights = W_20.clone();
  1194.                     break;
  1195.                 case 24:
  1196.                     this.nodePoints = P_24.clone();
  1197.                     this.nodeWeights = W_24.clone();
  1198.                     break;
  1199.                 case 32:
  1200.                     this.nodePoints = P_32.clone();
  1201.                     this.nodeWeights = W_32.clone();
  1202.                     break;
  1203.                 case 40:
  1204.                     this.nodePoints = P_40.clone();
  1205.                     this.nodeWeights = W_40.clone();
  1206.                     break;
  1207.                 case 48:
  1208.                 default:
  1209.                     this.nodePoints = P_48.clone();
  1210.                     this.nodeWeights = W_48.clone();
  1211.                     break;
  1212.             }

  1213.         }

  1214.         /**
  1215.          * Integrates a given function on the given interval.
  1216.          *
  1217.          * @param f          Function to integrate.
  1218.          * @param lowerBound Lower bound of the integration interval.
  1219.          * @param upperBound Upper bound of the integration interval.
  1220.          * @return the integral of the weighted function.
  1221.          */
  1222.         public double[] integrate(final UnivariateVectorFunction f, final double lowerBound, final double upperBound) {

  1223.             final double[] adaptedPoints = nodePoints.clone();
  1224.             final double[] adaptedWeights = nodeWeights.clone();
  1225.             transform(adaptedPoints, adaptedWeights, lowerBound, upperBound);
  1226.             return basicIntegrate(f, adaptedPoints, adaptedWeights);
  1227.         }

  1228.         /**
  1229.          * Integrates a given function on the given interval.
  1230.          *
  1231.          * @param <T>        the type of the field elements
  1232.          * @param f          Function to integrate.
  1233.          * @param lowerBound Lower bound of the integration interval.
  1234.          * @param upperBound Upper bound of the integration interval.
  1235.          * @param field      field utilized by default
  1236.          * @return the integral of the weighted function.
  1237.          */
  1238.         public <T extends CalculusFieldElement<T>> T[] integrate(final CalculusFieldUnivariateVectorFunction<T> f,
  1239.                 final T lowerBound, final T upperBound, final Field<T> field) {

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

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

  1243.             for (int i = 0; i < numberOfPoints; i++) {
  1244.                 adaptedPoints[i] = zero.newInstance(nodePoints[i]);
  1245.                 adaptedWeights[i] = zero.newInstance(nodeWeights[i]);
  1246.             }

  1247.             transform(adaptedPoints, adaptedWeights, lowerBound, upperBound);
  1248.             return basicIntegrate(f, adaptedPoints, adaptedWeights, field);
  1249.         }

  1250.         /**
  1251.          * Performs a change of variable so that the integration can be performed on an
  1252.          * arbitrary interval {@code [a, b]}.
  1253.          * <p>
  1254.          * It is assumed that the natural interval is {@code [-1, 1]}.
  1255.          * </p>
  1256.          *
  1257.          * @param points  Points to adapt to the new interval.
  1258.          * @param weights Weights to adapt to the new interval.
  1259.          * @param a       Lower bound of the integration interval.
  1260.          * @param b       Lower bound of the integration interval.
  1261.          */
  1262.         private void transform(final double[] points, final double[] weights, final double a, final double b) {
  1263.             // Scaling
  1264.             final double scale = (b - a) / 2;
  1265.             final double shift = a + scale;
  1266.             for (int i = 0; i < points.length; i++) {
  1267.                 points[i] = points[i] * scale + shift;
  1268.                 weights[i] *= scale;
  1269.             }
  1270.         }

  1271.         /**
  1272.          * Performs a change of variable so that the integration can be performed on an
  1273.          * arbitrary interval {@code [a, b]}.
  1274.          * <p>
  1275.          * It is assumed that the natural interval is {@code [-1, 1]}.
  1276.          * </p>
  1277.          * @param <T>     the type of the field elements
  1278.          * @param points  Points to adapt to the new interval.
  1279.          * @param weights Weights to adapt to the new interval.
  1280.          * @param a       Lower bound of the integration interval.
  1281.          * @param b       Lower bound of the integration interval
  1282.          */
  1283.         private <T extends CalculusFieldElement<T>> void transform(final T[] points, final T[] weights, final T a,
  1284.                 final T b) {
  1285.             // Scaling
  1286.             final T scale = (b.subtract(a)).divide(2.);
  1287.             final T shift = a.add(scale);
  1288.             for (int i = 0; i < points.length; i++) {
  1289.                 points[i] = scale.multiply(points[i]).add(shift);
  1290.                 weights[i] = scale.multiply(weights[i]);
  1291.             }
  1292.         }

  1293.         /**
  1294.          * Returns an estimate of the integral of {@code f(x) * w(x)}, where {@code w}
  1295.          * is a weight function that depends on the actual flavor of the Gauss
  1296.          * integration scheme.
  1297.          *
  1298.          * @param f       Function to integrate.
  1299.          * @param points  Nodes.
  1300.          * @param weights Nodes weights.
  1301.          * @return the integral of the weighted function.
  1302.          */
  1303.         private double[] basicIntegrate(final UnivariateVectorFunction f, final double[] points,
  1304.                 final double[] weights) {
  1305.             double x = points[0];
  1306.             double w = weights[0];
  1307.             double[] v = f.value(x);
  1308.             final double[] y = new double[v.length];
  1309.             for (int j = 0; j < v.length; j++) {
  1310.                 y[j] = w * v[j];
  1311.             }
  1312.             final double[] t = y.clone();
  1313.             final double[] c = new double[v.length];
  1314.             final double[] s = t.clone();
  1315.             for (int i = 1; i < points.length; i++) {
  1316.                 x = points[i];
  1317.                 w = weights[i];
  1318.                 v = f.value(x);
  1319.                 for (int j = 0; j < v.length; j++) {
  1320.                     y[j] = w * v[j] - c[j];
  1321.                     t[j] = s[j] + y[j];
  1322.                     c[j] = (t[j] - s[j]) - y[j];
  1323.                     s[j] = t[j];
  1324.                 }
  1325.             }
  1326.             return s;
  1327.         }

  1328.         /**
  1329.          * Returns an estimate of the integral of {@code f(x) * w(x)}, where {@code w}
  1330.          * is a weight function that depends on the actual flavor of the Gauss
  1331.          * integration scheme.
  1332.          *
  1333.          * @param <T>     the type of the field elements.
  1334.          * @param f       Function to integrate.
  1335.          * @param points  Nodes.
  1336.          * @param weights Nodes weight
  1337.          * @param field   field utilized by default
  1338.          * @return the integral of the weighted function.
  1339.          */
  1340.         private <T extends CalculusFieldElement<T>> T[] basicIntegrate(final CalculusFieldUnivariateVectorFunction<T> f,
  1341.                 final T[] points, final T[] weights, final Field<T> field) {

  1342.             T x = points[0];
  1343.             T w = weights[0];
  1344.             T[] v = f.value(x);

  1345.             final T[] y = MathArrays.buildArray(field, v.length);
  1346.             for (int j = 0; j < v.length; j++) {
  1347.                 y[j] = v[j].multiply(w);
  1348.             }
  1349.             final T[] t = y.clone();
  1350.             final T[] c = MathArrays.buildArray(field, v.length);
  1351.             final T[] s = t.clone();
  1352.             for (int i = 1; i < points.length; i++) {
  1353.                 x = points[i];
  1354.                 w = weights[i];
  1355.                 v = f.value(x);
  1356.                 for (int j = 0; j < v.length; j++) {
  1357.                     y[j] = v[j].multiply(w).subtract(c[j]);
  1358.                     t[j] = y[j].add(s[j]);
  1359.                     c[j] = (t[j].subtract(s[j])).subtract(y[j]);
  1360.                     s[j] = t[j];
  1361.                 }
  1362.             }
  1363.             return s;
  1364.         }

  1365.     }

  1366.     /**
  1367.      * Compute the C<sub>i</sub><sup>j</sup> and the S<sub>i</sub><sup>j</sup>
  1368.      * coefficients.
  1369.      * <p>
  1370.      * Those coefficients are given in Danielson paper by expression 4.4-(6)
  1371.      * </p>
  1372.      * @author Petre Bazavan
  1373.      * @author Lucian Barbulescu
  1374.      */
  1375.     protected class FourierCjSjCoefficients {

  1376.         /** Maximum possible value for j. */
  1377.         private final int jMax;

  1378.         /**
  1379.          * The C<sub>i</sub><sup>j</sup> coefficients.
  1380.          * <p>
  1381.          * the index i corresponds to the following elements: <br/>
  1382.          * - 0 for a <br>
  1383.          * - 1 for k <br>
  1384.          * - 2 for h <br>
  1385.          * - 3 for q <br>
  1386.          * - 4 for p <br>
  1387.          * - 5 for λ <br>
  1388.          * </p>
  1389.          */
  1390.         private final double[][] cCoef;

  1391.         /**
  1392.          * The C<sub>i</sub><sup>j</sup> coefficients.
  1393.          * <p>
  1394.          * the index i corresponds to the following elements: <br/>
  1395.          * - 0 for a <br>
  1396.          * - 1 for k <br>
  1397.          * - 2 for h <br>
  1398.          * - 3 for q <br>
  1399.          * - 4 for p <br>
  1400.          * - 5 for λ <br>
  1401.          * </p>
  1402.          */
  1403.         private final double[][] sCoef;

  1404.         /**
  1405.          * Standard constructor.
  1406.          * @param state             the current state
  1407.          * @param jMax              maximum value for j
  1408.          * @param auxiliaryElements auxiliary elements related to the current orbit
  1409.          * @param parameters        list of parameter values at state date for each driver
  1410.          * of the force model parameters (1 value per parameter)
  1411.          */
  1412.         FourierCjSjCoefficients(final SpacecraftState state, final int jMax, final AuxiliaryElements auxiliaryElements,
  1413.                 final double[] parameters) {

  1414.             // Initialise the fields
  1415.             this.jMax = jMax;

  1416.             // Allocate the arrays
  1417.             final int rows = jMax + 1;
  1418.             cCoef = new double[rows][6];
  1419.             sCoef = new double[rows][6];

  1420.             // Compute the coefficients
  1421.             computeCoefficients(state, auxiliaryElements, parameters);
  1422.         }

  1423.         /**
  1424.          * Compute the Fourrier coefficients.
  1425.          * <p>
  1426.          * Only the C<sub>i</sub><sup>j</sup> and S<sub>i</sub><sup>j</sup> coefficients
  1427.          * need to be computed as D<sub>i</sub><sup>m</sup> is always 0.
  1428.          * </p>
  1429.          * @param state             the current state
  1430.          * @param auxiliaryElements auxiliary elements related to the current orbit
  1431.          * @param parameters        list of parameter values at state date for each driver
  1432.          * of the force model parameters (1 value per parameter)
  1433.          */
  1434.         private void computeCoefficients(final SpacecraftState state, final AuxiliaryElements auxiliaryElements,
  1435.                 final double[] parameters) {

  1436.             // Computes the limits for the integral
  1437.             final double[] ll = getLLimits(state, auxiliaryElements);
  1438.             // Computes integrated mean element rates if Llow < Lhigh
  1439.             if (ll[0] < ll[1]) {
  1440.                 // Compute 1 / PI
  1441.                 final double ooPI = 1 / FastMath.PI;

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

  1446.                     // divide by PI and set the values for the coefficients
  1447.                     for (int i = 0; i < 6; i++) {
  1448.                         cCoef[j][i] = ooPI * curentCoefficients[i];
  1449.                         sCoef[j][i] = ooPI * curentCoefficients[i + 6];
  1450.                     }
  1451.                 }
  1452.             }
  1453.         }

  1454.         /**
  1455.          * Get the coefficient C<sub>i</sub><sup>j</sup>.
  1456.          * @param i i index - corresponds to the required variation
  1457.          * @param j j index
  1458.          * @return the coefficient C<sub>i</sub><sup>j</sup>
  1459.          */
  1460.         public double getCij(final int i, final int j) {
  1461.             return cCoef[j][i];
  1462.         }

  1463.         /**
  1464.          * Get the coefficient S<sub>i</sub><sup>j</sup>.
  1465.          * @param i i index - corresponds to the required variation
  1466.          * @param j j index
  1467.          * @return the coefficient S<sub>i</sub><sup>j</sup>
  1468.          */
  1469.         public double getSij(final int i, final int j) {
  1470.             return sCoef[j][i];
  1471.         }
  1472.     }

  1473.     /**
  1474.      * Compute the C<sub>i</sub><sup>j</sup> and the S<sub>i</sub><sup>j</sup>
  1475.      * coefficients with field elements.
  1476.      * <p>
  1477.      * Those coefficients are given in Danielson paper by expression 4.4-(6)
  1478.      * </p>
  1479.      * @author Petre Bazavan
  1480.      * @author Lucian Barbulescu
  1481.      * @param <T> type of the field elements
  1482.      */
  1483.     protected class FieldFourierCjSjCoefficients<T extends CalculusFieldElement<T>> {

  1484.         /** Maximum possible value for j. */
  1485.         private final int jMax;

  1486.         /**
  1487.          * The C<sub>i</sub><sup>j</sup> coefficients.
  1488.          * <p>
  1489.          * the index i corresponds to the following elements: <br/>
  1490.          * - 0 for a <br>
  1491.          * - 1 for k <br>
  1492.          * - 2 for h <br>
  1493.          * - 3 for q <br>
  1494.          * - 4 for p <br>
  1495.          * - 5 for λ <br>
  1496.          * </p>
  1497.          */
  1498.         private final T[][] cCoef;

  1499.         /**
  1500.          * The C<sub>i</sub><sup>j</sup> coefficients.
  1501.          * <p>
  1502.          * the index i corresponds to the following elements: <br/>
  1503.          * - 0 for a <br>
  1504.          * - 1 for k <br>
  1505.          * - 2 for h <br>
  1506.          * - 3 for q <br>
  1507.          * - 4 for p <br>
  1508.          * - 5 for λ <br>
  1509.          * </p>
  1510.          */
  1511.         private final T[][] sCoef;

  1512.         /**
  1513.          * Standard constructor.
  1514.          * @param state             the current state
  1515.          * @param jMax              maximum value for j
  1516.          * @param auxiliaryElements auxiliary elements related to the current orbit
  1517.          * @param parameters        values of the force model parameters
  1518.          * @param field             field used by default
  1519.          */
  1520.         FieldFourierCjSjCoefficients(final FieldSpacecraftState<T> state, final int jMax,
  1521.                 final FieldAuxiliaryElements<T> auxiliaryElements, final T[] parameters, final Field<T> field) {
  1522.             // Initialise the fields
  1523.             this.jMax = jMax;

  1524.             // Allocate the arrays
  1525.             final int rows = jMax + 1;
  1526.             cCoef = MathArrays.buildArray(field, rows, 6);
  1527.             sCoef = MathArrays.buildArray(field, rows, 6);

  1528.             // Compute the coefficients
  1529.             computeCoefficients(state, auxiliaryElements, parameters, field);
  1530.         }

  1531.         /**
  1532.          * Compute the Fourrier coefficients.
  1533.          * <p>
  1534.          * Only the C<sub>i</sub><sup>j</sup> and S<sub>i</sub><sup>j</sup> coefficients
  1535.          * need to be computed as D<sub>i</sub><sup>m</sup> is always 0.
  1536.          * </p>
  1537.          * @param state             the current state
  1538.          * @param auxiliaryElements auxiliary elements related to the current orbit
  1539.          * @param parameters        values of the force model parameters
  1540.          * @param field             field used by default
  1541.          */
  1542.         private void computeCoefficients(final FieldSpacecraftState<T> state,
  1543.                 final FieldAuxiliaryElements<T> auxiliaryElements, final T[] parameters, final Field<T> field) {
  1544.             // Zero
  1545.             final T zero = field.getZero();
  1546.             // Computes the limits for the integral
  1547.             final T[] ll = getLLimits(state, auxiliaryElements);
  1548.             // Computes integrated mean element rates if Llow < Lhigh
  1549.             if (ll[0].getReal() < ll[1].getReal()) {
  1550.                 // Compute 1 / PI
  1551.                 final T ooPI = zero.getPi().reciprocal();

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

  1556.                     // divide by PI and set the values for the coefficients
  1557.                     for (int i = 0; i < 6; i++) {
  1558.                         cCoef[j][i] = curentCoefficients[i].multiply(ooPI);
  1559.                         sCoef[j][i] = curentCoefficients[i + 6].multiply(ooPI);
  1560.                     }
  1561.                 }
  1562.             }
  1563.         }

  1564.         /**
  1565.          * Get the coefficient C<sub>i</sub><sup>j</sup>.
  1566.          * @param i i index - corresponds to the required variation
  1567.          * @param j j index
  1568.          * @return the coefficient C<sub>i</sub><sup>j</sup>
  1569.          */
  1570.         public T getCij(final int i, final int j) {
  1571.             return cCoef[j][i];
  1572.         }

  1573.         /**
  1574.          * Get the coefficient S<sub>i</sub><sup>j</sup>.
  1575.          * @param i i index - corresponds to the required variation
  1576.          * @param j j index
  1577.          * @return the coefficient S<sub>i</sub><sup>j</sup>
  1578.          */
  1579.         public T getSij(final int i, final int j) {
  1580.             return sCoef[j][i];
  1581.         }
  1582.     }

  1583.     /**
  1584.      * This class handles the short periodic coefficients described in Danielson
  1585.      * 2.5.3-26.
  1586.      *
  1587.      * <p>
  1588.      * The value of M is 0. Also, since the values of the Fourier coefficient
  1589.      * D<sub>i</sub><sup>m</sup> is 0 then the values of the coefficients
  1590.      * D<sub>i</sub><sup>m</sup> for m &gt; 2 are also 0.
  1591.      * </p>
  1592.      * @author Petre Bazavan
  1593.      * @author Lucian Barbulescu
  1594.      *
  1595.      */
  1596.     protected static class GaussianShortPeriodicCoefficients implements ShortPeriodTerms {

  1597.         /** Maximum value for j index. */
  1598.         private final int jMax;

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

  1601.         /** Prefix for coefficients keys. */
  1602.         private final String coefficientsKeyPrefix;

  1603.         /** All coefficients slots. */
  1604.         private final transient TimeSpanMap<Slot> slots;

  1605.         /**
  1606.          * Constructor.
  1607.          * @param coefficientsKeyPrefix prefix for coefficients keys
  1608.          * @param jMax                  maximum value for j index
  1609.          * @param interpolationPoints   number of points used in the interpolation
  1610.          *                              process
  1611.          * @param slots                 all coefficients slots
  1612.          */
  1613.         GaussianShortPeriodicCoefficients(final String coefficientsKeyPrefix, final int jMax,
  1614.                 final int interpolationPoints, final TimeSpanMap<Slot> slots) {
  1615.             // Initialize fields
  1616.             this.jMax = jMax;
  1617.             this.interpolationPoints = interpolationPoints;
  1618.             this.coefficientsKeyPrefix = coefficientsKeyPrefix;
  1619.             this.slots = slots;
  1620.         }

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

  1641.         /**
  1642.          * Compute the short periodic coefficients.
  1643.          *
  1644.          * @param state       current state information: date, kinematics, attitude
  1645.          * @param slot        coefficients slot
  1646.          * @param fourierCjSj Fourier coefficients
  1647.          * @param uijvij      U and V coefficients
  1648.          * @param n           Keplerian mean motion
  1649.          * @param a           semi major axis
  1650.          */
  1651.         private void computeCoefficients(final SpacecraftState state, final Slot slot,
  1652.                 final FourierCjSjCoefficients fourierCjSj, final UijVijCoefficients uijvij, final double n,
  1653.                 final double a) {

  1654.             // get the current date
  1655.             final AbsoluteDate date = state.getDate();

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

  1658.             // 1. / n
  1659.             final double oon = 1. / n;
  1660.             // 3. / (2 * a * n)
  1661.             final double to2an = 1.5 * oon / a;
  1662.             // 3. / (4 * a * n)
  1663.             final double to4an = to2an / 2;

  1664.             // Compute the coefficients for each element
  1665.             final int size = jMax + 1;
  1666.             final double[] di1 = new double[6];
  1667.             final double[] di2 = new double[6];
  1668.             final double[][] currentCij = new double[size][6];
  1669.             final double[][] currentSij = new double[size][6];
  1670.             for (int i = 0; i < 6; i++) {

  1671.                 // compute D<sub>i</sub>¹ and D<sub>i</sub>² (all others are 0)
  1672.                 di1[i] = -oon * fourierCjSj.getCij(i, 0);
  1673.                 if (i == 5) {
  1674.                     di1[i] += to2an * uijvij.getU1(0, 0);
  1675.                 }
  1676.                 di2[i] = 0.;
  1677.                 if (i == 5) {
  1678.                     di2[i] += -to4an * fourierCjSj.getCij(0, 0);
  1679.                 }

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

  1682.                 for (int j = 1; j <= jMax; j++) {
  1683.                     // compute the current C<sub>i</sub><sup>j</sup> and S<sub>i</sub><sup>j</sup>
  1684.                     currentCij[j][i] = oon * uijvij.getU1(j, i);
  1685.                     if (i == 5) {
  1686.                         currentCij[j][i] += -to2an * uijvij.getU2(j);
  1687.                     }
  1688.                     currentSij[j][i] = oon * uijvij.getV1(j, i);
  1689.                     if (i == 5) {
  1690.                         currentSij[j][i] += -to2an * uijvij.getV2(j);
  1691.                     }

  1692.                     // add the computed coefficients to C<sub>i</sub>⁰
  1693.                     currentCij[0][i] -= currentCij[j][i] * uijvij.currentRhoSigmaj[0][j] +
  1694.                         currentSij[j][i] * uijvij.currentRhoSigmaj[1][j];
  1695.                 }

  1696.             }

  1697.             // add the values to the interpolators
  1698.             slot.cij[0].addGridPoint(date, currentCij[0]);
  1699.             slot.dij[1].addGridPoint(date, di1);
  1700.             slot.dij[2].addGridPoint(date, di2);
  1701.             for (int j = 1; j <= jMax; j++) {
  1702.                 slot.cij[j].addGridPoint(date, currentCij[j]);
  1703.                 slot.sij[j].addGridPoint(date, currentSij[j]);
  1704.             }

  1705.         }

  1706.         /**
  1707.          * Compute the coefficient k₂⁰ by using the equation 2.5.3-(9a) from Danielson.
  1708.          * <p>
  1709.          * After inserting 2.5.3-(8) into 2.5.3-(9a) the result becomes:<br>
  1710.          * k₂⁰ = &Sigma;<sub>k=1</sub><sup>kMax</sup>[(2 / k²) * (σ<sub>k</sub>² +
  1711.          * ρ<sub>k</sub>²)]
  1712.          * </p>
  1713.          * @param kMax             max value fot k index
  1714.          * @param currentRhoSigmaj the current computed values for the ρ<sub>j</sub> and
  1715.          *                         σ<sub>j</sub> coefficients
  1716.          * @return the coefficient k₂⁰
  1717.          */
  1718.         private double computeK20(final int kMax, final double[][] currentRhoSigmaj) {
  1719.             double k20 = 0.;

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

  1726.                 // multiply by 2 / k²
  1727.                 currentTerm *= 2. / (kIndex * kIndex);

  1728.                 // add the term to the result
  1729.                 k20 += currentTerm;
  1730.             }

  1731.             return k20;
  1732.         }

  1733.         /** {@inheritDoc} */
  1734.         @Override
  1735.         public double[] value(final Orbit meanOrbit) {

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

  1738.             // Get the True longitude L
  1739.             final double L = meanOrbit.getLv();

  1740.             // Compute the center (l - λ)
  1741.             final double center = L - meanOrbit.getLM();
  1742.             // Compute (l - λ)²
  1743.             final double center2 = center * center;

  1744.             // Initialize short periodic variations
  1745.             final double[] shortPeriodicVariation = slot.cij[0].value(meanOrbit.getDate());
  1746.             final double[] d1 = slot.dij[1].value(meanOrbit.getDate());
  1747.             final double[] d2 = slot.dij[2].value(meanOrbit.getDate());
  1748.             for (int i = 0; i < 6; i++) {
  1749.                 shortPeriodicVariation[i] += center * d1[i] + center2 * d2[i];
  1750.             }

  1751.             for (int j = 1; j <= JMAX; j++) {
  1752.                 final double[] c = slot.cij[j].value(meanOrbit.getDate());
  1753.                 final double[] s = slot.sij[j].value(meanOrbit.getDate());
  1754.                 final SinCos sc  = FastMath.sinCos(j * L);
  1755.                 final double cos = sc.cos();
  1756.                 final double sin = sc.sin();
  1757.                 for (int i = 0; i < 6; i++) {
  1758.                     // add corresponding term to the short periodic variation
  1759.                     shortPeriodicVariation[i] += c[i] * cos;
  1760.                     shortPeriodicVariation[i] += s[i] * sin;
  1761.                 }
  1762.             }

  1763.             return shortPeriodicVariation;

  1764.         }

  1765.         /** {@inheritDoc} */
  1766.         public String getCoefficientsKeyPrefix() {
  1767.             return coefficientsKeyPrefix;
  1768.         }

  1769.         /**
  1770.          * {@inheritDoc}
  1771.          * <p>
  1772.          * For Gaussian forces, there are JMAX cj coefficients, JMAX sj coefficients and
  1773.          * 3 dj coefficients. As JMAX = 12, this sums up to 27 coefficients. The j index
  1774.          * is the integer multiplier for the true longitude argument in the cj and sj
  1775.          * coefficients and to the degree in the polynomial dj coefficients.
  1776.          * </p>
  1777.          */
  1778.         @Override
  1779.         public Map<String, double[]> getCoefficients(final AbsoluteDate date, final Set<String> selected) {

  1780.             // select the coefficients slot
  1781.             final Slot slot = slots.get(date);

  1782.             final Map<String, double[]> coefficients = new HashMap<>(2 * JMAX + 3);
  1783.             storeIfSelected(coefficients, selected, slot.cij[0].value(date), "d", 0);
  1784.             storeIfSelected(coefficients, selected, slot.dij[1].value(date), "d", 1);
  1785.             storeIfSelected(coefficients, selected, slot.dij[2].value(date), "d", 2);
  1786.             for (int j = 1; j <= JMAX; j++) {
  1787.                 storeIfSelected(coefficients, selected, slot.cij[j].value(date), "c", j);
  1788.                 storeIfSelected(coefficients, selected, slot.sij[j].value(date), "s", j);
  1789.             }

  1790.             return coefficients;

  1791.         }

  1792.         /**
  1793.          * Put a coefficient in a map if selected.
  1794.          * @param map      map to populate
  1795.          * @param selected set of coefficients that should be put in the map (empty set
  1796.          *                 means all coefficients are selected)
  1797.          * @param value    coefficient value
  1798.          * @param id       coefficient identifier
  1799.          * @param indices  list of coefficient indices
  1800.          */
  1801.         private void storeIfSelected(final Map<String, double[]> map, final Set<String> selected, final double[] value,
  1802.                 final String id, final int... indices) {
  1803.             final StringBuilder keyBuilder = new StringBuilder(getCoefficientsKeyPrefix());
  1804.             keyBuilder.append(id);
  1805.             for (int index : indices) {
  1806.                 keyBuilder.append('[').append(index).append(']');
  1807.             }
  1808.             final String key = keyBuilder.toString();
  1809.             if (selected.isEmpty() || selected.contains(key)) {
  1810.                 map.put(key, value);
  1811.             }
  1812.         }

  1813.     }

  1814.     /**
  1815.      * This class handles the short periodic coefficients described in Danielson
  1816.      * 2.5.3-26.
  1817.      *
  1818.      * <p>
  1819.      * The value of M is 0. Also, since the values of the Fourier coefficient
  1820.      * D<sub>i</sub><sup>m</sup> is 0 then the values of the coefficients
  1821.      * D<sub>i</sub><sup>m</sup> for m &gt; 2 are also 0.
  1822.      * </p>
  1823.      * @author Petre Bazavan
  1824.      * @author Lucian Barbulescu
  1825.      * @param <T> type of the field elements
  1826.      */
  1827.     protected static class FieldGaussianShortPeriodicCoefficients<T extends CalculusFieldElement<T>>
  1828.             implements FieldShortPeriodTerms<T> {

  1829.         /** Maximum value for j index. */
  1830.         private final int jMax;

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

  1833.         /** Prefix for coefficients keys. */
  1834.         private final String coefficientsKeyPrefix;

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

  1837.         /**
  1838.          * Constructor.
  1839.          * @param coefficientsKeyPrefix prefix for coefficients keys
  1840.          * @param jMax                  maximum value for j index
  1841.          * @param interpolationPoints   number of points used in the interpolation
  1842.          *                              process
  1843.          * @param slots                 all coefficients slots
  1844.          */
  1845.         FieldGaussianShortPeriodicCoefficients(final String coefficientsKeyPrefix, final int jMax,
  1846.                 final int interpolationPoints, final FieldTimeSpanMap<FieldSlot<T>, T> slots) {
  1847.             // Initialize fields
  1848.             this.jMax = jMax;
  1849.             this.interpolationPoints = interpolationPoints;
  1850.             this.coefficientsKeyPrefix = coefficientsKeyPrefix;
  1851.             this.slots = slots;
  1852.         }

  1853.         /**
  1854.          * Get the slot valid for some date.
  1855.          * @param meanStates mean states defining the slot
  1856.          * @return slot valid at the specified date
  1857.          */
  1858.         @SuppressWarnings("unchecked")
  1859.         public FieldSlot<T> createSlot(final FieldSpacecraftState<T>... meanStates) {
  1860.             final FieldSlot<T> slot = new FieldSlot<>(jMax, interpolationPoints);
  1861.             final FieldAbsoluteDate<T> first = meanStates[0].getDate();
  1862.             final FieldAbsoluteDate<T> last = meanStates[meanStates.length - 1].getDate();
  1863.             if (first.compareTo(last) <= 0) {
  1864.                 slots.addValidAfter(slot, first);
  1865.             } else {
  1866.                 slots.addValidBefore(slot, first);
  1867.             }
  1868.             return slot;
  1869.         }

  1870.         /**
  1871.          * Compute the short periodic coefficients.
  1872.          *
  1873.          * @param state       current state information: date, kinematics, attitude
  1874.          * @param slot        coefficients slot
  1875.          * @param fourierCjSj Fourier coefficients
  1876.          * @param uijvij      U and V coefficients
  1877.          * @param n           Keplerian mean motion
  1878.          * @param a           semi major axis
  1879.          * @param field       field used by default
  1880.          */
  1881.         private void computeCoefficients(final FieldSpacecraftState<T> state, final FieldSlot<T> slot,
  1882.                 final FieldFourierCjSjCoefficients<T> fourierCjSj, final FieldUijVijCoefficients<T> uijvij, final T n,
  1883.                 final T a, final Field<T> field) {

  1884.             // Zero
  1885.             final T zero = field.getZero();

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

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

  1890.             // 1. / n
  1891.             final T oon = n.reciprocal();
  1892.             // 3. / (2 * a * n)
  1893.             final T to2an = oon.multiply(1.5).divide(a);
  1894.             // 3. / (4 * a * n)
  1895.             final T to4an = to2an.divide(2.);

  1896.             // Compute the coefficients for each element
  1897.             final int size = jMax + 1;
  1898.             final T[] di1 = MathArrays.buildArray(field, 6);
  1899.             final T[] di2 = MathArrays.buildArray(field, 6);
  1900.             final T[][] currentCij = MathArrays.buildArray(field, size, 6);
  1901.             final T[][] currentSij = MathArrays.buildArray(field, size, 6);
  1902.             for (int i = 0; i < 6; i++) {

  1903.                 // compute D<sub>i</sub>¹ and D<sub>i</sub>² (all others are 0)
  1904.                 di1[i] = oon.negate().multiply(fourierCjSj.getCij(i, 0));
  1905.                 if (i == 5) {
  1906.                     di1[i] = di1[i].add(to2an.multiply(uijvij.getU1(0, 0)));
  1907.                 }
  1908.                 di2[i] = zero;
  1909.                 if (i == 5) {
  1910.                     di2[i] = di2[i].add(to4an.negate().multiply(fourierCjSj.getCij(0, 0)));
  1911.                 }

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

  1914.                 for (int j = 1; j <= jMax; j++) {
  1915.                     // compute the current C<sub>i</sub><sup>j</sup> and S<sub>i</sub><sup>j</sup>
  1916.                     currentCij[j][i] = oon.multiply(uijvij.getU1(j, i));
  1917.                     if (i == 5) {
  1918.                         currentCij[j][i] = currentCij[j][i].add(to2an.negate().multiply(uijvij.getU2(j)));
  1919.                     }
  1920.                     currentSij[j][i] = oon.multiply(uijvij.getV1(j, i));
  1921.                     if (i == 5) {
  1922.                         currentSij[j][i] = currentSij[j][i].add(to2an.negate().multiply(uijvij.getV2(j)));
  1923.                     }

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

  1928.             }

  1929.             // add the values to the interpolators
  1930.             slot.cij[0].addGridPoint(date, currentCij[0]);
  1931.             slot.dij[1].addGridPoint(date, di1);
  1932.             slot.dij[2].addGridPoint(date, di2);
  1933.             for (int j = 1; j <= jMax; j++) {
  1934.                 slot.cij[j].addGridPoint(date, currentCij[j]);
  1935.                 slot.sij[j].addGridPoint(date, currentSij[j]);
  1936.             }

  1937.         }

  1938.         /**
  1939.          * Compute the coefficient k₂⁰ by using the equation 2.5.3-(9a) from Danielson.
  1940.          * <p>
  1941.          * After inserting 2.5.3-(8) into 2.5.3-(9a) the result becomes:<br>
  1942.          * k₂⁰ = &Sigma;<sub>k=1</sub><sup>kMax</sup>[(2 / k²) * (σ<sub>k</sub>² +
  1943.          * ρ<sub>k</sub>²)]
  1944.          * </p>
  1945.          * @param kMax             max value fot k index
  1946.          * @param currentRhoSigmaj the current computed values for the ρ<sub>j</sub> and
  1947.          *                         σ<sub>j</sub> coefficients
  1948.          * @param field            field used by default
  1949.          * @return the coefficient k₂⁰
  1950.          */
  1951.         private T computeK20(final int kMax, final T[][] currentRhoSigmaj, final Field<T> field) {
  1952.             T k20 = field.getZero();

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

  1959.                 // multiply by 2 / k²
  1960.                 currentTerm = currentTerm.multiply(2. / (kIndex * kIndex));

  1961.                 // add the term to the result
  1962.                 k20 = k20.add(currentTerm);
  1963.             }

  1964.             return k20;
  1965.         }

  1966.         /** {@inheritDoc} */
  1967.         @Override
  1968.         public T[] value(final FieldOrbit<T> meanOrbit) {

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

  1971.             // Get the True longitude L
  1972.             final T L = meanOrbit.getLv();

  1973.             // Compute the center (l - λ)
  1974.             final T center = L.subtract(meanOrbit.getLM());
  1975.             // Compute (l - λ)²
  1976.             final T center2 = center.square();

  1977.             // Initialize short periodic variations
  1978.             final T[] shortPeriodicVariation = slot.cij[0].value(meanOrbit.getDate());
  1979.             final T[] d1 = slot.dij[1].value(meanOrbit.getDate());
  1980.             final T[] d2 = slot.dij[2].value(meanOrbit.getDate());
  1981.             for (int i = 0; i < 6; i++) {
  1982.                 shortPeriodicVariation[i] = shortPeriodicVariation[i]
  1983.                         .add(center.multiply(d1[i]).add(center2.multiply(d2[i])));
  1984.             }

  1985.             for (int j = 1; j <= JMAX; j++) {
  1986.                 final T[] c = slot.cij[j].value(meanOrbit.getDate());
  1987.                 final T[] s = slot.sij[j].value(meanOrbit.getDate());
  1988.                 final FieldSinCos<T> sc = FastMath.sinCos(L.multiply(j));
  1989.                 final T cos = sc.cos();
  1990.                 final T sin = sc.sin();
  1991.                 for (int i = 0; i < 6; i++) {
  1992.                     // add corresponding term to the short periodic variation
  1993.                     shortPeriodicVariation[i] = shortPeriodicVariation[i].add(c[i].multiply(cos));
  1994.                     shortPeriodicVariation[i] = shortPeriodicVariation[i].add(s[i].multiply(sin));
  1995.                 }
  1996.             }

  1997.             return shortPeriodicVariation;

  1998.         }

  1999.         /** {@inheritDoc} */
  2000.         public String getCoefficientsKeyPrefix() {
  2001.             return coefficientsKeyPrefix;
  2002.         }

  2003.         /**
  2004.          * {@inheritDoc}
  2005.          * <p>
  2006.          * For Gaussian forces, there are JMAX cj coefficients, JMAX sj coefficients and
  2007.          * 3 dj coefficients. As JMAX = 12, this sums up to 27 coefficients. The j index
  2008.          * is the integer multiplier for the true longitude argument in the cj and sj
  2009.          * coefficients and to the degree in the polynomial dj coefficients.
  2010.          * </p>
  2011.          */
  2012.         @Override
  2013.         public Map<String, T[]> getCoefficients(final FieldAbsoluteDate<T> date, final Set<String> selected) {

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

  2016.             final Map<String, T[]> coefficients = new HashMap<>(2 * JMAX + 3);
  2017.             storeIfSelected(coefficients, selected, slot.cij[0].value(date), "d", 0);
  2018.             storeIfSelected(coefficients, selected, slot.dij[1].value(date), "d", 1);
  2019.             storeIfSelected(coefficients, selected, slot.dij[2].value(date), "d", 2);
  2020.             for (int j = 1; j <= JMAX; j++) {
  2021.                 storeIfSelected(coefficients, selected, slot.cij[j].value(date), "c", j);
  2022.                 storeIfSelected(coefficients, selected, slot.sij[j].value(date), "s", j);
  2023.             }

  2024.             return coefficients;

  2025.         }

  2026.         /**
  2027.          * Put a coefficient in a map if selected.
  2028.          * @param map      map to populate
  2029.          * @param selected set of coefficients that should be put in the map (empty set
  2030.          *                 means all coefficients are selected)
  2031.          * @param value    coefficient value
  2032.          * @param id       coefficient identifier
  2033.          * @param indices  list of coefficient indices
  2034.          */
  2035.         private void storeIfSelected(final Map<String, T[]> map, final Set<String> selected, final T[] value,
  2036.                 final String id, final int... indices) {
  2037.             final StringBuilder keyBuilder = new StringBuilder(getCoefficientsKeyPrefix());
  2038.             keyBuilder.append(id);
  2039.             for (int index : indices) {
  2040.                 keyBuilder.append('[').append(index).append(']');
  2041.             }
  2042.             final String key = keyBuilder.toString();
  2043.             if (selected.isEmpty() || selected.contains(key)) {
  2044.                 map.put(key, value);
  2045.             }
  2046.         }

  2047.     }

  2048.     /**
  2049.      * The U<sub>i</sub><sup>j</sup> and V<sub>i</sub><sup>j</sup> coefficients
  2050.      * described by equations 2.5.3-(21) and 2.5.3-(22) from Danielson.
  2051.      * <p>
  2052.      * The index i takes only the values 1 and 2<br>
  2053.      * For U only the index 0 for j is used.
  2054.      * </p>
  2055.      *
  2056.      * @author Petre Bazavan
  2057.      * @author Lucian Barbulescu
  2058.      */
  2059.     protected static class UijVijCoefficients {

  2060.         /**
  2061.          * The U₁<sup>j</sup> coefficients.
  2062.          * <p>
  2063.          * The first index identifies the Fourier coefficients used<br>
  2064.          * Those coefficients are computed for all Fourier C<sub>i</sub><sup>j</sup> and
  2065.          * S<sub>i</sub><sup>j</sup><br>
  2066.          * The only exception is when j = 0 when only the coefficient for fourier index
  2067.          * = 1 (i == 0) is needed.<br>
  2068.          * Also, for fourier index = 1 (i == 0), the coefficients up to 2 * jMax are
  2069.          * computed, because are required to compute the coefficients U₂<sup>j</sup>
  2070.          * </p>
  2071.          */
  2072.         private final double[][] u1ij;

  2073.         /**
  2074.          * The V₁<sup>j</sup> coefficients.
  2075.          * <p>
  2076.          * The first index identifies the Fourier coefficients used<br>
  2077.          * Those coefficients are computed for all Fourier C<sub>i</sub><sup>j</sup> and
  2078.          * S<sub>i</sub><sup>j</sup><br>
  2079.          * for fourier index = 1 (i == 0), the coefficients up to 2 * jMax are computed,
  2080.          * because are required to compute the coefficients V₂<sup>j</sup>
  2081.          * </p>
  2082.          */
  2083.         private final double[][] v1ij;

  2084.         /**
  2085.          * The U₂<sup>j</sup> coefficients.
  2086.          * <p>
  2087.          * Only the coefficients that use the Fourier index = 1 (i == 0) are computed as
  2088.          * they are the only ones required.
  2089.          * </p>
  2090.          */
  2091.         private final double[] u2ij;

  2092.         /**
  2093.          * The V₂<sup>j</sup> coefficients.
  2094.          * <p>
  2095.          * Only the coefficients that use the Fourier index = 1 (i == 0) are computed as
  2096.          * they are the only ones required.
  2097.          * </p>
  2098.          */
  2099.         private final double[] v2ij;

  2100.         /**
  2101.          * The current computed values for the ρ<sub>j</sub> and σ<sub>j</sub>
  2102.          * coefficients.
  2103.          */
  2104.         private final double[][] currentRhoSigmaj;

  2105.         /**
  2106.          * The C<sub>i</sub><sup>j</sup> and the S<sub>i</sub><sup>j</sup> Fourier
  2107.          * coefficients.
  2108.          */
  2109.         private final FourierCjSjCoefficients fourierCjSj;

  2110.         /** The maximum value for j index. */
  2111.         private final int jMax;

  2112.         /**
  2113.          * Constructor.
  2114.          * @param currentRhoSigmaj the current computed values for the ρ<sub>j</sub> and
  2115.          *                         σ<sub>j</sub> coefficients
  2116.          * @param fourierCjSj      the fourier coefficients C<sub>i</sub><sup>j</sup>
  2117.          *                         and the S<sub>i</sub><sup>j</sup>
  2118.          * @param jMax             maximum value for j index
  2119.          */
  2120.         UijVijCoefficients(final double[][] currentRhoSigmaj, final FourierCjSjCoefficients fourierCjSj,
  2121.                 final int jMax) {
  2122.             this.currentRhoSigmaj = currentRhoSigmaj;
  2123.             this.fourierCjSj = fourierCjSj;
  2124.             this.jMax = jMax;

  2125.             // initialize the internal arrays.
  2126.             this.u1ij = new double[6][2 * jMax + 1];
  2127.             this.v1ij = new double[6][2 * jMax + 1];
  2128.             this.u2ij = new double[jMax + 1];
  2129.             this.v2ij = new double[jMax + 1];

  2130.             // compute the coefficients
  2131.             computeU1V1Coefficients();
  2132.             computeU2V2Coefficients();
  2133.         }

  2134.         /** Build the U₁<sup>j</sup> and V₁<sup>j</sup> coefficients. */
  2135.         private void computeU1V1Coefficients() {
  2136.             // generate the U₁<sup>j</sup> and V₁<sup>j</sup> coefficients
  2137.             // for j >= 1
  2138.             // also the U₁⁰ for Fourier index = 1 (i == 0) coefficient will be computed
  2139.             u1ij[0][0] = 0;
  2140.             for (int j = 1; j <= jMax; j++) {
  2141.                 // compute 1 / j
  2142.                 final double ooj = 1. / j;

  2143.                 for (int i = 0; i < 6; i++) {
  2144.                     // j is aready between 1 and J
  2145.                     u1ij[i][j] = fourierCjSj.getSij(i, j);
  2146.                     v1ij[i][j] = fourierCjSj.getCij(i, j);

  2147.                     // 1 - δ<sub>1j</sub> is 1 for all j > 1
  2148.                     if (j > 1) {
  2149.                         // k starts with 1 because j-J is less than or equal to 0
  2150.                         for (int kIndex = 1; kIndex <= j - 1; kIndex++) {
  2151.                             // C<sub>i</sub><sup>j-k</sup> * σ<sub>k</sub> +
  2152.                             // S<sub>i</sub><sup>j-k</sup> * ρ<sub>k</sub>
  2153.                             u1ij[i][j] += fourierCjSj.getCij(i, j - kIndex) * currentRhoSigmaj[1][kIndex] +
  2154.                                     fourierCjSj.getSij(i, j - kIndex) * currentRhoSigmaj[0][kIndex];

  2155.                             // C<sub>i</sub><sup>j-k</sup> * ρ<sub>k</sub> -
  2156.                             // S<sub>i</sub><sup>j-k</sup> * σ<sub>k</sub>
  2157.                             v1ij[i][j] += fourierCjSj.getCij(i, j - kIndex) * currentRhoSigmaj[0][kIndex] -
  2158.                                     fourierCjSj.getSij(i, j - kIndex) * currentRhoSigmaj[1][kIndex];
  2159.                         }
  2160.                     }

  2161.                     // since j must be between 1 and J-1 and is already between 1 and J
  2162.                     // the following sum is skiped only for j = jMax
  2163.                     if (j != jMax) {
  2164.                         for (int kIndex = 1; kIndex <= jMax - j; kIndex++) {
  2165.                             // -C<sub>i</sub><sup>j+k</sup> * σ<sub>k</sub> +
  2166.                             // S<sub>i</sub><sup>j+k</sup> * ρ<sub>k</sub>
  2167.                             u1ij[i][j] += -fourierCjSj.getCij(i, j + kIndex) * currentRhoSigmaj[1][kIndex] +
  2168.                                     fourierCjSj.getSij(i, j + kIndex) * currentRhoSigmaj[0][kIndex];

  2169.                             // C<sub>i</sub><sup>j+k</sup> * ρ<sub>k</sub> +
  2170.                             // S<sub>i</sub><sup>j+k</sup> * σ<sub>k</sub>
  2171.                             v1ij[i][j] += fourierCjSj.getCij(i, j + kIndex) * currentRhoSigmaj[0][kIndex] +
  2172.                                     fourierCjSj.getSij(i, j + kIndex) * currentRhoSigmaj[1][kIndex];
  2173.                         }
  2174.                     }

  2175.                     for (int kIndex = 1; kIndex <= jMax; kIndex++) {
  2176.                         // C<sub>i</sub><sup>k</sup> * σ<sub>j+k</sub> -
  2177.                         // S<sub>i</sub><sup>k</sup> * ρ<sub>j+k</sub>
  2178.                         u1ij[i][j] += -fourierCjSj.getCij(i, kIndex) * currentRhoSigmaj[1][j + kIndex] -
  2179.                                 fourierCjSj.getSij(i, kIndex) * currentRhoSigmaj[0][j + kIndex];

  2180.                         // C<sub>i</sub><sup>k</sup> * ρ<sub>j+k</sub> +
  2181.                         // S<sub>i</sub><sup>k</sup> * σ<sub>j+k</sub>
  2182.                         v1ij[i][j] += fourierCjSj.getCij(i, kIndex) * currentRhoSigmaj[0][j + kIndex] +
  2183.                                 fourierCjSj.getSij(i, kIndex) * currentRhoSigmaj[1][j + kIndex];
  2184.                     }

  2185.                     // divide by 1 / j
  2186.                     u1ij[i][j] *= -ooj;
  2187.                     v1ij[i][j] *= ooj;

  2188.                     // if index = 1 (i == 0) add the computed terms to U₁⁰
  2189.                     if (i == 0) {
  2190.                         // - (U₁<sup>j</sup> * ρ<sub>j</sub> + V₁<sup>j</sup> * σ<sub>j</sub>
  2191.                         u1ij[0][0] += -u1ij[0][j] * currentRhoSigmaj[0][j] - v1ij[0][j] * currentRhoSigmaj[1][j];
  2192.                     }
  2193.                 }
  2194.             }

  2195.             // Terms with j > jMax are required only when computing the coefficients
  2196.             // U₂<sup>j</sup> and V₂<sup>j</sup>
  2197.             // and those coefficients are only required for Fourier index = 1 (i == 0).
  2198.             for (int j = jMax + 1; j <= 2 * jMax; j++) {
  2199.                 // compute 1 / j
  2200.                 final double ooj = 1. / j;
  2201.                 // the value of i is 0
  2202.                 u1ij[0][j] = 0.;
  2203.                 v1ij[0][j] = 0.;

  2204.                 // k starts from j-J as it is always greater than or equal to 1
  2205.                 for (int kIndex = j - jMax; kIndex <= j - 1; kIndex++) {
  2206.                     // C<sub>i</sub><sup>j-k</sup> * σ<sub>k</sub> +
  2207.                     // S<sub>i</sub><sup>j-k</sup> * ρ<sub>k</sub>
  2208.                     u1ij[0][j] += fourierCjSj.getCij(0, j - kIndex) * currentRhoSigmaj[1][kIndex] +
  2209.                             fourierCjSj.getSij(0, j - kIndex) * currentRhoSigmaj[0][kIndex];

  2210.                     // C<sub>i</sub><sup>j-k</sup> * ρ<sub>k</sub> -
  2211.                     // S<sub>i</sub><sup>j-k</sup> * σ<sub>k</sub>
  2212.                     v1ij[0][j] += fourierCjSj.getCij(0, j - kIndex) * currentRhoSigmaj[0][kIndex] -
  2213.                             fourierCjSj.getSij(0, j - kIndex) * currentRhoSigmaj[1][kIndex];
  2214.                 }
  2215.                 for (int kIndex = 1; kIndex <= jMax; kIndex++) {
  2216.                     // C<sub>i</sub><sup>k</sup> * σ<sub>j+k</sub> -
  2217.                     // S<sub>i</sub><sup>k</sup> * ρ<sub>j+k</sub>
  2218.                     u1ij[0][j] += -fourierCjSj.getCij(0, kIndex) * currentRhoSigmaj[1][j + kIndex] -
  2219.                             fourierCjSj.getSij(0, kIndex) * currentRhoSigmaj[0][j + kIndex];

  2220.                     // C<sub>i</sub><sup>k</sup> * ρ<sub>j+k</sub> +
  2221.                     // S<sub>i</sub><sup>k</sup> * σ<sub>j+k</sub>
  2222.                     v1ij[0][j] += fourierCjSj.getCij(0, kIndex) * currentRhoSigmaj[0][j + kIndex] +
  2223.                             fourierCjSj.getSij(0, kIndex) * currentRhoSigmaj[1][j + kIndex];
  2224.                 }

  2225.                 // divide by 1 / j
  2226.                 u1ij[0][j] *= -ooj;
  2227.                 v1ij[0][j] *= ooj;
  2228.             }
  2229.         }

  2230.         /**
  2231.          * Build the U₁<sup>j</sup> and V₁<sup>j</sup> coefficients.
  2232.          * <p>
  2233.          * Only the coefficients for Fourier index = 1 (i == 0) are required.
  2234.          * </p>
  2235.          */
  2236.         private void computeU2V2Coefficients() {
  2237.             for (int j = 1; j <= jMax; j++) {
  2238.                 // compute 1 / j
  2239.                 final double ooj = 1. / j;

  2240.                 // only the values for i == 0 are computed
  2241.                 u2ij[j] = v1ij[0][j];
  2242.                 v2ij[j] = u1ij[0][j];

  2243.                 // 1 - δ<sub>1j</sub> is 1 for all j > 1
  2244.                 if (j > 1) {
  2245.                     for (int l = 1; l <= j - 1; l++) {
  2246.                         // U₁<sup>j-l</sup> * σ<sub>l</sub> +
  2247.                         // V₁<sup>j-l</sup> * ρ<sub>l</sub>
  2248.                         u2ij[j] += u1ij[0][j - l] * currentRhoSigmaj[1][l] + v1ij[0][j - l] * currentRhoSigmaj[0][l];

  2249.                         // U₁<sup>j-l</sup> * ρ<sub>l</sub> -
  2250.                         // V₁<sup>j-l</sup> * σ<sub>l</sub>
  2251.                         v2ij[j] += u1ij[0][j - l] * currentRhoSigmaj[0][l] - v1ij[0][j - l] * currentRhoSigmaj[1][l];
  2252.                     }
  2253.                 }

  2254.                 for (int l = 1; l <= jMax; l++) {
  2255.                     // -U₁<sup>j+l</sup> * σ<sub>l</sub> +
  2256.                     // U₁<sup>l</sup> * σ<sub>j+l</sub> +
  2257.                     // V₁<sup>j+l</sup> * ρ<sub>l</sub> -
  2258.                     // V₁<sup>l</sup> * ρ<sub>j+l</sub>
  2259.                     u2ij[j] += -u1ij[0][j + l] * currentRhoSigmaj[1][l] + u1ij[0][l] * currentRhoSigmaj[1][j + l] +
  2260.                             v1ij[0][j + l] * currentRhoSigmaj[0][l] - v1ij[0][l] * currentRhoSigmaj[0][j + l];

  2261.                     // U₁<sup>j+l</sup> * ρ<sub>l</sub> +
  2262.                     // U₁<sup>l</sup> * ρ<sub>j+l</sub> +
  2263.                     // V₁<sup>j+l</sup> * σ<sub>l</sub> +
  2264.                     // V₁<sup>l</sup> * σ<sub>j+l</sub>
  2265.                     u2ij[j] += u1ij[0][j + l] * currentRhoSigmaj[0][l] + u1ij[0][l] * currentRhoSigmaj[0][j + l] +
  2266.                             v1ij[0][j + l] * currentRhoSigmaj[1][l] + v1ij[0][l] * currentRhoSigmaj[1][j + l];
  2267.                 }

  2268.                 // divide by 1 / j
  2269.                 u2ij[j] *= -ooj;
  2270.                 v2ij[j] *= ooj;
  2271.             }
  2272.         }

  2273.         /**
  2274.          * Get the coefficient U₁<sup>j</sup> for Fourier index i.
  2275.          *
  2276.          * @param j j index
  2277.          * @param i Fourier index (starts at 0)
  2278.          * @return the coefficient U₁<sup>j</sup> for the given Fourier index i
  2279.          */
  2280.         public double getU1(final int j, final int i) {
  2281.             return u1ij[i][j];
  2282.         }

  2283.         /**
  2284.          * Get the coefficient V₁<sup>j</sup> for Fourier index i.
  2285.          *
  2286.          * @param j j index
  2287.          * @param i Fourier index (starts at 0)
  2288.          * @return the coefficient V₁<sup>j</sup> for the given Fourier index i
  2289.          */
  2290.         public double getV1(final int j, final int i) {
  2291.             return v1ij[i][j];
  2292.         }

  2293.         /**
  2294.          * Get the coefficient U₂<sup>j</sup> for Fourier index = 1 (i == 0).
  2295.          *
  2296.          * @param j j index
  2297.          * @return the coefficient U₂<sup>j</sup> for Fourier index = 1 (i == 0)
  2298.          */
  2299.         public double getU2(final int j) {
  2300.             return u2ij[j];
  2301.         }

  2302.         /**
  2303.          * Get the coefficient V₂<sup>j</sup> for Fourier index = 1 (i == 0).
  2304.          *
  2305.          * @param j j index
  2306.          * @return the coefficient V₂<sup>j</sup> for Fourier index = 1 (i == 0)
  2307.          */
  2308.         public double getV2(final int j) {
  2309.             return v2ij[j];
  2310.         }
  2311.     }

  2312.     /**
  2313.      * The U<sub>i</sub><sup>j</sup> and V<sub>i</sub><sup>j</sup> coefficients
  2314.      * described by equations 2.5.3-(21) and 2.5.3-(22) from Danielson.
  2315.      * <p>
  2316.      * The index i takes only the values 1 and 2<br>
  2317.      * For U only the index 0 for j is used.
  2318.      * </p>
  2319.      *
  2320.      * @author Petre Bazavan
  2321.      * @author Lucian Barbulescu
  2322.      * @param <T> type of the field elements
  2323.      */
  2324.     protected static class FieldUijVijCoefficients<T extends CalculusFieldElement<T>> {

  2325.         /**
  2326.          * The U₁<sup>j</sup> coefficients.
  2327.          * <p>
  2328.          * The first index identifies the Fourier coefficients used<br>
  2329.          * Those coefficients are computed for all Fourier C<sub>i</sub><sup>j</sup> and
  2330.          * S<sub>i</sub><sup>j</sup><br>
  2331.          * The only exception is when j = 0 when only the coefficient for fourier index
  2332.          * = 1 (i == 0) is needed.<br>
  2333.          * Also, for fourier index = 1 (i == 0), the coefficients up to 2 * jMax are
  2334.          * computed, because are required to compute the coefficients U₂<sup>j</sup>
  2335.          * </p>
  2336.          */
  2337.         private final T[][] u1ij;

  2338.         /**
  2339.          * The V₁<sup>j</sup> coefficients.
  2340.          * <p>
  2341.          * The first index identifies the Fourier coefficients used<br>
  2342.          * Those coefficients are computed for all Fourier C<sub>i</sub><sup>j</sup> and
  2343.          * S<sub>i</sub><sup>j</sup><br>
  2344.          * for fourier index = 1 (i == 0), the coefficients up to 2 * jMax are computed,
  2345.          * because are required to compute the coefficients V₂<sup>j</sup>
  2346.          * </p>
  2347.          */
  2348.         private final T[][] v1ij;

  2349.         /**
  2350.          * The U₂<sup>j</sup> coefficients.
  2351.          * <p>
  2352.          * Only the coefficients that use the Fourier index = 1 (i == 0) are computed as
  2353.          * they are the only ones required.
  2354.          * </p>
  2355.          */
  2356.         private final T[] u2ij;

  2357.         /**
  2358.          * The V₂<sup>j</sup> coefficients.
  2359.          * <p>
  2360.          * Only the coefficients that use the Fourier index = 1 (i == 0) are computed as
  2361.          * they are the only ones required.
  2362.          * </p>
  2363.          */
  2364.         private final T[] v2ij;

  2365.         /**
  2366.          * The current computed values for the ρ<sub>j</sub> and σ<sub>j</sub>
  2367.          * coefficients.
  2368.          */
  2369.         private final T[][] currentRhoSigmaj;

  2370.         /**
  2371.          * The C<sub>i</sub><sup>j</sup> and the S<sub>i</sub><sup>j</sup> Fourier
  2372.          * coefficients.
  2373.          */
  2374.         private final FieldFourierCjSjCoefficients<T> fourierCjSj;

  2375.         /** The maximum value for j index. */
  2376.         private final int jMax;

  2377.         /**
  2378.          * Constructor.
  2379.          * @param currentRhoSigmaj the current computed values for the ρ<sub>j</sub> and
  2380.          *                         σ<sub>j</sub> coefficients
  2381.          * @param fourierCjSj      the fourier coefficients C<sub>i</sub><sup>j</sup>
  2382.          *                         and the S<sub>i</sub><sup>j</sup>
  2383.          * @param jMax             maximum value for j index
  2384.          * @param field            field used by default
  2385.          */
  2386.         FieldUijVijCoefficients(final T[][] currentRhoSigmaj, final FieldFourierCjSjCoefficients<T> fourierCjSj,
  2387.                 final int jMax, final Field<T> field) {
  2388.             this.currentRhoSigmaj = currentRhoSigmaj;
  2389.             this.fourierCjSj = fourierCjSj;
  2390.             this.jMax = jMax;

  2391.             // initialize the internal arrays.
  2392.             this.u1ij = MathArrays.buildArray(field, 6, 2 * jMax + 1);
  2393.             this.v1ij = MathArrays.buildArray(field, 6, 2 * jMax + 1);
  2394.             this.u2ij = MathArrays.buildArray(field, jMax + 1);
  2395.             this.v2ij = MathArrays.buildArray(field, jMax + 1);

  2396.             // compute the coefficients
  2397.             computeU1V1Coefficients(field);
  2398.             computeU2V2Coefficients();
  2399.         }

  2400.         /**
  2401.          * Build the U₁<sup>j</sup> and V₁<sup>j</sup> coefficients.
  2402.          * @param field field used by default
  2403.          */
  2404.         private void computeU1V1Coefficients(final Field<T> field) {
  2405.             // Zero
  2406.             final T zero = field.getZero();

  2407.             // generate the U₁<sup>j</sup> and V₁<sup>j</sup> coefficients
  2408.             // for j >= 1
  2409.             // also the U₁⁰ for Fourier index = 1 (i == 0) coefficient will be computed
  2410.             u1ij[0][0] = zero;
  2411.             for (int j = 1; j <= jMax; j++) {
  2412.                 // compute 1 / j
  2413.                 final double ooj = 1. / j;

  2414.                 for (int i = 0; i < 6; i++) {
  2415.                     // j is aready between 1 and J
  2416.                     u1ij[i][j] = fourierCjSj.getSij(i, j);
  2417.                     v1ij[i][j] = fourierCjSj.getCij(i, j);

  2418.                     // 1 - δ<sub>1j</sub> is 1 for all j > 1
  2419.                     if (j > 1) {
  2420.                         // k starts with 1 because j-J is less than or equal to 0
  2421.                         for (int kIndex = 1; kIndex <= j - 1; kIndex++) {
  2422.                             // C<sub>i</sub><sup>j-k</sup> * σ<sub>k</sub> +
  2423.                             // S<sub>i</sub><sup>j-k</sup> * ρ<sub>k</sub>
  2424.                             u1ij[i][j] = u1ij[i][j]
  2425.                                     .add(fourierCjSj.getCij(i, j - kIndex).multiply(currentRhoSigmaj[1][kIndex]).add(
  2426.                                             fourierCjSj.getSij(i, j - kIndex).multiply(currentRhoSigmaj[0][kIndex])));

  2427.                             // C<sub>i</sub><sup>j-k</sup> * ρ<sub>k</sub> -
  2428.                             // S<sub>i</sub><sup>j-k</sup> * σ<sub>k</sub>
  2429.                             v1ij[i][j] = v1ij[i][j].add(
  2430.                                     fourierCjSj.getCij(i, j - kIndex).multiply(currentRhoSigmaj[0][kIndex]).subtract(
  2431.                                             fourierCjSj.getSij(i, j - kIndex).multiply(currentRhoSigmaj[1][kIndex])));
  2432.                         }
  2433.                     }

  2434.                     // since j must be between 1 and J-1 and is already between 1 and J
  2435.                     // the following sum is skiped only for j = jMax
  2436.                     if (j != jMax) {
  2437.                         for (int kIndex = 1; kIndex <= jMax - j; kIndex++) {
  2438.                             // -C<sub>i</sub><sup>j+k</sup> * σ<sub>k</sub> +
  2439.                             // S<sub>i</sub><sup>j+k</sup> * ρ<sub>k</sub>
  2440.                             u1ij[i][j] = u1ij[i][j].add(fourierCjSj.getCij(i, j + kIndex).negate()
  2441.                                     .multiply(currentRhoSigmaj[1][kIndex])
  2442.                                     .add(fourierCjSj.getSij(i, j + kIndex).multiply(currentRhoSigmaj[0][kIndex])));

  2443.                             // C<sub>i</sub><sup>j+k</sup> * ρ<sub>k</sub> +
  2444.                             // S<sub>i</sub><sup>j+k</sup> * σ<sub>k</sub>
  2445.                             v1ij[i][j] = v1ij[i][j]
  2446.                                     .add(fourierCjSj.getCij(i, j + kIndex).multiply(currentRhoSigmaj[0][kIndex]).add(
  2447.                                             fourierCjSj.getSij(i, j + kIndex).multiply(currentRhoSigmaj[1][kIndex])));
  2448.                         }
  2449.                     }

  2450.                     for (int kIndex = 1; kIndex <= jMax; kIndex++) {
  2451.                         // C<sub>i</sub><sup>k</sup> * σ<sub>j+k</sub> -
  2452.                         // S<sub>i</sub><sup>k</sup> * ρ<sub>j+k</sub>
  2453.                         u1ij[i][j] = u1ij[i][j].add(fourierCjSj.getCij(i, kIndex).negate()
  2454.                                 .multiply(currentRhoSigmaj[1][j + kIndex])
  2455.                                 .subtract(fourierCjSj.getSij(i, kIndex).multiply(currentRhoSigmaj[0][j + kIndex])));

  2456.                         // C<sub>i</sub><sup>k</sup> * ρ<sub>j+k</sub> +
  2457.                         // S<sub>i</sub><sup>k</sup> * σ<sub>j+k</sub>
  2458.                         v1ij[i][j] = v1ij[i][j]
  2459.                                 .add(fourierCjSj.getCij(i, kIndex).multiply(currentRhoSigmaj[0][j + kIndex])
  2460.                                         .add(fourierCjSj.getSij(i, kIndex).multiply(currentRhoSigmaj[1][j + kIndex])));
  2461.                     }

  2462.                     // divide by 1 / j
  2463.                     u1ij[i][j] = u1ij[i][j].multiply(-ooj);
  2464.                     v1ij[i][j] = v1ij[i][j].multiply(ooj);

  2465.                     // if index = 1 (i == 0) add the computed terms to U₁⁰
  2466.                     if (i == 0) {
  2467.                         // - (U₁<sup>j</sup> * ρ<sub>j</sub> + V₁<sup>j</sup> * σ<sub>j</sub>
  2468.                         u1ij[0][0] = u1ij[0][0].add(u1ij[0][j].negate().multiply(currentRhoSigmaj[0][j])
  2469.                                 .subtract(v1ij[0][j].multiply(currentRhoSigmaj[1][j])));
  2470.                     }
  2471.                 }
  2472.             }

  2473.             // Terms with j > jMax are required only when computing the coefficients
  2474.             // U₂<sup>j</sup> and V₂<sup>j</sup>
  2475.             // and those coefficients are only required for Fourier index = 1 (i == 0).
  2476.             for (int j = jMax + 1; j <= 2 * jMax; j++) {
  2477.                 // compute 1 / j
  2478.                 final double ooj = 1. / j;
  2479.                 // the value of i is 0
  2480.                 u1ij[0][j] = zero;
  2481.                 v1ij[0][j] = zero;

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

  2488.                     // C<sub>i</sub><sup>j-k</sup> * ρ<sub>k</sub> -
  2489.                     // S<sub>i</sub><sup>j-k</sup> * σ<sub>k</sub>
  2490.                     v1ij[0][j] = v1ij[0][j].add(fourierCjSj.getCij(0, j - kIndex).multiply(currentRhoSigmaj[0][kIndex])
  2491.                             .subtract(fourierCjSj.getSij(0, j - kIndex).multiply(currentRhoSigmaj[1][kIndex])));
  2492.                 }
  2493.                 for (int kIndex = 1; kIndex <= jMax; kIndex++) {
  2494.                     // C<sub>i</sub><sup>k</sup> * σ<sub>j+k</sub> -
  2495.                     // S<sub>i</sub><sup>k</sup> * ρ<sub>j+k</sub>
  2496.                     u1ij[0][j] = u1ij[0][j]
  2497.                             .add(fourierCjSj.getCij(0, kIndex).negate().multiply(currentRhoSigmaj[1][j + kIndex])
  2498.                                     .subtract(fourierCjSj.getSij(0, kIndex).multiply(currentRhoSigmaj[0][j + kIndex])));

  2499.                     // C<sub>i</sub><sup>k</sup> * ρ<sub>j+k</sub> +
  2500.                     // S<sub>i</sub><sup>k</sup> * σ<sub>j+k</sub>
  2501.                     v1ij[0][j] = v1ij[0][j].add(fourierCjSj.getCij(0, kIndex).multiply(currentRhoSigmaj[0][j + kIndex])
  2502.                             .add(fourierCjSj.getSij(0, kIndex).multiply(currentRhoSigmaj[1][j + kIndex])));
  2503.                 }

  2504.                 // divide by 1 / j
  2505.                 u1ij[0][j] = u1ij[0][j].multiply(-ooj);
  2506.                 v1ij[0][j] = v1ij[0][j].multiply(ooj);
  2507.             }
  2508.         }

  2509.         /**
  2510.          * Build the U₁<sup>j</sup> and V₁<sup>j</sup> coefficients.
  2511.          * <p>
  2512.          * Only the coefficients for Fourier index = 1 (i == 0) are required.
  2513.          * </p>
  2514.          */
  2515.         private void computeU2V2Coefficients() {
  2516.             for (int j = 1; j <= jMax; j++) {
  2517.                 // compute 1 / j
  2518.                 final double ooj = 1. / j;

  2519.                 // only the values for i == 0 are computed
  2520.                 u2ij[j] = v1ij[0][j];
  2521.                 v2ij[j] = u1ij[0][j];

  2522.                 // 1 - δ<sub>1j</sub> is 1 for all j > 1
  2523.                 if (j > 1) {
  2524.                     for (int l = 1; l <= j - 1; l++) {
  2525.                         // U₁<sup>j-l</sup> * σ<sub>l</sub> +
  2526.                         // V₁<sup>j-l</sup> * ρ<sub>l</sub>
  2527.                         u2ij[j] = u2ij[j].add(u1ij[0][j - l].multiply(currentRhoSigmaj[1][l])
  2528.                                 .add(v1ij[0][j - l].multiply(currentRhoSigmaj[0][l])));

  2529.                         // U₁<sup>j-l</sup> * ρ<sub>l</sub> -
  2530.                         // V₁<sup>j-l</sup> * σ<sub>l</sub>
  2531.                         v2ij[j] = v2ij[j].add(u1ij[0][j - l].multiply(currentRhoSigmaj[0][l])
  2532.                                 .subtract(v1ij[0][j - l].multiply(currentRhoSigmaj[1][l])));
  2533.                     }
  2534.                 }

  2535.                 for (int l = 1; l <= jMax; l++) {
  2536.                     // -U₁<sup>j+l</sup> * σ<sub>l</sub> +
  2537.                     // U₁<sup>l</sup> * σ<sub>j+l</sub> +
  2538.                     // V₁<sup>j+l</sup> * ρ<sub>l</sub> -
  2539.                     // V₁<sup>l</sup> * ρ<sub>j+l</sub>
  2540.                     u2ij[j] = u2ij[j].add(u1ij[0][j + l].negate().multiply(currentRhoSigmaj[1][l])
  2541.                             .add(u1ij[0][l].multiply(currentRhoSigmaj[1][j + l]))
  2542.                             .add(v1ij[0][j + l].multiply(currentRhoSigmaj[0][l]))
  2543.                             .subtract(v1ij[0][l].multiply(currentRhoSigmaj[0][j + l])));

  2544.                     // U₁<sup>j+l</sup> * ρ<sub>l</sub> +
  2545.                     // U₁<sup>l</sup> * ρ<sub>j+l</sub> +
  2546.                     // V₁<sup>j+l</sup> * σ<sub>l</sub> +
  2547.                     // V₁<sup>l</sup> * σ<sub>j+l</sub>
  2548.                     u2ij[j] = u2ij[j].add(u1ij[0][j + l].multiply(currentRhoSigmaj[0][l])
  2549.                             .add(u1ij[0][l].multiply(currentRhoSigmaj[0][j + l]))
  2550.                             .add(v1ij[0][j + l].multiply(currentRhoSigmaj[1][l]))
  2551.                             .add(v1ij[0][l].multiply(currentRhoSigmaj[1][j + l])));
  2552.                 }

  2553.                 // divide by 1 / j
  2554.                 u2ij[j] = u2ij[j].multiply(-ooj);
  2555.                 v2ij[j] = v2ij[j].multiply(ooj);
  2556.             }
  2557.         }

  2558.         /**
  2559.          * Get the coefficient U₁<sup>j</sup> for Fourier index i.
  2560.          *
  2561.          * @param j j index
  2562.          * @param i Fourier index (starts at 0)
  2563.          * @return the coefficient U₁<sup>j</sup> for the given Fourier index i
  2564.          */
  2565.         public T getU1(final int j, final int i) {
  2566.             return u1ij[i][j];
  2567.         }

  2568.         /**
  2569.          * Get the coefficient V₁<sup>j</sup> for Fourier index i.
  2570.          *
  2571.          * @param j j index
  2572.          * @param i Fourier index (starts at 0)
  2573.          * @return the coefficient V₁<sup>j</sup> for the given Fourier index i
  2574.          */
  2575.         public T getV1(final int j, final int i) {
  2576.             return v1ij[i][j];
  2577.         }

  2578.         /**
  2579.          * Get the coefficient U₂<sup>j</sup> for Fourier index = 1 (i == 0).
  2580.          *
  2581.          * @param j j index
  2582.          * @return the coefficient U₂<sup>j</sup> for Fourier index = 1 (i == 0)
  2583.          */
  2584.         public T getU2(final int j) {
  2585.             return u2ij[j];
  2586.         }

  2587.         /**
  2588.          * Get the coefficient V₂<sup>j</sup> for Fourier index = 1 (i == 0).
  2589.          *
  2590.          * @param j j index
  2591.          * @return the coefficient V₂<sup>j</sup> for Fourier index = 1 (i == 0)
  2592.          */
  2593.         public T getV2(final int j) {
  2594.             return v2ij[j];
  2595.         }
  2596.     }

  2597.     /** Coefficients valid for one time slot. */
  2598.     protected static class Slot {

  2599.         /**
  2600.          * The coefficients D<sub>i</sub><sup>j</sup>.
  2601.          * <p>
  2602.          * Only for j = 1 and j = 2 the coefficients are not 0. <br>
  2603.          * i corresponds to the equinoctial element, as follows: - i=0 for a <br/>
  2604.          * - i=1 for k <br/>
  2605.          * - i=2 for h <br/>
  2606.          * - i=3 for q <br/>
  2607.          * - i=4 for p <br/>
  2608.          * - i=5 for λ <br/>
  2609.          * </p>
  2610.          */
  2611.         private final ShortPeriodicsInterpolatedCoefficient[] dij;

  2612.         /**
  2613.          * The coefficients C<sub>i</sub><sup>j</sup>.
  2614.          * <p>
  2615.          * The index order is cij[j][i] <br/>
  2616.          * i corresponds to the equinoctial element, as follows: <br/>
  2617.          * - i=0 for a <br/>
  2618.          * - i=1 for k <br/>
  2619.          * - i=2 for h <br/>
  2620.          * - i=3 for q <br/>
  2621.          * - i=4 for p <br/>
  2622.          * - i=5 for λ <br/>
  2623.          * </p>
  2624.          */
  2625.         private final ShortPeriodicsInterpolatedCoefficient[] cij;

  2626.         /**
  2627.          * The coefficients S<sub>i</sub><sup>j</sup>.
  2628.          * <p>
  2629.          * The index order is sij[j][i] <br/>
  2630.          * i corresponds to the equinoctial element, as follows: <br/>
  2631.          * - i=0 for a <br/>
  2632.          * - i=1 for k <br/>
  2633.          * - i=2 for h <br/>
  2634.          * - i=3 for q <br/>
  2635.          * - i=4 for p <br/>
  2636.          * - i=5 for λ <br/>
  2637.          * </p>
  2638.          */
  2639.         private final ShortPeriodicsInterpolatedCoefficient[] sij;

  2640.         /**
  2641.          * Simple constructor.
  2642.          * @param jMax                maximum value for j index
  2643.          * @param interpolationPoints number of points used in the interpolation process
  2644.          */
  2645.         Slot(final int jMax, final int interpolationPoints) {

  2646.             dij = new ShortPeriodicsInterpolatedCoefficient[3];
  2647.             cij = new ShortPeriodicsInterpolatedCoefficient[jMax + 1];
  2648.             sij = new ShortPeriodicsInterpolatedCoefficient[jMax + 1];

  2649.             // Initialize the C<sub>i</sub><sup>j</sup>, S<sub>i</sub><sup>j</sup> and
  2650.             // D<sub>i</sub><sup>j</sup> coefficients
  2651.             for (int j = 0; j <= jMax; j++) {
  2652.                 cij[j] = new ShortPeriodicsInterpolatedCoefficient(interpolationPoints);
  2653.                 if (j > 0) {
  2654.                     sij[j] = new ShortPeriodicsInterpolatedCoefficient(interpolationPoints);
  2655.                 }
  2656.                 // Initialize only the non-zero D<sub>i</sub><sup>j</sup> coefficients
  2657.                 if (j == 1 || j == 2) {
  2658.                     dij[j] = new ShortPeriodicsInterpolatedCoefficient(interpolationPoints);
  2659.                 }
  2660.             }

  2661.         }

  2662.     }

  2663.     /** Coefficients valid for one time slot.
  2664.      * @param <T> type of the field elements
  2665.      */
  2666.     protected static class FieldSlot<T extends CalculusFieldElement<T>> {

  2667.         /**
  2668.          * The coefficients D<sub>i</sub><sup>j</sup>.
  2669.          * <p>
  2670.          * Only for j = 1 and j = 2 the coefficients are not 0. <br>
  2671.          * i corresponds to the equinoctial element, as follows: - i=0 for a <br/>
  2672.          * - i=1 for k <br/>
  2673.          * - i=2 for h <br/>
  2674.          * - i=3 for q <br/>
  2675.          * - i=4 for p <br/>
  2676.          * - i=5 for λ <br/>
  2677.          * </p>
  2678.          */
  2679.         private final FieldShortPeriodicsInterpolatedCoefficient<T>[] dij;

  2680.         /**
  2681.          * The coefficients C<sub>i</sub><sup>j</sup>.
  2682.          * <p>
  2683.          * The index order is cij[j][i] <br/>
  2684.          * i corresponds to the equinoctial element, as follows: <br/>
  2685.          * - i=0 for a <br/>
  2686.          * - i=1 for k <br/>
  2687.          * - i=2 for h <br/>
  2688.          * - i=3 for q <br/>
  2689.          * - i=4 for p <br/>
  2690.          * - i=5 for λ <br/>
  2691.          * </p>
  2692.          */
  2693.         private final FieldShortPeriodicsInterpolatedCoefficient<T>[] cij;

  2694.         /**
  2695.          * The coefficients S<sub>i</sub><sup>j</sup>.
  2696.          * <p>
  2697.          * The index order is sij[j][i] <br/>
  2698.          * i corresponds to the equinoctial element, as follows: <br/>
  2699.          * - i=0 for a <br/>
  2700.          * - i=1 for k <br/>
  2701.          * - i=2 for h <br/>
  2702.          * - i=3 for q <br/>
  2703.          * - i=4 for p <br/>
  2704.          * - i=5 for λ <br/>
  2705.          * </p>
  2706.          */
  2707.         private final FieldShortPeriodicsInterpolatedCoefficient<T>[] sij;

  2708.         /**
  2709.          * Simple constructor.
  2710.          * @param jMax                maximum value for j index
  2711.          * @param interpolationPoints number of points used in the interpolation process
  2712.          */
  2713.         @SuppressWarnings("unchecked")
  2714.         FieldSlot(final int jMax, final int interpolationPoints) {

  2715.             dij = (FieldShortPeriodicsInterpolatedCoefficient<T>[]) Array
  2716.                     .newInstance(FieldShortPeriodicsInterpolatedCoefficient.class, 3);
  2717.             cij = (FieldShortPeriodicsInterpolatedCoefficient<T>[]) Array
  2718.                     .newInstance(FieldShortPeriodicsInterpolatedCoefficient.class, jMax + 1);
  2719.             sij = (FieldShortPeriodicsInterpolatedCoefficient<T>[]) Array
  2720.                     .newInstance(FieldShortPeriodicsInterpolatedCoefficient.class, jMax + 1);

  2721.             // Initialize the C<sub>i</sub><sup>j</sup>, S<sub>i</sub><sup>j</sup> and
  2722.             // D<sub>i</sub><sup>j</sup> coefficients
  2723.             for (int j = 0; j <= jMax; j++) {
  2724.                 cij[j] = new FieldShortPeriodicsInterpolatedCoefficient<>(interpolationPoints);
  2725.                 if (j > 0) {
  2726.                     sij[j] = new FieldShortPeriodicsInterpolatedCoefficient<>(interpolationPoints);
  2727.                 }
  2728.                 // Initialize only the non-zero D<sub>i</sub><sup>j</sup> coefficients
  2729.                 if (j == 1 || j == 2) {
  2730.                     dij[j] = new FieldShortPeriodicsInterpolatedCoefficient<>(interpolationPoints);
  2731.                 }
  2732.             }

  2733.         }

  2734.     }

  2735. }