DSSTZonal.java

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

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

  27. import org.hipparchus.CalculusFieldElement;
  28. import org.hipparchus.Field;
  29. import org.hipparchus.exception.LocalizedCoreFormats;
  30. import org.hipparchus.util.CombinatoricsUtils;
  31. import org.hipparchus.util.FastMath;
  32. import org.hipparchus.util.FieldSinCos;
  33. import org.hipparchus.util.MathArrays;
  34. import org.hipparchus.util.SinCos;
  35. import org.orekit.attitudes.AttitudeProvider;
  36. import org.orekit.errors.OrekitException;
  37. import org.orekit.errors.OrekitInternalError;
  38. import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider;
  39. import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider.UnnormalizedSphericalHarmonics;
  40. import org.orekit.orbits.FieldOrbit;
  41. import org.orekit.orbits.Orbit;
  42. import org.orekit.propagation.FieldSpacecraftState;
  43. import org.orekit.propagation.PropagationType;
  44. import org.orekit.propagation.SpacecraftState;
  45. import org.orekit.propagation.semianalytical.dsst.utilities.AuxiliaryElements;
  46. import org.orekit.propagation.semianalytical.dsst.utilities.CjSjCoefficient;
  47. import org.orekit.propagation.semianalytical.dsst.utilities.CoefficientsFactory;
  48. import org.orekit.propagation.semianalytical.dsst.utilities.CoefficientsFactory.NSKey;
  49. import org.orekit.propagation.semianalytical.dsst.utilities.FieldAuxiliaryElements;
  50. import org.orekit.propagation.semianalytical.dsst.utilities.FieldCjSjCoefficient;
  51. import org.orekit.propagation.semianalytical.dsst.utilities.FieldGHIJjsPolynomials;
  52. import org.orekit.propagation.semianalytical.dsst.utilities.FieldLnsCoefficients;
  53. import org.orekit.propagation.semianalytical.dsst.utilities.FieldShortPeriodicsInterpolatedCoefficient;
  54. import org.orekit.propagation.semianalytical.dsst.utilities.GHIJjsPolynomials;
  55. import org.orekit.propagation.semianalytical.dsst.utilities.LnsCoefficients;
  56. import org.orekit.propagation.semianalytical.dsst.utilities.ShortPeriodicsInterpolatedCoefficient;
  57. import org.orekit.propagation.semianalytical.dsst.utilities.UpperBounds;
  58. import org.orekit.propagation.semianalytical.dsst.utilities.hansen.FieldHansenZonalLinear;
  59. import org.orekit.propagation.semianalytical.dsst.utilities.hansen.HansenZonalLinear;
  60. import org.orekit.time.AbsoluteDate;
  61. import org.orekit.time.FieldAbsoluteDate;
  62. import org.orekit.utils.FieldTimeSpanMap;
  63. import org.orekit.utils.ParameterDriver;
  64. import org.orekit.utils.TimeSpanMap;

  65. /** Zonal contribution to the central body gravitational perturbation.
  66.  *
  67.  *   @author Romain Di Costanzo
  68.  *   @author Pascal Parraud
  69.  *   @author Bryan Cazabonne (field translation)
  70.  */
  71. public class DSSTZonal implements DSSTForceModel {

  72.     /**  Name of the prefix for short period coefficients keys. */
  73.     public static final String SHORT_PERIOD_PREFIX = "DSST-central-body-zonal-";

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

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

  90.     /** Truncation tolerance. */
  91.     private static final double TRUNCATION_TOLERANCE = 1e-4;

  92.     /** Central attraction scaling factor.
  93.      * <p>
  94.      * We use a power of 2 to avoid numeric noise introduction
  95.      * in the multiplications/divisions sequences.
  96.      * </p>
  97.      */
  98.     private static final double MU_SCALE = FastMath.scalb(1.0, 32);

  99.     /** Coefficient used to define the mean disturbing function V<sub>ns</sub> coefficient. */
  100.     private final SortedMap<NSKey, Double> Vns;

  101.     /** Provider for spherical harmonics. */
  102.     private final UnnormalizedSphericalHarmonicsProvider provider;

  103.     /** Maximal degree to consider for harmonics potential. */
  104.     private final int maxDegree;

  105.     /** Maximal degree to consider for harmonics potential. */
  106.     private final int maxDegreeShortPeriodics;

  107.     /** Highest power of the eccentricity to be used in short periodic computations. */
  108.     private final int maxEccPowShortPeriodics;

  109.     /** Maximum frequency in true longitude for short periodic computations. */
  110.     private final int maxFrequencyShortPeriodics;

  111.     /** Highest power of the eccentricity to be used in mean elements computations. */
  112.     private int maxEccPowMeanElements;

  113.     /** Highest power of the eccentricity. */
  114.     private int maxEccPow;

  115.     /** Short period terms. */
  116.     private ZonalShortPeriodicCoefficients zonalSPCoefs;

  117.     /** Short period terms. */
  118.     private Map<Field<?>, FieldZonalShortPeriodicCoefficients<?>> zonalFieldSPCoefs;

  119.     /** Driver for gravitational parameter. */
  120.     private final ParameterDriver gmParameterDriver;

  121.     /** Hansen objects. */
  122.     private HansenObjects hansen;

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

  125.     /** Constructor with default reference values.
  126.      * <p>
  127.      * When this constructor is used, maximum allowed values are used
  128.      * for the short periodic coefficients:
  129.      * </p>
  130.      * <ul>
  131.      *    <li> {@link #maxDegreeShortPeriodics} is set to {@code provider.getMaxDegree()} </li>
  132.      *    <li> {@link #maxEccPowShortPeriodics} is set to {@code min(provider.getMaxDegree() - 1, 4)}.
  133.      *         This parameter should not exceed 4 as higher values will exceed computer capacity </li>
  134.      *    <li> {@link #maxFrequencyShortPeriodics} is set to {@code 2 * provider.getMaxDegree() + 1} </li>
  135.      * </ul>
  136.      * @param provider provider for spherical harmonics
  137.      * @since 10.1
  138.      */
  139.     public DSSTZonal(final UnnormalizedSphericalHarmonicsProvider provider) {
  140.         this(provider, provider.getMaxDegree(), FastMath.min(4, provider.getMaxDegree() - 1), 2 * provider.getMaxDegree() + 1);
  141.     }

  142.     /** Simple constructor.
  143.      * @param provider provider for spherical harmonics
  144.      * @param maxDegreeShortPeriodics maximum degree to consider for short periodics zonal harmonics potential
  145.      * (must be between 2 and {@code provider.getMaxDegree()})
  146.      * @param maxEccPowShortPeriodics maximum power of the eccentricity to be used in short periodic computations
  147.      * (must be between 0 and {@code maxDegreeShortPeriodics - 1}, but should typically not exceed 4 as higher
  148.      * values will exceed computer capacity)
  149.      * @param maxFrequencyShortPeriodics maximum frequency in true longitude for short periodic computations
  150.      * (must be between 1 and {@code 2 * maxDegreeShortPeriodics + 1})
  151.      * @since 7.2
  152.      */
  153.     public DSSTZonal(final UnnormalizedSphericalHarmonicsProvider provider,
  154.                      final int maxDegreeShortPeriodics,
  155.                      final int maxEccPowShortPeriodics,
  156.                      final int maxFrequencyShortPeriodics) {

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

  160.         // Vns coefficients
  161.         this.Vns = CoefficientsFactory.computeVns(provider.getMaxDegree() + 1);

  162.         this.provider  = provider;
  163.         this.maxDegree = provider.getMaxDegree();

  164.         checkIndexRange(maxDegreeShortPeriodics, 2, provider.getMaxDegree());
  165.         this.maxDegreeShortPeriodics = maxDegreeShortPeriodics;

  166.         checkIndexRange(maxEccPowShortPeriodics, 0, maxDegreeShortPeriodics - 1);
  167.         this.maxEccPowShortPeriodics = maxEccPowShortPeriodics;

  168.         checkIndexRange(maxFrequencyShortPeriodics, 1, 2 * maxDegreeShortPeriodics + 1);
  169.         this.maxFrequencyShortPeriodics = maxFrequencyShortPeriodics;

  170.         // Initialize default values
  171.         this.maxEccPowMeanElements = (maxDegree == 2) ? 0 : Integer.MIN_VALUE;

  172.         zonalFieldSPCoefs = new HashMap<>();
  173.         fieldHansen       = new HashMap<>();

  174.     }

  175.     /** Check an index range.
  176.      * @param index index value
  177.      * @param min minimum value for index
  178.      * @param max maximum value for index
  179.      */
  180.     private void checkIndexRange(final int index, final int min, final int max) {
  181.         if (index < min || index > max) {
  182.             throw new OrekitException(LocalizedCoreFormats.OUT_OF_RANGE_SIMPLE, index, min, max);
  183.         }
  184.     }

  185.     /** Get the spherical harmonics provider.
  186.      *  @return the spherical harmonics provider
  187.      */
  188.     public UnnormalizedSphericalHarmonicsProvider getProvider() {
  189.         return provider;
  190.     }

  191.     /** {@inheritDoc}
  192.      *  <p>
  193.      *  Computes the highest power of the eccentricity to appear in the truncated
  194.      *  analytical power series expansion.
  195.      *  </p>
  196.      *  <p>
  197.      *  This method computes the upper value for the central body potential and
  198.      *  determines the maximal power for the eccentricity producing potential
  199.      *  terms bigger than a defined tolerance.
  200.      *  </p>
  201.      */
  202.     @Override
  203.     public List<ShortPeriodTerms> initializeShortPeriodTerms(final AuxiliaryElements auxiliaryElements,
  204.                                              final PropagationType type,
  205.                                              final double[] parameters) {

  206.         computeMeanElementsTruncations(auxiliaryElements, parameters);

  207.         switch (type) {
  208.             case MEAN:
  209.                 maxEccPow = maxEccPowMeanElements;
  210.                 break;
  211.             case OSCULATING:
  212.                 maxEccPow = FastMath.max(maxEccPowMeanElements, maxEccPowShortPeriodics);
  213.                 break;
  214.             default:
  215.                 throw new OrekitInternalError(null);
  216.         }

  217.         hansen = new HansenObjects();

  218.         final List<ShortPeriodTerms> list = new ArrayList<ShortPeriodTerms>();
  219.         zonalSPCoefs = new ZonalShortPeriodicCoefficients(maxFrequencyShortPeriodics,
  220.                                                           INTERPOLATION_POINTS,
  221.                                                           new TimeSpanMap<Slot>(new Slot(maxFrequencyShortPeriodics,
  222.                                                                                          INTERPOLATION_POINTS)));
  223.         list.add(zonalSPCoefs);
  224.         return list;

  225.     }

  226.     /** {@inheritDoc}
  227.      *  <p>
  228.      *  Computes the highest power of the eccentricity to appear in the truncated
  229.      *  analytical power series expansion.
  230.      *  </p>
  231.      *  <p>
  232.      *  This method computes the upper value for the central body potential and
  233.      *  determines the maximal power for the eccentricity producing potential
  234.      *  terms bigger than a defined tolerance.
  235.      *  </p>
  236.      */
  237.     @Override
  238.     public <T extends CalculusFieldElement<T>> List<FieldShortPeriodTerms<T>> initializeShortPeriodTerms(final FieldAuxiliaryElements<T> auxiliaryElements,
  239.                                                                                      final PropagationType type,
  240.                                                                                      final T[] parameters) {

  241.         // Field used by default
  242.         final Field<T> field = auxiliaryElements.getDate().getField();
  243.         computeMeanElementsTruncations(auxiliaryElements, parameters, field);

  244.         switch (type) {
  245.             case MEAN:
  246.                 maxEccPow = maxEccPowMeanElements;
  247.                 break;
  248.             case OSCULATING:
  249.                 maxEccPow = FastMath.max(maxEccPowMeanElements, maxEccPowShortPeriodics);
  250.                 break;
  251.             default:
  252.                 throw new OrekitInternalError(null);
  253.         }

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

  255.         final FieldZonalShortPeriodicCoefficients<T> fzspc =
  256.                         new FieldZonalShortPeriodicCoefficients<>(maxFrequencyShortPeriodics,
  257.                                                                   INTERPOLATION_POINTS,
  258.                                                                   new FieldTimeSpanMap<>(new FieldSlot<>(maxFrequencyShortPeriodics,
  259.                                                                                                          INTERPOLATION_POINTS),
  260.                                                                                          field));
  261.         zonalFieldSPCoefs.put(field, fzspc);
  262.         return Collections.singletonList(fzspc);

  263.     }

  264.     /** Compute indices truncations for mean elements computations.
  265.      * @param auxiliaryElements auxiliary elements
  266.      * @param parameters values of the force model parameters for state date (only 1 value for each parameter)
  267.      */
  268.     private void computeMeanElementsTruncations(final AuxiliaryElements auxiliaryElements, final double[] parameters) {

  269.         final DSSTZonalContext context = new DSSTZonalContext(auxiliaryElements, provider, parameters);
  270.         //Compute the max eccentricity power for the mean element rate expansion
  271.         if (maxDegree == 2) {
  272.             maxEccPowMeanElements = 0;
  273.         } else {
  274.             // Initializes specific parameters.
  275.             final UnnormalizedSphericalHarmonics harmonics = provider.onDate(auxiliaryElements.getDate());

  276.             // Utilities for truncation
  277.             final double ax2or = 2. * auxiliaryElements.getSma() / provider.getAe();
  278.             double xmuran = parameters[0] / auxiliaryElements.getSma();
  279.             // Set a lower bound for eccentricity
  280.             final double eo2  = FastMath.max(0.0025, auxiliaryElements.getEcc() / 2.);
  281.             final double x2o2 = context.getXX() / 2.;
  282.             final double[] eccPwr = new double[maxDegree + 1];
  283.             final double[] chiPwr = new double[maxDegree + 1];
  284.             final double[] hafPwr = new double[maxDegree + 1];
  285.             eccPwr[0] = 1.;
  286.             chiPwr[0] = context.getX();
  287.             hafPwr[0] = 1.;
  288.             for (int i = 1; i <= maxDegree; i++) {
  289.                 eccPwr[i] = eccPwr[i - 1] * eo2;
  290.                 chiPwr[i] = chiPwr[i - 1] * x2o2;
  291.                 hafPwr[i] = hafPwr[i - 1] * 0.5;
  292.                 xmuran  /= ax2or;
  293.             }

  294.             // Set highest power of e and degree of current spherical harmonic.
  295.             maxEccPowMeanElements = 0;
  296.             int maxDeg = maxDegree;
  297.             // Loop over n
  298.             do {
  299.                 // Set order of current spherical harmonic.
  300.                 int m = 0;
  301.                 // Loop over m
  302.                 do {
  303.                     // Compute magnitude of current spherical harmonic coefficient.
  304.                     final double cnm = harmonics.getUnnormalizedCnm(maxDeg, m);
  305.                     final double snm = harmonics.getUnnormalizedSnm(maxDeg, m);
  306.                     final double csnm = FastMath.hypot(cnm, snm);
  307.                     if (csnm == 0.) {
  308.                         break;
  309.                     }
  310.                     // Set magnitude of last spherical harmonic term.
  311.                     double lastTerm = 0.;
  312.                     // Set current power of e and related indices.
  313.                     int nsld2 = (maxDeg - maxEccPowMeanElements - 1) / 2;
  314.                     int l = maxDeg - 2 * nsld2;
  315.                     // Loop over l
  316.                     double term = 0.;
  317.                     do {
  318.                         // Compute magnitude of current spherical harmonic term.
  319.                         if (m < l) {
  320.                             term =  csnm * xmuran *
  321.                                     (CombinatoricsUtils.factorialDouble(maxDeg - l) / (CombinatoricsUtils.factorialDouble(maxDeg - m))) *
  322.                                     (CombinatoricsUtils.factorialDouble(maxDeg + l) / (CombinatoricsUtils.factorialDouble(nsld2) * CombinatoricsUtils.factorialDouble(nsld2 + l))) *
  323.                                     eccPwr[l] * UpperBounds.getDnl(context.getXX(), chiPwr[l], maxDeg, l) *
  324.                                     (UpperBounds.getRnml(auxiliaryElements.getGamma(), maxDeg, l, m, 1, I) + UpperBounds.getRnml(auxiliaryElements.getGamma(), maxDeg, l, m, -1, I));
  325.                         } else {
  326.                             term =  csnm * xmuran *
  327.                                     (CombinatoricsUtils.factorialDouble(maxDeg + m) / (CombinatoricsUtils.factorialDouble(nsld2) * CombinatoricsUtils.factorialDouble(nsld2 + l))) *
  328.                                     eccPwr[l] * hafPwr[m - l] * UpperBounds.getDnl(context.getXX(), chiPwr[l], maxDeg, l) *
  329.                                     (UpperBounds.getRnml(auxiliaryElements.getGamma(), maxDeg, m, l, 1, I) + UpperBounds.getRnml(auxiliaryElements.getGamma(), maxDeg, m, l, -1, I));
  330.                         }
  331.                         // Is the current spherical harmonic term bigger than the truncation tolerance ?
  332.                         if (term >= TRUNCATION_TOLERANCE) {
  333.                             maxEccPowMeanElements = l;
  334.                         } else {
  335.                             // Is the current term smaller than the last term ?
  336.                             if (term < lastTerm) {
  337.                                 break;
  338.                             }
  339.                         }
  340.                         // Proceed to next power of e.
  341.                         lastTerm = term;
  342.                         l += 2;
  343.                         nsld2--;
  344.                     } while (l < maxDeg);
  345.                     // Is the current spherical harmonic term bigger than the truncation tolerance ?
  346.                     if (term >= TRUNCATION_TOLERANCE) {
  347.                         maxEccPowMeanElements = FastMath.min(maxDegree - 2, maxEccPowMeanElements);
  348.                         return;
  349.                     }
  350.                     // Proceed to next order.
  351.                     m++;
  352.                 } while (m <= FastMath.min(maxDeg, provider.getMaxOrder()));
  353.                 // Proceed to next degree.
  354.                 xmuran *= ax2or;
  355.                 maxDeg--;
  356.             } while (maxDeg > maxEccPowMeanElements + 2);

  357.             maxEccPowMeanElements = FastMath.min(maxDegree - 2, maxEccPowMeanElements);
  358.         }
  359.     }

  360.     /** Compute indices truncations for mean elements computations.
  361.      * @param <T> type of the elements
  362.      * @param auxiliaryElements auxiliary elements
  363.      * @param parameters values of the force model parameters
  364.      * @param field field used by default
  365.      */
  366.     private <T extends CalculusFieldElement<T>> void computeMeanElementsTruncations(final FieldAuxiliaryElements<T> auxiliaryElements,
  367.                                                                                 final T[] parameters,
  368.                                                                                 final Field<T> field) {

  369.         final T zero = field.getZero();
  370.         final FieldDSSTZonalContext<T> context = new FieldDSSTZonalContext<>(auxiliaryElements, provider, parameters);
  371.         //Compute the max eccentricity power for the mean element rate expansion
  372.         if (maxDegree == 2) {
  373.             maxEccPowMeanElements = 0;
  374.         } else {
  375.             // Initializes specific parameters.
  376.             final UnnormalizedSphericalHarmonics harmonics = provider.onDate(auxiliaryElements.getDate().toAbsoluteDate());

  377.             // Utilities for truncation
  378.             final T ax2or = auxiliaryElements.getSma().multiply(2.).divide(provider.getAe());
  379.             T xmuran = parameters[0].divide(auxiliaryElements.getSma());
  380.             // Set a lower bound for eccentricity
  381.             final T eo2  = FastMath.max(zero.add(0.0025), auxiliaryElements.getEcc().divide(2.));
  382.             final T x2o2 = context.getXX().divide(2.);
  383.             final T[] eccPwr = MathArrays.buildArray(field, maxDegree + 1);
  384.             final T[] chiPwr = MathArrays.buildArray(field, maxDegree + 1);
  385.             final T[] hafPwr = MathArrays.buildArray(field, maxDegree + 1);
  386.             eccPwr[0] = zero.add(1.);
  387.             chiPwr[0] = context.getX();
  388.             hafPwr[0] = zero.add(1.);
  389.             for (int i = 1; i <= maxDegree; i++) {
  390.                 eccPwr[i] = eccPwr[i - 1].multiply(eo2);
  391.                 chiPwr[i] = chiPwr[i - 1].multiply(x2o2);
  392.                 hafPwr[i] = hafPwr[i - 1].multiply(0.5);
  393.                 xmuran  = xmuran.divide(ax2or);
  394.             }

  395.             // Set highest power of e and degree of current spherical harmonic.
  396.             maxEccPowMeanElements = 0;
  397.             int maxDeg = maxDegree;
  398.             // Loop over n
  399.             do {
  400.                 // Set order of current spherical harmonic.
  401.                 int m = 0;
  402.                 // Loop over m
  403.                 do {
  404.                     // Compute magnitude of current spherical harmonic coefficient.
  405.                     final T cnm = zero.add(harmonics.getUnnormalizedCnm(maxDeg, m));
  406.                     final T snm = zero.add(harmonics.getUnnormalizedSnm(maxDeg, m));
  407.                     final T csnm = FastMath.hypot(cnm, snm);
  408.                     if (csnm.getReal() == 0.) {
  409.                         break;
  410.                     }
  411.                     // Set magnitude of last spherical harmonic term.
  412.                     T lastTerm = zero;
  413.                     // Set current power of e and related indices.
  414.                     int nsld2 = (maxDeg - maxEccPowMeanElements - 1) / 2;
  415.                     int l = maxDeg - 2 * nsld2;
  416.                     // Loop over l
  417.                     T term = zero;
  418.                     do {
  419.                         // Compute magnitude of current spherical harmonic term.
  420.                         if (m < l) {
  421.                             term = csnm.multiply(xmuran).
  422.                                    multiply((CombinatoricsUtils.factorialDouble(maxDeg - l) / (CombinatoricsUtils.factorialDouble(maxDeg - m))) *
  423.                                    (CombinatoricsUtils.factorialDouble(maxDeg + l) / (CombinatoricsUtils.factorialDouble(nsld2) * CombinatoricsUtils.factorialDouble(nsld2 + l)))).
  424.                                    multiply(eccPwr[l]).multiply(UpperBounds.getDnl(context.getXX(), chiPwr[l], maxDeg, l)).
  425.                                    multiply(UpperBounds.getRnml(auxiliaryElements.getGamma(), maxDeg, l, m, 1, I).add(UpperBounds.getRnml(auxiliaryElements.getGamma(), maxDeg, l, m, -1, I)));
  426.                         } else {
  427.                             term = csnm.multiply(xmuran).
  428.                                    multiply(CombinatoricsUtils.factorialDouble(maxDeg + m) / (CombinatoricsUtils.factorialDouble(nsld2) * CombinatoricsUtils.factorialDouble(nsld2 + l))).
  429.                                    multiply(eccPwr[l]).multiply(hafPwr[m - l]).multiply(UpperBounds.getDnl(context.getXX(), chiPwr[l], maxDeg, l)).
  430.                                    multiply(UpperBounds.getRnml(auxiliaryElements.getGamma(), maxDeg, m, l, 1, I).add(UpperBounds.getRnml(auxiliaryElements.getGamma(), maxDeg, m, l, -1, I)));
  431.                         }
  432.                         // Is the current spherical harmonic term bigger than the truncation tolerance ?
  433.                         if (term.getReal() >= TRUNCATION_TOLERANCE) {
  434.                             maxEccPowMeanElements = l;
  435.                         } else {
  436.                             // Is the current term smaller than the last term ?
  437.                             if (term.getReal() < lastTerm.getReal()) {
  438.                                 break;
  439.                             }
  440.                         }
  441.                         // Proceed to next power of e.
  442.                         lastTerm = term;
  443.                         l += 2;
  444.                         nsld2--;
  445.                     } while (l < maxDeg);
  446.                     // Is the current spherical harmonic term bigger than the truncation tolerance ?
  447.                     if (term.getReal() >= TRUNCATION_TOLERANCE) {
  448.                         maxEccPowMeanElements = FastMath.min(maxDegree - 2, maxEccPowMeanElements);
  449.                         return;
  450.                     }
  451.                     // Proceed to next order.
  452.                     m++;
  453.                 } while (m <= FastMath.min(maxDeg, provider.getMaxOrder()));
  454.                 // Proceed to next degree.
  455.                 xmuran = xmuran.multiply(ax2or);
  456.                 maxDeg--;
  457.             } while (maxDeg > maxEccPowMeanElements + 2);

  458.             maxEccPowMeanElements = FastMath.min(maxDegree - 2, maxEccPowMeanElements);
  459.         }
  460.     }

  461.     /** Performs initialization at each integration step for the current force model.
  462.      *  <p>
  463.      *  This method aims at being called before mean elements rates computation.
  464.      *  </p>
  465.      *  @param auxiliaryElements auxiliary elements related to the current orbit
  466.      *  @param parameters values of the force model parameters
  467.      *  @return new force model context
  468.      */
  469.     private DSSTZonalContext initializeStep(final AuxiliaryElements auxiliaryElements, final double[] parameters) {
  470.         return new DSSTZonalContext(auxiliaryElements, provider, parameters);
  471.     }

  472.     /** Performs initialization at each integration step for the current force model.
  473.      *  <p>
  474.      *  This method aims at being called before mean elements rates computation.
  475.      *  </p>
  476.      *  @param <T> type of the elements
  477.      *  @param auxiliaryElements auxiliary elements related to the current orbit
  478.      *  @param parameters values of the force model parameters
  479.      *  @return new force model context
  480.      */
  481.     private <T extends CalculusFieldElement<T>> FieldDSSTZonalContext<T> initializeStep(final FieldAuxiliaryElements<T> auxiliaryElements,
  482.                                                                                     final T[] parameters) {
  483.         return new FieldDSSTZonalContext<>(auxiliaryElements, provider, parameters);
  484.     }

  485.     /** {@inheritDoc} */
  486.     @Override
  487.     public double[] getMeanElementRate(final SpacecraftState spacecraftState,
  488.                                        final AuxiliaryElements auxiliaryElements, final double[] parameters) {

  489.         // Container of attributes

  490.         final DSSTZonalContext context = initializeStep(auxiliaryElements, parameters);
  491.         // Access to potential U derivatives
  492.         final UAnddU udu = new UAnddU(spacecraftState.getDate(), context, auxiliaryElements, hansen);

  493.         return computeMeanElementRates(spacecraftState.getDate(), context, udu);

  494.     }

  495.     /** {@inheritDoc} */
  496.     @Override
  497.     public <T extends CalculusFieldElement<T>> T[] getMeanElementRate(final FieldSpacecraftState<T> spacecraftState,
  498.                                                                   final FieldAuxiliaryElements<T> auxiliaryElements,
  499.                                                                   final T[] parameters) {

  500.         // Field used by default
  501.         final Field<T> field = auxiliaryElements.getDate().getField();
  502.         // Container of attributes
  503.         final FieldDSSTZonalContext<T> context = initializeStep(auxiliaryElements, parameters);

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

  506.         // Access to potential U derivatives
  507.         final FieldUAnddU<T> udu = new FieldUAnddU<>(spacecraftState.getDate(), context, auxiliaryElements, fho);

  508.         return computeMeanElementRates(spacecraftState.getDate(), context, udu);

  509.     }

  510.     /** Compute the mean element rates.
  511.      * @param date current date
  512.      * @param context container for attributes
  513.      * @param udu derivatives of the gravitational potential U
  514.      * @return the mean element rates
  515.      */
  516.     private double[] computeMeanElementRates(final AbsoluteDate date,
  517.                                              final DSSTZonalContext context,
  518.                                              final UAnddU udu) {

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

  521.         // Compute cross derivatives [Eq. 2.2-(8)]
  522.         // U(alpha,gamma) = alpha * dU/dgamma - gamma * dU/dalpha
  523.         final double UAlphaGamma   = auxiliaryElements.getAlpha() * udu.getdUdGa() - auxiliaryElements.getGamma() * udu.getdUdAl();
  524.         // U(beta,gamma) = beta * dU/dgamma - gamma * dU/dbeta
  525.         final double UBetaGamma    =  auxiliaryElements.getBeta() * udu.getdUdGa() - auxiliaryElements.getGamma() * udu.getdUdBe();
  526.         // Common factor
  527.         final double pUAGmIqUBGoAB = (auxiliaryElements.getP() * UAlphaGamma - I * auxiliaryElements.getQ() * UBetaGamma) * context.getOoAB();

  528.         // Compute mean elements rates [Eq. 3.1-(1)]
  529.         final double da =  0.;
  530.         final double dh =  context.getBoA() * udu.getdUdk() + auxiliaryElements.getK() * pUAGmIqUBGoAB;
  531.         final double dk = -context.getBoA() * udu.getdUdh() - auxiliaryElements.getH() * pUAGmIqUBGoAB;
  532.         final double dp =  context.getMCo2AB() * UBetaGamma;
  533.         final double dq =  context.getMCo2AB() * UAlphaGamma * I;
  534.         final double dM =  context.getM2aoA() * udu.getdUda() + context.getBoABpo() * (auxiliaryElements.getH() * udu.getdUdh() + auxiliaryElements.getK() * udu.getdUdk()) + pUAGmIqUBGoAB;

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

  537.     /** Compute the mean element rates.
  538.      * @param <T> type of the elements
  539.      * @param date current date
  540.      * @param context container for attributes
  541.      * @param udu derivatives of the gravitational potential U
  542.      * @return the mean element rates
  543.      */
  544.     private <T extends CalculusFieldElement<T>> T[] computeMeanElementRates(final FieldAbsoluteDate<T> date,
  545.                                                                         final FieldDSSTZonalContext<T> context,
  546.                                                                         final FieldUAnddU<T> udu) {

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

  549.         // Parameter for array building
  550.         final Field<T> field = date.getField();

  551.         // Compute cross derivatives [Eq. 2.2-(8)]
  552.         // U(alpha,gamma) = alpha * dU/dgamma - gamma * dU/dalpha
  553.         final T UAlphaGamma   = udu.getdUdGa().multiply(auxiliaryElements.getAlpha()).subtract(udu.getdUdAl().multiply(auxiliaryElements.getGamma()));
  554.         // U(beta,gamma) = beta * dU/dgamma - gamma * dU/dbeta
  555.         final T UBetaGamma    =  udu.getdUdGa().multiply(auxiliaryElements.getBeta()).subtract(udu.getdUdBe().multiply(auxiliaryElements.getGamma()));
  556.         // Common factor
  557.         final T pUAGmIqUBGoAB = (UAlphaGamma.multiply(auxiliaryElements.getP()).subtract(UBetaGamma.multiply(I).multiply(auxiliaryElements.getQ()))).multiply(context.getOoAB());

  558.         // Compute mean elements rates [Eq. 3.1-(1)]
  559.         final T da =  field.getZero();
  560.         final T dh =  udu.getdUdk().multiply(context.getBoA()).add(pUAGmIqUBGoAB.multiply(auxiliaryElements.getK()));
  561.         final T dk =  (udu.getdUdh().multiply(context.getBoA()).negate()).subtract(pUAGmIqUBGoAB.multiply(auxiliaryElements.getH()));
  562.         final T dp =  UBetaGamma.multiply(context.getMCo2AB());
  563.         final T dq =  UAlphaGamma.multiply(context.getMCo2AB()).multiply(I);
  564.         final T dM =  pUAGmIqUBGoAB.add(udu.getdUda().multiply(context.getM2aoA())).add((udu.getdUdh().multiply(auxiliaryElements.getH()).add(udu.getdUdk().multiply(auxiliaryElements.getK()))).multiply(context.getBoABpo()));

  565.         final T[] elements =  MathArrays.buildArray(field, 6);
  566.         elements[0] = da;
  567.         elements[1] = dk;
  568.         elements[2] = dh;
  569.         elements[3] = dq;
  570.         elements[4] = dp;
  571.         elements[5] = dM;

  572.         return elements;
  573.     }

  574.     /** {@inheritDoc} */
  575.     @Override
  576.     public void registerAttitudeProvider(final AttitudeProvider attitudeProvider) {
  577.         //nothing is done since this contribution is not sensitive to attitude
  578.     }

  579.     /** Check if an index is within the accepted interval.
  580.      *
  581.      * @param index the index to check
  582.      * @param lowerBound the lower bound of the interval
  583.      * @param upperBound the upper bound of the interval
  584.      * @return true if the index is between the lower and upper bounds, false otherwise
  585.      */
  586.     private boolean isBetween(final int index, final int lowerBound, final int upperBound) {
  587.         return index >= lowerBound && index <= upperBound;
  588.     }

  589.     /** {@inheritDoc} */
  590.     @Override
  591.     public void updateShortPeriodTerms(final double[] parameters, final SpacecraftState... meanStates) {

  592.         final Slot slot = zonalSPCoefs.createSlot(meanStates);
  593.         for (final SpacecraftState meanState : meanStates) {

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

  596.             // Container of attributes
  597.             // Extract the proper parameters valid for the corresponding meanState date from the input array
  598.             final double[] extractedParameters = this.extractParameters(parameters, auxiliaryElements.getDate());
  599.             final DSSTZonalContext context = initializeStep(auxiliaryElements, extractedParameters);

  600.             // Access to potential U derivatives
  601.             final UAnddU udu = new UAnddU(meanState.getDate(), context, auxiliaryElements, hansen);

  602.             // Compute rhoj and sigmaj
  603.             final double[][] rhoSigma = computeRhoSigmaCoefficients(meanState.getDate(), slot, auxiliaryElements);

  604.             // Compute Di
  605.             computeDiCoefficients(meanState.getDate(), slot, context, udu);

  606.             // generate the Cij and Sij coefficients
  607.             final FourierCjSjCoefficients cjsj = new FourierCjSjCoefficients(meanState.getDate(),
  608.                                                                              maxDegreeShortPeriodics,
  609.                                                                              maxEccPowShortPeriodics,
  610.                                                                              maxFrequencyShortPeriodics,
  611.                                                                              context,
  612.                                                                              hansen);

  613.             computeCijSijCoefficients(meanState.getDate(), slot, cjsj, rhoSigma, context, auxiliaryElements, udu);
  614.         }

  615.     }

  616.     /** {@inheritDoc} */
  617.     @Override
  618.     @SuppressWarnings("unchecked")
  619.     public <T extends CalculusFieldElement<T>> void updateShortPeriodTerms(final T[] parameters,
  620.                                                                        final FieldSpacecraftState<T>... meanStates) {

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

  623.         final FieldZonalShortPeriodicCoefficients<T> fzspc = (FieldZonalShortPeriodicCoefficients<T>) zonalFieldSPCoefs.get(field);
  624.         final FieldSlot<T> slot = fzspc.createSlot(meanStates);
  625.         for (final FieldSpacecraftState<T> meanState : meanStates) {

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

  628.             // Container of attributes
  629.             // Extract the proper parameters valid for the corresponding meanState date from the input array
  630.             final T[] extractedParameters = this.extractParameters(parameters, auxiliaryElements.getDate());
  631.             final FieldDSSTZonalContext<T> context = initializeStep(auxiliaryElements, extractedParameters);

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

  633.             // Access to potential U derivatives
  634.             final FieldUAnddU<T> udu = new FieldUAnddU<>(meanState.getDate(), context, auxiliaryElements, fho);

  635.             // Compute rhoj and sigmaj
  636.             final T[][] rhoSigma = computeRhoSigmaCoefficients(meanState.getDate(), slot, auxiliaryElements, field);

  637.             // Compute Di
  638.             computeDiCoefficients(meanState.getDate(), slot, context, field, udu);

  639.             // generate the Cij and Sij coefficients
  640.             final FieldFourierCjSjCoefficients<T> cjsj = new FieldFourierCjSjCoefficients<>(meanState.getDate(),
  641.                                                                                             maxDegreeShortPeriodics,
  642.                                                                                             maxEccPowShortPeriodics,
  643.                                                                                             maxFrequencyShortPeriodics,
  644.                                                                                             context,
  645.                                                                                             fho);


  646.             computeCijSijCoefficients(meanState.getDate(), slot, cjsj, rhoSigma, context, auxiliaryElements, field, udu);
  647.         }
  648.     }

  649.     /** {@inheritDoc} */
  650.     public List<ParameterDriver> getParametersDrivers() {
  651.         return Collections.singletonList(gmParameterDriver);
  652.     }

  653.     /** Generate the values for the D<sub>i</sub> coefficients.
  654.      * @param date target date
  655.      * @param slot slot to which the coefficients belong
  656.      * @param context container for attributes
  657.      * @param udu derivatives of the gravitational potential U
  658.      */
  659.     private void computeDiCoefficients(final AbsoluteDate date,
  660.                                        final Slot slot,
  661.                                        final DSSTZonalContext context,
  662.                                        final UAnddU udu) {

  663.         final double[] meanElementRates = computeMeanElementRates(date, context, udu);

  664.         final double[] currentDi = new double[6];

  665.         // Add the coefficients to the interpolation grid
  666.         for (int i = 0; i < 6; i++) {
  667.             currentDi[i] = meanElementRates[i] / context.getMeanMotion();

  668.             if (i == 5) {
  669.                 currentDi[i] += -1.5 * 2 * udu.getU() * context.getOON2A2();
  670.             }

  671.         }

  672.         slot.di.addGridPoint(date, currentDi);

  673.     }

  674.     /** Generate the values for the D<sub>i</sub> coefficients.
  675.      * @param <T> type of the elements
  676.      * @param date target date
  677.      * @param slot slot to which the coefficients belong
  678.      * @param context container for attributes
  679.      * @param field field used by default
  680.      * @param udu derivatives of the gravitational potential U
  681.      */
  682.     private <T extends CalculusFieldElement<T>> void computeDiCoefficients(final FieldAbsoluteDate<T> date,
  683.                                                                        final FieldSlot<T> slot,
  684.                                                                        final FieldDSSTZonalContext<T> context,
  685.                                                                        final Field<T> field,
  686.                                                                        final FieldUAnddU<T> udu) {

  687.         final T[] meanElementRates = computeMeanElementRates(date, context, udu);

  688.         final T[] currentDi = MathArrays.buildArray(field, 6);

  689.         // Add the coefficients to the interpolation grid
  690.         for (int i = 0; i < 6; i++) {
  691.             currentDi[i] = meanElementRates[i].divide(context.getMeanMotion());

  692.             if (i == 5) {
  693.                 currentDi[i] = currentDi[i].add(context.getOON2A2().multiply(udu.getU()).multiply(2.).multiply(-1.5));
  694.             }

  695.         }

  696.         slot.di.addGridPoint(date, currentDi);

  697.     }

  698.     /**
  699.      * Generate the values for the C<sub>i</sub><sup>j</sup> and the S<sub>i</sub><sup>j</sup> coefficients.
  700.      * @param date date of computation
  701.      * @param slot slot to which the coefficients belong
  702.      * @param cjsj Fourier coefficients
  703.      * @param rhoSigma ρ<sub>j</sub> and σ<sub>j</sub>
  704.      * @param context container for attributes
  705.      * @param auxiliaryElements auxiliary elements related to the current orbit
  706.      * @param udu derivatives of the gravitational potential U
  707.      */
  708.     private void computeCijSijCoefficients(final AbsoluteDate date, final Slot slot,
  709.                                            final FourierCjSjCoefficients cjsj,
  710.                                            final double[][] rhoSigma, final DSSTZonalContext context,
  711.                                            final AuxiliaryElements auxiliaryElements,
  712.                                            final UAnddU udu) {

  713.         final int nMax = maxDegreeShortPeriodics;

  714.         // The C<sub>i</sub>⁰ coefficients
  715.         final double[] currentCi0 = new double[] {0., 0., 0., 0., 0., 0.};
  716.         for (int j = 1; j < slot.cij.length; j++) {

  717.             // Create local arrays
  718.             final double[] currentCij = new double[] {0., 0., 0., 0., 0., 0.};
  719.             final double[] currentSij = new double[] {0., 0., 0., 0., 0., 0.};

  720.             // j == 1
  721.             if (j == 1) {
  722.                 final double coef1 = 4 * auxiliaryElements.getK() * udu.getU() - context.getHK() * cjsj.getCj(1) + context.getK2MH2O2() * cjsj.getSj(1);
  723.                 final double coef2 = 4 * auxiliaryElements.getH() * udu.getU() + context.getK2MH2O2() * cjsj.getCj(1) + context.getHK() * cjsj.getSj(1);
  724.                 final double coef3 = (auxiliaryElements.getK() * cjsj.getCj(1) + auxiliaryElements.getH() * cjsj.getSj(1)) / 4.;
  725.                 final double coef4 = (8 * udu.getU() - auxiliaryElements.getH() * cjsj.getCj(1) + auxiliaryElements.getK() * cjsj.getSj(1)) / 4.;

  726.                 //Coefficients for a
  727.                 currentCij[0] += coef1;
  728.                 currentSij[0] += coef2;

  729.                 //Coefficients for k
  730.                 currentCij[1] += coef4;
  731.                 currentSij[1] += coef3;

  732.                 //Coefficients for h
  733.                 currentCij[2] -= coef3;
  734.                 currentSij[2] += coef4;

  735.                 //Coefficients for λ
  736.                 currentCij[5] -= coef2 / 2;
  737.                 currentSij[5] += coef1 / 2;
  738.             }

  739.             // j == 2
  740.             if (j == 2) {
  741.                 final double coef1 = context.getK2MH2() * udu.getU();
  742.                 final double coef2 = 2 * context.getHK() * udu.getU();
  743.                 final double coef3 = auxiliaryElements.getH() * udu.getU() / 2;
  744.                 final double coef4 = auxiliaryElements.getK() * udu.getU() / 2;

  745.                 //Coefficients for a
  746.                 currentCij[0] += coef1;
  747.                 currentSij[0] += coef2;

  748.                 //Coefficients for k
  749.                 currentCij[1] += coef4;
  750.                 currentSij[1] += coef3;

  751.                 //Coefficients for h
  752.                 currentCij[2] -= coef3;
  753.                 currentSij[2] += coef4;

  754.                 //Coefficients for λ
  755.                 currentCij[5] -= coef2 / 2;
  756.                 currentSij[5] += coef1 / 2;
  757.             }

  758.             // j between 1 and 2N-3
  759.             if (isBetween(j, 1, 2 * nMax - 3) && j + 2 < cjsj.jMax) {
  760.                 final double coef1 = ( j + 2 ) * (-context.getHK() * cjsj.getCj(j + 2) + context.getK2MH2O2() * cjsj.getSj(j + 2));
  761.                 final double coef2 = ( j + 2 ) * (context.getK2MH2O2() * cjsj.getCj(j + 2) + context.getHK() * cjsj.getSj(j + 2));
  762.                 final double coef3 = ( j + 2 ) * (auxiliaryElements.getK() * cjsj.getCj(j + 2) + auxiliaryElements.getH() * cjsj.getSj(j + 2)) / 4;
  763.                 final double coef4 = ( j + 2 ) * (auxiliaryElements.getH() * cjsj.getCj(j + 2) - auxiliaryElements.getK() * cjsj.getSj(j + 2)) / 4;

  764.                 //Coefficients for a
  765.                 currentCij[0] += coef1;
  766.                 currentSij[0] -= coef2;

  767.                 //Coefficients for k
  768.                 currentCij[1] += -coef4;
  769.                 currentSij[1] -= coef3;

  770.                 //Coefficients for h
  771.                 currentCij[2] -= coef3;
  772.                 currentSij[2] += coef4;

  773.                 //Coefficients for λ
  774.                 currentCij[5] -= coef2 / 2;
  775.                 currentSij[5] += -coef1 / 2;
  776.             }

  777.             // j between 1 and 2N-2
  778.             if (isBetween(j, 1, 2 * nMax - 2) && j + 1 < cjsj.jMax) {
  779.                 final double coef1 = 2 * ( j + 1 ) * (-auxiliaryElements.getH() * cjsj.getCj(j + 1) + auxiliaryElements.getK() * cjsj.getSj(j + 1));
  780.                 final double coef2 = 2 * ( j + 1 ) * (auxiliaryElements.getK() * cjsj.getCj(j + 1) + auxiliaryElements.getH() * cjsj.getSj(j + 1));
  781.                 final double coef3 = ( j + 1 ) * cjsj.getCj(j + 1);
  782.                 final double coef4 = ( j + 1 ) * cjsj.getSj(j + 1);

  783.                 //Coefficients for a
  784.                 currentCij[0] += coef1;
  785.                 currentSij[0] -= coef2;

  786.                 //Coefficients for k
  787.                 currentCij[1] += coef4;
  788.                 currentSij[1] -= coef3;

  789.                 //Coefficients for h
  790.                 currentCij[2] -= coef3;
  791.                 currentSij[2] -= coef4;

  792.                 //Coefficients for λ
  793.                 currentCij[5] -= coef2 / 2;
  794.                 currentSij[5] += -coef1 / 2;
  795.             }

  796.             // j between 2 and 2N
  797.             if (isBetween(j, 2, 2 * nMax) && j - 1 < cjsj.jMax) {
  798.                 final double coef1 = 2 * ( j - 1 ) * (auxiliaryElements.getH() * cjsj.getCj(j - 1) + auxiliaryElements.getK() * cjsj.getSj(j - 1));
  799.                 final double coef2 = 2 * ( j - 1 ) * (auxiliaryElements.getK() * cjsj.getCj(j - 1) - auxiliaryElements.getH() * cjsj.getSj(j - 1));
  800.                 final double coef3 = ( j - 1 ) * cjsj.getCj(j - 1);
  801.                 final double coef4 = ( j - 1 ) * cjsj.getSj(j - 1);

  802.                 //Coefficients for a
  803.                 currentCij[0] += coef1;
  804.                 currentSij[0] -= coef2;

  805.                 //Coefficients for k
  806.                 currentCij[1] += coef4;
  807.                 currentSij[1] -= coef3;

  808.                 //Coefficients for h
  809.                 currentCij[2] += coef3;
  810.                 currentSij[2] += coef4;

  811.                 //Coefficients for λ
  812.                 currentCij[5] += coef2 / 2;
  813.                 currentSij[5] += coef1 / 2;
  814.             }

  815.             // j between 3 and 2N + 1
  816.             if (isBetween(j, 3, 2 * nMax + 1) && j - 2 < cjsj.jMax) {
  817.                 final double coef1 = ( j - 2 ) * (context.getHK() * cjsj.getCj(j - 2) + context.getK2MH2O2() * cjsj.getSj(j - 2));
  818.                 final double coef2 = ( j - 2 ) * (-context.getK2MH2O2() * cjsj.getCj(j - 2) + context.getHK() * cjsj.getSj(j - 2));
  819.                 final double coef3 = ( j - 2 ) * (auxiliaryElements.getK() * cjsj.getCj(j - 2) - auxiliaryElements.getH() * cjsj.getSj(j - 2)) / 4;
  820.                 final double coef4 = ( j - 2 ) * (auxiliaryElements.getH() * cjsj.getCj(j - 2) + auxiliaryElements.getK() * cjsj.getSj(j - 2)) / 4;
  821.                 final double coef5 = ( j - 2 ) * (context.getK2MH2O2() * cjsj.getCj(j - 2) - context.getHK() * cjsj.getSj(j - 2));

  822.                 //Coefficients for a
  823.                 currentCij[0] += coef1;
  824.                 currentSij[0] += coef2;

  825.                 //Coefficients for k
  826.                 currentCij[1] += coef4;
  827.                 currentSij[1] += -coef3;

  828.                 //Coefficients for h
  829.                 currentCij[2] += coef3;
  830.                 currentSij[2] += coef4;

  831.                 //Coefficients for λ
  832.                 currentCij[5] += coef5 / 2;
  833.                 currentSij[5] += coef1 / 2;
  834.             }

  835.             //multiply by the common factor
  836.             //for a (i == 0) -> χ³ / (n² * a)
  837.             currentCij[0] *= context.getX3ON2A();
  838.             currentSij[0] *= context.getX3ON2A();
  839.             //for k (i == 1) -> χ / (n² * a²)
  840.             currentCij[1] *= context.getXON2A2();
  841.             currentSij[1] *= context.getXON2A2();
  842.             //for h (i == 2) -> χ / (n² * a²)
  843.             currentCij[2] *= context.getXON2A2();
  844.             currentSij[2] *= context.getXON2A2();
  845.             //for λ (i == 5) -> (χ²) / (n² * a² * (χ + 1 ) )
  846.             currentCij[5] *= context.getX2ON2A2XP1();
  847.             currentSij[5] *= context.getX2ON2A2XP1();

  848.             // j is between 1 and 2 * N - 1
  849.             if (isBetween(j, 1, 2 * nMax - 1) && j < cjsj.jMax) {
  850.                 // Compute cross derivatives
  851.                 // Cj(alpha,gamma) = alpha * dC/dgamma - gamma * dC/dalpha
  852.                 final double CjAlphaGamma   = auxiliaryElements.getAlpha() * cjsj.getdCjdGamma(j) - auxiliaryElements.getGamma() * cjsj.getdCjdAlpha(j);
  853.                 // Cj(alpha,beta) = alpha * dC/dbeta - beta * dC/dalpha
  854.                 final double CjAlphaBeta   = auxiliaryElements.getAlpha() * cjsj.getdCjdBeta(j) - auxiliaryElements.getBeta() * cjsj.getdCjdAlpha(j);
  855.                 // Cj(beta,gamma) = beta * dC/dgamma - gamma * dC/dbeta
  856.                 final double CjBetaGamma    =  auxiliaryElements.getBeta() * cjsj.getdCjdGamma(j) - auxiliaryElements.getGamma() * cjsj.getdCjdBeta(j);
  857.                 // Cj(h,k) = h * dC/dk - k * dC/dh
  858.                 final double CjHK   = auxiliaryElements.getH() * cjsj.getdCjdK(j) - auxiliaryElements.getK() * cjsj.getdCjdH(j);
  859.                 // Sj(alpha,gamma) = alpha * dS/dgamma - gamma * dS/dalpha
  860.                 final double SjAlphaGamma   = auxiliaryElements.getAlpha() * cjsj.getdSjdGamma(j) - auxiliaryElements.getGamma() * cjsj.getdSjdAlpha(j);
  861.                 // Sj(alpha,beta) = alpha * dS/dbeta - beta * dS/dalpha
  862.                 final double SjAlphaBeta   = auxiliaryElements.getAlpha() * cjsj.getdSjdBeta(j) - auxiliaryElements.getBeta() * cjsj.getdSjdAlpha(j);
  863.                 // Sj(beta,gamma) = beta * dS/dgamma - gamma * dS/dbeta
  864.                 final double SjBetaGamma    =  auxiliaryElements.getBeta() * cjsj.getdSjdGamma(j) - auxiliaryElements.getGamma() * cjsj.getdSjdBeta(j);
  865.                 // Sj(h,k) = h * dS/dk - k * dS/dh
  866.                 final double SjHK   = auxiliaryElements.getH() * cjsj.getdSjdK(j) - auxiliaryElements.getK() * cjsj.getdSjdH(j);

  867.                 //Coefficients for a
  868.                 final double coef1 = context.getX3ON2A() * (3 - context.getBB()) * j;
  869.                 currentCij[0] += coef1 * cjsj.getSj(j);
  870.                 currentSij[0] -= coef1 * cjsj.getCj(j);

  871.                 //Coefficients for k and h
  872.                 final double coef2 = auxiliaryElements.getP() * CjAlphaGamma - I * auxiliaryElements.getQ() * CjBetaGamma;
  873.                 final double coef3 = auxiliaryElements.getP() * SjAlphaGamma - I * auxiliaryElements.getQ() * SjBetaGamma;
  874.                 currentCij[1] -= context.getXON2A2() * (auxiliaryElements.getH() * coef2 + context.getBB() * cjsj.getdCjdH(j) - 1.5 * auxiliaryElements.getK() * j * cjsj.getSj(j));
  875.                 currentSij[1] -= context.getXON2A2() * (auxiliaryElements.getH() * coef3 + context.getBB() * cjsj.getdSjdH(j) + 1.5 * auxiliaryElements.getK() * j * cjsj.getCj(j));
  876.                 currentCij[2] += context.getXON2A2() * (auxiliaryElements.getK() * coef2 + context.getBB() * cjsj.getdCjdK(j) + 1.5 * auxiliaryElements.getH() * j * cjsj.getSj(j));
  877.                 currentSij[2] += context.getXON2A2() * (auxiliaryElements.getK() * coef3 + context.getBB() * cjsj.getdSjdK(j) - 1.5 * auxiliaryElements.getH() * j * cjsj.getCj(j));

  878.                 //Coefficients for q and p
  879.                 final double coef4 = CjHK - CjAlphaBeta - j * cjsj.getSj(j);
  880.                 final double coef5 = SjHK - SjAlphaBeta + j * cjsj.getCj(j);
  881.                 currentCij[3] = context.getCXO2N2A2() * (-I * CjAlphaGamma + auxiliaryElements.getQ() * coef4);
  882.                 currentSij[3] = context.getCXO2N2A2() * (-I * SjAlphaGamma + auxiliaryElements.getQ() * coef5);
  883.                 currentCij[4] = context.getCXO2N2A2() * (-CjBetaGamma + auxiliaryElements.getP() * coef4);
  884.                 currentSij[4] = context.getCXO2N2A2() * (-SjBetaGamma + auxiliaryElements.getP() * coef5);

  885.                 //Coefficients for λ
  886.                 final double coef6 = auxiliaryElements.getH() * cjsj.getdCjdH(j) + auxiliaryElements.getK() * cjsj.getdCjdK(j);
  887.                 final double coef7 = auxiliaryElements.getH() * cjsj.getdSjdH(j) + auxiliaryElements.getK() * cjsj.getdSjdK(j);
  888.                 currentCij[5] += context.getOON2A2() * (-2 * auxiliaryElements.getSma() * cjsj.getdCjdA(j) + coef6 / (context.getX() + 1) + context.getX() * coef2 - 3 * cjsj.getCj(j));
  889.                 currentSij[5] += context.getOON2A2() * (-2 * auxiliaryElements.getSma() * cjsj.getdSjdA(j) + coef7 / (context.getX() + 1) + context.getX() * coef3 - 3 * cjsj.getSj(j));
  890.             }

  891.             for (int i = 0; i < 6; i++) {
  892.                 //Add the current coefficients contribution to C<sub>i</sub>⁰
  893.                 currentCi0[i] -= currentCij[i] * rhoSigma[j][0] + currentSij[i] * rhoSigma[j][1];
  894.             }

  895.             // Add the coefficients to the interpolation grid
  896.             slot.cij[j].addGridPoint(date, currentCij);
  897.             slot.sij[j].addGridPoint(date, currentSij);

  898.         }

  899.         //Add C<sub>i</sub>⁰ to the interpolation grid
  900.         slot.cij[0].addGridPoint(date, currentCi0);

  901.     }

  902.     /**
  903.      * Generate the values for the C<sub>i</sub><sup>j</sup> and the S<sub>i</sub><sup>j</sup> coefficients.
  904.      * @param <T> type of the elements
  905.      * @param date date of computation
  906.      * @param slot slot to which the coefficients belong
  907.      * @param cjsj Fourier coefficients
  908.      * @param rhoSigma ρ<sub>j</sub> and σ<sub>j</sub>
  909.      * @param context container for attributes
  910.      * @param auxiliaryElements auxiliary elements related to the current orbit
  911.      * @param field field used by default
  912.      * @param udu derivatives of the gravitational potential U
  913.      */
  914.     private <T extends CalculusFieldElement<T>> void computeCijSijCoefficients(final FieldAbsoluteDate<T> date,
  915.                                                                            final FieldSlot<T> slot,
  916.                                                                            final FieldFourierCjSjCoefficients<T> cjsj,
  917.                                                                            final T[][] rhoSigma,
  918.                                                                            final FieldDSSTZonalContext<T> context,
  919.                                                                            final FieldAuxiliaryElements<T> auxiliaryElements,
  920.                                                                            final Field<T> field,
  921.                                                                            final FieldUAnddU<T> udu) {

  922.         // Zero
  923.         final T zero = field.getZero();

  924.         final int nMax = maxDegreeShortPeriodics;

  925.         // The C<sub>i</sub>⁰ coefficients
  926.         final T[] currentCi0 = MathArrays.buildArray(field, 6);
  927.         Arrays.fill(currentCi0, zero);

  928.         for (int j = 1; j < slot.cij.length; j++) {

  929.             // Create local arrays
  930.             final T[] currentCij = MathArrays.buildArray(field, 6);
  931.             final T[] currentSij = MathArrays.buildArray(field, 6);

  932.             Arrays.fill(currentCij, zero);
  933.             Arrays.fill(currentSij, zero);

  934.             // j == 1
  935.             if (j == 1) {
  936.                 final T coef1 = auxiliaryElements.getK().multiply(udu.getU()).multiply(4.).subtract(context.getHK().multiply(cjsj.getCj(1))).add(context.getK2MH2O2().multiply(cjsj.getSj(1)));
  937.                 final T coef2 = auxiliaryElements.getH().multiply(udu.getU()).multiply(4.).add(context.getK2MH2O2().multiply(cjsj.getCj(1))).add(context.getHK().multiply(cjsj.getSj(1)));
  938.                 final T coef3 = auxiliaryElements.getK().multiply(cjsj.getCj(1)).add(auxiliaryElements.getH().multiply(cjsj.getSj(1))).divide(4.);
  939.                 final T coef4 = udu.getU().multiply(8.).subtract(auxiliaryElements.getH().multiply(cjsj.getCj(1))).add(auxiliaryElements.getK().multiply(cjsj.getSj(1))).divide(4.);

  940.                 //Coefficients for a
  941.                 currentCij[0] = currentCij[0].add(coef1);
  942.                 currentSij[0] = currentSij[0].add(coef2);

  943.                 //Coefficients for k
  944.                 currentCij[1] = currentCij[1].add(coef4);
  945.                 currentSij[1] = currentSij[1].add(coef3);

  946.                 //Coefficients for h
  947.                 currentCij[2] = currentCij[2].subtract(coef3);
  948.                 currentSij[2] = currentSij[2].add(coef4);

  949.                 //Coefficients for λ
  950.                 currentCij[5] = currentCij[5].subtract(coef2.divide(2.));
  951.                 currentSij[5] = currentSij[5].add(coef1.divide(2.));
  952.             }

  953.             // j == 2
  954.             if (j == 2) {
  955.                 final T coef1 = context.getK2MH2().multiply(udu.getU());
  956.                 final T coef2 = context.getHK().multiply(udu.getU()).multiply(2.);
  957.                 final T coef3 = auxiliaryElements.getH().multiply(udu.getU()).divide(2.);
  958.                 final T coef4 = auxiliaryElements.getK().multiply(udu.getU()).divide(2.);

  959.                 //Coefficients for a
  960.                 currentCij[0] = currentCij[0].add(coef1);
  961.                 currentSij[0] = currentSij[0].add(coef2);

  962.                 //Coefficients for k
  963.                 currentCij[1] = currentCij[1].add(coef4);
  964.                 currentSij[1] = currentSij[1].add(coef3);

  965.                 //Coefficients for h
  966.                 currentCij[2] = currentCij[2].subtract(coef3);
  967.                 currentSij[2] = currentSij[2].add(coef4);

  968.                 //Coefficients for λ
  969.                 currentCij[5] = currentCij[5].subtract(coef2.divide(2.));
  970.                 currentSij[5] = currentSij[5].add(coef1.divide(2.));
  971.             }

  972.             // j between 1 and 2N-3
  973.             if (isBetween(j, 1, 2 * nMax - 3) && j + 2 < cjsj.jMax) {
  974.                 final T coef1 = context.getHK().negate().multiply(cjsj.getCj(j + 2)).add(context.getK2MH2O2().multiply(cjsj.getSj(j + 2))).multiply(j + 2);
  975.                 final T coef2 = context.getK2MH2O2().multiply(cjsj.getCj(j + 2)).add(context.getHK().multiply(cjsj.getSj(j + 2))).multiply(j + 2);
  976.                 final T coef3 = auxiliaryElements.getK().multiply(cjsj.getCj(j + 2)).add(auxiliaryElements.getH().multiply(cjsj.getSj(j + 2))).multiply(j + 2).divide(4.);
  977.                 final T coef4 = auxiliaryElements.getH().multiply(cjsj.getCj(j + 2)).subtract(auxiliaryElements.getK().multiply(cjsj.getSj(j + 2))).multiply(j + 2).divide(4.);

  978.                 //Coefficients for a
  979.                 currentCij[0] = currentCij[0].add(coef1);
  980.                 currentSij[0] = currentSij[0].subtract(coef2);

  981.                 //Coefficients for k
  982.                 currentCij[1] = currentCij[1].add(coef4.negate());
  983.                 currentSij[1] = currentSij[1].subtract(coef3);

  984.                 //Coefficients for h
  985.                 currentCij[2] = currentCij[2].subtract(coef3);
  986.                 currentSij[2] = currentSij[2].add(coef4);

  987.                 //Coefficients for λ
  988.                 currentCij[5] = currentCij[5].subtract(coef2.divide(2.));
  989.                 currentSij[5] = currentSij[5].add(coef1.negate().divide(2.));
  990.             }

  991.             // j between 1 and 2N-2
  992.             if (isBetween(j, 1, 2 * nMax - 2) && j + 1 < cjsj.jMax) {
  993.                 final T coef1 = auxiliaryElements.getH().negate().multiply(cjsj.getCj(j + 1)).add(auxiliaryElements.getK().multiply(cjsj.getSj(j + 1))).multiply(2. * (j + 1));
  994.                 final T coef2 = auxiliaryElements.getK().multiply(cjsj.getCj(j + 1)).add(auxiliaryElements.getH().multiply(cjsj.getSj(j + 1))).multiply(2. * (j + 1));
  995.                 final T coef3 = cjsj.getCj(j + 1).multiply(j + 1);
  996.                 final T coef4 = cjsj.getSj(j + 1).multiply(j + 1);

  997.                 //Coefficients for a
  998.                 currentCij[0] = currentCij[0].add(coef1);
  999.                 currentSij[0] = currentSij[0].subtract(coef2);

  1000.                 //Coefficients for k
  1001.                 currentCij[1] = currentCij[1].add(coef4);
  1002.                 currentSij[1] = currentSij[1].subtract(coef3);

  1003.                 //Coefficients for h
  1004.                 currentCij[2] = currentCij[2].subtract(coef3);
  1005.                 currentSij[2] = currentSij[2].subtract(coef4);

  1006.                 //Coefficients for λ
  1007.                 currentCij[5] = currentCij[5].subtract(coef2.divide(2.));
  1008.                 currentSij[5] = currentSij[5].add(coef1.negate().divide(2.));
  1009.             }

  1010.             // j between 2 and 2N
  1011.             if (isBetween(j, 2, 2 * nMax) && j - 1 < cjsj.jMax) {
  1012.                 final T coef1 = auxiliaryElements.getH().multiply(cjsj.getCj(j - 1)).add(auxiliaryElements.getK().multiply(cjsj.getSj(j - 1))).multiply(2 * ( j - 1 ));
  1013.                 final T coef2 = auxiliaryElements.getK().multiply(cjsj.getCj(j - 1)).subtract(auxiliaryElements.getH().multiply(cjsj.getSj(j - 1))).multiply(2 * ( j - 1 ));
  1014.                 final T coef3 = cjsj.getCj(j - 1).multiply(j - 1);
  1015.                 final T coef4 = cjsj.getSj(j - 1).multiply(j - 1);

  1016.                 //Coefficients for a
  1017.                 currentCij[0] = currentCij[0].add(coef1);
  1018.                 currentSij[0] = currentSij[0].subtract(coef2);

  1019.                 //Coefficients for k
  1020.                 currentCij[1] = currentCij[1].add(coef4);
  1021.                 currentSij[1] = currentSij[1].subtract(coef3);

  1022.                 //Coefficients for h
  1023.                 currentCij[2] = currentCij[2].add(coef3);
  1024.                 currentSij[2] = currentSij[2].add(coef4);

  1025.                 //Coefficients for λ
  1026.                 currentCij[5] = currentCij[5].add(coef2.divide(2.));
  1027.                 currentSij[5] = currentSij[5].add(coef1.divide(2.));
  1028.             }

  1029.             // j between 3 and 2N + 1
  1030.             if (isBetween(j, 3, 2 * nMax + 1) && j - 2 < cjsj.jMax) {
  1031.                 final T coef1 = context.getHK().multiply(cjsj.getCj(j - 2)).add(context.getK2MH2O2().multiply(cjsj.getSj(j - 2))).multiply(j - 2);
  1032.                 final T coef2 = context.getK2MH2O2().negate().multiply(cjsj.getCj(j - 2)).add(context.getHK().multiply(cjsj.getSj(j - 2))).multiply(j - 2);
  1033.                 final T coef3 = auxiliaryElements.getK().multiply(cjsj.getCj(j - 2)).subtract(auxiliaryElements.getH().multiply(cjsj.getSj(j - 2))).multiply(j - 2).divide(4.);
  1034.                 final T coef4 = auxiliaryElements.getH().multiply(cjsj.getCj(j - 2)).add(auxiliaryElements.getK().multiply(cjsj.getSj(j - 2))).multiply(j - 2).divide(4.);
  1035.                 final T coef5 = context.getK2MH2O2().multiply(cjsj.getCj(j - 2)).subtract(context.getHK().multiply(cjsj.getSj(j - 2))).multiply(j - 2);

  1036.                 //Coefficients for a
  1037.                 currentCij[0] = currentCij[0].add(coef1);
  1038.                 currentSij[0] = currentSij[0].add(coef2);

  1039.                 //Coefficients for k
  1040.                 currentCij[1] = currentCij[1].add(coef4);
  1041.                 currentSij[1] = currentSij[1].add(coef3.negate());

  1042.                 //Coefficients for h
  1043.                 currentCij[2] = currentCij[2].add(coef3);
  1044.                 currentSij[2] = currentSij[2].add(coef4);

  1045.                 //Coefficients for λ
  1046.                 currentCij[5] = currentCij[5].add(coef5.divide(2.));
  1047.                 currentSij[5] = currentSij[5].add(coef1.divide(2.));
  1048.             }

  1049.             //multiply by the common factor
  1050.             //for a (i == 0) -> χ³ / (n² * a)
  1051.             currentCij[0] = currentCij[0].multiply(context.getX3ON2A());
  1052.             currentSij[0] = currentSij[0].multiply(context.getX3ON2A());
  1053.             //for k (i == 1) -> χ / (n² * a²)
  1054.             currentCij[1] = currentCij[1].multiply(context.getXON2A2());
  1055.             currentSij[1] = currentSij[1].multiply(context.getXON2A2());
  1056.             //for h (i == 2) -> χ / (n² * a²)
  1057.             currentCij[2] = currentCij[2].multiply(context.getXON2A2());
  1058.             currentSij[2] = currentSij[2].multiply(context.getXON2A2());
  1059.             //for λ (i == 5) -> (χ²) / (n² * a² * (χ + 1 ) )
  1060.             currentCij[5] = currentCij[5].multiply(context.getX2ON2A2XP1());
  1061.             currentSij[5] = currentSij[5].multiply(context.getX2ON2A2XP1());

  1062.             // j is between 1 and 2 * N - 1
  1063.             if (isBetween(j, 1, 2 * nMax - 1) && j < cjsj.jMax) {
  1064.                 // Compute cross derivatives
  1065.                 // Cj(alpha,gamma) = alpha * dC/dgamma - gamma * dC/dalpha
  1066.                 final T CjAlphaGamma = auxiliaryElements.getAlpha().multiply(cjsj.getdCjdGamma(j)).subtract(auxiliaryElements.getGamma().multiply(cjsj.getdCjdAlpha(j)));
  1067.                 // Cj(alpha,beta) = alpha * dC/dbeta - beta * dC/dalpha
  1068.                 final T CjAlphaBeta  = auxiliaryElements.getAlpha().multiply(cjsj.getdCjdBeta(j)).subtract(auxiliaryElements.getBeta().multiply(cjsj.getdCjdAlpha(j)));
  1069.                 // Cj(beta,gamma) = beta * dC/dgamma - gamma * dC/dbeta
  1070.                 final T CjBetaGamma  = auxiliaryElements.getBeta().multiply(cjsj.getdCjdGamma(j)).subtract(auxiliaryElements.getGamma().multiply(cjsj.getdCjdBeta(j)));
  1071.                 // Cj(h,k) = h * dC/dk - k * dC/dh
  1072.                 final T CjHK         = auxiliaryElements.getH().multiply(cjsj.getdCjdK(j)).subtract(auxiliaryElements.getK().multiply(cjsj.getdCjdH(j)));
  1073.                 // Sj(alpha,gamma) = alpha * dS/dgamma - gamma * dS/dalpha
  1074.                 final T SjAlphaGamma = auxiliaryElements.getAlpha().multiply(cjsj.getdSjdGamma(j)).subtract(auxiliaryElements.getGamma().multiply(cjsj.getdSjdAlpha(j)));
  1075.                 // Sj(alpha,beta) = alpha * dS/dbeta - beta * dS/dalpha
  1076.                 final T SjAlphaBeta  = auxiliaryElements.getAlpha().multiply(cjsj.getdSjdBeta(j)).subtract(auxiliaryElements.getBeta().multiply(cjsj.getdSjdAlpha(j)));
  1077.                 // Sj(beta,gamma) = beta * dS/dgamma - gamma * dS/dbeta
  1078.                 final T SjBetaGamma  = auxiliaryElements.getBeta().multiply(cjsj.getdSjdGamma(j)).subtract(auxiliaryElements.getGamma().multiply(cjsj.getdSjdBeta(j)));
  1079.                 // Sj(h,k) = h * dS/dk - k * dS/dh
  1080.                 final T SjHK         = auxiliaryElements.getH().multiply(cjsj.getdSjdK(j)).subtract(auxiliaryElements.getK().multiply(cjsj.getdSjdH(j)));

  1081.                 //Coefficients for a
  1082.                 final T coef1 = context.getX3ON2A().multiply(context.getBB().negate().add(3.)).multiply(j);
  1083.                 currentCij[0] = currentCij[0].add(coef1.multiply(cjsj.getSj(j)));
  1084.                 currentSij[0] = currentSij[0].subtract(coef1.multiply(cjsj.getCj(j)));

  1085.                 //Coefficients for k and h
  1086.                 final T coef2 = auxiliaryElements.getP().multiply(CjAlphaGamma).subtract(auxiliaryElements.getQ().multiply(CjBetaGamma).multiply(I));
  1087.                 final T coef3 = auxiliaryElements.getP().multiply(SjAlphaGamma).subtract(auxiliaryElements.getQ().multiply(SjBetaGamma).multiply(I));
  1088.                 currentCij[1] = currentCij[1].subtract(context.getXON2A2().multiply(auxiliaryElements.getH().multiply(coef2).add(context.getBB().multiply(cjsj.getdCjdH(j))).subtract(auxiliaryElements.getK().multiply(1.5).multiply(j).multiply(cjsj.getSj(j)))));
  1089.                 currentSij[1] = currentSij[1].subtract(context.getXON2A2().multiply(auxiliaryElements.getH().multiply(coef3).add(context.getBB().multiply(cjsj.getdSjdH(j))).add(auxiliaryElements.getK().multiply(1.5).multiply(j).multiply(cjsj.getCj(j)))));
  1090.                 currentCij[2] = currentCij[2].add(context.getXON2A2().multiply(auxiliaryElements.getK().multiply(coef2).add(context.getBB().multiply(cjsj.getdCjdK(j))).add(auxiliaryElements.getH().multiply(1.5).multiply(j).multiply(cjsj.getSj(j)))));
  1091.                 currentSij[2] = currentSij[2].add(context.getXON2A2().multiply(auxiliaryElements.getK().multiply(coef3).add(context.getBB().multiply(cjsj.getdSjdK(j))).subtract(auxiliaryElements.getH().multiply(1.5).multiply(j).multiply(cjsj.getCj(j)))));

  1092.                 //Coefficients for q and p
  1093.                 final T coef4 = CjHK.subtract(CjAlphaBeta).subtract(cjsj.getSj(j).multiply(j));
  1094.                 final T coef5 = SjHK.subtract(SjAlphaBeta).add(cjsj.getCj(j).multiply(j));
  1095.                 currentCij[3] = context.getCXO2N2A2().multiply(CjAlphaGamma.multiply(-I).add(auxiliaryElements.getQ().multiply(coef4)));
  1096.                 currentSij[3] = context.getCXO2N2A2().multiply(SjAlphaGamma.multiply(-I).add(auxiliaryElements.getQ().multiply(coef5)));
  1097.                 currentCij[4] = context.getCXO2N2A2().multiply(CjBetaGamma.negate().add(auxiliaryElements.getP().multiply(coef4)));
  1098.                 currentSij[4] = context.getCXO2N2A2().multiply(SjBetaGamma.negate().add(auxiliaryElements.getP().multiply(coef5)));

  1099.                 //Coefficients for λ
  1100.                 final T coef6 = auxiliaryElements.getH().multiply(cjsj.getdCjdH(j)).add(auxiliaryElements.getK().multiply(cjsj.getdCjdK(j)));
  1101.                 final T coef7 = auxiliaryElements.getH().multiply(cjsj.getdSjdH(j)).add(auxiliaryElements.getK().multiply(cjsj.getdSjdK(j)));
  1102.                 currentCij[5] = currentCij[5].add(context.getOON2A2().multiply(auxiliaryElements.getSma().multiply(-2.).multiply(cjsj.getdCjdA(j)).add(coef6.divide(context.getX().add(1.))).add(context.getX().multiply(coef2)).subtract(cjsj.getCj(j).multiply(3.))));
  1103.                 currentSij[5] = currentSij[5].add(context.getOON2A2().multiply(auxiliaryElements.getSma().multiply(-2.).multiply(cjsj.getdSjdA(j)).add(coef7.divide(context.getX().add(1.))).add(context.getX().multiply(coef3)).subtract(cjsj.getSj(j).multiply(3.))));
  1104.             }

  1105.             for (int i = 0; i < 6; i++) {
  1106.                 //Add the current coefficients contribution to C<sub>i</sub>⁰
  1107.                 currentCi0[i] = currentCi0[i].subtract(currentCij[i].multiply(rhoSigma[j][0]).add(currentSij[i].multiply(rhoSigma[j][1])));
  1108.             }

  1109.             // Add the coefficients to the interpolation grid
  1110.             slot.cij[j].addGridPoint(date, currentCij);
  1111.             slot.sij[j].addGridPoint(date, currentSij);

  1112.         }

  1113.         //Add C<sub>i</sub>⁰ to the interpolation grid
  1114.         slot.cij[0].addGridPoint(date, currentCi0);

  1115.     }

  1116.     /**
  1117.      * Compute the auxiliary quantities ρ<sub>j</sub> and σ<sub>j</sub>.
  1118.      * <p>
  1119.      * The expressions used are equations 2.5.3-(4) from the Danielson paper. <br/>
  1120.      *  ρ<sub>j</sub> = (1+jB)(-b)<sup>j</sup>C<sub>j</sub>(k, h) <br/>
  1121.      *  σ<sub>j</sub> = (1+jB)(-b)<sup>j</sup>S<sub>j</sub>(k, h) <br/>
  1122.      * </p>
  1123.      * @param date target date
  1124.      * @param slot slot to which the coefficients belong
  1125.      * @param auxiliaryElements auxiliary elements related to the current orbit
  1126.      * @return array containing ρ<sub>j</sub> and σ<sub>j</sub>
  1127.      */
  1128.     private double[][] computeRhoSigmaCoefficients(final AbsoluteDate date,
  1129.                                                    final Slot slot,
  1130.                                                    final AuxiliaryElements auxiliaryElements) {

  1131.         final CjSjCoefficient cjsjKH = new CjSjCoefficient(auxiliaryElements.getK(), auxiliaryElements.getH());
  1132.         final double b = 1. / (1 + auxiliaryElements.getB());

  1133.         // (-b)<sup>j</sup>
  1134.         double mbtj = 1;

  1135.         final double[][] rhoSigma = new double[slot.cij.length][2];
  1136.         for (int j = 1; j < rhoSigma.length; j++) {

  1137.             //Compute current rho and sigma;
  1138.             mbtj *= -b;
  1139.             final double coef  = (1 + j * auxiliaryElements.getB()) * mbtj;
  1140.             final double rho   = coef * cjsjKH.getCj(j);
  1141.             final double sigma = coef * cjsjKH.getSj(j);

  1142.             // Add the coefficients to the interpolation grid
  1143.             rhoSigma[j][0] = rho;
  1144.             rhoSigma[j][1] = sigma;
  1145.         }

  1146.         return rhoSigma;

  1147.     }

  1148.     /**
  1149.      * Compute the auxiliary quantities ρ<sub>j</sub> and σ<sub>j</sub>.
  1150.      * <p>
  1151.      * The expressions used are equations 2.5.3-(4) from the Danielson paper. <br/>
  1152.      *  ρ<sub>j</sub> = (1+jB)(-b)<sup>j</sup>C<sub>j</sub>(k, h) <br/>
  1153.      *  σ<sub>j</sub> = (1+jB)(-b)<sup>j</sup>S<sub>j</sub>(k, h) <br/>
  1154.      * </p>
  1155.      * @param <T> type of the elements
  1156.      * @param date target date
  1157.      * @param slot slot to which the coefficients belong
  1158.      * @param auxiliaryElements auxiliary elements related to the current orbit
  1159.      * @param field field used by default
  1160.      * @return array containing ρ<sub>j</sub> and σ<sub>j</sub>
  1161.      */
  1162.     private <T extends CalculusFieldElement<T>> T[][] computeRhoSigmaCoefficients(final FieldAbsoluteDate<T> date,
  1163.                                                                               final FieldSlot<T> slot,
  1164.                                                                               final FieldAuxiliaryElements<T> auxiliaryElements,
  1165.                                                                               final Field<T> field) {
  1166.         final T zero = field.getZero();

  1167.         final FieldCjSjCoefficient<T> cjsjKH = new FieldCjSjCoefficient<>(auxiliaryElements.getK(), auxiliaryElements.getH(), field);
  1168.         final T b = auxiliaryElements.getB().add(1.).reciprocal();

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

  1171.         final T[][] rhoSigma = MathArrays.buildArray(field, slot.cij.length, 2);
  1172.         for (int j = 1; j < rhoSigma.length; j++) {

  1173.             //Compute current rho and sigma;
  1174.             mbtj = mbtj.multiply(b.negate());
  1175.             final T coef  = mbtj.multiply(auxiliaryElements.getB().multiply(j).add(1.));
  1176.             final T rho   = coef.multiply(cjsjKH.getCj(j));
  1177.             final T sigma = coef.multiply(cjsjKH.getSj(j));

  1178.             // Add the coefficients to the interpolation grid
  1179.             rhoSigma[j][0] = rho;
  1180.             rhoSigma[j][1] = sigma;
  1181.         }

  1182.         return rhoSigma;

  1183.     }

  1184.     /** The coefficients used to compute the short-periodic zonal contribution.
  1185.      *
  1186.      * <p>
  1187.      * Those coefficients are given in Danielson paper by expressions 4.1-(20) to 4.1.-(25)
  1188.      * </p>
  1189.      * <p>
  1190.      * The coefficients are: <br>
  1191.      * - C<sub>i</sub><sup>j</sup> and S<sub>i</sub><sup>j</sup> <br>
  1192.      * - ρ<sub>j</sub> and σ<sub>j</sub> <br>
  1193.      * - C<sub>i</sub>⁰
  1194.      * </p>
  1195.      *
  1196.      * @author Lucian Barbulescu
  1197.      */
  1198.     private static class ZonalShortPeriodicCoefficients implements ShortPeriodTerms {

  1199.         /** Maximum value for j index. */
  1200.         private final int maxFrequencyShortPeriodics;

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

  1203.         /** All coefficients slots. */
  1204.         private final transient TimeSpanMap<Slot> slots;

  1205.         /** Constructor.
  1206.          * @param maxFrequencyShortPeriodics maximum value for j index
  1207.          * @param interpolationPoints number of points used in the interpolation process
  1208.          * @param slots all coefficients slots
  1209.          */
  1210.         ZonalShortPeriodicCoefficients(final int maxFrequencyShortPeriodics, final int interpolationPoints,
  1211.                                        final TimeSpanMap<Slot> slots) {

  1212.             // Save parameters
  1213.             this.maxFrequencyShortPeriodics = maxFrequencyShortPeriodics;
  1214.             this.interpolationPoints        = interpolationPoints;
  1215.             this.slots                      = slots;

  1216.         }

  1217.         /** Get the slot valid for some date.
  1218.          * @param meanStates mean states defining the slot
  1219.          * @return slot valid at the specified date
  1220.          */
  1221.         public Slot createSlot(final SpacecraftState... meanStates) {
  1222.             final Slot         slot  = new Slot(maxFrequencyShortPeriodics, interpolationPoints);
  1223.             final AbsoluteDate first = meanStates[0].getDate();
  1224.             final AbsoluteDate last  = meanStates[meanStates.length - 1].getDate();
  1225.             final int compare = first.compareTo(last);
  1226.             if (compare < 0) {
  1227.                 slots.addValidAfter(slot, first, false);
  1228.             } else if (compare > 0) {
  1229.                 slots.addValidBefore(slot, first, false);
  1230.             } else {
  1231.                 // single date, valid for all time
  1232.                 slots.addValidAfter(slot, AbsoluteDate.PAST_INFINITY, false);
  1233.             }
  1234.             return slot;
  1235.         }

  1236.         /** {@inheritDoc} */
  1237.         @Override
  1238.         public double[] value(final Orbit meanOrbit) {

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

  1241.             // Get the True longitude L
  1242.             final double L = meanOrbit.getLv();

  1243.             // Compute the center
  1244.             final double center = L - meanOrbit.getLM();

  1245.             // Initialize short periodic variations
  1246.             final double[] shortPeriodicVariation = slot.cij[0].value(meanOrbit.getDate());
  1247.             final double[] d = slot.di.value(meanOrbit.getDate());
  1248.             for (int i = 0; i < 6; i++) {
  1249.                 shortPeriodicVariation[i] +=  center * d[i];
  1250.             }

  1251.             for (int j = 1; j <= maxFrequencyShortPeriodics; j++) {
  1252.                 final double[] c = slot.cij[j].value(meanOrbit.getDate());
  1253.                 final double[] s = slot.sij[j].value(meanOrbit.getDate());
  1254.                 final SinCos sc  = FastMath.sinCos(j * L);
  1255.                 for (int i = 0; i < 6; i++) {
  1256.                     // add corresponding term to the short periodic variation
  1257.                     shortPeriodicVariation[i] += c[i] * sc.cos();
  1258.                     shortPeriodicVariation[i] += s[i] * sc.sin();
  1259.                 }
  1260.             }

  1261.             return shortPeriodicVariation;
  1262.         }

  1263.         /** {@inheritDoc} */
  1264.         @Override
  1265.         public String getCoefficientsKeyPrefix() {
  1266.             return DSSTZonal.SHORT_PERIOD_PREFIX;
  1267.         }

  1268.         /** {@inheritDoc}
  1269.          * <p>
  1270.          * For zonal terms contributions,there are maxJ cj coefficients,
  1271.          * maxJ sj coefficients and 2 dj coefficients, where maxJ depends
  1272.          * on the orbit. The j index is the integer multiplier for the true
  1273.          * longitude argument in the cj and sj coefficients and the degree
  1274.          * in the polynomial dj coefficients.
  1275.          * </p>
  1276.          */
  1277.         @Override
  1278.         public Map<String, double[]> getCoefficients(final AbsoluteDate date, final Set<String> selected) {

  1279.             // select the coefficients slot
  1280.             final Slot slot = slots.get(date);

  1281.             final Map<String, double[]> coefficients = new HashMap<String, double[]>(2 * maxFrequencyShortPeriodics + 2);
  1282.             storeIfSelected(coefficients, selected, slot.cij[0].value(date), "d", 0);
  1283.             storeIfSelected(coefficients, selected, slot.di.value(date), "d", 1);
  1284.             for (int j = 1; j <= maxFrequencyShortPeriodics; j++) {
  1285.                 storeIfSelected(coefficients, selected, slot.cij[j].value(date), "c", j);
  1286.                 storeIfSelected(coefficients, selected, slot.sij[j].value(date), "s", j);
  1287.             }
  1288.             return coefficients;

  1289.         }

  1290.         /** Put a coefficient in a map if selected.
  1291.          * @param map map to populate
  1292.          * @param selected set of coefficients that should be put in the map
  1293.          * (empty set means all coefficients are selected)
  1294.          * @param value coefficient value
  1295.          * @param id coefficient identifier
  1296.          * @param indices list of coefficient indices
  1297.          */
  1298.         private void storeIfSelected(final Map<String, double[]> map, final Set<String> selected,
  1299.                                      final double[] value, final String id, final int... indices) {
  1300.             final StringBuilder keyBuilder = new StringBuilder(getCoefficientsKeyPrefix());
  1301.             keyBuilder.append(id);
  1302.             for (int index : indices) {
  1303.                 keyBuilder.append('[').append(index).append(']');
  1304.             }
  1305.             final String key = keyBuilder.toString();
  1306.             if (selected.isEmpty() || selected.contains(key)) {
  1307.                 map.put(key, value);
  1308.             }
  1309.         }

  1310.     }

  1311.     /** The coefficients used to compute the short-periodic zonal contribution.
  1312.     *
  1313.     * <p>
  1314.     * Those coefficients are given in Danielson paper by expressions 4.1-(20) to 4.1.-(25)
  1315.     * </p>
  1316.     * <p>
  1317.     * The coefficients are: <br>
  1318.     * - C<sub>i</sub><sup>j</sup> and S<sub>i</sub><sup>j</sup> <br>
  1319.     * - ρ<sub>j</sub> and σ<sub>j</sub> <br>
  1320.     * - C<sub>i</sub>⁰
  1321.     * </p>
  1322.     *
  1323.     * @author Lucian Barbulescu
  1324.     */
  1325.     private static class FieldZonalShortPeriodicCoefficients <T extends CalculusFieldElement<T>> implements FieldShortPeriodTerms<T> {

  1326.         /** Maximum value for j index. */
  1327.         private final int maxFrequencyShortPeriodics;

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

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

  1332.        /** Constructor.
  1333.         * @param maxFrequencyShortPeriodics maximum value for j index
  1334.         * @param interpolationPoints number of points used in the interpolation process
  1335.         * @param slots all coefficients slots
  1336.         */
  1337.         FieldZonalShortPeriodicCoefficients(final int maxFrequencyShortPeriodics, final int interpolationPoints,
  1338.                                             final FieldTimeSpanMap<FieldSlot<T>, T> slots) {

  1339.             // Save parameters
  1340.             this.maxFrequencyShortPeriodics = maxFrequencyShortPeriodics;
  1341.             this.interpolationPoints        = interpolationPoints;
  1342.             this.slots                      = slots;

  1343.         }

  1344.        /** Get the slot valid for some date.
  1345.         * @param meanStates mean states defining the slot
  1346.         * @return slot valid at the specified date
  1347.         */
  1348.         @SuppressWarnings("unchecked")
  1349.         public FieldSlot<T> createSlot(final FieldSpacecraftState<T>... meanStates) {
  1350.             final FieldSlot<T>         slot  = new FieldSlot<>(maxFrequencyShortPeriodics, interpolationPoints);
  1351.             final FieldAbsoluteDate<T> first = meanStates[0].getDate();
  1352.             final FieldAbsoluteDate<T> last  = meanStates[meanStates.length - 1].getDate();
  1353.             if (first.compareTo(last) <= 0) {
  1354.                 slots.addValidAfter(slot, first);
  1355.             } else {
  1356.                 slots.addValidBefore(slot, first);
  1357.             }
  1358.             return slot;
  1359.         }

  1360.         /** {@inheritDoc} */
  1361.         @Override
  1362.         public T[] value(final FieldOrbit<T> meanOrbit) {

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

  1365.             // Get the True longitude L
  1366.             final T L = meanOrbit.getLv();

  1367.             // Compute the center
  1368.             final T center = L.subtract(meanOrbit.getLM());

  1369.             // Initialize short periodic variations
  1370.             final T[] shortPeriodicVariation = slot.cij[0].value(meanOrbit.getDate());
  1371.             final T[] d = slot.di.value(meanOrbit.getDate());
  1372.             for (int i = 0; i < 6; i++) {
  1373.                 shortPeriodicVariation[i] = shortPeriodicVariation[i].add(center.multiply(d[i]));
  1374.             }

  1375.             for (int j = 1; j <= maxFrequencyShortPeriodics; j++) {
  1376.                 final T[]            c   = slot.cij[j].value(meanOrbit.getDate());
  1377.                 final T[]            s   = slot.sij[j].value(meanOrbit.getDate());
  1378.                 final FieldSinCos<T> sc  = FastMath.sinCos(L.multiply(j));
  1379.                 for (int i = 0; i < 6; i++) {
  1380.                     // add corresponding term to the short periodic variation
  1381.                     shortPeriodicVariation[i] = shortPeriodicVariation[i].add(c[i].multiply(sc.cos()));
  1382.                     shortPeriodicVariation[i] = shortPeriodicVariation[i].add(s[i].multiply(sc.sin()));
  1383.                 }
  1384.             }

  1385.             return shortPeriodicVariation;
  1386.         }

  1387.         /** {@inheritDoc} */
  1388.         @Override
  1389.         public String getCoefficientsKeyPrefix() {
  1390.             return DSSTZonal.SHORT_PERIOD_PREFIX;
  1391.         }

  1392.        /** {@inheritDoc}
  1393.         * <p>
  1394.         * For zonal terms contributions,there are maxJ cj coefficients,
  1395.         * maxJ sj coefficients and 2 dj coefficients, where maxJ depends
  1396.         * on the orbit. The j index is the integer multiplier for the true
  1397.         * longitude argument in the cj and sj coefficients and the degree
  1398.         * in the polynomial dj coefficients.
  1399.         * </p>
  1400.         */
  1401.         @Override
  1402.         public Map<String, T[]> getCoefficients(final FieldAbsoluteDate<T> date, final Set<String> selected) {

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

  1405.             final Map<String, T[]> coefficients = new HashMap<String, T[]>(2 * maxFrequencyShortPeriodics + 2);
  1406.             storeIfSelected(coefficients, selected, slot.cij[0].value(date), "d", 0);
  1407.             storeIfSelected(coefficients, selected, slot.di.value(date), "d", 1);
  1408.             for (int j = 1; j <= maxFrequencyShortPeriodics; j++) {
  1409.                 storeIfSelected(coefficients, selected, slot.cij[j].value(date), "c", j);
  1410.                 storeIfSelected(coefficients, selected, slot.sij[j].value(date), "s", j);
  1411.             }
  1412.             return coefficients;

  1413.         }

  1414.        /** Put a coefficient in a map if selected.
  1415.         * @param map map to populate
  1416.         * @param selected set of coefficients that should be put in the map
  1417.         * (empty set means all coefficients are selected)
  1418.         * @param value coefficient value
  1419.         * @param id coefficient identifier
  1420.         * @param indices list of coefficient indices
  1421.         */
  1422.         private void storeIfSelected(final Map<String, T[]> map, final Set<String> selected,
  1423.                                      final T[] value, final String id, final int... indices) {
  1424.             final StringBuilder keyBuilder = new StringBuilder(getCoefficientsKeyPrefix());
  1425.             keyBuilder.append(id);
  1426.             for (int index : indices) {
  1427.                 keyBuilder.append('[').append(index).append(']');
  1428.             }
  1429.             final String key = keyBuilder.toString();
  1430.             if (selected.isEmpty() || selected.contains(key)) {
  1431.                 map.put(key, value);
  1432.             }
  1433.         }

  1434.     }

  1435.     /** Compute the C<sup>j</sup> and the S<sup>j</sup> coefficients.
  1436.      *  <p>
  1437.      *  Those coefficients are given in Danielson paper by expressions 4.1-(13) to 4.1.-(16b)
  1438.      *  </p>
  1439.      */
  1440.     private class FourierCjSjCoefficients {

  1441.         /** The G<sub>js</sub>, H<sub>js</sub>, I<sub>js</sub> and J<sub>js</sub> polynomials. */
  1442.         private final GHIJjsPolynomials ghijCoef;

  1443.         /** L<sub>n</sub><sup>s</sup>(γ). */
  1444.         private final LnsCoefficients lnsCoef;

  1445.         /** Maximum possible value for n. */
  1446.         private final int nMax;

  1447.         /** Maximum possible value for s. */
  1448.         private final int sMax;

  1449.         /** Maximum possible value for j. */
  1450.         private final int jMax;

  1451.         /** The C<sup>j</sup> coefficients and their derivatives.
  1452.          * <p>
  1453.          * Each column of the matrix contains the following values: <br/>
  1454.          * - C<sup>j</sup> <br/>
  1455.          * - dC<sup>j</sup> / da <br/>
  1456.          * - dC<sup>j</sup> / dh <br/>
  1457.          * - dC<sup>j</sup> / dk <br/>
  1458.          * - dC<sup>j</sup> / dα <br/>
  1459.          * - dC<sup>j</sup> / dβ <br/>
  1460.          * - dC<sup>j</sup> / dγ <br/>
  1461.          * </p>
  1462.          */
  1463.         private final double[][] cCoef;

  1464.         /** The S<sup>j</sup> coefficients and their derivatives.
  1465.          * <p>
  1466.          * Each column of the matrix contains the following values: <br/>
  1467.          * - S<sup>j</sup> <br/>
  1468.          * - dS<sup>j</sup> / da <br/>
  1469.          * - dS<sup>j</sup> / dh <br/>
  1470.          * - dS<sup>j</sup> / dk <br/>
  1471.          * - dS<sup>j</sup> / dα <br/>
  1472.          * - dS<sup>j</sup> / dβ <br/>
  1473.          * - dS<sup>j</sup> / dγ <br/>
  1474.          * </p>
  1475.          */
  1476.         private final double[][] sCoef;

  1477.         /** h * &Chi;³. */
  1478.         private final double hXXX;
  1479.         /** k * &Chi;³. */
  1480.         private final double kXXX;

  1481.         /** Create a set of C<sup>j</sup> and the S<sup>j</sup> coefficients.
  1482.          *  @param date the current date
  1483.          *  @param nMax maximum possible value for n
  1484.          *  @param sMax maximum possible value for s
  1485.          *  @param jMax maximum possible value for j
  1486.          *  @param context container for attributes
  1487.          *  @param hansenObjects initialization of hansen objects
  1488.          */
  1489.         FourierCjSjCoefficients(final AbsoluteDate date,
  1490.                                 final int nMax, final int sMax, final int jMax, final DSSTZonalContext context,
  1491.                                 final HansenObjects hansenObjects) {

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

  1493.             this.ghijCoef = new GHIJjsPolynomials(auxiliaryElements.getK(), auxiliaryElements.getH(), auxiliaryElements.getAlpha(), auxiliaryElements.getBeta());
  1494.             // Qns coefficients
  1495.             final double[][] Qns  = CoefficientsFactory.computeQns(auxiliaryElements.getGamma(), nMax, nMax);

  1496.             this.lnsCoef = new LnsCoefficients(nMax, nMax, Qns, Vns, context.getRoa());
  1497.             this.nMax = nMax;
  1498.             this.sMax = sMax;
  1499.             this.jMax = jMax;

  1500.             // compute the common factors that depends on the mean elements
  1501.             this.hXXX = auxiliaryElements.getH() * context.getXXX();
  1502.             this.kXXX = auxiliaryElements.getK() * context.getXXX();

  1503.             this.cCoef = new double[7][jMax + 1];
  1504.             this.sCoef = new double[7][jMax + 1];

  1505.             for (int s = 0; s <= sMax; s++) {
  1506.                 //Initialise the Hansen roots
  1507.                 hansenObjects.computeHansenObjectsInitValues(context, s);
  1508.             }
  1509.             generateCoefficients(date, context, auxiliaryElements, hansenObjects);
  1510.         }

  1511.         /** Generate all coefficients.
  1512.          * @param date the current date
  1513.          * @param context container for attributes
  1514.          * @param auxiliaryElements auxiliary elements related to the current orbit
  1515.          * @param hansenObjects initialization of hansen objects
  1516.          */
  1517.         private void generateCoefficients(final AbsoluteDate date,
  1518.                                           final DSSTZonalContext context,
  1519.                                           final AuxiliaryElements auxiliaryElements,
  1520.                                           final HansenObjects hansenObjects) {

  1521.             final UnnormalizedSphericalHarmonics harmonics = provider.onDate(date);
  1522.             for (int j = 1; j <= jMax; j++) {

  1523.                 //init arrays
  1524.                 for (int i = 0; i <= 6; i++) {
  1525.                     cCoef[i][j] = 0.;
  1526.                     sCoef[i][j] = 0.;
  1527.                 }

  1528.                 if (isBetween(j, 1, nMax - 1)) {

  1529.                     //compute first double sum where s: j -> N-1 and n: s+1 -> N
  1530.                     for (int s = j; s <= FastMath.min(nMax - 1, sMax); s++) {
  1531.                         // j - s
  1532.                         final int jms = j - s;
  1533.                         // Kronecker symbols (2 - delta(0,s-j)) and (2 - delta(0,j-s))
  1534.                         final int d0smj = (s == j) ? 1 : 2;

  1535.                         for (int n = s + 1; n <= nMax; n++) {
  1536.                             // if n + (j-s) is odd, then the term is equal to zero due to the factor Vn,s-j
  1537.                             if ((n + jms) % 2 == 0) {
  1538.                                 // (2 - delta(0,s-j)) * J<sub>n</sub> * K₀<sup>-n-1,s</sup> * L<sub>n</sub><sup>j-s</sup>
  1539.                                 final double lns = lnsCoef.getLns(n, -jms);
  1540.                                 final double dlns = lnsCoef.getdLnsdGamma(n, -jms);

  1541.                                 final double hjs = ghijCoef.getHjs(s, -jms);
  1542.                                 final double dHjsdh = ghijCoef.getdHjsdh(s, -jms);
  1543.                                 final double dHjsdk = ghijCoef.getdHjsdk(s, -jms);
  1544.                                 final double dHjsdAlpha = ghijCoef.getdHjsdAlpha(s, -jms);
  1545.                                 final double dHjsdBeta = ghijCoef.getdHjsdBeta(s, -jms);

  1546.                                 final double gjs = ghijCoef.getGjs(s, -jms);
  1547.                                 final double dGjsdh = ghijCoef.getdGjsdh(s, -jms);
  1548.                                 final double dGjsdk = ghijCoef.getdGjsdk(s, -jms);
  1549.                                 final double dGjsdAlpha = ghijCoef.getdGjsdAlpha(s, -jms);
  1550.                                 final double dGjsdBeta = ghijCoef.getdGjsdBeta(s, -jms);

  1551.                                 // J<sub>n</sub>
  1552.                                 final double jn = -harmonics.getUnnormalizedCnm(n, 0);

  1553.                                 // K₀<sup>-n-1,s</sup>
  1554.                                 final double kns   = hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getX());
  1555.                                 final double dkns  = hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getX());

  1556.                                 final double coef0 = d0smj * jn;
  1557.                                 final double coef1 = coef0 * lns;
  1558.                                 final double coef2 = coef1 * kns;
  1559.                                 final double coef3 = coef2 * hjs;
  1560.                                 final double coef4 = coef2 * gjs;

  1561.                                 // Add the term to the coefficients
  1562.                                 cCoef[0][j] += coef3;
  1563.                                 cCoef[1][j] += coef3 * (n + 1);
  1564.                                 cCoef[2][j] += coef1 * (kns * dHjsdh + hjs * hXXX * dkns);
  1565.                                 cCoef[3][j] += coef1 * (kns * dHjsdk + hjs * kXXX * dkns);
  1566.                                 cCoef[4][j] += coef2 * dHjsdAlpha;
  1567.                                 cCoef[5][j] += coef2 * dHjsdBeta;
  1568.                                 cCoef[6][j] += coef0 * dlns * kns * hjs;

  1569.                                 sCoef[0][j] += coef4;
  1570.                                 sCoef[1][j] += coef4 * (n + 1);
  1571.                                 sCoef[2][j] += coef1 * (kns * dGjsdh + gjs * hXXX * dkns);
  1572.                                 sCoef[3][j] += coef1 * (kns * dGjsdk + gjs * kXXX * dkns);
  1573.                                 sCoef[4][j] += coef2 * dGjsdAlpha;
  1574.                                 sCoef[5][j] += coef2 * dGjsdBeta;
  1575.                                 sCoef[6][j] += coef0 * dlns * kns * gjs;
  1576.                             }
  1577.                         }
  1578.                     }

  1579.                     //compute second double sum where s: 0 -> N-j and n: max(j+s, j+1) -> N
  1580.                     for (int s = 0; s <= FastMath.min(nMax - j, sMax); s++) {
  1581.                         // j + s
  1582.                         final int jps = j + s;
  1583.                         // Kronecker symbols (2 - delta(0,j+s))
  1584.                         final double d0spj = (s == -j) ? 1 : 2;

  1585.                         for (int n = FastMath.max(j + s, j + 1); n <= nMax; n++) {
  1586.                             // if n + (j+s) is odd, then the term is equal to zero due to the factor Vn,s+j
  1587.                             if ((n + jps) % 2 == 0) {
  1588.                                 // (2 - delta(0,s+j)) * J<sub>n</sub> * K₀<sup>-n-1,s</sup> * L<sub>n</sub><sup>j+s</sup>
  1589.                                 final double lns = lnsCoef.getLns(n, jps);
  1590.                                 final double dlns = lnsCoef.getdLnsdGamma(n, jps);

  1591.                                 final double hjs = ghijCoef.getHjs(s, jps);
  1592.                                 final double dHjsdh = ghijCoef.getdHjsdh(s, jps);
  1593.                                 final double dHjsdk = ghijCoef.getdHjsdk(s, jps);
  1594.                                 final double dHjsdAlpha = ghijCoef.getdHjsdAlpha(s, jps);
  1595.                                 final double dHjsdBeta = ghijCoef.getdHjsdBeta(s, jps);

  1596.                                 final double gjs = ghijCoef.getGjs(s, jps);
  1597.                                 final double dGjsdh = ghijCoef.getdGjsdh(s, jps);
  1598.                                 final double dGjsdk = ghijCoef.getdGjsdk(s, jps);
  1599.                                 final double dGjsdAlpha = ghijCoef.getdGjsdAlpha(s, jps);
  1600.                                 final double dGjsdBeta = ghijCoef.getdGjsdBeta(s, jps);

  1601.                                 // J<sub>n</sub>
  1602.                                 final double jn = -harmonics.getUnnormalizedCnm(n, 0);

  1603.                                 // K₀<sup>-n-1,s</sup>
  1604.                                 final double kns   = hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getX());
  1605.                                 final double dkns  = hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getX());

  1606.                                 final double coef0 = d0spj * jn;
  1607.                                 final double coef1 = coef0 * lns;
  1608.                                 final double coef2 = coef1 * kns;

  1609.                                 final double coef3 = coef2 * hjs;
  1610.                                 final double coef4 = coef2 * gjs;

  1611.                                 // Add the term to the coefficients
  1612.                                 cCoef[0][j] -= coef3;
  1613.                                 cCoef[1][j] -= coef3 * (n + 1);
  1614.                                 cCoef[2][j] -= coef1 * (kns * dHjsdh + hjs * hXXX * dkns);
  1615.                                 cCoef[3][j] -= coef1 * (kns * dHjsdk + hjs * kXXX * dkns);
  1616.                                 cCoef[4][j] -= coef2 * dHjsdAlpha;
  1617.                                 cCoef[5][j] -= coef2 * dHjsdBeta;
  1618.                                 cCoef[6][j] -= coef0 * dlns * kns * hjs;

  1619.                                 sCoef[0][j] += coef4;
  1620.                                 sCoef[1][j] += coef4 * (n + 1);
  1621.                                 sCoef[2][j] += coef1 * (kns * dGjsdh + gjs * hXXX * dkns);
  1622.                                 sCoef[3][j] += coef1 * (kns * dGjsdk + gjs * kXXX * dkns);
  1623.                                 sCoef[4][j] += coef2 * dGjsdAlpha;
  1624.                                 sCoef[5][j] += coef2 * dGjsdBeta;
  1625.                                 sCoef[6][j] += coef0 * dlns * kns * gjs;
  1626.                             }
  1627.                         }
  1628.                     }

  1629.                     //compute third double sum where s: 1 -> j and  n: j+1 -> N
  1630.                     for (int s = 1; s <= FastMath.min(j, sMax); s++) {
  1631.                         // j - s
  1632.                         final int jms = j - s;
  1633.                         // Kronecker symbols (2 - delta(0,s-j)) and (2 - delta(0,j-s))
  1634.                         final int d0smj = (s == j) ? 1 : 2;

  1635.                         for (int n = j + 1; n <= nMax; n++) {
  1636.                             // if n + (j-s) is odd, then the term is equal to zero due to the factor Vn,s-j
  1637.                             if ((n + jms) % 2 == 0) {
  1638.                                 // (2 - delta(0,j-s)) * J<sub>n</sub> * K₀<sup>-n-1,s</sup> * L<sub>n</sub><sup>j-s</sup>
  1639.                                 final double lns = lnsCoef.getLns(n, jms);
  1640.                                 final double dlns = lnsCoef.getdLnsdGamma(n, jms);

  1641.                                 final double ijs = ghijCoef.getIjs(s, jms);
  1642.                                 final double dIjsdh = ghijCoef.getdIjsdh(s, jms);
  1643.                                 final double dIjsdk = ghijCoef.getdIjsdk(s, jms);
  1644.                                 final double dIjsdAlpha = ghijCoef.getdIjsdAlpha(s, jms);
  1645.                                 final double dIjsdBeta = ghijCoef.getdIjsdBeta(s, jms);

  1646.                                 final double jjs = ghijCoef.getJjs(s, jms);
  1647.                                 final double dJjsdh = ghijCoef.getdJjsdh(s, jms);
  1648.                                 final double dJjsdk = ghijCoef.getdJjsdk(s, jms);
  1649.                                 final double dJjsdAlpha = ghijCoef.getdJjsdAlpha(s, jms);
  1650.                                 final double dJjsdBeta = ghijCoef.getdJjsdBeta(s, jms);

  1651.                                 // J<sub>n</sub>
  1652.                                 final double jn = -harmonics.getUnnormalizedCnm(n, 0);

  1653.                                 // K₀<sup>-n-1,s</sup>
  1654.                                 final double kns   = hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getX());
  1655.                                 final double dkns  = hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getX());

  1656.                                 final double coef0 = d0smj * jn;
  1657.                                 final double coef1 = coef0 * lns;
  1658.                                 final double coef2 = coef1 * kns;

  1659.                                 final double coef3 = coef2 * ijs;
  1660.                                 final double coef4 = coef2 * jjs;

  1661.                                 // Add the term to the coefficients
  1662.                                 cCoef[0][j] -= coef3;
  1663.                                 cCoef[1][j] -= coef3 * (n + 1);
  1664.                                 cCoef[2][j] -= coef1 * (kns * dIjsdh + ijs * hXXX * dkns);
  1665.                                 cCoef[3][j] -= coef1 * (kns * dIjsdk + ijs * kXXX * dkns);
  1666.                                 cCoef[4][j] -= coef2 * dIjsdAlpha;
  1667.                                 cCoef[5][j] -= coef2 * dIjsdBeta;
  1668.                                 cCoef[6][j] -= coef0 * dlns * kns * ijs;

  1669.                                 sCoef[0][j] += coef4;
  1670.                                 sCoef[1][j] += coef4 * (n + 1);
  1671.                                 sCoef[2][j] += coef1 * (kns * dJjsdh + jjs * hXXX * dkns);
  1672.                                 sCoef[3][j] += coef1 * (kns * dJjsdk + jjs * kXXX * dkns);
  1673.                                 sCoef[4][j] += coef2 * dJjsdAlpha;
  1674.                                 sCoef[5][j] += coef2 * dJjsdBeta;
  1675.                                 sCoef[6][j] += coef0 * dlns * kns * jjs;
  1676.                             }
  1677.                         }
  1678.                     }
  1679.                 }

  1680.                 if (isBetween(j, 2, nMax)) {
  1681.                     //add first term
  1682.                     // J<sub>j</sub>
  1683.                     final double jj = -harmonics.getUnnormalizedCnm(j, 0);
  1684.                     double kns = hansenObjects.getHansenObjects()[0].getValue(-j - 1, context.getX());
  1685.                     double dkns = hansenObjects.getHansenObjects()[0].getDerivative(-j - 1, context.getX());

  1686.                     double lns = lnsCoef.getLns(j, j);
  1687.                     //dlns is 0 because n == s == j

  1688.                     final double hjs = ghijCoef.getHjs(0, j);
  1689.                     final double dHjsdh = ghijCoef.getdHjsdh(0, j);
  1690.                     final double dHjsdk = ghijCoef.getdHjsdk(0, j);
  1691.                     final double dHjsdAlpha = ghijCoef.getdHjsdAlpha(0, j);
  1692.                     final double dHjsdBeta = ghijCoef.getdHjsdBeta(0, j);

  1693.                     final double gjs = ghijCoef.getGjs(0, j);
  1694.                     final double dGjsdh = ghijCoef.getdGjsdh(0, j);
  1695.                     final double dGjsdk = ghijCoef.getdGjsdk(0, j);
  1696.                     final double dGjsdAlpha = ghijCoef.getdGjsdAlpha(0, j);
  1697.                     final double dGjsdBeta = ghijCoef.getdGjsdBeta(0, j);

  1698.                     // 2 * J<sub>j</sub> * K₀<sup>-j-1,0</sup> * L<sub>j</sub><sup>j</sup>
  1699.                     double coef0 = 2 * jj;
  1700.                     double coef1 = coef0 * lns;
  1701.                     double coef2 = coef1 * kns;

  1702.                     double coef3 = coef2 * hjs;
  1703.                     double coef4 = coef2 * gjs;

  1704.                     // Add the term to the coefficients
  1705.                     cCoef[0][j] -= coef3;
  1706.                     cCoef[1][j] -= coef3 * (j + 1);
  1707.                     cCoef[2][j] -= coef1 * (kns * dHjsdh + hjs * hXXX * dkns);
  1708.                     cCoef[3][j] -= coef1 * (kns * dHjsdk + hjs * kXXX * dkns);
  1709.                     cCoef[4][j] -= coef2 * dHjsdAlpha;
  1710.                     cCoef[5][j] -= coef2 * dHjsdBeta;
  1711.                     //no contribution to cCoef[6][j] because dlns is 0

  1712.                     sCoef[0][j] += coef4;
  1713.                     sCoef[1][j] += coef4 * (j + 1);
  1714.                     sCoef[2][j] += coef1 * (kns * dGjsdh + gjs * hXXX * dkns);
  1715.                     sCoef[3][j] += coef1 * (kns * dGjsdk + gjs * kXXX * dkns);
  1716.                     sCoef[4][j] += coef2 * dGjsdAlpha;
  1717.                     sCoef[5][j] += coef2 * dGjsdBeta;
  1718.                     //no contribution to sCoef[6][j] because dlns is 0

  1719.                     //compute simple sum where s: 1 -> j-1
  1720.                     for (int s = 1; s <= FastMath.min(j - 1, sMax); s++) {
  1721.                         // j - s
  1722.                         final int jms = j - s;
  1723.                         // Kronecker symbols (2 - delta(0,s-j)) and (2 - delta(0,j-s))
  1724.                         final int d0smj = (s == j) ? 1 : 2;

  1725.                         // if s is odd, then the term is equal to zero due to the factor Vj,s-j
  1726.                         if (s % 2 == 0) {
  1727.                             // (2 - delta(0,j-s)) * J<sub>j</sub> * K₀<sup>-j-1,s</sup> * L<sub>j</sub><sup>j-s</sup>
  1728.                             kns   = hansenObjects.getHansenObjects()[s].getValue(-j - 1, context.getX());
  1729.                             dkns  = hansenObjects.getHansenObjects()[s].getDerivative(-j - 1, context.getX());

  1730.                             lns = lnsCoef.getLns(j, jms);
  1731.                             final double dlns = lnsCoef.getdLnsdGamma(j, jms);

  1732.                             final double ijs = ghijCoef.getIjs(s, jms);
  1733.                             final double dIjsdh = ghijCoef.getdIjsdh(s, jms);
  1734.                             final double dIjsdk = ghijCoef.getdIjsdk(s, jms);
  1735.                             final double dIjsdAlpha = ghijCoef.getdIjsdAlpha(s, jms);
  1736.                             final double dIjsdBeta = ghijCoef.getdIjsdBeta(s, jms);

  1737.                             final double jjs = ghijCoef.getJjs(s, jms);
  1738.                             final double dJjsdh = ghijCoef.getdJjsdh(s, jms);
  1739.                             final double dJjsdk = ghijCoef.getdJjsdk(s, jms);
  1740.                             final double dJjsdAlpha = ghijCoef.getdJjsdAlpha(s, jms);
  1741.                             final double dJjsdBeta = ghijCoef.getdJjsdBeta(s, jms);

  1742.                             coef0 = d0smj * jj;
  1743.                             coef1 = coef0 * lns;
  1744.                             coef2 = coef1 * kns;

  1745.                             coef3 = coef2 * ijs;
  1746.                             coef4 = coef2 * jjs;

  1747.                             // Add the term to the coefficients
  1748.                             cCoef[0][j] -= coef3;
  1749.                             cCoef[1][j] -= coef3 * (j + 1);
  1750.                             cCoef[2][j] -= coef1 * (kns * dIjsdh + ijs * hXXX * dkns);
  1751.                             cCoef[3][j] -= coef1 * (kns * dIjsdk + ijs * kXXX * dkns);
  1752.                             cCoef[4][j] -= coef2 * dIjsdAlpha;
  1753.                             cCoef[5][j] -= coef2 * dIjsdBeta;
  1754.                             cCoef[6][j] -= coef0 * dlns * kns * ijs;

  1755.                             sCoef[0][j] += coef4;
  1756.                             sCoef[1][j] += coef4 * (j + 1);
  1757.                             sCoef[2][j] += coef1 * (kns * dJjsdh + jjs * hXXX * dkns);
  1758.                             sCoef[3][j] += coef1 * (kns * dJjsdk + jjs * kXXX * dkns);
  1759.                             sCoef[4][j] += coef2 * dJjsdAlpha;
  1760.                             sCoef[5][j] += coef2 * dJjsdBeta;
  1761.                             sCoef[6][j] += coef0 * dlns * kns * jjs;
  1762.                         }
  1763.                     }
  1764.                 }

  1765.                 if (isBetween(j, 3, 2 * nMax - 1)) {
  1766.                     //compute uppercase sigma expressions

  1767.                     //min(j-1,N)
  1768.                     final int minjm1on = FastMath.min(j - 1, nMax);

  1769.                     //if j is even
  1770.                     if (j % 2 == 0) {
  1771.                         //compute first double sum where s: j-min(j-1,N) -> j/2-1 and n: j-s -> min(j-1,N)
  1772.                         for (int s = j - minjm1on; s <= FastMath.min(j / 2 - 1, sMax); s++) {
  1773.                             // j - s
  1774.                             final int jms = j - s;
  1775.                             // Kronecker symbols (2 - delta(0,s-j)) and (2 - delta(0,j-s))
  1776.                             final int d0smj = (s == j) ? 1 : 2;

  1777.                             for (int n = j - s; n <= minjm1on; n++) {
  1778.                                 // if n + (j-s) is odd, then the term is equal to zero due to the factor Vn,s-j
  1779.                                 if ((n + jms) % 2 == 0) {
  1780.                                     // (2 - delta(0,j-s)) * J<sub>n</sub> * K₀<sup>-n-1,s</sup> * L<sub>n</sub><sup>j-s</sup>
  1781.                                     final double lns = lnsCoef.getLns(n, jms);
  1782.                                     final double dlns = lnsCoef.getdLnsdGamma(n, jms);

  1783.                                     final double ijs = ghijCoef.getIjs(s, jms);
  1784.                                     final double dIjsdh = ghijCoef.getdIjsdh(s, jms);
  1785.                                     final double dIjsdk = ghijCoef.getdIjsdk(s, jms);
  1786.                                     final double dIjsdAlpha = ghijCoef.getdIjsdAlpha(s, jms);
  1787.                                     final double dIjsdBeta = ghijCoef.getdIjsdBeta(s, jms);

  1788.                                     final double jjs = ghijCoef.getJjs(s, jms);
  1789.                                     final double dJjsdh = ghijCoef.getdJjsdh(s, jms);
  1790.                                     final double dJjsdk = ghijCoef.getdJjsdk(s, jms);
  1791.                                     final double dJjsdAlpha = ghijCoef.getdJjsdAlpha(s, jms);
  1792.                                     final double dJjsdBeta = ghijCoef.getdJjsdBeta(s, jms);

  1793.                                     // J<sub>n</sub>
  1794.                                     final double jn = -harmonics.getUnnormalizedCnm(n, 0);

  1795.                                     // K₀<sup>-n-1,s</sup>
  1796.                                     final double kns   = hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getX());
  1797.                                     final double dkns  = hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getX());

  1798.                                     final double coef0 = d0smj * jn;
  1799.                                     final double coef1 = coef0 * lns;
  1800.                                     final double coef2 = coef1 * kns;

  1801.                                     final double coef3 = coef2 * ijs;
  1802.                                     final double coef4 = coef2 * jjs;

  1803.                                     // Add the term to the coefficients
  1804.                                     cCoef[0][j] -= coef3;
  1805.                                     cCoef[1][j] -= coef3 * (n + 1);
  1806.                                     cCoef[2][j] -= coef1 * (kns * dIjsdh + ijs * hXXX * dkns);
  1807.                                     cCoef[3][j] -= coef1 * (kns * dIjsdk + ijs * kXXX * dkns);
  1808.                                     cCoef[4][j] -= coef2 * dIjsdAlpha;
  1809.                                     cCoef[5][j] -= coef2 * dIjsdBeta;
  1810.                                     cCoef[6][j] -= coef0 * dlns * kns * ijs;

  1811.                                     sCoef[0][j] += coef4;
  1812.                                     sCoef[1][j] += coef4 * (n + 1);
  1813.                                     sCoef[2][j] += coef1 * (kns * dJjsdh + jjs * hXXX * dkns);
  1814.                                     sCoef[3][j] += coef1 * (kns * dJjsdk + jjs * kXXX * dkns);
  1815.                                     sCoef[4][j] += coef2 * dJjsdAlpha;
  1816.                                     sCoef[5][j] += coef2 * dJjsdBeta;
  1817.                                     sCoef[6][j] += coef0 * dlns * kns * jjs;
  1818.                                 }
  1819.                             }
  1820.                         }

  1821.                         //compute second double sum where s: j/2 -> min(j-1,N)-1 and n: s+1 -> min(j-1,N)
  1822.                         for (int s = j / 2; s <=  FastMath.min(minjm1on - 1, sMax); s++) {
  1823.                             // j - s
  1824.                             final int jms = j - s;
  1825.                             // Kronecker symbols (2 - delta(0,s-j)) and (2 - delta(0,j-s))
  1826.                             final int d0smj = (s == j) ? 1 : 2;

  1827.                             for (int n = s + 1; n <= minjm1on; n++) {
  1828.                                 // if n + (j-s) is odd, then the term is equal to zero due to the factor Vn,s-j
  1829.                                 if ((n + jms) % 2 == 0) {
  1830.                                     // (2 - delta(0,j-s)) * J<sub>n</sub> * K₀<sup>-n-1,s</sup> * L<sub>n</sub><sup>j-s</sup>
  1831.                                     final double lns = lnsCoef.getLns(n, jms);
  1832.                                     final double dlns = lnsCoef.getdLnsdGamma(n, jms);

  1833.                                     final double ijs = ghijCoef.getIjs(s, jms);
  1834.                                     final double dIjsdh = ghijCoef.getdIjsdh(s, jms);
  1835.                                     final double dIjsdk = ghijCoef.getdIjsdk(s, jms);
  1836.                                     final double dIjsdAlpha = ghijCoef.getdIjsdAlpha(s, jms);
  1837.                                     final double dIjsdBeta = ghijCoef.getdIjsdBeta(s, jms);

  1838.                                     final double jjs = ghijCoef.getJjs(s, jms);
  1839.                                     final double dJjsdh = ghijCoef.getdJjsdh(s, jms);
  1840.                                     final double dJjsdk = ghijCoef.getdJjsdk(s, jms);
  1841.                                     final double dJjsdAlpha = ghijCoef.getdJjsdAlpha(s, jms);
  1842.                                     final double dJjsdBeta = ghijCoef.getdJjsdBeta(s, jms);

  1843.                                     // J<sub>n</sub>
  1844.                                     final double jn = -harmonics.getUnnormalizedCnm(n, 0);

  1845.                                     // K₀<sup>-n-1,s</sup>
  1846.                                     final double kns   = hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getX());
  1847.                                     final double dkns  = hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getX());

  1848.                                     final double coef0 = d0smj * jn;
  1849.                                     final double coef1 = coef0 * lns;
  1850.                                     final double coef2 = coef1 * kns;

  1851.                                     final double coef3 = coef2 * ijs;
  1852.                                     final double coef4 = coef2 * jjs;

  1853.                                     // Add the term to the coefficients
  1854.                                     cCoef[0][j] -= coef3;
  1855.                                     cCoef[1][j] -= coef3 * (n + 1);
  1856.                                     cCoef[2][j] -= coef1 * (kns * dIjsdh + ijs * hXXX * dkns);
  1857.                                     cCoef[3][j] -= coef1 * (kns * dIjsdk + ijs * kXXX * dkns);
  1858.                                     cCoef[4][j] -= coef2 * dIjsdAlpha;
  1859.                                     cCoef[5][j] -= coef2 * dIjsdBeta;
  1860.                                     cCoef[6][j] -= coef0 * dlns * kns * ijs;

  1861.                                     sCoef[0][j] += coef4;
  1862.                                     sCoef[1][j] += coef4 * (n + 1);
  1863.                                     sCoef[2][j] += coef1 * (kns * dJjsdh + jjs * hXXX * dkns);
  1864.                                     sCoef[3][j] += coef1 * (kns * dJjsdk + jjs * kXXX * dkns);
  1865.                                     sCoef[4][j] += coef2 * dJjsdAlpha;
  1866.                                     sCoef[5][j] += coef2 * dJjsdBeta;
  1867.                                     sCoef[6][j] += coef0 * dlns * kns * jjs;
  1868.                                 }
  1869.                             }
  1870.                         }
  1871.                     }

  1872.                     //if j is odd
  1873.                     else {
  1874.                         //compute first double sum where s: (j-1)/2 -> min(j-1,N)-1 and n: s+1 -> min(j-1,N)
  1875.                         for (int s = (j - 1) / 2; s <= FastMath.min(minjm1on - 1, sMax); s++) {
  1876.                             // j - s
  1877.                             final int jms = j - s;
  1878.                             // Kronecker symbols (2 - delta(0,s-j)) and (2 - delta(0,j-s))
  1879.                             final int d0smj = (s == j) ? 1 : 2;

  1880.                             for (int n = s + 1; n <= minjm1on; n++) {
  1881.                                 // if n + (j-s) is odd, then the term is equal to zero due to the factor Vn,s-j
  1882.                                 if ((n + jms) % 2 == 0) {
  1883.                                     // (2 - delta(0,j-s)) * J<sub>n</sub> * K₀<sup>-n-1,s</sup> * L<sub>n</sub><sup>j-s</sup>
  1884.                                     final double lns = lnsCoef.getLns(n, jms);
  1885.                                     final double dlns = lnsCoef.getdLnsdGamma(n, jms);

  1886.                                     final double ijs = ghijCoef.getIjs(s, jms);
  1887.                                     final double dIjsdh = ghijCoef.getdIjsdh(s, jms);
  1888.                                     final double dIjsdk = ghijCoef.getdIjsdk(s, jms);
  1889.                                     final double dIjsdAlpha = ghijCoef.getdIjsdAlpha(s, jms);
  1890.                                     final double dIjsdBeta = ghijCoef.getdIjsdBeta(s, jms);

  1891.                                     final double jjs = ghijCoef.getJjs(s, jms);
  1892.                                     final double dJjsdh = ghijCoef.getdJjsdh(s, jms);
  1893.                                     final double dJjsdk = ghijCoef.getdJjsdk(s, jms);
  1894.                                     final double dJjsdAlpha = ghijCoef.getdJjsdAlpha(s, jms);
  1895.                                     final double dJjsdBeta = ghijCoef.getdJjsdBeta(s, jms);

  1896.                                     // J<sub>n</sub>
  1897.                                     final double jn = -harmonics.getUnnormalizedCnm(n, 0);

  1898.                                     // K₀<sup>-n-1,s</sup>

  1899.                                     final double kns = hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getX());
  1900.                                     final double dkns  = hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getX());

  1901.                                     final double coef0 = d0smj * jn;
  1902.                                     final double coef1 = coef0 * lns;
  1903.                                     final double coef2 = coef1 * kns;

  1904.                                     final double coef3 = coef2 * ijs;
  1905.                                     final double coef4 = coef2 * jjs;

  1906.                                     // Add the term to the coefficients
  1907.                                     cCoef[0][j] -= coef3;
  1908.                                     cCoef[1][j] -= coef3 * (n + 1);
  1909.                                     cCoef[2][j] -= coef1 * (kns * dIjsdh + ijs * hXXX * dkns);
  1910.                                     cCoef[3][j] -= coef1 * (kns * dIjsdk + ijs * kXXX * dkns);
  1911.                                     cCoef[4][j] -= coef2 * dIjsdAlpha;
  1912.                                     cCoef[5][j] -= coef2 * dIjsdBeta;
  1913.                                     cCoef[6][j] -= coef0 * dlns * kns * ijs;

  1914.                                     sCoef[0][j] += coef4;
  1915.                                     sCoef[1][j] += coef4 * (n + 1);
  1916.                                     sCoef[2][j] += coef1 * (kns * dJjsdh + jjs * hXXX * dkns);
  1917.                                     sCoef[3][j] += coef1 * (kns * dJjsdk + jjs * kXXX * dkns);
  1918.                                     sCoef[4][j] += coef2 * dJjsdAlpha;
  1919.                                     sCoef[5][j] += coef2 * dJjsdBeta;
  1920.                                     sCoef[6][j] += coef0 * dlns * kns * jjs;
  1921.                                 }
  1922.                             }
  1923.                         }

  1924.                         //the second double sum is added only if N >= 4 and j between 5 and 2*N-3
  1925.                         if (nMax >= 4 && isBetween(j, 5, 2 * nMax - 3)) {
  1926.                             //compute second double sum where s: j-min(j-1,N) -> (j-3)/2 and n: j-s -> min(j-1,N)
  1927.                             for (int s = j - minjm1on; s <= FastMath.min((j - 3) / 2, sMax); s++) {
  1928.                                 // j - s
  1929.                                 final int jms = j - s;
  1930.                                 // Kronecker symbols (2 - delta(0,s-j)) and (2 - delta(0,j-s))
  1931.                                 final int d0smj = (s == j) ? 1 : 2;

  1932.                                 for (int n = j - s; n <= minjm1on; n++) {
  1933.                                     // if n + (j-s) is odd, then the term is equal to zero due to the factor Vn,s-j
  1934.                                     if ((n + jms) % 2 == 0) {
  1935.                                         // (2 - delta(0,j-s)) * J<sub>n</sub> * K₀<sup>-n-1,s</sup> * L<sub>n</sub><sup>j-s</sup>
  1936.                                         final double lns = lnsCoef.getLns(n, jms);
  1937.                                         final double dlns = lnsCoef.getdLnsdGamma(n, jms);

  1938.                                         final double ijs = ghijCoef.getIjs(s, jms);
  1939.                                         final double dIjsdh = ghijCoef.getdIjsdh(s, jms);
  1940.                                         final double dIjsdk = ghijCoef.getdIjsdk(s, jms);
  1941.                                         final double dIjsdAlpha = ghijCoef.getdIjsdAlpha(s, jms);
  1942.                                         final double dIjsdBeta = ghijCoef.getdIjsdBeta(s, jms);

  1943.                                         final double jjs = ghijCoef.getJjs(s, jms);
  1944.                                         final double dJjsdh = ghijCoef.getdJjsdh(s, jms);
  1945.                                         final double dJjsdk = ghijCoef.getdJjsdk(s, jms);
  1946.                                         final double dJjsdAlpha = ghijCoef.getdJjsdAlpha(s, jms);
  1947.                                         final double dJjsdBeta = ghijCoef.getdJjsdBeta(s, jms);

  1948.                                         // J<sub>n</sub>
  1949.                                         final double jn = -harmonics.getUnnormalizedCnm(n, 0);

  1950.                                         // K₀<sup>-n-1,s</sup>
  1951.                                         final double kns   = hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getX());
  1952.                                         final double dkns  = hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getX());

  1953.                                         final double coef0 = d0smj * jn;
  1954.                                         final double coef1 = coef0 * lns;
  1955.                                         final double coef2 = coef1 * kns;

  1956.                                         final double coef3 = coef2 * ijs;
  1957.                                         final double coef4 = coef2 * jjs;

  1958.                                         // Add the term to the coefficients
  1959.                                         cCoef[0][j] -= coef3;
  1960.                                         cCoef[1][j] -= coef3 * (n + 1);
  1961.                                         cCoef[2][j] -= coef1 * (kns * dIjsdh + ijs * hXXX * dkns);
  1962.                                         cCoef[3][j] -= coef1 * (kns * dIjsdk + ijs * kXXX * dkns);
  1963.                                         cCoef[4][j] -= coef2 * dIjsdAlpha;
  1964.                                         cCoef[5][j] -= coef2 * dIjsdBeta;
  1965.                                         cCoef[6][j] -= coef0 * dlns * kns * ijs;

  1966.                                         sCoef[0][j] += coef4;
  1967.                                         sCoef[1][j] += coef4 * (n + 1);
  1968.                                         sCoef[2][j] += coef1 * (kns * dJjsdh + jjs * hXXX * dkns);
  1969.                                         sCoef[3][j] += coef1 * (kns * dJjsdk + jjs * kXXX * dkns);
  1970.                                         sCoef[4][j] += coef2 * dJjsdAlpha;
  1971.                                         sCoef[5][j] += coef2 * dJjsdBeta;
  1972.                                         sCoef[6][j] += coef0 * dlns * kns * jjs;
  1973.                                     }
  1974.                                 }
  1975.                             }
  1976.                         }
  1977.                     }
  1978.                 }

  1979.                 cCoef[0][j] *= -context.getMuoa() / j;
  1980.                 cCoef[1][j] *=  context.getMuoa() / ( j * auxiliaryElements.getSma() );
  1981.                 cCoef[2][j] *= -context.getMuoa() / j;
  1982.                 cCoef[3][j] *= -context.getMuoa() / j;
  1983.                 cCoef[4][j] *= -context.getMuoa() / j;
  1984.                 cCoef[5][j] *= -context.getMuoa() / j;
  1985.                 cCoef[6][j] *= -context.getMuoa() / j;

  1986.                 sCoef[0][j] *= -context.getMuoa() / j;
  1987.                 sCoef[1][j] *=  context.getMuoa() / ( j * auxiliaryElements.getSma() );
  1988.                 sCoef[2][j] *= -context.getMuoa() / j;
  1989.                 sCoef[3][j] *= -context.getMuoa() / j;
  1990.                 sCoef[4][j] *= -context.getMuoa() / j;
  1991.                 sCoef[5][j] *= -context.getMuoa() / j;
  1992.                 sCoef[6][j] *= -context.getMuoa() / j;

  1993.             }
  1994.         }

  1995.         /** Check if an index is within the accepted interval.
  1996.          *
  1997.          * @param index the index to check
  1998.          * @param lowerBound the lower bound of the interval
  1999.          * @param upperBound the upper bound of the interval
  2000.          * @return true if the index is between the lower and upper bounds, false otherwise
  2001.          */
  2002.         private boolean isBetween(final int index, final int lowerBound, final int upperBound) {
  2003.             return index >= lowerBound && index <= upperBound;
  2004.         }

  2005.         /**Get the value of C<sup>j</sup>.
  2006.          *
  2007.          * @param j j index
  2008.          * @return C<sup>j</sup>
  2009.          */
  2010.         public double getCj(final int j) {
  2011.             return cCoef[0][j];
  2012.         }

  2013.         /**Get the value of dC<sup>j</sup> / da.
  2014.          *
  2015.          * @param j j index
  2016.          * @return dC<sup>j</sup> / da
  2017.          */
  2018.         public double getdCjdA(final int j) {
  2019.             return cCoef[1][j];
  2020.         }

  2021.         /**Get the value of dC<sup>j</sup> / dh.
  2022.          *
  2023.          * @param j j index
  2024.          * @return dC<sup>j</sup> / dh
  2025.          */
  2026.         public double getdCjdH(final int j) {
  2027.             return cCoef[2][j];
  2028.         }

  2029.         /**Get the value of dC<sup>j</sup> / dk.
  2030.          *
  2031.          * @param j j index
  2032.          * @return dC<sup>j</sup> / dk
  2033.          */
  2034.         public double getdCjdK(final int j) {
  2035.             return cCoef[3][j];
  2036.         }

  2037.         /**Get the value of dC<sup>j</sup> / dα.
  2038.          *
  2039.          * @param j j index
  2040.          * @return dC<sup>j</sup> / dα
  2041.          */
  2042.         public double getdCjdAlpha(final int j) {
  2043.             return cCoef[4][j];
  2044.         }

  2045.         /**Get the value of dC<sup>j</sup> / dβ.
  2046.          *
  2047.          * @param j j index
  2048.          * @return dC<sup>j</sup> / dβ
  2049.          */
  2050.         public double getdCjdBeta(final int j) {
  2051.             return cCoef[5][j];
  2052.         }

  2053.         /**Get the value of dC<sup>j</sup> / dγ.
  2054.          *
  2055.          * @param j j index
  2056.          * @return dC<sup>j</sup> / dγ
  2057.          */
  2058.         public double getdCjdGamma(final int j) {
  2059.             return cCoef[6][j];
  2060.         }

  2061.         /**Get the value of S<sup>j</sup>.
  2062.          *
  2063.          * @param j j index
  2064.          * @return S<sup>j</sup>
  2065.          */
  2066.         public double getSj(final int j) {
  2067.             return sCoef[0][j];
  2068.         }

  2069.         /**Get the value of dS<sup>j</sup> / da.
  2070.          *
  2071.          * @param j j index
  2072.          * @return dS<sup>j</sup> / da
  2073.          */
  2074.         public double getdSjdA(final int j) {
  2075.             return sCoef[1][j];
  2076.         }

  2077.         /**Get the value of dS<sup>j</sup> / dh.
  2078.          *
  2079.          * @param j j index
  2080.          * @return dS<sup>j</sup> / dh
  2081.          */
  2082.         public double getdSjdH(final int j) {
  2083.             return sCoef[2][j];
  2084.         }

  2085.         /**Get the value of dS<sup>j</sup> / dk.
  2086.          *
  2087.          * @param j j index
  2088.          * @return dS<sup>j</sup> / dk
  2089.          */
  2090.         public double getdSjdK(final int j) {
  2091.             return sCoef[3][j];
  2092.         }

  2093.         /**Get the value of dS<sup>j</sup> / dα.
  2094.          *
  2095.          * @param j j index
  2096.          * @return dS<sup>j</sup> / dα
  2097.          */
  2098.         public double getdSjdAlpha(final int j) {
  2099.             return sCoef[4][j];
  2100.         }

  2101.         /**Get the value of dS<sup>j</sup> / dβ.
  2102.          *
  2103.          * @param j j index
  2104.          * @return dS<sup>j</sup> / dβ
  2105.          */
  2106.         public double getdSjdBeta(final int j) {
  2107.             return sCoef[5][j];
  2108.         }

  2109.         /**Get the value of dS<sup>j</sup> /  dγ.
  2110.          *
  2111.          * @param j j index
  2112.          * @return dS<sup>j</sup> /  dγ
  2113.          */
  2114.         public double getdSjdGamma(final int j) {
  2115.             return sCoef[6][j];
  2116.         }
  2117.     }

  2118.     /** Compute the C<sup>j</sup> and the S<sup>j</sup> coefficients.
  2119.      *  <p>
  2120.      *  Those coefficients are given in Danielson paper by expressions 4.1-(13) to 4.1.-(16b)
  2121.      *  </p>
  2122.      */
  2123.     private class FieldFourierCjSjCoefficients <T extends CalculusFieldElement<T>> {

  2124.         /** The G<sub>js</sub>, H<sub>js</sub>, I<sub>js</sub> and J<sub>js</sub> polynomials. */
  2125.         private final FieldGHIJjsPolynomials<T> ghijCoef;

  2126.         /** L<sub>n</sub><sup>s</sup>(γ). */
  2127.         private final FieldLnsCoefficients<T> lnsCoef;

  2128.         /** Maximum possible value for n. */
  2129.         private final int nMax;

  2130.         /** Maximum possible value for s. */
  2131.         private final int sMax;

  2132.         /** Maximum possible value for j. */
  2133.         private final int jMax;

  2134.         /** The C<sup>j</sup> coefficients and their derivatives.
  2135.          * <p>
  2136.          * Each column of the matrix contains the following values: <br/>
  2137.          * - C<sup>j</sup> <br/>
  2138.          * - dC<sup>j</sup> / da <br/>
  2139.          * - dC<sup>j</sup> / dh <br/>
  2140.          * - dC<sup>j</sup> / dk <br/>
  2141.          * - dC<sup>j</sup> / dα <br/>
  2142.          * - dC<sup>j</sup> / dβ <br/>
  2143.          * - dC<sup>j</sup> / dγ <br/>
  2144.          * </p>
  2145.          */
  2146.         private final T[][] cCoef;

  2147.         /** The S<sup>j</sup> coefficients and their derivatives.
  2148.          * <p>
  2149.          * Each column of the matrix contains the following values: <br/>
  2150.          * - S<sup>j</sup> <br/>
  2151.          * - dS<sup>j</sup> / da <br/>
  2152.          * - dS<sup>j</sup> / dh <br/>
  2153.          * - dS<sup>j</sup> / dk <br/>
  2154.          * - dS<sup>j</sup> / dα <br/>
  2155.          * - dS<sup>j</sup> / dβ <br/>
  2156.          * - dS<sup>j</sup> / dγ <br/>
  2157.          * </p>
  2158.          */
  2159.         private final T[][] sCoef;

  2160.         /** h * &Chi;³. */
  2161.         private final T hXXX;
  2162.         /** k * &Chi;³. */
  2163.         private final T kXXX;

  2164.         /** Create a set of C<sup>j</sup> and the S<sup>j</sup> coefficients.
  2165.          *  @param date the current date
  2166.          *  @param nMax maximum possible value for n
  2167.          *  @param sMax maximum possible value for s
  2168.          *  @param jMax maximum possible value for j
  2169.          *  @param context container for attributes
  2170.          *  @param hansenObjects initialization of hansen objects
  2171.          */
  2172.         FieldFourierCjSjCoefficients(final FieldAbsoluteDate<T> date,
  2173.                                      final int nMax, final int sMax, final int jMax,
  2174.                                      final FieldDSSTZonalContext<T> context,
  2175.                                      final FieldHansenObjects<T> hansenObjects) {

  2176.             //Field used by default
  2177.             final Field<T> field = date.getField();

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

  2179.             this.ghijCoef = new FieldGHIJjsPolynomials<>(auxiliaryElements.getK(), auxiliaryElements.getH(), auxiliaryElements.getAlpha(), auxiliaryElements.getBeta());
  2180.             // Qns coefficients
  2181.             final T[][] Qns = CoefficientsFactory.computeQns(auxiliaryElements.getGamma(), nMax, nMax);

  2182.             this.lnsCoef = new FieldLnsCoefficients<>(nMax, nMax, Qns, Vns, context.getRoa(), field);
  2183.             this.nMax = nMax;
  2184.             this.sMax = sMax;
  2185.             this.jMax = jMax;

  2186.             // compute the common factors that depends on the mean elements
  2187.             this.hXXX = auxiliaryElements.getH().multiply(context.getXXX());
  2188.             this.kXXX = auxiliaryElements.getK().multiply(context.getXXX());

  2189.             this.cCoef = MathArrays.buildArray(field, 7, jMax + 1);
  2190.             this.sCoef = MathArrays.buildArray(field, 7, jMax + 1);

  2191.             for (int s = 0; s <= sMax; s++) {
  2192.                 //Initialise the Hansen roots
  2193.                 hansenObjects.computeHansenObjectsInitValues(context, s);
  2194.             }
  2195.             generateCoefficients(date, context, auxiliaryElements, hansenObjects, field);
  2196.         }

  2197.         /** Generate all coefficients.
  2198.          * @param date the current date
  2199.          * @param context container for attributes
  2200.          * @param hansenObjects initialization of hansen objects
  2201.          * @param auxiliaryElements auxiliary elements related to the current orbit
  2202.          * @param field field used by default
  2203.          */
  2204.         private void generateCoefficients(final FieldAbsoluteDate<T> date,
  2205.                                           final FieldDSSTZonalContext<T> context,
  2206.                                           final FieldAuxiliaryElements<T> auxiliaryElements,
  2207.                                           final FieldHansenObjects<T> hansenObjects,
  2208.                                           final Field<T> field) {

  2209.             //Zero
  2210.             final T zero = field.getZero();

  2211.             final UnnormalizedSphericalHarmonics harmonics = provider.onDate(date.toAbsoluteDate());
  2212.             for (int j = 1; j <= jMax; j++) {

  2213.                 //init arrays
  2214.                 for (int i = 0; i <= 6; i++) {
  2215.                     cCoef[i][j] = zero;
  2216.                     sCoef[i][j] = zero;
  2217.                 }

  2218.                 if (isBetween(j, 1, nMax - 1)) {

  2219.                     //compute first double sum where s: j -> N-1 and n: s+1 -> N
  2220.                     for (int s = j; s <= FastMath.min(nMax - 1, sMax); s++) {
  2221.                         // j - s
  2222.                         final int jms = j - s;
  2223.                         // Kronecker symbols (2 - delta(0,s-j)) and (2 - delta(0,j-s))
  2224.                         final int d0smj = (s == j) ? 1 : 2;

  2225.                         for (int n = s + 1; n <= nMax; n++) {
  2226.                             // if n + (j-s) is odd, then the term is equal to zero due to the factor Vn,s-j
  2227.                             if ((n + jms) % 2 == 0) {
  2228.                                 // (2 - delta(0,s-j)) * J<sub>n</sub> * K₀<sup>-n-1,s</sup> * L<sub>n</sub><sup>j-s</sup>
  2229.                                 final T lns  = lnsCoef.getLns(n, -jms);
  2230.                                 final T dlns = lnsCoef.getdLnsdGamma(n, -jms);

  2231.                                 final T hjs        = ghijCoef.getHjs(s, -jms);
  2232.                                 final T dHjsdh     = ghijCoef.getdHjsdh(s, -jms);
  2233.                                 final T dHjsdk     = ghijCoef.getdHjsdk(s, -jms);
  2234.                                 final T dHjsdAlpha = ghijCoef.getdHjsdAlpha(s, -jms);
  2235.                                 final T dHjsdBeta  = ghijCoef.getdHjsdBeta(s, -jms);

  2236.                                 final T gjs        = ghijCoef.getGjs(s, -jms);
  2237.                                 final T dGjsdh     = ghijCoef.getdGjsdh(s, -jms);
  2238.                                 final T dGjsdk     = ghijCoef.getdGjsdk(s, -jms);
  2239.                                 final T dGjsdAlpha = ghijCoef.getdGjsdAlpha(s, -jms);
  2240.                                 final T dGjsdBeta  = ghijCoef.getdGjsdBeta(s, -jms);

  2241.                                 // J<sub>n</sub>
  2242.                                 final T jn = zero.subtract(harmonics.getUnnormalizedCnm(n, 0));

  2243.                                 // K₀<sup>-n-1,s</sup>
  2244.                                 final T kns   = (T) hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getX());
  2245.                                 final T dkns  = (T) hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getX());

  2246.                                 final T coef0 = jn.multiply(d0smj);
  2247.                                 final T coef1 = coef0.multiply(lns);
  2248.                                 final T coef2 = coef1.multiply(kns);
  2249.                                 final T coef3 = coef2.multiply(hjs);
  2250.                                 final T coef4 = coef2.multiply(gjs);

  2251.                                 // Add the term to the coefficients
  2252.                                 cCoef[0][j] = cCoef[0][j].add(coef3);
  2253.                                 cCoef[1][j] = cCoef[1][j].add(coef3.multiply(n + 1));
  2254.                                 cCoef[2][j] = cCoef[2][j].add(coef1.multiply(kns.multiply(dHjsdh).add(hjs.multiply(hXXX).multiply(dkns))));
  2255.                                 cCoef[3][j] = cCoef[3][j].add(coef1.multiply(kns.multiply(dHjsdk).add(hjs.multiply(kXXX).multiply(dkns))));
  2256.                                 cCoef[4][j] = cCoef[4][j].add(coef2.multiply(dHjsdAlpha));
  2257.                                 cCoef[5][j] = cCoef[5][j].add(coef2.multiply(dHjsdBeta));
  2258.                                 cCoef[6][j] = cCoef[6][j].add(coef0.multiply(dlns).multiply(kns).multiply(hjs));

  2259.                                 sCoef[0][j] = sCoef[0][j].add(coef4);
  2260.                                 sCoef[1][j] = sCoef[1][j].add(coef4.multiply(n + 1));
  2261.                                 sCoef[2][j] = sCoef[2][j].add(coef1.multiply(kns.multiply(dGjsdh).add(gjs.multiply(hXXX).multiply(dkns))));
  2262.                                 sCoef[3][j] = sCoef[3][j].add(coef1.multiply(kns.multiply(dGjsdk).add(gjs.multiply(kXXX).multiply(dkns))));
  2263.                                 sCoef[4][j] = sCoef[4][j].add(coef2.multiply(dGjsdAlpha));
  2264.                                 sCoef[5][j] = sCoef[5][j].add(coef2.multiply(dGjsdBeta));
  2265.                                 sCoef[6][j] = sCoef[6][j].add(coef0.multiply(dlns).multiply(kns).multiply(gjs));
  2266.                             }
  2267.                         }
  2268.                     }

  2269.                     //compute second double sum where s: 0 -> N-j and n: max(j+s, j+1) -> N
  2270.                     for (int s = 0; s <= FastMath.min(nMax - j, sMax); s++) {
  2271.                         // j + s
  2272.                         final int jps = j + s;
  2273.                         // Kronecker symbols (2 - delta(0,j+s))
  2274.                         final double d0spj = (s == -j) ? 1 : 2;

  2275.                         for (int n = FastMath.max(j + s, j + 1); n <= nMax; n++) {
  2276.                             // if n + (j+s) is odd, then the term is equal to zero due to the factor Vn,s+j
  2277.                             if ((n + jps) % 2 == 0) {
  2278.                                 // (2 - delta(0,s+j)) * J<sub>n</sub> * K₀<sup>-n-1,s</sup> * L<sub>n</sub><sup>j+s</sup>
  2279.                                 final T lns  = lnsCoef.getLns(n, jps);
  2280.                                 final T dlns = lnsCoef.getdLnsdGamma(n, jps);

  2281.                                 final T hjs        = ghijCoef.getHjs(s, jps);
  2282.                                 final T dHjsdh     = ghijCoef.getdHjsdh(s, jps);
  2283.                                 final T dHjsdk     = ghijCoef.getdHjsdk(s, jps);
  2284.                                 final T dHjsdAlpha = ghijCoef.getdHjsdAlpha(s, jps);
  2285.                                 final T dHjsdBeta  = ghijCoef.getdHjsdBeta(s, jps);

  2286.                                 final T gjs        = ghijCoef.getGjs(s, jps);
  2287.                                 final T dGjsdh     = ghijCoef.getdGjsdh(s, jps);
  2288.                                 final T dGjsdk     = ghijCoef.getdGjsdk(s, jps);
  2289.                                 final T dGjsdAlpha = ghijCoef.getdGjsdAlpha(s, jps);
  2290.                                 final T dGjsdBeta  = ghijCoef.getdGjsdBeta(s, jps);

  2291.                                 // J<sub>n</sub>
  2292.                                 final T jn = zero.subtract(harmonics.getUnnormalizedCnm(n, 0));

  2293.                                 // K₀<sup>-n-1,s</sup>
  2294.                                 final T kns   = (T) hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getX());
  2295.                                 final T dkns  = (T) hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getX());

  2296.                                 final T coef0 = jn.multiply(d0spj);
  2297.                                 final T coef1 = coef0.multiply(lns);
  2298.                                 final T coef2 = coef1.multiply(kns);

  2299.                                 final T coef3 = coef2.multiply(hjs);
  2300.                                 final T coef4 = coef2.multiply(gjs);

  2301.                                 // Add the term to the coefficients
  2302.                                 cCoef[0][j] = cCoef[0][j].subtract(coef3);
  2303.                                 cCoef[1][j] = cCoef[1][j].subtract(coef3.multiply(n + 1));
  2304.                                 cCoef[2][j] = cCoef[2][j].subtract(coef1.multiply(kns.multiply(dHjsdh).add(hjs.multiply(hXXX).multiply(dkns))));
  2305.                                 cCoef[3][j] = cCoef[3][j].subtract(coef1.multiply(kns.multiply(dHjsdk).add(hjs.multiply(kXXX).multiply(dkns))));
  2306.                                 cCoef[4][j] = cCoef[4][j].subtract(coef2.multiply(dHjsdAlpha));
  2307.                                 cCoef[5][j] = cCoef[5][j].subtract(coef2.multiply(dHjsdBeta));
  2308.                                 cCoef[6][j] = cCoef[6][j].subtract(coef0.multiply(dlns).multiply(kns).multiply(hjs));

  2309.                                 sCoef[0][j] = sCoef[0][j].add(coef4);
  2310.                                 sCoef[1][j] = sCoef[1][j].add(coef4.multiply(n + 1));
  2311.                                 sCoef[2][j] = sCoef[2][j].add(coef1.multiply(kns.multiply(dGjsdh).add(gjs.multiply(hXXX).multiply(dkns))));
  2312.                                 sCoef[3][j] = sCoef[3][j].add(coef1.multiply(kns.multiply(dGjsdk).add(gjs.multiply(kXXX).multiply(dkns))));
  2313.                                 sCoef[4][j] = sCoef[4][j].add(coef2.multiply(dGjsdAlpha));
  2314.                                 sCoef[5][j] = sCoef[5][j].add(coef2.multiply(dGjsdBeta));
  2315.                                 sCoef[6][j] = sCoef[6][j].add(coef0.multiply(dlns).multiply(kns).multiply(gjs));
  2316.                             }
  2317.                         }
  2318.                     }

  2319.                     //compute third double sum where s: 1 -> j and  n: j+1 -> N
  2320.                     for (int s = 1; s <= FastMath.min(j, sMax); s++) {
  2321.                         // j - s
  2322.                         final int jms = j - s;
  2323.                         // Kronecker symbols (2 - delta(0,s-j)) and (2 - delta(0,j-s))
  2324.                         final int d0smj = (s == j) ? 1 : 2;

  2325.                         for (int n = j + 1; n <= nMax; n++) {
  2326.                             // if n + (j-s) is odd, then the term is equal to zero due to the factor Vn,s-j
  2327.                             if ((n + jms) % 2 == 0) {
  2328.                                 // (2 - delta(0,j-s)) * J<sub>n</sub> * K₀<sup>-n-1,s</sup> * L<sub>n</sub><sup>j-s</sup>
  2329.                                 final T lns  = lnsCoef.getLns(n, jms);
  2330.                                 final T dlns = lnsCoef.getdLnsdGamma(n, jms);

  2331.                                 final T ijs        = ghijCoef.getIjs(s, jms);
  2332.                                 final T dIjsdh     = ghijCoef.getdIjsdh(s, jms);
  2333.                                 final T dIjsdk     = ghijCoef.getdIjsdk(s, jms);
  2334.                                 final T dIjsdAlpha = ghijCoef.getdIjsdAlpha(s, jms);
  2335.                                 final T dIjsdBeta  = ghijCoef.getdIjsdBeta(s, jms);

  2336.                                 final T jjs        = ghijCoef.getJjs(s, jms);
  2337.                                 final T dJjsdh     = ghijCoef.getdJjsdh(s, jms);
  2338.                                 final T dJjsdk     = ghijCoef.getdJjsdk(s, jms);
  2339.                                 final T dJjsdAlpha = ghijCoef.getdJjsdAlpha(s, jms);
  2340.                                 final T dJjsdBeta  = ghijCoef.getdJjsdBeta(s, jms);

  2341.                                 // J<sub>n</sub>
  2342.                                 final T jn = zero.subtract(harmonics.getUnnormalizedCnm(n, 0));

  2343.                                 // K₀<sup>-n-1,s</sup>
  2344.                                 final T kns   = (T) hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getX());
  2345.                                 final T dkns  = (T) hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getX());

  2346.                                 final T coef0 = jn.multiply(d0smj);
  2347.                                 final T coef1 = coef0.multiply(lns);
  2348.                                 final T coef2 = coef1.multiply(kns);

  2349.                                 final T coef3 = coef2.multiply(ijs);
  2350.                                 final T coef4 = coef2.multiply(jjs);

  2351.                                 // Add the term to the coefficients
  2352.                                 cCoef[0][j] = cCoef[0][j].subtract(coef3);
  2353.                                 cCoef[1][j] = cCoef[1][j].subtract(coef3.multiply(n + 1));
  2354.                                 cCoef[2][j] = cCoef[2][j].subtract(coef1.multiply(kns.multiply(dIjsdh).add(ijs.multiply(hXXX).multiply(dkns))));
  2355.                                 cCoef[3][j] = cCoef[3][j].subtract(coef1.multiply(kns.multiply(dIjsdk).add(ijs.multiply(kXXX).multiply(dkns))));
  2356.                                 cCoef[4][j] = cCoef[4][j].subtract(coef2.multiply(dIjsdAlpha));
  2357.                                 cCoef[5][j] = cCoef[5][j].subtract(coef2.multiply(dIjsdBeta));
  2358.                                 cCoef[6][j] = cCoef[6][j].subtract(coef0.multiply(dlns).multiply(kns).multiply(ijs));

  2359.                                 sCoef[0][j] = sCoef[0][j].add(coef4);
  2360.                                 sCoef[1][j] = sCoef[1][j].add(coef4.multiply(n + 1));
  2361.                                 sCoef[2][j] = sCoef[2][j].add(coef1.multiply(kns.multiply(dJjsdh).add(jjs.multiply(hXXX).multiply(dkns))));
  2362.                                 sCoef[3][j] = sCoef[3][j].add(coef1.multiply(kns.multiply(dJjsdk).add(jjs.multiply(kXXX).multiply(dkns))));
  2363.                                 sCoef[4][j] = sCoef[4][j].add(coef2.multiply(dJjsdAlpha));
  2364.                                 sCoef[5][j] = sCoef[5][j].add(coef2.multiply(dJjsdBeta));
  2365.                                 sCoef[6][j] = sCoef[6][j].add(coef0.multiply(dlns).multiply(kns).multiply(jjs));
  2366.                             }
  2367.                         }
  2368.                     }
  2369.                 }

  2370.                 if (isBetween(j, 2, nMax)) {
  2371.                     //add first term
  2372.                     // J<sub>j</sub>
  2373.                     final T jj = zero.subtract(harmonics.getUnnormalizedCnm(j, 0));
  2374.                     T kns  = (T) hansenObjects.getHansenObjects()[0].getValue(-j - 1, context.getX());
  2375.                     T dkns = (T) hansenObjects.getHansenObjects()[0].getDerivative(-j - 1, context.getX());

  2376.                     T lns = lnsCoef.getLns(j, j);
  2377.                     //dlns is 0 because n == s == j

  2378.                     final T hjs        = ghijCoef.getHjs(0, j);
  2379.                     final T dHjsdh     = ghijCoef.getdHjsdh(0, j);
  2380.                     final T dHjsdk     = ghijCoef.getdHjsdk(0, j);
  2381.                     final T dHjsdAlpha = ghijCoef.getdHjsdAlpha(0, j);
  2382.                     final T dHjsdBeta  = ghijCoef.getdHjsdBeta(0, j);

  2383.                     final T gjs        = ghijCoef.getGjs(0, j);
  2384.                     final T dGjsdh     = ghijCoef.getdGjsdh(0, j);
  2385.                     final T dGjsdk     = ghijCoef.getdGjsdk(0, j);
  2386.                     final T dGjsdAlpha = ghijCoef.getdGjsdAlpha(0, j);
  2387.                     final T dGjsdBeta  = ghijCoef.getdGjsdBeta(0, j);

  2388.                     // 2 * J<sub>j</sub> * K₀<sup>-j-1,0</sup> * L<sub>j</sub><sup>j</sup>
  2389.                     T coef0 = jj.multiply(2.);
  2390.                     T coef1 = coef0.multiply(lns);
  2391.                     T coef2 = coef1.multiply(kns);

  2392.                     T coef3 = coef2.multiply(hjs);
  2393.                     T coef4 = coef2.multiply(gjs);

  2394.                     // Add the term to the coefficients
  2395.                     cCoef[0][j] = cCoef[0][j].subtract(coef3);
  2396.                     cCoef[1][j] = cCoef[1][j].subtract(coef3.multiply(j + 1));
  2397.                     cCoef[2][j] = cCoef[2][j].subtract(coef1.multiply(kns.multiply(dHjsdh).add(hjs.multiply(hXXX).multiply(dkns))));
  2398.                     cCoef[3][j] = cCoef[3][j].subtract(coef1.multiply(kns.multiply(dHjsdk).add(hjs.multiply(kXXX).multiply(dkns))));
  2399.                     cCoef[4][j] = cCoef[4][j].subtract(coef2.multiply(dHjsdAlpha));
  2400.                     cCoef[5][j] = cCoef[5][j].subtract(coef2.multiply(dHjsdBeta));
  2401.                     //no contribution to cCoef[6][j] because dlns is 0

  2402.                     sCoef[0][j] = sCoef[0][j].add(coef4);
  2403.                     sCoef[1][j] = sCoef[1][j].add(coef4.multiply(j + 1));
  2404.                     sCoef[2][j] = sCoef[2][j].add(coef1.multiply(kns.multiply(dGjsdh).add(gjs.multiply(hXXX).multiply(dkns))));
  2405.                     sCoef[3][j] = sCoef[3][j].add(coef1.multiply(kns.multiply(dGjsdk).add(gjs.multiply(kXXX).multiply(dkns))));
  2406.                     sCoef[4][j] = sCoef[4][j].add(coef2.multiply(dGjsdAlpha));
  2407.                     sCoef[5][j] = sCoef[5][j].add(coef2.multiply(dGjsdBeta));
  2408.                     //no contribution to sCoef[6][j] because dlns is 0

  2409.                     //compute simple sum where s: 1 -> j-1
  2410.                     for (int s = 1; s <= FastMath.min(j - 1, sMax); s++) {
  2411.                         // j - s
  2412.                         final int jms = j - s;
  2413.                         // Kronecker symbols (2 - delta(0,s-j)) and (2 - delta(0,j-s))
  2414.                         final int d0smj = (s == j) ? 1 : 2;

  2415.                         // if s is odd, then the term is equal to zero due to the factor Vj,s-j
  2416.                         if (s % 2 == 0) {
  2417.                             // (2 - delta(0,j-s)) * J<sub>j</sub> * K₀<sup>-j-1,s</sup> * L<sub>j</sub><sup>j-s</sup>
  2418.                             kns   = (T) hansenObjects.getHansenObjects()[s].getValue(-j - 1, context.getX());
  2419.                             dkns  = (T) hansenObjects.getHansenObjects()[s].getDerivative(-j - 1, context.getX());

  2420.                             lns = lnsCoef.getLns(j, jms);
  2421.                             final T dlns = lnsCoef.getdLnsdGamma(j, jms);

  2422.                             final T ijs        = ghijCoef.getIjs(s, jms);
  2423.                             final T dIjsdh     = ghijCoef.getdIjsdh(s, jms);
  2424.                             final T dIjsdk     = ghijCoef.getdIjsdk(s, jms);
  2425.                             final T dIjsdAlpha = ghijCoef.getdIjsdAlpha(s, jms);
  2426.                             final T dIjsdBeta  = ghijCoef.getdIjsdBeta(s, jms);

  2427.                             final T jjs        = ghijCoef.getJjs(s, jms);
  2428.                             final T dJjsdh     = ghijCoef.getdJjsdh(s, jms);
  2429.                             final T dJjsdk     = ghijCoef.getdJjsdk(s, jms);
  2430.                             final T dJjsdAlpha = ghijCoef.getdJjsdAlpha(s, jms);
  2431.                             final T dJjsdBeta  = ghijCoef.getdJjsdBeta(s, jms);

  2432.                             coef0 = jj.multiply(d0smj);
  2433.                             coef1 = coef0.multiply(lns);
  2434.                             coef2 = coef1.multiply(kns);

  2435.                             coef3 = coef2.multiply(ijs);
  2436.                             coef4 = coef2.multiply(jjs);

  2437.                             // Add the term to the coefficients
  2438.                             cCoef[0][j] = cCoef[0][j].subtract(coef3);
  2439.                             cCoef[1][j] = cCoef[1][j].subtract(coef3.multiply(j + 1));
  2440.                             cCoef[2][j] = cCoef[2][j].subtract(coef1.multiply(kns.multiply(dIjsdh).add(ijs.multiply(hXXX).multiply(dkns))));
  2441.                             cCoef[3][j] = cCoef[3][j].subtract(coef1.multiply(kns.multiply(dIjsdk).add(ijs.multiply(kXXX).multiply(dkns))));
  2442.                             cCoef[4][j] = cCoef[4][j].subtract(coef2.multiply(dIjsdAlpha));
  2443.                             cCoef[5][j] = cCoef[5][j].subtract(coef2.multiply(dIjsdBeta));
  2444.                             cCoef[6][j] = cCoef[6][j].subtract(coef0.multiply(dlns).multiply(kns).multiply(ijs));

  2445.                             sCoef[0][j] = sCoef[0][j].add(coef4);
  2446.                             sCoef[1][j] = sCoef[1][j].add(coef4.multiply(j + 1));
  2447.                             sCoef[2][j] = sCoef[2][j].add(coef1.multiply(kns.multiply(dJjsdh).add(jjs.multiply(hXXX).multiply(dkns))));
  2448.                             sCoef[3][j] = sCoef[3][j].add(coef1.multiply(kns.multiply(dJjsdk).add(jjs.multiply(kXXX).multiply(dkns))));
  2449.                             sCoef[4][j] = sCoef[4][j].add(coef2.multiply(dJjsdAlpha));
  2450.                             sCoef[5][j] = sCoef[5][j].add(coef2.multiply(dJjsdBeta));
  2451.                             sCoef[6][j] = sCoef[6][j].add(coef0.multiply(dlns).multiply(kns).multiply(jjs));
  2452.                         }
  2453.                     }
  2454.                 }

  2455.                 if (isBetween(j, 3, 2 * nMax - 1)) {
  2456.                     //compute uppercase sigma expressions

  2457.                     //min(j-1,N)
  2458.                     final int minjm1on = FastMath.min(j - 1, nMax);

  2459.                     //if j is even
  2460.                     if (j % 2 == 0) {
  2461.                         //compute first double sum where s: j-min(j-1,N) -> j/2-1 and n: j-s -> min(j-1,N)
  2462.                         for (int s = j - minjm1on; s <= FastMath.min(j / 2 - 1, sMax); s++) {
  2463.                             // j - s
  2464.                             final int jms = j - s;
  2465.                             // Kronecker symbols (2 - delta(0,s-j)) and (2 - delta(0,j-s))
  2466.                             final int d0smj = (s == j) ? 1 : 2;

  2467.                             for (int n = j - s; n <= minjm1on; n++) {
  2468.                                 // if n + (j-s) is odd, then the term is equal to zero due to the factor Vn,s-j
  2469.                                 if ((n + jms) % 2 == 0) {
  2470.                                     // (2 - delta(0,j-s)) * J<sub>n</sub> * K₀<sup>-n-1,s</sup> * L<sub>n</sub><sup>j-s</sup>
  2471.                                     final T lns  = lnsCoef.getLns(n, jms);
  2472.                                     final T dlns = lnsCoef.getdLnsdGamma(n, jms);

  2473.                                     final T ijs        = ghijCoef.getIjs(s, jms);
  2474.                                     final T dIjsdh     = ghijCoef.getdIjsdh(s, jms);
  2475.                                     final T dIjsdk     = ghijCoef.getdIjsdk(s, jms);
  2476.                                     final T dIjsdAlpha = ghijCoef.getdIjsdAlpha(s, jms);
  2477.                                     final T dIjsdBeta  = ghijCoef.getdIjsdBeta(s, jms);

  2478.                                     final T jjs        = ghijCoef.getJjs(s, jms);
  2479.                                     final T dJjsdh     = ghijCoef.getdJjsdh(s, jms);
  2480.                                     final T dJjsdk     = ghijCoef.getdJjsdk(s, jms);
  2481.                                     final T dJjsdAlpha = ghijCoef.getdJjsdAlpha(s, jms);
  2482.                                     final T dJjsdBeta  = ghijCoef.getdJjsdBeta(s, jms);

  2483.                                     // J<sub>n</sub>
  2484.                                     final T jn = zero.subtract(harmonics.getUnnormalizedCnm(n, 0));

  2485.                                     // K₀<sup>-n-1,s</sup>
  2486.                                     final T kns   = (T) hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getX());
  2487.                                     final T dkns  = (T) hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getX());

  2488.                                     final T coef0 = jn.multiply(d0smj);
  2489.                                     final T coef1 = coef0.multiply(lns);
  2490.                                     final T coef2 = coef1.multiply(kns);

  2491.                                     final T coef3 = coef2.multiply(ijs);
  2492.                                     final T coef4 = coef2.multiply(jjs);

  2493.                                     // Add the term to the coefficients
  2494.                                     cCoef[0][j] = cCoef[0][j].subtract(coef3);
  2495.                                     cCoef[1][j] = cCoef[1][j].subtract(coef3.multiply(n + 1));
  2496.                                     cCoef[2][j] = cCoef[2][j].subtract(coef1.multiply(kns.multiply(dIjsdh).add(ijs.multiply(hXXX).multiply(dkns))));
  2497.                                     cCoef[3][j] = cCoef[3][j].subtract(coef1.multiply(kns.multiply(dIjsdk).add(ijs.multiply(kXXX).multiply(dkns))));
  2498.                                     cCoef[4][j] = cCoef[4][j].subtract(coef2.multiply(dIjsdAlpha));
  2499.                                     cCoef[5][j] = cCoef[5][j].subtract(coef2.multiply(dIjsdBeta));
  2500.                                     cCoef[6][j] = cCoef[6][j].subtract(coef0.multiply(dlns).multiply(kns).multiply(ijs));

  2501.                                     sCoef[0][j] = sCoef[0][j].add(coef4);
  2502.                                     sCoef[1][j] = sCoef[1][j].add(coef4.multiply(n + 1));
  2503.                                     sCoef[2][j] = sCoef[2][j].add(coef1.multiply(kns.multiply(dJjsdh).add(jjs.multiply(hXXX).multiply(dkns))));
  2504.                                     sCoef[3][j] = sCoef[3][j].add(coef1.multiply(kns.multiply(dJjsdk).add(jjs.multiply(kXXX).multiply(dkns))));
  2505.                                     sCoef[4][j] = sCoef[4][j].add(coef2.multiply(dJjsdAlpha));
  2506.                                     sCoef[5][j] = sCoef[5][j].add(coef2.multiply(dJjsdBeta));
  2507.                                     sCoef[6][j] = sCoef[6][j].add(coef0.multiply(dlns).multiply(kns).multiply(jjs));
  2508.                                 }
  2509.                             }
  2510.                         }

  2511.                         //compute second double sum where s: j/2 -> min(j-1,N)-1 and n: s+1 -> min(j-1,N)
  2512.                         for (int s = j / 2; s <=  FastMath.min(minjm1on - 1, sMax); s++) {
  2513.                             // j - s
  2514.                             final int jms = j - s;
  2515.                             // Kronecker symbols (2 - delta(0,s-j)) and (2 - delta(0,j-s))
  2516.                             final int d0smj = (s == j) ? 1 : 2;

  2517.                             for (int n = s + 1; n <= minjm1on; n++) {
  2518.                                 // if n + (j-s) is odd, then the term is equal to zero due to the factor Vn,s-j
  2519.                                 if ((n + jms) % 2 == 0) {
  2520.                                     // (2 - delta(0,j-s)) * J<sub>n</sub> * K₀<sup>-n-1,s</sup> * L<sub>n</sub><sup>j-s</sup>
  2521.                                     final T lns  = lnsCoef.getLns(n, jms);
  2522.                                     final T dlns = lnsCoef.getdLnsdGamma(n, jms);

  2523.                                     final T ijs        = ghijCoef.getIjs(s, jms);
  2524.                                     final T dIjsdh     = ghijCoef.getdIjsdh(s, jms);
  2525.                                     final T dIjsdk     = ghijCoef.getdIjsdk(s, jms);
  2526.                                     final T dIjsdAlpha = ghijCoef.getdIjsdAlpha(s, jms);
  2527.                                     final T dIjsdBeta  = ghijCoef.getdIjsdBeta(s, jms);

  2528.                                     final T jjs        = ghijCoef.getJjs(s, jms);
  2529.                                     final T dJjsdh     = ghijCoef.getdJjsdh(s, jms);
  2530.                                     final T dJjsdk     = ghijCoef.getdJjsdk(s, jms);
  2531.                                     final T dJjsdAlpha = ghijCoef.getdJjsdAlpha(s, jms);
  2532.                                     final T dJjsdBeta  = ghijCoef.getdJjsdBeta(s, jms);

  2533.                                     // J<sub>n</sub>
  2534.                                     final T jn = zero.subtract(harmonics.getUnnormalizedCnm(n, 0));

  2535.                                     // K₀<sup>-n-1,s</sup>
  2536.                                     final T kns   = (T) hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getX());
  2537.                                     final T dkns  = (T) hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getX());

  2538.                                     final T coef0 = jn.multiply(d0smj);
  2539.                                     final T coef1 = coef0.multiply(lns);
  2540.                                     final T coef2 = coef1.multiply(kns);

  2541.                                     final T coef3 = coef2.multiply(ijs);
  2542.                                     final T coef4 = coef2.multiply(jjs);

  2543.                                     // Add the term to the coefficients
  2544.                                     cCoef[0][j] = cCoef[0][j].subtract(coef3);
  2545.                                     cCoef[1][j] = cCoef[1][j].subtract(coef3.multiply(n + 1));
  2546.                                     cCoef[2][j] = cCoef[2][j].subtract(coef1.multiply(kns.multiply(dIjsdh).add(ijs.multiply(hXXX).multiply(dkns))));
  2547.                                     cCoef[3][j] = cCoef[3][j].subtract(coef1.multiply(kns.multiply(dIjsdk).add(ijs.multiply(kXXX).multiply(dkns))));
  2548.                                     cCoef[4][j] = cCoef[4][j].subtract(coef2.multiply(dIjsdAlpha));
  2549.                                     cCoef[5][j] = cCoef[5][j].subtract(coef2.multiply(dIjsdBeta));
  2550.                                     cCoef[6][j] = cCoef[6][j].subtract(coef0.multiply(dlns).multiply(kns).multiply(ijs));

  2551.                                     sCoef[0][j] = sCoef[0][j].add(coef4);
  2552.                                     sCoef[1][j] = sCoef[1][j].add(coef4.multiply(n + 1));
  2553.                                     sCoef[2][j] = sCoef[2][j].add(coef1.multiply(kns.multiply(dJjsdh).add(jjs.multiply(hXXX).multiply(dkns))));
  2554.                                     sCoef[3][j] = sCoef[3][j].add(coef1.multiply(kns.multiply(dJjsdk).add(jjs.multiply(kXXX).multiply(dkns))));
  2555.                                     sCoef[4][j] = sCoef[4][j].add(coef2.multiply(dJjsdAlpha));
  2556.                                     sCoef[5][j] = sCoef[5][j].add(coef2.multiply(dJjsdBeta));
  2557.                                     sCoef[6][j] = sCoef[6][j].add(coef0.multiply(dlns).multiply(kns).multiply(jjs));
  2558.                                 }
  2559.                             }
  2560.                         }
  2561.                     }

  2562.                     //if j is odd
  2563.                     else {
  2564.                         //compute first double sum where s: (j-1)/2 -> min(j-1,N)-1 and n: s+1 -> min(j-1,N)
  2565.                         for (int s = (j - 1) / 2; s <= FastMath.min(minjm1on - 1, sMax); s++) {
  2566.                             // j - s
  2567.                             final int jms = j - s;
  2568.                             // Kronecker symbols (2 - delta(0,s-j)) and (2 - delta(0,j-s))
  2569.                             final int d0smj = (s == j) ? 1 : 2;

  2570.                             for (int n = s + 1; n <= minjm1on; n++) {
  2571.                                 // if n + (j-s) is odd, then the term is equal to zero due to the factor Vn,s-j
  2572.                                 if ((n + jms) % 2 == 0) {
  2573.                                     // (2 - delta(0,j-s)) * J<sub>n</sub> * K₀<sup>-n-1,s</sup> * L<sub>n</sub><sup>j-s</sup>
  2574.                                     final T lns  = lnsCoef.getLns(n, jms);
  2575.                                     final T dlns = lnsCoef.getdLnsdGamma(n, jms);

  2576.                                     final T ijs        = ghijCoef.getIjs(s, jms);
  2577.                                     final T dIjsdh     = ghijCoef.getdIjsdh(s, jms);
  2578.                                     final T dIjsdk     = ghijCoef.getdIjsdk(s, jms);
  2579.                                     final T dIjsdAlpha = ghijCoef.getdIjsdAlpha(s, jms);
  2580.                                     final T dIjsdBeta  = ghijCoef.getdIjsdBeta(s, jms);

  2581.                                     final T jjs        = ghijCoef.getJjs(s, jms);
  2582.                                     final T dJjsdh     = ghijCoef.getdJjsdh(s, jms);
  2583.                                     final T dJjsdk     = ghijCoef.getdJjsdk(s, jms);
  2584.                                     final T dJjsdAlpha = ghijCoef.getdJjsdAlpha(s, jms);
  2585.                                     final T dJjsdBeta  = ghijCoef.getdJjsdBeta(s, jms);

  2586.                                     // J<sub>n</sub>
  2587.                                     final T jn = zero.subtract(harmonics.getUnnormalizedCnm(n, 0));

  2588.                                     // K₀<sup>-n-1,s</sup>

  2589.                                     final T kns   = (T) hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getX());
  2590.                                     final T dkns  = (T) hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getX());

  2591.                                     final T coef0 = jn.multiply(d0smj);
  2592.                                     final T coef1 = coef0.multiply(lns);
  2593.                                     final T coef2 = coef1.multiply(kns);

  2594.                                     final T coef3 = coef2.multiply(ijs);
  2595.                                     final T coef4 = coef2.multiply(jjs);

  2596.                                     // Add the term to the coefficients
  2597.                                     cCoef[0][j] = cCoef[0][j].subtract(coef3);
  2598.                                     cCoef[1][j] = cCoef[1][j].subtract(coef3.multiply(n + 1));
  2599.                                     cCoef[2][j] = cCoef[2][j].subtract(coef1.multiply(kns.multiply(dIjsdh).add(ijs.multiply(hXXX).multiply(dkns))));
  2600.                                     cCoef[3][j] = cCoef[3][j].subtract(coef1.multiply(kns.multiply(dIjsdk).add(ijs.multiply(kXXX).multiply(dkns))));
  2601.                                     cCoef[4][j] = cCoef[4][j].subtract(coef2.multiply(dIjsdAlpha));
  2602.                                     cCoef[5][j] = cCoef[5][j].subtract(coef2.multiply(dIjsdBeta));
  2603.                                     cCoef[6][j] = cCoef[6][j].subtract(coef0.multiply(dlns).multiply(kns).multiply(ijs));

  2604.                                     sCoef[0][j] = sCoef[0][j].add(coef4);
  2605.                                     sCoef[1][j] = sCoef[1][j].add(coef4.multiply(n + 1));
  2606.                                     sCoef[2][j] = sCoef[2][j].add(coef1.multiply(kns.multiply(dJjsdh).add(jjs.multiply(hXXX).multiply(dkns))));
  2607.                                     sCoef[3][j] = sCoef[3][j].add(coef1.multiply(kns.multiply(dJjsdk).add(jjs.multiply(kXXX).multiply(dkns))));
  2608.                                     sCoef[4][j] = sCoef[4][j].add(coef2.multiply(dJjsdAlpha));
  2609.                                     sCoef[5][j] = sCoef[5][j].add(coef2.multiply(dJjsdBeta));
  2610.                                     sCoef[6][j] = sCoef[6][j].add(coef0.multiply(dlns).multiply(kns).multiply(jjs));
  2611.                                 }
  2612.                             }
  2613.                         }

  2614.                         //the second double sum is added only if N >= 4 and j between 5 and 2*N-3
  2615.                         if (nMax >= 4 && isBetween(j, 5, 2 * nMax - 3)) {
  2616.                             //compute second double sum where s: j-min(j-1,N) -> (j-3)/2 and n: j-s -> min(j-1,N)
  2617.                             for (int s = j - minjm1on; s <= FastMath.min((j - 3) / 2, sMax); s++) {
  2618.                                 // j - s
  2619.                                 final int jms = j - s;
  2620.                                 // Kronecker symbols (2 - delta(0,s-j)) and (2 - delta(0,j-s))
  2621.                                 final int d0smj = (s == j) ? 1 : 2;

  2622.                                 for (int n = j - s; n <= minjm1on; n++) {
  2623.                                     // if n + (j-s) is odd, then the term is equal to zero due to the factor Vn,s-j
  2624.                                     if ((n + jms) % 2 == 0) {
  2625.                                         // (2 - delta(0,j-s)) * J<sub>n</sub> * K₀<sup>-n-1,s</sup> * L<sub>n</sub><sup>j-s</sup>
  2626.                                         final T lns  = lnsCoef.getLns(n, jms);
  2627.                                         final T dlns = lnsCoef.getdLnsdGamma(n, jms);

  2628.                                         final T ijs        = ghijCoef.getIjs(s, jms);
  2629.                                         final T dIjsdh     = ghijCoef.getdIjsdh(s, jms);
  2630.                                         final T dIjsdk     = ghijCoef.getdIjsdk(s, jms);
  2631.                                         final T dIjsdAlpha = ghijCoef.getdIjsdAlpha(s, jms);
  2632.                                         final T dIjsdBeta  = ghijCoef.getdIjsdBeta(s, jms);

  2633.                                         final T jjs        = ghijCoef.getJjs(s, jms);
  2634.                                         final T dJjsdh     = ghijCoef.getdJjsdh(s, jms);
  2635.                                         final T dJjsdk     = ghijCoef.getdJjsdk(s, jms);
  2636.                                         final T dJjsdAlpha = ghijCoef.getdJjsdAlpha(s, jms);
  2637.                                         final T dJjsdBeta  = ghijCoef.getdJjsdBeta(s, jms);

  2638.                                         // J<sub>n</sub>
  2639.                                         final T jn = zero.subtract(harmonics.getUnnormalizedCnm(n, 0));

  2640.                                         // K₀<sup>-n-1,s</sup>
  2641.                                         final T kns   = (T) hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getX());
  2642.                                         final T dkns  = (T) hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getX());

  2643.                                         final T coef0 = jn.multiply(d0smj);
  2644.                                         final T coef1 = coef0.multiply(lns);
  2645.                                         final T coef2 = coef1.multiply(kns);

  2646.                                         final T coef3 = coef2.multiply(ijs);
  2647.                                         final T coef4 = coef2.multiply(jjs);

  2648.                                         // Add the term to the coefficients
  2649.                                         cCoef[0][j] = cCoef[0][j].subtract(coef3);
  2650.                                         cCoef[1][j] = cCoef[1][j].subtract(coef3.multiply(n + 1));
  2651.                                         cCoef[2][j] = cCoef[2][j].subtract(coef1.multiply(kns.multiply(dIjsdh).add(ijs.multiply(hXXX).multiply(dkns))));
  2652.                                         cCoef[3][j] = cCoef[3][j].subtract(coef1.multiply(kns.multiply(dIjsdk).add(ijs.multiply(kXXX).multiply(dkns))));
  2653.                                         cCoef[4][j] = cCoef[4][j].subtract(coef2.multiply(dIjsdAlpha));
  2654.                                         cCoef[5][j] = cCoef[5][j].subtract(coef2.multiply(dIjsdBeta));
  2655.                                         cCoef[6][j] = cCoef[6][j].subtract(coef0.multiply(dlns).multiply(kns).multiply(ijs));

  2656.                                         sCoef[0][j] = sCoef[0][j].add(coef4);
  2657.                                         sCoef[1][j] = sCoef[1][j].add(coef4.multiply(n + 1));
  2658.                                         sCoef[2][j] = sCoef[2][j].add(coef1.multiply(kns.multiply(dJjsdh).add(jjs.multiply(hXXX).multiply(dkns))));
  2659.                                         sCoef[3][j] = sCoef[3][j].add(coef1.multiply(kns.multiply(dJjsdk).add(jjs.multiply(kXXX).multiply(dkns))));
  2660.                                         sCoef[4][j] = sCoef[4][j].add(coef2.multiply(dJjsdAlpha));
  2661.                                         sCoef[5][j] = sCoef[5][j].add(coef2.multiply(dJjsdBeta));
  2662.                                         sCoef[6][j] = sCoef[6][j].add(coef0.multiply(dlns).multiply(kns).multiply(jjs));
  2663.                                     }
  2664.                                 }
  2665.                             }
  2666.                         }
  2667.                     }
  2668.                 }

  2669.                 cCoef[0][j] = cCoef[0][j].multiply(context.getMuoa().divide(j).negate());
  2670.                 cCoef[1][j] = cCoef[1][j].multiply(context.getMuoa().divide(auxiliaryElements.getSma().multiply(j)));
  2671.                 cCoef[2][j] = cCoef[2][j].multiply(context.getMuoa().divide(j).negate());
  2672.                 cCoef[3][j] = cCoef[3][j].multiply(context.getMuoa().divide(j).negate());
  2673.                 cCoef[4][j] = cCoef[4][j].multiply(context.getMuoa().divide(j).negate());
  2674.                 cCoef[5][j] = cCoef[5][j].multiply(context.getMuoa().divide(j).negate());
  2675.                 cCoef[6][j] = cCoef[6][j].multiply(context.getMuoa().divide(j).negate());

  2676.                 sCoef[0][j] = sCoef[0][j].multiply(context.getMuoa().divide(j).negate());
  2677.                 sCoef[1][j] = sCoef[1][j].multiply(context.getMuoa().divide(auxiliaryElements.getSma().multiply(j)));
  2678.                 sCoef[2][j] = sCoef[2][j].multiply(context.getMuoa().divide(j).negate());
  2679.                 sCoef[3][j] = sCoef[3][j].multiply(context.getMuoa().divide(j).negate());
  2680.                 sCoef[4][j] = sCoef[4][j].multiply(context.getMuoa().divide(j).negate());
  2681.                 sCoef[5][j] = sCoef[5][j].multiply(context.getMuoa().divide(j).negate());
  2682.                 sCoef[6][j] = sCoef[6][j].multiply(context.getMuoa().divide(j).negate());

  2683.             }
  2684.         }

  2685.         /** Check if an index is within the accepted interval.
  2686.          *
  2687.          * @param index the index to check
  2688.          * @param lowerBound the lower bound of the interval
  2689.          * @param upperBound the upper bound of the interval
  2690.          * @return true if the index is between the lower and upper bounds, false otherwise
  2691.          */
  2692.         private boolean isBetween(final int index, final int lowerBound, final int upperBound) {
  2693.             return index >= lowerBound && index <= upperBound;
  2694.         }

  2695.         /**Get the value of C<sup>j</sup>.
  2696.          *
  2697.          * @param j j index
  2698.          * @return C<sup>j</sup>
  2699.          */
  2700.         public T getCj(final int j) {
  2701.             return cCoef[0][j];
  2702.         }

  2703.         /**Get the value of dC<sup>j</sup> / da.
  2704.          *
  2705.          * @param j j index
  2706.          * @return dC<sup>j</sup> / da
  2707.          */
  2708.         public T getdCjdA(final int j) {
  2709.             return cCoef[1][j];
  2710.         }

  2711.         /**Get the value of dC<sup>j</sup> / dh.
  2712.          *
  2713.          * @param j j index
  2714.          * @return dC<sup>j</sup> / dh
  2715.          */
  2716.         public T getdCjdH(final int j) {
  2717.             return cCoef[2][j];
  2718.         }

  2719.         /**Get the value of dC<sup>j</sup> / dk.
  2720.          *
  2721.          * @param j j index
  2722.          * @return dC<sup>j</sup> / dk
  2723.          */
  2724.         public T getdCjdK(final int j) {
  2725.             return cCoef[3][j];
  2726.         }

  2727.         /**Get the value of dC<sup>j</sup> / dα.
  2728.          *
  2729.          * @param j j index
  2730.          * @return dC<sup>j</sup> / dα
  2731.          */
  2732.         public T getdCjdAlpha(final int j) {
  2733.             return cCoef[4][j];
  2734.         }

  2735.         /**Get the value of dC<sup>j</sup> / dβ.
  2736.          *
  2737.          * @param j j index
  2738.          * @return dC<sup>j</sup> / dβ
  2739.          */
  2740.         public T getdCjdBeta(final int j) {
  2741.             return cCoef[5][j];
  2742.         }

  2743.         /**Get the value of dC<sup>j</sup> / dγ.
  2744.          *
  2745.          * @param j j index
  2746.          * @return dC<sup>j</sup> / dγ
  2747.          */
  2748.         public T getdCjdGamma(final int j) {
  2749.             return cCoef[6][j];
  2750.         }

  2751.         /**Get the value of S<sup>j</sup>.
  2752.          *
  2753.          * @param j j index
  2754.          * @return S<sup>j</sup>
  2755.          */
  2756.         public T getSj(final int j) {
  2757.             return sCoef[0][j];
  2758.         }

  2759.         /**Get the value of dS<sup>j</sup> / da.
  2760.          *
  2761.          * @param j j index
  2762.          * @return dS<sup>j</sup> / da
  2763.          */
  2764.         public T getdSjdA(final int j) {
  2765.             return sCoef[1][j];
  2766.         }

  2767.         /**Get the value of dS<sup>j</sup> / dh.
  2768.          *
  2769.          * @param j j index
  2770.          * @return dS<sup>j</sup> / dh
  2771.          */
  2772.         public T getdSjdH(final int j) {
  2773.             return sCoef[2][j];
  2774.         }

  2775.         /**Get the value of dS<sup>j</sup> / dk.
  2776.          *
  2777.          * @param j j index
  2778.          * @return dS<sup>j</sup> / dk
  2779.          */
  2780.         public T getdSjdK(final int j) {
  2781.             return sCoef[3][j];
  2782.         }

  2783.         /**Get the value of dS<sup>j</sup> / dα.
  2784.          *
  2785.          * @param j j index
  2786.          * @return dS<sup>j</sup> / dα
  2787.          */
  2788.         public T getdSjdAlpha(final int j) {
  2789.             return sCoef[4][j];
  2790.         }

  2791.         /**Get the value of dS<sup>j</sup> / dβ.
  2792.          *
  2793.          * @param j j index
  2794.          * @return dS<sup>j</sup> / dβ
  2795.          */
  2796.         public T getdSjdBeta(final int j) {
  2797.             return sCoef[5][j];
  2798.         }

  2799.         /**Get the value of dS<sup>j</sup> /  dγ.
  2800.          *
  2801.          * @param j j index
  2802.          * @return dS<sup>j</sup> /  dγ
  2803.          */
  2804.         public T getdSjdGamma(final int j) {
  2805.             return sCoef[6][j];
  2806.         }
  2807.     }

  2808.     /** Coefficients valid for one time slot. */
  2809.     private static class Slot {

  2810.         /**The coefficients D<sub>i</sub>.
  2811.          * <p>
  2812.          * i corresponds to the equinoctial element, as follows:
  2813.          * - i=0 for a <br/>
  2814.          * - i=1 for k <br/>
  2815.          * - i=2 for h <br/>
  2816.          * - i=3 for q <br/>
  2817.          * - i=4 for p <br/>
  2818.          * - i=5 for λ <br/>
  2819.          * </p>
  2820.          */
  2821.         private final ShortPeriodicsInterpolatedCoefficient di;

  2822.         /** The coefficients C<sub>i</sub><sup>j</sup>.
  2823.          * <p>
  2824.          * The constant term C<sub>i</sub>⁰ is also stored in this variable at index j = 0 <br>
  2825.          * The index order is cij[j][i] <br/>
  2826.          * i corresponds to the equinoctial element, as follows: <br/>
  2827.          * - i=0 for a <br/>
  2828.          * - i=1 for k <br/>
  2829.          * - i=2 for h <br/>
  2830.          * - i=3 for q <br/>
  2831.          * - i=4 for p <br/>
  2832.          * - i=5 for λ <br/>
  2833.          * </p>
  2834.          */
  2835.         private final ShortPeriodicsInterpolatedCoefficient[] cij;

  2836.         /** The coefficients S<sub>i</sub><sup>j</sup>.
  2837.          * <p>
  2838.          * The index order is sij[j][i] <br/>
  2839.          * i corresponds to the equinoctial element, as follows: <br/>
  2840.          * - i=0 for a <br/>
  2841.          * - i=1 for k <br/>
  2842.          * - i=2 for h <br/>
  2843.          * - i=3 for q <br/>
  2844.          * - i=4 for p <br/>
  2845.          * - i=5 for λ <br/>
  2846.          * </p>
  2847.          */
  2848.         private final ShortPeriodicsInterpolatedCoefficient[] sij;

  2849.         /** Simple constructor.
  2850.          *  @param maxFrequencyShortPeriodics maximum value for j index
  2851.          *  @param interpolationPoints number of points used in the interpolation process
  2852.          */
  2853.         Slot(final int maxFrequencyShortPeriodics, final int interpolationPoints) {

  2854.             final int rows = maxFrequencyShortPeriodics + 1;
  2855.             di  = new ShortPeriodicsInterpolatedCoefficient(interpolationPoints);
  2856.             cij = new ShortPeriodicsInterpolatedCoefficient[rows];
  2857.             sij = new ShortPeriodicsInterpolatedCoefficient[rows];

  2858.             //Initialize the arrays
  2859.             for (int j = 0; j <= maxFrequencyShortPeriodics; j++) {
  2860.                 cij[j] = new ShortPeriodicsInterpolatedCoefficient(interpolationPoints);
  2861.                 sij[j] = new ShortPeriodicsInterpolatedCoefficient(interpolationPoints);
  2862.             }

  2863.         }

  2864.     }

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

  2867.         /**The coefficients D<sub>i</sub>.
  2868.          * <p>
  2869.          * i corresponds to the equinoctial element, as follows:
  2870.          * - i=0 for a <br/>
  2871.          * - i=1 for k <br/>
  2872.          * - i=2 for h <br/>
  2873.          * - i=3 for q <br/>
  2874.          * - i=4 for p <br/>
  2875.          * - i=5 for λ <br/>
  2876.          * </p>
  2877.          */
  2878.         private final FieldShortPeriodicsInterpolatedCoefficient<T> di;

  2879.         /** The coefficients C<sub>i</sub><sup>j</sup>.
  2880.          * <p>
  2881.          * The constant term C<sub>i</sub>⁰ is also stored in this variable at index j = 0 <br>
  2882.          * The index order is cij[j][i] <br/>
  2883.          * i corresponds to the equinoctial element, as follows: <br/>
  2884.          * - i=0 for a <br/>
  2885.          * - i=1 for k <br/>
  2886.          * - i=2 for h <br/>
  2887.          * - i=3 for q <br/>
  2888.          * - i=4 for p <br/>
  2889.          * - i=5 for λ <br/>
  2890.          * </p>
  2891.          */
  2892.         private final FieldShortPeriodicsInterpolatedCoefficient<T>[] cij;

  2893.         /** The coefficients S<sub>i</sub><sup>j</sup>.
  2894.          * <p>
  2895.          * The index order is sij[j][i] <br/>
  2896.          * i corresponds to the equinoctial element, as follows: <br/>
  2897.          * - i=0 for a <br/>
  2898.          * - i=1 for k <br/>
  2899.          * - i=2 for h <br/>
  2900.          * - i=3 for q <br/>
  2901.          * - i=4 for p <br/>
  2902.          * - i=5 for λ <br/>
  2903.          * </p>
  2904.          */
  2905.         private final FieldShortPeriodicsInterpolatedCoefficient<T>[] sij;

  2906.         /** Simple constructor.
  2907.          *  @param maxFrequencyShortPeriodics maximum value for j index
  2908.          *  @param interpolationPoints number of points used in the interpolation process
  2909.          */
  2910.         @SuppressWarnings("unchecked")
  2911.         FieldSlot(final int maxFrequencyShortPeriodics, final int interpolationPoints) {

  2912.             final int rows = maxFrequencyShortPeriodics + 1;
  2913.             di  = new FieldShortPeriodicsInterpolatedCoefficient<>(interpolationPoints);
  2914.             cij = (FieldShortPeriodicsInterpolatedCoefficient<T>[]) Array.newInstance(FieldShortPeriodicsInterpolatedCoefficient.class, rows);
  2915.             sij = (FieldShortPeriodicsInterpolatedCoefficient<T>[]) Array.newInstance(FieldShortPeriodicsInterpolatedCoefficient.class, rows);

  2916.             //Initialize the arrays
  2917.             for (int j = 0; j <= maxFrequencyShortPeriodics; j++) {
  2918.                 cij[j] = new FieldShortPeriodicsInterpolatedCoefficient<>(interpolationPoints);
  2919.                 sij[j] = new FieldShortPeriodicsInterpolatedCoefficient<>(interpolationPoints);
  2920.             }

  2921.         }

  2922.     }

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

  2925.         /** The current value of the U function. <br/>
  2926.          * Needed for the short periodic contribution */
  2927.         private double U;

  2928.         /** dU / da. */
  2929.         private double dUda;

  2930.         /** dU / dk. */
  2931.         private double dUdk;

  2932.         /** dU / dh. */
  2933.         private double dUdh;

  2934.         /** dU / dAlpha. */
  2935.         private double dUdAl;

  2936.         /** dU / dBeta. */
  2937.         private double dUdBe;

  2938.         /** dU / dGamma. */
  2939.         private double dUdGa;

  2940.         /** Simple constuctor.
  2941.          *  @param date current date
  2942.          *  @param context container for attributes
  2943.          *  @param auxiliaryElements auxiliary elements related to the current orbit
  2944.          *  @param hansen initialization of hansen objects
  2945.          */
  2946.         UAnddU(final AbsoluteDate date,
  2947.                final DSSTZonalContext context,
  2948.                final AuxiliaryElements auxiliaryElements,
  2949.                final HansenObjects hansen) {

  2950.             final UnnormalizedSphericalHarmonics harmonics = provider.onDate(date);

  2951.             //Reset U
  2952.             U = 0.;

  2953.             // Gs and Hs coefficients
  2954.             final double[][] GsHs = CoefficientsFactory.computeGsHs(auxiliaryElements.getK(), auxiliaryElements.getH(), auxiliaryElements.getAlpha(), auxiliaryElements.getBeta(), maxEccPowMeanElements);
  2955.             // Qns coefficients
  2956.             final double[][] Qns  = CoefficientsFactory.computeQns(auxiliaryElements.getGamma(), maxDegree, maxEccPowMeanElements);

  2957.             final double[] roaPow = new double[maxDegree + 1];
  2958.             roaPow[0] = 1.;
  2959.             for (int i = 1; i <= maxDegree; i++) {
  2960.                 roaPow[i] = context.getRoa() * roaPow[i - 1];
  2961.             }

  2962.             // Potential derivatives
  2963.             dUda  = 0.;
  2964.             dUdk  = 0.;
  2965.             dUdh  = 0.;
  2966.             dUdAl = 0.;
  2967.             dUdBe = 0.;
  2968.             dUdGa = 0.;

  2969.             for (int s = 0; s <= maxEccPowMeanElements; s++) {
  2970.                 //Initialize the Hansen roots
  2971.                 hansen.computeHansenObjectsInitValues(context, s);

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

  2974.                 // Compute Gs partial derivatives from 3.1-(9)
  2975.                 double dGsdh  = 0.;
  2976.                 double dGsdk  = 0.;
  2977.                 double dGsdAl = 0.;
  2978.                 double dGsdBe = 0.;
  2979.                 if (s > 0) {
  2980.                     // First get the G(s-1) and the H(s-1) coefficients
  2981.                     final double sxgsm1 = s * GsHs[0][s - 1];
  2982.                     final double sxhsm1 = s * GsHs[1][s - 1];
  2983.                     // Then compute derivatives
  2984.                     dGsdh  = auxiliaryElements.getBeta()  * sxgsm1 - auxiliaryElements.getAlpha() * sxhsm1;
  2985.                     dGsdk  = auxiliaryElements.getAlpha() * sxgsm1 + auxiliaryElements.getBeta()  * sxhsm1;
  2986.                     dGsdAl = auxiliaryElements.getK() * sxgsm1 - auxiliaryElements.getH() * sxhsm1;
  2987.                     dGsdBe = auxiliaryElements.getH() * sxgsm1 + auxiliaryElements.getK() * sxhsm1;
  2988.                 }

  2989.                 // Kronecker symbol (2 - delta(0,s))
  2990.                 final double d0s = (s == 0) ? 1 : 2;

  2991.                 for (int n = s + 2; n <= maxDegree; n++) {
  2992.                     // (n - s) must be even
  2993.                     if ((n - s) % 2 == 0) {

  2994.                         //Extract data from previous computation :
  2995.                         final double kns   = hansen.getHansenObjects()[s].getValue(-n - 1, context.getX());
  2996.                         final double dkns  = hansen.getHansenObjects()[s].getDerivative(-n - 1, context.getX());

  2997.                         final double vns   = Vns.get(new NSKey(n, s));
  2998.                         final double coef0 = d0s * roaPow[n] * vns * -harmonics.getUnnormalizedCnm(n, 0);
  2999.                         final double coef1 = coef0 * Qns[n][s];
  3000.                         final double coef2 = coef1 * kns;
  3001.                         final double coef3 = coef2 * gs;
  3002.                         // dQns/dGamma = Q(n, s + 1) from Equation 3.1-(8)
  3003.                         final double dqns  = Qns[n][s + 1];

  3004.                         // Compute U
  3005.                         U += coef3;
  3006.                         // Compute dU / da :
  3007.                         dUda += coef3 * (n + 1);
  3008.                         // Compute dU / dEx
  3009.                         dUdk += coef1 * (kns * dGsdk + auxiliaryElements.getK() * context.getXXX() * gs * dkns);
  3010.                         // Compute dU / dEy
  3011.                         dUdh += coef1 * (kns * dGsdh + auxiliaryElements.getH() * context.getXXX() * gs * dkns);
  3012.                         // Compute dU / dAlpha
  3013.                         dUdAl += coef2 * dGsdAl;
  3014.                         // Compute dU / dBeta
  3015.                         dUdBe += coef2 * dGsdBe;
  3016.                         // Compute dU / dGamma
  3017.                         dUdGa += coef0 * kns * dqns * gs;

  3018.                     }
  3019.                 }
  3020.             }

  3021.             // Multiply by -(μ / a)
  3022.             this.U = -context.getMuoa() * U;

  3023.             this.dUda = dUda *  context.getMuoa() / auxiliaryElements.getSma();
  3024.             this.dUdk = dUdk * -context.getMuoa();
  3025.             this.dUdh = dUdh * -context.getMuoa();
  3026.             this.dUdAl = dUdAl * -context.getMuoa();
  3027.             this.dUdBe = dUdBe * -context.getMuoa();
  3028.             this.dUdGa = dUdGa * -context.getMuoa();

  3029.         }

  3030.         /** Return value of U.
  3031.          * @return U
  3032.          */
  3033.         public double getU() {
  3034.             return U;
  3035.         }

  3036.         /** Return value of dU / da.
  3037.          * @return dUda
  3038.          */
  3039.         public double getdUda() {
  3040.             return dUda;
  3041.         }

  3042.         /** Return value of dU / dk.
  3043.          * @return dUdk
  3044.          */
  3045.         public double getdUdk() {
  3046.             return dUdk;
  3047.         }

  3048.         /** Return value of dU / dh.
  3049.          * @return dUdh
  3050.          */
  3051.         public double getdUdh() {
  3052.             return dUdh;
  3053.         }

  3054.         /** Return value of dU / dAlpha.
  3055.          * @return dUdAl
  3056.          */
  3057.         public double getdUdAl() {
  3058.             return dUdAl;
  3059.         }

  3060.         /** Return value of dU / dBeta.
  3061.          * @return dUdBe
  3062.          */
  3063.         public double getdUdBe() {
  3064.             return dUdBe;
  3065.         }

  3066.         /** Return value of dU / dGamma.
  3067.          * @return dUdGa
  3068.          */
  3069.         public double getdUdGa() {
  3070.             return dUdGa;
  3071.         }

  3072.     }

  3073.     /** Compute the derivatives of the gravitational potential U [Eq. 3.1-(6)].
  3074.      *  <p>
  3075.      *  The result is the array
  3076.      *  [dU/da, dU/dk, dU/dh, dU/dα, dU/dβ, dU/dγ]
  3077.      *  </p>
  3078.      */
  3079.     private class FieldUAnddU <T extends CalculusFieldElement<T>> {

  3080.          /** The current value of the U function. <br/>
  3081.           * Needed for the short periodic contribution */
  3082.         private T U;

  3083.          /** dU / da. */
  3084.         private T dUda;

  3085.          /** dU / dk. */
  3086.         private T dUdk;

  3087.          /** dU / dh. */
  3088.         private T dUdh;

  3089.          /** dU / dAlpha. */
  3090.         private T dUdAl;

  3091.          /** dU / dBeta. */
  3092.         private T dUdBe;

  3093.          /** dU / dGamma. */
  3094.         private T dUdGa;

  3095.          /** Simple constuctor.
  3096.           *  @param date current date
  3097.           *  @param context container for attributes
  3098.           *  @param auxiliaryElements auxiliary elements related to the current orbit
  3099.           *  @param hansen initialization of hansen objects
  3100.           */
  3101.         FieldUAnddU(final FieldAbsoluteDate<T> date,
  3102.                     final FieldDSSTZonalContext<T> context,
  3103.                     final FieldAuxiliaryElements<T> auxiliaryElements,
  3104.                     final FieldHansenObjects<T> hansen) {

  3105.             // Zero for initialization
  3106.             final Field<T> field = date.getField();
  3107.             final T zero = field.getZero();

  3108.             //spherical harmonics
  3109.             final UnnormalizedSphericalHarmonics harmonics = provider.onDate(date.toAbsoluteDate());

  3110.             //Reset U
  3111.             U = zero;

  3112.             // Gs and Hs coefficients
  3113.             final T[][] GsHs = CoefficientsFactory.computeGsHs(auxiliaryElements.getK(), auxiliaryElements.getH(), auxiliaryElements.getAlpha(), auxiliaryElements.getBeta(), maxEccPowMeanElements, field);
  3114.             // Qns coefficients
  3115.             final T[][] Qns  = CoefficientsFactory.computeQns(auxiliaryElements.getGamma(), maxDegree, maxEccPowMeanElements);

  3116.             final T[] roaPow = MathArrays.buildArray(field, maxDegree + 1);
  3117.             roaPow[0] = zero.add(1.);
  3118.             for (int i = 1; i <= maxDegree; i++) {
  3119.                 roaPow[i] = roaPow[i - 1].multiply(context.getRoa());
  3120.             }

  3121.             // Potential derivatives
  3122.             dUda  = zero;
  3123.             dUdk  = zero;
  3124.             dUdh  = zero;
  3125.             dUdAl = zero;
  3126.             dUdBe = zero;
  3127.             dUdGa = zero;

  3128.             for (int s = 0; s <= maxEccPowMeanElements; s++) {
  3129.                 //Initialize the Hansen roots
  3130.                 hansen.computeHansenObjectsInitValues(context, s);

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

  3133.                 // Compute Gs partial derivatives from 3.1-(9)
  3134.                 T dGsdh  = zero;
  3135.                 T dGsdk  = zero;
  3136.                 T dGsdAl = zero;
  3137.                 T dGsdBe = zero;
  3138.                 if (s > 0) {
  3139.                     // First get the G(s-1) and the H(s-1) coefficients
  3140.                     final T sxgsm1 = GsHs[0][s - 1].multiply(s);
  3141.                     final T sxhsm1 = GsHs[1][s - 1].multiply(s);
  3142.                     // Then compute derivatives
  3143.                     dGsdh  = sxgsm1.multiply(auxiliaryElements.getBeta()).subtract(sxhsm1.multiply(auxiliaryElements.getAlpha()));
  3144.                     dGsdk  = sxgsm1.multiply(auxiliaryElements.getAlpha()).add(sxhsm1.multiply(auxiliaryElements.getBeta()));
  3145.                     dGsdAl = sxgsm1.multiply(auxiliaryElements.getK()).subtract(sxhsm1.multiply(auxiliaryElements.getH()));
  3146.                     dGsdBe = sxgsm1.multiply(auxiliaryElements.getH()).add(sxhsm1.multiply(auxiliaryElements.getK()));
  3147.                 }

  3148.                 // Kronecker symbol (2 - delta(0,s))
  3149.                 final T d0s = zero.add((s == 0) ? 1 : 2);

  3150.                 for (int n = s + 2; n <= maxDegree; n++) {
  3151.                     // (n - s) must be even
  3152.                     if ((n - s) % 2 == 0) {

  3153.                         //Extract data from previous computation :
  3154.                         final T kns   = (T) hansen.getHansenObjects()[s].getValue(-n - 1, context.getX());
  3155.                         final T dkns  = (T) hansen.getHansenObjects()[s].getDerivative(-n - 1, context.getX());

  3156.                         final double vns   = Vns.get(new NSKey(n, s));
  3157.                         final T coef0 = d0s.multiply(roaPow[n]).multiply(vns).multiply(-harmonics.getUnnormalizedCnm(n, 0));
  3158.                         final T coef1 = coef0.multiply(Qns[n][s]);
  3159.                         final T coef2 = coef1.multiply(kns);
  3160.                         final T coef3 = coef2.multiply(gs);
  3161.                         // dQns/dGamma = Q(n, s + 1) from Equation 3.1-(8)
  3162.                         final T dqns  = Qns[n][s + 1];

  3163.                         // Compute U
  3164.                         U = U.add(coef3);
  3165.                         // Compute dU / da :
  3166.                         dUda  = dUda.add(coef3.multiply(n + 1));
  3167.                         // Compute dU / dEx
  3168.                         dUdk  = dUdk.add(coef1.multiply(dGsdk.multiply(kns).add(auxiliaryElements.getK().multiply(context.getXXX()).multiply(dkns).multiply(gs))));
  3169.                         // Compute dU / dEy
  3170.                         dUdh  = dUdh.add(coef1.multiply(dGsdh.multiply(kns).add(auxiliaryElements.getH().multiply(context.getXXX()).multiply(dkns).multiply(gs))));
  3171.                         // Compute dU / dAlpha
  3172.                         dUdAl = dUdAl.add(coef2.multiply(dGsdAl));
  3173.                         // Compute dU / dBeta
  3174.                         dUdBe = dUdBe.add(coef2.multiply(dGsdBe));
  3175.                         // Compute dU / dGamma
  3176.                         dUdGa = dUdGa.add(coef0.multiply(kns).multiply(dqns).multiply(gs));
  3177.                     }
  3178.                 }
  3179.             }

  3180.             // Multiply by -(μ / a)
  3181.             U = U.multiply(context.getMuoa().negate());

  3182.             dUda  = dUda.multiply(context.getMuoa().divide(auxiliaryElements.getSma()));
  3183.             dUdk  = dUdk.multiply(context.getMuoa()).negate();
  3184.             dUdh  = dUdh.multiply(context.getMuoa()).negate();
  3185.             dUdAl = dUdAl.multiply(context.getMuoa()).negate();
  3186.             dUdBe = dUdBe.multiply(context.getMuoa()).negate();
  3187.             dUdGa = dUdGa.multiply(context.getMuoa()).negate();

  3188.         }

  3189.         /** Return value of U.
  3190.          * @return U
  3191.          */
  3192.         public T getU() {
  3193.             return U;
  3194.         }

  3195.          /** Return value of dU / da.
  3196.           * @return dUda
  3197.           */
  3198.         public T getdUda() {
  3199.             return dUda;
  3200.         }

  3201.          /** Return value of dU / dk.
  3202.           * @return dUdk
  3203.           */
  3204.         public T getdUdk() {
  3205.             return dUdk;
  3206.         }

  3207.          /** Return value of dU / dh.
  3208.           * @return dUdh
  3209.           */
  3210.         public T getdUdh() {
  3211.             return dUdh;
  3212.         }

  3213.          /** Return value of dU / dAlpha.
  3214.           * @return dUdAl
  3215.           */
  3216.         public T getdUdAl() {
  3217.             return dUdAl;
  3218.         }

  3219.          /** Return value of dU / dBeta.
  3220.           * @return dUdBe
  3221.           */
  3222.         public T getdUdBe() {
  3223.             return dUdBe;
  3224.         }

  3225.          /** Return value of dU / dGamma.
  3226.           * @return dUdGa
  3227.           */
  3228.         public T getdUdGa() {
  3229.             return dUdGa;
  3230.         }

  3231.     }

  3232.     /** Computes init values of the Hansen Objects. */
  3233.     private class HansenObjects {

  3234.         /** An array that contains the objects needed to build the Hansen coefficients. <br/>
  3235.          * The index is s*/
  3236.         private HansenZonalLinear[] hansenObjects;

  3237.         /** Simple constructor. */
  3238.         HansenObjects() {
  3239.             this.hansenObjects = new HansenZonalLinear[maxEccPow + 1];
  3240.             for (int s = 0; s <= maxEccPow; s++) {
  3241.                 this.hansenObjects[s] = new HansenZonalLinear(maxDegree, s);
  3242.             }
  3243.         }

  3244.         /** Compute init values for hansen objects.
  3245.          * @param context container for attributes
  3246.          * @param element element of the array to compute the init values
  3247.          */
  3248.         public void computeHansenObjectsInitValues(final DSSTZonalContext context, final int element) {
  3249.             hansenObjects[element].computeInitValues(context.getX());
  3250.         }

  3251.         /** Get the Hansen Objects.
  3252.          * @return hansenObjects
  3253.          */
  3254.         public HansenZonalLinear[] getHansenObjects() {
  3255.             return hansenObjects;
  3256.         }

  3257.     }

  3258.     /** Computes init values of the Hansen Objects.
  3259.      * @param <T> type of the elements
  3260.      */
  3261.     private class FieldHansenObjects<T extends CalculusFieldElement<T>> {

  3262.         /** An array that contains the objects needed to build the Hansen coefficients. <br/>
  3263.          * The index is s*/
  3264.         private FieldHansenZonalLinear<T>[] hansenObjects;

  3265.         /** Simple constructor.
  3266.          * @param field field used by default
  3267.          */
  3268.         @SuppressWarnings("unchecked")
  3269.         FieldHansenObjects(final Field<T> field) {
  3270.             this.hansenObjects = (FieldHansenZonalLinear<T>[]) Array.newInstance(FieldHansenZonalLinear.class, maxEccPow + 1);
  3271.             for (int s = 0; s <= maxEccPow; s++) {
  3272.                 this.hansenObjects[s] = new FieldHansenZonalLinear<>(maxDegree, s, field);
  3273.             }
  3274.         }

  3275.         /** Compute init values for hansen objects.
  3276.          * @param context container for attributes
  3277.          * @param element element of the array to compute the init values
  3278.          */
  3279.         public void computeHansenObjectsInitValues(final FieldDSSTZonalContext<T> context, final int element) {
  3280.             hansenObjects[element].computeInitValues(context.getX());
  3281.         }

  3282.         /** Get the Hansen Objects.
  3283.          * @return hansenObjects
  3284.          */
  3285.         public FieldHansenZonalLinear<T>[] getHansenObjects() {
  3286.             return hansenObjects;
  3287.         }

  3288.     }

  3289. }