DSSTTesseral.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 java.util.TreeMap;

  28. import org.hipparchus.CalculusFieldElement;
  29. import org.hipparchus.Field;
  30. import org.hipparchus.analysis.differentiation.FieldGradient;
  31. import org.hipparchus.exception.LocalizedCoreFormats;
  32. import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
  33. import org.hipparchus.geometry.euclidean.threed.Vector3D;
  34. import org.hipparchus.util.FastMath;
  35. import org.hipparchus.util.FieldSinCos;
  36. import org.hipparchus.util.MathArrays;
  37. import org.hipparchus.util.MathUtils;
  38. import org.hipparchus.util.SinCos;
  39. import org.orekit.attitudes.AttitudeProvider;
  40. import org.orekit.errors.OrekitException;
  41. import org.orekit.errors.OrekitInternalError;
  42. import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider;
  43. import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider.UnnormalizedSphericalHarmonics;
  44. import org.orekit.frames.FieldStaticTransform;
  45. import org.orekit.frames.Frame;
  46. import org.orekit.frames.StaticTransform;
  47. import org.orekit.orbits.FieldOrbit;
  48. import org.orekit.orbits.Orbit;
  49. import org.orekit.propagation.FieldSpacecraftState;
  50. import org.orekit.propagation.PropagationType;
  51. import org.orekit.propagation.SpacecraftState;
  52. import org.orekit.propagation.semianalytical.dsst.utilities.AuxiliaryElements;
  53. import org.orekit.propagation.semianalytical.dsst.utilities.CoefficientsFactory;
  54. import org.orekit.propagation.semianalytical.dsst.utilities.FieldAuxiliaryElements;
  55. import org.orekit.propagation.semianalytical.dsst.utilities.FieldGHmsjPolynomials;
  56. import org.orekit.propagation.semianalytical.dsst.utilities.FieldGammaMnsFunction;
  57. import org.orekit.propagation.semianalytical.dsst.utilities.FieldShortPeriodicsInterpolatedCoefficient;
  58. import org.orekit.propagation.semianalytical.dsst.utilities.GHmsjPolynomials;
  59. import org.orekit.propagation.semianalytical.dsst.utilities.GammaMnsFunction;
  60. import org.orekit.propagation.semianalytical.dsst.utilities.JacobiPolynomials;
  61. import org.orekit.propagation.semianalytical.dsst.utilities.ShortPeriodicsInterpolatedCoefficient;
  62. import org.orekit.propagation.semianalytical.dsst.utilities.hansen.FieldHansenTesseralLinear;
  63. import org.orekit.propagation.semianalytical.dsst.utilities.hansen.HansenTesseralLinear;
  64. import org.orekit.time.AbsoluteDate;
  65. import org.orekit.time.FieldAbsoluteDate;
  66. import org.orekit.utils.FieldTimeSpanMap;
  67. import org.orekit.utils.ParameterDriver;
  68. import org.orekit.utils.TimeSpanMap;

  69. /** Tesseral contribution to the central body gravitational perturbation.
  70.  *  <p>
  71.  *  Only resonant tesserals are considered.
  72.  *  </p>
  73.  *
  74.  *  @author Romain Di Costanzo
  75.  *  @author Pascal Parraud
  76.  *  @author Bryan Cazabonne (field translation)
  77.  */
  78. public class DSSTTesseral implements DSSTForceModel {

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

  81.     /** Identifier for cMm coefficients. */
  82.     public static final String CM_COEFFICIENTS = "cM";

  83.     /** Identifier for sMm coefficients. */
  84.     public static final String SM_COEFFICIENTS = "sM";

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

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

  106.     /** Minimum period for analytically averaged high-order resonant
  107.      *  central body spherical harmonics in seconds.
  108.      */
  109.     private static final double MIN_PERIOD_IN_SECONDS = 864000.;

  110.     /** Minimum period for analytically averaged high-order resonant
  111.      *  central body spherical harmonics in satellite revolutions.
  112.      */
  113.     private static final double MIN_PERIOD_IN_SAT_REV = 10.;

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

  116.     /** Provider for spherical harmonics. */
  117.     private final UnnormalizedSphericalHarmonicsProvider provider;

  118.     /** Central body rotating frame. */
  119.     private final Frame bodyFrame;

  120.     /** Central body rotation rate (rad/s). */
  121.     private final double centralBodyRotationRate;

  122.     /** Central body rotation period (seconds). */
  123.     private final double bodyPeriod;

  124.     /** Maximal degree to consider for harmonics potential. */
  125.     private final int maxDegree;

  126.     /** Maximal degree to consider for short periodics tesseral harmonics potential (without m-daily). */
  127.     private final int maxDegreeTesseralSP;

  128.     /** Maximal degree to consider for short periodics m-daily tesseral harmonics potential . */
  129.     private final int maxDegreeMdailyTesseralSP;

  130.     /** Maximal order to consider for harmonics potential. */
  131.     private final int maxOrder;

  132.     /** Maximal order to consider for short periodics tesseral harmonics potential (without m-daily). */
  133.     private final int maxOrderTesseralSP;

  134.     /** Maximal order to consider for short periodics m-daily tesseral harmonics potential . */
  135.     private final int maxOrderMdailyTesseralSP;

  136.     /** Maximum power of the eccentricity to use in summation over s for
  137.      * short periodic tesseral harmonics (without m-daily). */
  138.     private final int maxEccPowTesseralSP;

  139.     /** Maximum power of the eccentricity to use in summation over s for
  140.      * m-daily tesseral harmonics. */
  141.     private final int maxEccPowMdailyTesseralSP;

  142.     /** Maximum value for j. */
  143.     private final int maxFrequencyShortPeriodics;

  144.     /** Maximum power of the eccentricity to use in summation over s. */
  145.     private int maxEccPow;

  146.     /** Maximum power of the eccentricity to use in Hansen coefficient Kernel expansion. */
  147.     private int maxHansen;

  148.     /** Maximum value between maxOrderMdailyTesseralSP and maxOrderTesseralSP. */
  149.     private int mMax;

  150.     /** List of non resonant orders with j != 0. */
  151.     private final SortedMap<Integer, List<Integer> > nonResOrders;

  152.     /** List of resonant orders. */
  153.     private final List<Integer> resOrders;

  154.     /** Short period terms. */
  155.     private TesseralShortPeriodicCoefficients shortPeriodTerms;

  156.     /** Short period terms. */
  157.     private Map<Field<?>, FieldTesseralShortPeriodicCoefficients<?>> fieldShortPeriodTerms;

  158.     /** Driver for gravitational parameter. */
  159.     private final ParameterDriver gmParameterDriver;

  160.     /** Hansen objects. */
  161.     private HansenObjects hansen;

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

  164.     /** Simple constructor with default reference values.
  165.      * <p>
  166.      * When this constructor is used, maximum allowed values are used
  167.      * for the short periodic coefficients:
  168.      * </p>
  169.      * <ul>
  170.      *    <li> {@link #maxDegreeTesseralSP} is set to {@code provider.getMaxDegree()} </li>
  171.      *    <li> {@link #maxOrderTesseralSP} is set to {@code provider.getMaxOrder()}. </li>
  172.      *    <li> {@link #maxEccPowTesseralSP} is set to {@code min(4, provider.getMaxOrder())} </li>
  173.      *    <li> {@link #maxFrequencyShortPeriodics} is set to {@code min(provider.getMaxDegree() + 4, 12)}.
  174.      *         This parameter should not exceed 12 as higher values will exceed computer capacity </li>
  175.      *    <li> {@link #maxDegreeMdailyTesseralSP} is set to {@code provider.getMaxDegree()} </li>
  176.      *    <li> {@link #maxOrderMdailyTesseralSP} is set to {@code provider.getMaxOrder()} </li>
  177.      *    <li> {@link #maxEccPowMdailyTesseralSP} is set to min(provider.getMaxDegree() - 2, 4).
  178.      *         This parameter should not exceed 4 as higher values will exceed computer capacity </li>
  179.      * </ul>
  180.      * @param centralBodyFrame rotating body frame
  181.      * @param centralBodyRotationRate central body rotation rate (rad/s)
  182.      * @param provider provider for spherical harmonics
  183.      * @since 10.1
  184.      */
  185.     public DSSTTesseral(final Frame centralBodyFrame,
  186.                         final double centralBodyRotationRate,
  187.                         final UnnormalizedSphericalHarmonicsProvider provider) {
  188.         this(centralBodyFrame, centralBodyRotationRate, provider, provider.getMaxDegree(),
  189.              provider.getMaxOrder(), FastMath.min(4, provider.getMaxOrder()),  FastMath.min(12, provider.getMaxDegree() + 4),
  190.              provider.getMaxDegree(), provider.getMaxOrder(), FastMath.min(4, provider.getMaxDegree() - 2));
  191.     }

  192.     /** Simple constructor.
  193.      * @param centralBodyFrame rotating body frame
  194.      * @param centralBodyRotationRate central body rotation rate (rad/s)
  195.      * @param provider provider for spherical harmonics
  196.      * @param maxDegreeTesseralSP maximal degree to consider for short periodics tesseral harmonics potential
  197.      *  (must be between 2 and {@code provider.getMaxDegree()})
  198.      * @param maxOrderTesseralSP maximal order to consider for short periodics tesseral harmonics potential
  199.      *  (must be between 0 and {@code provider.getMaxOrder()})
  200.      * @param maxEccPowTesseralSP maximum power of the eccentricity to use in summation over s for
  201.      * short periodic tesseral harmonics (without m-daily), should typically not exceed 4 as higher
  202.      * values will exceed computer capacity
  203.      * (must be between 0 and {@code provider.getMaxOrder()} though, however if order = 0 the value can be anything
  204.      *  since it won't be used in the code)
  205.      * @param maxFrequencyShortPeriodics maximum frequency in mean longitude for short periodic computations
  206.      * (typically {@code maxDegreeTesseralSP} + {@code maxEccPowTesseralSP and no more than 12})
  207.      * @param maxDegreeMdailyTesseralSP maximal degree to consider for short periodics m-daily tesseral harmonics potential
  208.      *  (must be between 2 and {@code provider.getMaxDegree()})
  209.      * @param maxOrderMdailyTesseralSP maximal order to consider for short periodics m-daily tesseral harmonics potential
  210.      *  (must be between 0 and {@code provider.getMaxOrder()})
  211.      * @param maxEccPowMdailyTesseralSP maximum power of the eccentricity to use in summation over s for
  212.      * m-daily tesseral harmonics, (must be between 0 and {@code maxDegreeMdailyTesseralSP - 2},
  213.      * but should typically not exceed 4 as higher values will exceed computer capacity)
  214.      * @since 7.2
  215.      */
  216.     public DSSTTesseral(final Frame centralBodyFrame,
  217.                         final double centralBodyRotationRate,
  218.                         final UnnormalizedSphericalHarmonicsProvider provider,
  219.                         final int maxDegreeTesseralSP, final int maxOrderTesseralSP,
  220.                         final int maxEccPowTesseralSP, final int maxFrequencyShortPeriodics,
  221.                         final int maxDegreeMdailyTesseralSP, final int maxOrderMdailyTesseralSP,
  222.                         final int maxEccPowMdailyTesseralSP) {

  223.         gmParameterDriver = new ParameterDriver(DSSTNewtonianAttraction.CENTRAL_ATTRACTION_COEFFICIENT,
  224.                                                 provider.getMu(), MU_SCALE,
  225.                                                 0.0, Double.POSITIVE_INFINITY);

  226.         // Central body rotating frame
  227.         this.bodyFrame = centralBodyFrame;

  228.         //Save the rotation rate
  229.         this.centralBodyRotationRate = centralBodyRotationRate;

  230.         // Central body rotation period in seconds
  231.         this.bodyPeriod = MathUtils.TWO_PI / centralBodyRotationRate;

  232.         // Provider for spherical harmonics
  233.         this.provider      = provider;
  234.         this.maxDegree     = provider.getMaxDegree();
  235.         this.maxOrder      = provider.getMaxOrder();

  236.         //set the maximum degree order for short periodics
  237.         checkIndexRange(maxDegreeTesseralSP, 2, maxDegree);
  238.         this.maxDegreeTesseralSP       = maxDegreeTesseralSP;

  239.         checkIndexRange(maxDegreeMdailyTesseralSP, 2, maxDegree);
  240.         this.maxDegreeMdailyTesseralSP = maxDegreeMdailyTesseralSP;

  241.         checkIndexRange(maxOrderTesseralSP, 0, maxOrder);
  242.         this.maxOrderTesseralSP        = maxOrderTesseralSP;

  243.         checkIndexRange(maxOrderMdailyTesseralSP, 0, maxOrder);
  244.         this.maxOrderMdailyTesseralSP  = maxOrderMdailyTesseralSP;

  245.         // set the maximum value for eccentricity power
  246.         if (maxOrder > 0) {
  247.             // Range check can be silently ignored if order = 0
  248.             checkIndexRange(maxEccPowTesseralSP, 0, maxOrder);
  249.         }
  250.         this.maxEccPowTesseralSP       = maxEccPowTesseralSP;

  251.         checkIndexRange(maxEccPowMdailyTesseralSP, 0, maxDegreeMdailyTesseralSP - 2);
  252.         this.maxEccPowMdailyTesseralSP = maxEccPowMdailyTesseralSP;

  253.         // set the maximum value for frequency
  254.         this.maxFrequencyShortPeriodics = maxFrequencyShortPeriodics;

  255.         // Initialize default values
  256.         this.resOrders    = new ArrayList<Integer>();
  257.         this.nonResOrders = new TreeMap<Integer, List <Integer>>();

  258.         // Initialize default values
  259.         this.fieldShortPeriodTerms = new HashMap<>();
  260.         this.fieldHansen           = new HashMap<>();
  261.         this.maxEccPow             = 0;
  262.         this.maxHansen             = 0;

  263.     }

  264.     /** Check an index range.
  265.      * @param index index value
  266.      * @param min minimum value for index
  267.      * @param max maximum value for index
  268.      */
  269.     private void checkIndexRange(final int index, final int min, final int max) {
  270.         if (index < min || index > max) {
  271.             throw new OrekitException(LocalizedCoreFormats.OUT_OF_RANGE_SIMPLE, index, min, max);
  272.         }
  273.     }

  274.     /** {@inheritDoc} */
  275.     @Override
  276.     public List<ShortPeriodTerms> initializeShortPeriodTerms(final AuxiliaryElements auxiliaryElements,
  277.                                              final PropagationType type,
  278.                                              final double[] parameters) {

  279.         // Initializes specific parameters.

  280.         final DSSTTesseralContext context = initializeStep(auxiliaryElements, parameters);

  281.         // Set the highest power of the eccentricity in the analytical power
  282.         // series expansion for the averaged high order resonant central body
  283.         // spherical harmonic perturbation
  284.         maxEccPow = getMaxEccPow(auxiliaryElements.getEcc());

  285.         // Set the maximum power of the eccentricity to use in Hansen coefficient Kernel expansion.
  286.         maxHansen = maxEccPow / 2;

  287.         // The following terms are only used for hansen objects initialization
  288.         final double ratio = context.getRatio();

  289.         // Compute the non resonant tesseral harmonic terms if not set by the user
  290.         getResonantAndNonResonantTerms(type, context.getOrbitPeriod(), ratio);

  291.         hansen = new HansenObjects(ratio, type);

  292.         mMax = FastMath.max(maxOrderTesseralSP, maxOrderMdailyTesseralSP);

  293.         shortPeriodTerms = new TesseralShortPeriodicCoefficients(bodyFrame, maxOrderMdailyTesseralSP,
  294.                                                                  maxDegreeTesseralSP < 0, nonResOrders,
  295.                                                                  mMax, maxFrequencyShortPeriodics, INTERPOLATION_POINTS,
  296.                                                                  new TimeSpanMap<Slot>(new Slot(mMax, maxFrequencyShortPeriodics,
  297.                                                                                                 INTERPOLATION_POINTS)));

  298.         final List<ShortPeriodTerms> list = new ArrayList<ShortPeriodTerms>();
  299.         list.add(shortPeriodTerms);
  300.         return list;

  301.     }

  302.     /** {@inheritDoc} */
  303.     @Override
  304.     public <T extends CalculusFieldElement<T>> List<FieldShortPeriodTerms<T>> initializeShortPeriodTerms(final FieldAuxiliaryElements<T> auxiliaryElements,
  305.                                                                                      final PropagationType type,
  306.                                                                                      final T[] parameters) {

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

  309.         // Initializes specific parameters.
  310.         final FieldDSSTTesseralContext<T> context = initializeStep(auxiliaryElements, parameters);

  311.         // Set the highest power of the eccentricity in the analytical power
  312.         // series expansion for the averaged high order resonant central body
  313.         // spherical harmonic perturbation
  314.         maxEccPow = getMaxEccPow(auxiliaryElements.getEcc().getReal());

  315.         // Set the maximum power of the eccentricity to use in Hansen coefficient Kernel expansion.
  316.         maxHansen = maxEccPow / 2;

  317.         // The following terms are only used for hansen objects initialization
  318.         final T ratio = context.getRatio();

  319.         // Compute the non resonant tesseral harmonic terms if not set by the user
  320.         // Field information is not important here
  321.         getResonantAndNonResonantTerms(type, context.getOrbitPeriod().getReal(), ratio.getReal());

  322.         mMax = FastMath.max(maxOrderTesseralSP, maxOrderMdailyTesseralSP);

  323.         fieldHansen.put(field, new FieldHansenObjects<>(ratio, type));

  324.         final FieldTesseralShortPeriodicCoefficients<T> ftspc =
  325.                         new FieldTesseralShortPeriodicCoefficients<>(bodyFrame, maxOrderMdailyTesseralSP,
  326.                                                                      maxDegreeTesseralSP < 0, nonResOrders,
  327.                                                                      mMax, maxFrequencyShortPeriodics, INTERPOLATION_POINTS,
  328.                                                                      new FieldTimeSpanMap<>(new FieldSlot<>(mMax,
  329.                                                                                                             maxFrequencyShortPeriodics,
  330.                                                                                                             INTERPOLATION_POINTS),
  331.                                                                                             field));

  332.         fieldShortPeriodTerms.put(field, ftspc);
  333.         return Collections.singletonList(ftspc);

  334.     }

  335.     /**
  336.      * Get the maximum power of the eccentricity to use in summation over s.
  337.      * @param e eccentricity
  338.      * @return the maximum power of the eccentricity
  339.      */
  340.     private int getMaxEccPow(final double e) {
  341.         // maxEccPow depends on satellite eccentricity
  342.         if (e <= 0.005) {
  343.             return 3;
  344.         } else if (e <= 0.02) {
  345.             return 4;
  346.         } else if (e <= 0.1) {
  347.             return 7;
  348.         } else if (e <= 0.2) {
  349.             return 10;
  350.         } else if (e <= 0.3) {
  351.             return 12;
  352.         } else if (e <= 0.4) {
  353.             return 15;
  354.         } else {
  355.             return 20;
  356.         }
  357.     }

  358.     /** Performs initialization at each integration step for the current force model.
  359.      *  <p>
  360.      *  This method aims at being called before mean elements rates computation.
  361.      *  </p>
  362.      *  @param auxiliaryElements auxiliary elements related to the current orbit
  363.      *  @param parameters values of the force model parameters (only 1 value for each parameter)
  364.      *  that is to say that the extract parameter method {@link #extractParameters(double[], AbsoluteDate)}
  365.      *  should have be called before or the parameters list given in argument must correspond
  366.      *  to the extraction of parameter for a precise date {@link #getParameters(AbsoluteDate)}.
  367.      *  @return new force model context
  368.      */
  369.     private DSSTTesseralContext initializeStep(final AuxiliaryElements auxiliaryElements, final double[] parameters) {
  370.         return new DSSTTesseralContext(auxiliaryElements, bodyFrame, provider, maxFrequencyShortPeriodics, bodyPeriod, parameters);
  371.     }

  372.     /** Performs initialization at each integration step for the current force model.
  373.      *  <p>
  374.      *  This method aims at being called before mean elements rates computation.
  375.      *  </p>
  376.      *  @param <T> type of the elements
  377.      *  @param auxiliaryElements auxiliary elements related to the current orbit
  378.      *  @param parameters list of each estimated values for each driver of the force model parameters
  379.          *                (each span of each driver)
  380.      *  @return new force model context
  381.      */
  382.     private <T extends CalculusFieldElement<T>> FieldDSSTTesseralContext<T> initializeStep(final FieldAuxiliaryElements<T> auxiliaryElements,
  383.                                                                                        final T[] parameters) {
  384.         return new FieldDSSTTesseralContext<>(auxiliaryElements, bodyFrame, provider, maxFrequencyShortPeriodics, bodyPeriod, parameters);
  385.     }

  386.     /** {@inheritDoc} */
  387.     @Override
  388.     public double[] getMeanElementRate(final SpacecraftState spacecraftState,
  389.                                        final AuxiliaryElements auxiliaryElements, final double[] parameters) {

  390.         // Container for attributes

  391.         final DSSTTesseralContext context = initializeStep(auxiliaryElements, parameters);

  392.         // Access to potential U derivatives
  393.         final UAnddU udu = new UAnddU(spacecraftState.getDate(), context, hansen);

  394.         // Compute the cross derivative operator :
  395.         final double UAlphaGamma   = auxiliaryElements.getAlpha() * udu.getdUdGa() - auxiliaryElements.getGamma() * udu.getdUdAl();
  396.         final double UAlphaBeta    = auxiliaryElements.getAlpha() * udu.getdUdBe() - auxiliaryElements.getBeta()  * udu.getdUdAl();
  397.         final double UBetaGamma    = auxiliaryElements.getBeta() * udu.getdUdGa() - auxiliaryElements.getGamma() * udu.getdUdBe();
  398.         final double Uhk           = auxiliaryElements.getH() * udu.getdUdk()  - auxiliaryElements.getK() * udu.getdUdh();
  399.         final double pUagmIqUbgoAB = (auxiliaryElements.getP() * UAlphaGamma - I * auxiliaryElements.getQ() * UBetaGamma) * context.getOoAB();
  400.         final double UhkmUabmdUdl  = Uhk - UAlphaBeta - udu.getdUdl();

  401.         final double da =  context.getAx2oA() * udu.getdUdl();
  402.         final double dh =  context.getBoA() * udu.getdUdk() + auxiliaryElements.getK() * pUagmIqUbgoAB - auxiliaryElements.getH() * context.getBoABpo() * udu.getdUdl();
  403.         final double dk =  -(context.getBoA() * udu.getdUdh() + auxiliaryElements.getH() * pUagmIqUbgoAB + auxiliaryElements.getK() * context.getBoABpo() * udu.getdUdl());
  404.         final double dp =  context.getCo2AB() * (auxiliaryElements.getP() * UhkmUabmdUdl - UBetaGamma);
  405.         final double dq =  context.getCo2AB() * (auxiliaryElements.getQ() * UhkmUabmdUdl - I * UAlphaGamma);
  406.         final double dM = -context.getAx2oA() * udu.getdUda() + context.getBoABpo() * (auxiliaryElements.getH() * udu.getdUdh() + auxiliaryElements.getK() * udu.getdUdk()) + pUagmIqUbgoAB;

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

  409.     /** {@inheritDoc} */
  410.     @Override
  411.     public <T extends CalculusFieldElement<T>> T[] getMeanElementRate(final FieldSpacecraftState<T> spacecraftState,
  412.                                                                   final FieldAuxiliaryElements<T> auxiliaryElements,
  413.                                                                   final T[] parameters) {

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

  416.         // Container for attributes

  417.         final FieldDSSTTesseralContext<T> context = initializeStep(auxiliaryElements, parameters);

  418.         @SuppressWarnings("unchecked")
  419.         final FieldHansenObjects<T> fho = (FieldHansenObjects<T>) fieldHansen.get(field);
  420.         // Access to potential U derivatives
  421.         final FieldUAnddU<T> udu = new FieldUAnddU<>(spacecraftState.getDate(), context, fho);

  422.         // Compute the cross derivative operator :
  423.         final T UAlphaGamma   = udu.getdUdGa().multiply(auxiliaryElements.getAlpha()).subtract(udu.getdUdAl().multiply(auxiliaryElements.getGamma()));
  424.         final T UAlphaBeta    = udu.getdUdBe().multiply(auxiliaryElements.getAlpha()).subtract(udu.getdUdAl().multiply(auxiliaryElements.getBeta()));
  425.         final T UBetaGamma    = udu.getdUdGa().multiply(auxiliaryElements.getBeta()).subtract(udu.getdUdBe().multiply(auxiliaryElements.getGamma()));
  426.         final T Uhk           = udu.getdUdk().multiply(auxiliaryElements.getH()).subtract(udu.getdUdh().multiply(auxiliaryElements.getK()));
  427.         final T pUagmIqUbgoAB = (UAlphaGamma.multiply(auxiliaryElements.getP()).subtract(UBetaGamma.multiply(auxiliaryElements.getQ()).multiply(I))).multiply(context.getOoAB());
  428.         final T UhkmUabmdUdl  = Uhk.subtract(UAlphaBeta).subtract(udu.getdUdl());

  429.         final T da = udu.getdUdl().multiply(context.getAx2oA());
  430.         final T dh = udu.getdUdk().multiply(context.getBoA()).add(pUagmIqUbgoAB.multiply(auxiliaryElements.getK())).subtract(udu.getdUdl().multiply(auxiliaryElements.getH()).multiply(context.getBoABpo()));
  431.         final T dk = (udu.getdUdh().multiply(context.getBoA()).add(pUagmIqUbgoAB.multiply(auxiliaryElements.getH())).add(udu.getdUdl().multiply(context.getBoABpo()).multiply(auxiliaryElements.getK()))).negate();
  432.         final T dp = context.getCo2AB().multiply(auxiliaryElements.getP().multiply(UhkmUabmdUdl).subtract(UBetaGamma));
  433.         final T dq = context.getCo2AB().multiply(auxiliaryElements.getQ().multiply(UhkmUabmdUdl).subtract(UAlphaGamma.multiply(I)));
  434.         final T dM = pUagmIqUbgoAB.add(udu.getdUda().multiply(context.getAx2oA()).negate()).add((udu.getdUdh().multiply(auxiliaryElements.getH()).add(udu.getdUdk().multiply(auxiliaryElements.getK()))).multiply(context.getBoABpo()));

  435.         final T[] elements = MathArrays.buildArray(field, 6);
  436.         elements[0] = da;
  437.         elements[1] = dk;
  438.         elements[2] = dh;
  439.         elements[3] = dq;
  440.         elements[4] = dp;
  441.         elements[5] = dM;

  442.         return elements;

  443.     }

  444.     /** {@inheritDoc} */
  445.     @Override
  446.     public void updateShortPeriodTerms(final double[] parameters, final SpacecraftState... meanStates) {

  447.         final Slot slot = shortPeriodTerms.createSlot(meanStates);

  448.         for (final SpacecraftState meanState : meanStates) {

  449.             final AuxiliaryElements auxiliaryElements = new AuxiliaryElements(meanState.getOrbit(), I);

  450.             // Extract the proper parameters valid at date from the input array
  451.             final double[] extractedParameters = this.extractParameters(parameters, auxiliaryElements.getDate());
  452.             final DSSTTesseralContext context = initializeStep(auxiliaryElements, extractedParameters);

  453.             // Initialise the Hansen coefficients
  454.             for (int s = -maxDegree; s <= maxDegree; s++) {
  455.                 // coefficients with j == 0 are always needed
  456.                 hansen.computeHansenObjectsInitValues(context, s + maxDegree, 0);
  457.                 if (maxDegreeTesseralSP >= 0) {
  458.                     // initialize other objects only if required
  459.                     for (int j = 1; j <= maxFrequencyShortPeriodics; j++) {
  460.                         hansen.computeHansenObjectsInitValues(context, s + maxDegree, j);
  461.                     }
  462.                 }
  463.             }

  464.             final FourierCjSjCoefficients cjsjFourier = new FourierCjSjCoefficients(maxFrequencyShortPeriodics, mMax);

  465.             // Compute coefficients
  466.             // Compute only if there is at least one non-resonant tesseral
  467.             if (!nonResOrders.isEmpty() || maxDegreeTesseralSP < 0) {
  468.                 // Generate the fourrier coefficients
  469.                 cjsjFourier.generateCoefficients(meanState.getDate(), context, hansen);

  470.                 // the coefficient 3n / 2a
  471.                 final double tnota = 1.5 * context.getMeanMotion() / auxiliaryElements.getSma();

  472.                 // build the mDaily coefficients
  473.                 for (int m = 1; m <= maxOrderMdailyTesseralSP; m++) {
  474.                     // build the coefficients
  475.                     buildCoefficients(cjsjFourier, meanState.getDate(), slot, m, 0, tnota, context);
  476.                 }

  477.                 if (maxDegreeTesseralSP >= 0) {
  478.                     // generate the other coefficients, if required
  479.                     for (final Map.Entry<Integer, List<Integer>> entry : nonResOrders.entrySet()) {

  480.                         for (int j : entry.getValue()) {
  481.                             // build the coefficients
  482.                             buildCoefficients(cjsjFourier, meanState.getDate(), slot, entry.getKey(), j, tnota, context);
  483.                         }
  484.                     }
  485.                 }
  486.             }

  487.         }

  488.     }

  489.     /** {@inheritDoc} */
  490.     @Override
  491.     @SuppressWarnings("unchecked")
  492.     public <T extends CalculusFieldElement<T>> void updateShortPeriodTerms(final T[] parameters,
  493.                                                                        final FieldSpacecraftState<T>... meanStates) {

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

  496.         final FieldTesseralShortPeriodicCoefficients<T> ftspc = (FieldTesseralShortPeriodicCoefficients<T>) fieldShortPeriodTerms.get(field);
  497.         final FieldSlot<T> slot = ftspc.createSlot(meanStates);

  498.         for (final FieldSpacecraftState<T> meanState : meanStates) {

  499.             final FieldAuxiliaryElements<T> auxiliaryElements = new FieldAuxiliaryElements<>(meanState.getOrbit(), I);

  500.             // Extract the proper parameters valid at date from the input array
  501.             final T[] extractedParameters = this.extractParameters(parameters, auxiliaryElements.getDate());
  502.             final FieldDSSTTesseralContext<T> context = initializeStep(auxiliaryElements, extractedParameters);

  503.             final FieldHansenObjects<T> fho = (FieldHansenObjects<T>) fieldHansen.get(field);
  504.             // Initialise the Hansen coefficients
  505.             for (int s = -maxDegree; s <= maxDegree; s++) {
  506.                 // coefficients with j == 0 are always needed
  507.                 fho.computeHansenObjectsInitValues(context, s + maxDegree, 0);
  508.                 if (maxDegreeTesseralSP >= 0) {
  509.                     // initialize other objects only if required
  510.                     for (int j = 1; j <= maxFrequencyShortPeriodics; j++) {
  511.                         fho.computeHansenObjectsInitValues(context, s + maxDegree, j);
  512.                     }
  513.                 }
  514.             }

  515.             final FieldFourierCjSjCoefficients<T> cjsjFourier = new FieldFourierCjSjCoefficients<>(maxFrequencyShortPeriodics, mMax, field);

  516.             // Compute coefficients
  517.             // Compute only if there is at least one non-resonant tesseral
  518.             if (!nonResOrders.isEmpty() || maxDegreeTesseralSP < 0) {
  519.                 // Generate the fourrier coefficients
  520.                 cjsjFourier.generateCoefficients(meanState.getDate(), context, fho, field);

  521.                 // the coefficient 3n / 2a
  522.                 final T tnota = context.getMeanMotion().multiply(1.5).divide(auxiliaryElements.getSma());

  523.                 // build the mDaily coefficients
  524.                 for (int m = 1; m <= maxOrderMdailyTesseralSP; m++) {
  525.                     // build the coefficients
  526.                     buildCoefficients(cjsjFourier, meanState.getDate(), slot, m, 0, tnota, context, field);
  527.                 }

  528.                 if (maxDegreeTesseralSP >= 0) {
  529.                     // generate the other coefficients, if required
  530.                     for (final Map.Entry<Integer, List<Integer>> entry : nonResOrders.entrySet()) {

  531.                         for (int j : entry.getValue()) {
  532.                             // build the coefficients
  533.                             buildCoefficients(cjsjFourier, meanState.getDate(), slot, entry.getKey(), j, tnota, context, field);
  534.                         }
  535.                     }
  536.                 }
  537.             }

  538.         }

  539.     }

  540.     /** {@inheritDoc} */
  541.     public List<ParameterDriver> getParametersDrivers() {
  542.         return Collections.singletonList(gmParameterDriver);
  543.     }

  544.     /** Build a set of coefficients.
  545.      * @param cjsjFourier the fourier coefficients C<sub>i</sub><sup>j</sup> and the S<sub>i</sub><sup>j</sup>
  546.      * @param date the current date
  547.      * @param slot slot to which the coefficients belong
  548.      * @param m m index
  549.      * @param j j index
  550.      * @param tnota 3n/2a
  551.      * @param context container for attributes
  552.      */
  553.     private void buildCoefficients(final FourierCjSjCoefficients cjsjFourier,
  554.                                    final AbsoluteDate date, final Slot slot,
  555.                                    final int m, final int j, final double tnota, final DSSTTesseralContext context) {

  556.         // Create local arrays
  557.         final double[] currentCijm = new double[] {0., 0., 0., 0., 0., 0.};
  558.         final double[] currentSijm = new double[] {0., 0., 0., 0., 0., 0.};

  559.         // compute the term 1 / (jn - mθ<sup>.</sup>)
  560.         final double oojnmt = 1. / (j * context.getMeanMotion() - m * centralBodyRotationRate);

  561.         // initialise the coeficients
  562.         for (int i = 0; i < 6; i++) {
  563.             currentCijm[i] = -cjsjFourier.getSijm(i, j, m);
  564.             currentSijm[i] = cjsjFourier.getCijm(i, j, m);
  565.         }
  566.         // Add the separate part for δ<sub>6i</sub>
  567.         currentCijm[5] += tnota * oojnmt * cjsjFourier.getCijm(0, j, m);
  568.         currentSijm[5] += tnota * oojnmt * cjsjFourier.getSijm(0, j, m);

  569.         //Multiply by 1 / (jn - mθ<sup>.</sup>)
  570.         for (int i = 0; i < 6; i++) {
  571.             currentCijm[i] *= oojnmt;
  572.             currentSijm[i] *= oojnmt;
  573.         }

  574.         // Add the coefficients to the interpolation grid
  575.         slot.cijm[m][j + maxFrequencyShortPeriodics].addGridPoint(date, currentCijm);
  576.         slot.sijm[m][j + maxFrequencyShortPeriodics].addGridPoint(date, currentSijm);

  577.     }

  578.      /** Build a set of coefficients.
  579.      * @param <T> the type of the field elements
  580.      * @param cjsjFourier the fourier coefficients C<sub>i</sub><sup>j</sup> and the S<sub>i</sub><sup>j</sup>
  581.      * @param date the current date
  582.      * @param slot slot to which the coefficients belong
  583.      * @param m m index
  584.      * @param j j index
  585.      * @param tnota 3n/2a
  586.      * @param context container for attributes
  587.      * @param field field used by default
  588.      */
  589.     private <T extends CalculusFieldElement<T>> void buildCoefficients(final FieldFourierCjSjCoefficients<T> cjsjFourier,
  590.                                                                    final FieldAbsoluteDate<T> date,
  591.                                                                    final FieldSlot<T> slot,
  592.                                                                    final int m, final int j, final T tnota,
  593.                                                                    final FieldDSSTTesseralContext<T> context,
  594.                                                                    final Field<T> field) {

  595.         // Zero
  596.         final T zero = field.getZero();

  597.         // Create local arrays
  598.         final T[] currentCijm = MathArrays.buildArray(field, 6);
  599.         final T[] currentSijm = MathArrays.buildArray(field, 6);

  600.         Arrays.fill(currentCijm, zero);
  601.         Arrays.fill(currentSijm, zero);

  602.         // compute the term 1 / (jn - mθ<sup>.</sup>)
  603.         final T oojnmt = (context.getMeanMotion().multiply(j).subtract(m * centralBodyRotationRate)).reciprocal();

  604.         // initialise the coeficients
  605.         for (int i = 0; i < 6; i++) {
  606.             currentCijm[i] = cjsjFourier.getSijm(i, j, m).negate();
  607.             currentSijm[i] = cjsjFourier.getCijm(i, j, m);
  608.         }
  609.         // Add the separate part for δ<sub>6i</sub>
  610.         currentCijm[5] = currentCijm[5].add(tnota.multiply(oojnmt).multiply(cjsjFourier.getCijm(0, j, m)));
  611.         currentSijm[5] = currentSijm[5].add(tnota.multiply(oojnmt).multiply(cjsjFourier.getSijm(0, j, m)));

  612.         //Multiply by 1 / (jn - mθ<sup>.</sup>)
  613.         for (int i = 0; i < 6; i++) {
  614.             currentCijm[i] = currentCijm[i].multiply(oojnmt);
  615.             currentSijm[i] = currentSijm[i].multiply(oojnmt);
  616.         }

  617.         // Add the coefficients to the interpolation grid
  618.         slot.cijm[m][j + maxFrequencyShortPeriodics].addGridPoint(date, currentCijm);
  619.         slot.sijm[m][j + maxFrequencyShortPeriodics].addGridPoint(date, currentSijm);

  620.     }

  621.      /**
  622.       * Get the resonant and non-resonant tesseral terms in the central body spherical harmonic field.
  623.       *
  624.       * @param type type of the elements used during the propagation
  625.       * @param orbitPeriod Keplerian period
  626.       * @param ratio ratio of satellite period to central body rotation period
  627.       */
  628.     private void getResonantAndNonResonantTerms(final PropagationType type, final double orbitPeriod,
  629.                                                 final double ratio) {

  630.         // Compute natural resonant terms
  631.         final double tolerance = 1. / FastMath.max(MIN_PERIOD_IN_SAT_REV,
  632.                                                    MIN_PERIOD_IN_SECONDS / orbitPeriod);

  633.         // Search the resonant orders in the tesseral harmonic field
  634.         resOrders.clear();
  635.         nonResOrders.clear();
  636.         for (int m = 1; m <= maxOrder; m++) {
  637.             final double resonance = ratio * m;
  638.             int jRes = 0;
  639.             final int jComputedRes = (int) FastMath.round(resonance);
  640.             if (jComputedRes > 0 && jComputedRes <= maxFrequencyShortPeriodics && FastMath.abs(resonance - jComputedRes) <= tolerance) {
  641.                 // Store each resonant index and order
  642.                 resOrders.add(m);
  643.                 jRes = jComputedRes;
  644.             }

  645.             if (type == PropagationType.OSCULATING && maxDegreeTesseralSP >= 0 && m <= maxOrderTesseralSP) {
  646.                 //compute non resonant orders in the tesseral harmonic field
  647.                 final List<Integer> listJofM = new ArrayList<Integer>();
  648.                 //for the moment we take only the pairs (j,m) with |j| <= maxDegree + maxEccPow (from |s-j| <= maxEccPow and |s| <= maxDegree)
  649.                 for (int j = -maxFrequencyShortPeriodics; j <= maxFrequencyShortPeriodics; j++) {
  650.                     if (j != 0 && j != jRes) {
  651.                         listJofM.add(j);
  652.                     }
  653.                 }

  654.                 nonResOrders.put(m, listJofM);
  655.             }
  656.         }
  657.     }

  658.     /** Compute the n-SUM for potential derivatives components.
  659.      *  @param date current date
  660.      *  @param j resonant index <i>j</i>
  661.      *  @param m resonant order <i>m</i>
  662.      *  @param s d'Alembert characteristic <i>s</i>
  663.      *  @param maxN maximum possible value for <i>n</i> index
  664.      *  @param roaPow powers of R/a up to degree <i>n</i>
  665.      *  @param ghMSJ G<sup>j</sup><sub>m,s</sub> and H<sup>j</sup><sub>m,s</sub> polynomials
  666.      *  @param gammaMNS &Gamma;<sup>m</sup><sub>n,s</sub>(γ) function
  667.      *  @param context container for attributes
  668.      *  @param hansenObjects initialization of hansen objects
  669.      *  @return Components of U<sub>n</sub> derivatives for fixed j, m, s
  670.      */
  671.     private double[][] computeNSum(final AbsoluteDate date,
  672.                                    final int j, final int m, final int s, final int maxN, final double[] roaPow,
  673.                                    final GHmsjPolynomials ghMSJ, final GammaMnsFunction gammaMNS, final DSSTTesseralContext context,
  674.                                    final HansenObjects hansenObjects) {

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

  677.         //spherical harmonics
  678.         final UnnormalizedSphericalHarmonics harmonics = provider.onDate(date);

  679.         // Potential derivatives components
  680.         double dUdaCos  = 0.;
  681.         double dUdaSin  = 0.;
  682.         double dUdhCos  = 0.;
  683.         double dUdhSin  = 0.;
  684.         double dUdkCos  = 0.;
  685.         double dUdkSin  = 0.;
  686.         double dUdlCos  = 0.;
  687.         double dUdlSin  = 0.;
  688.         double dUdAlCos = 0.;
  689.         double dUdAlSin = 0.;
  690.         double dUdBeCos = 0.;
  691.         double dUdBeSin = 0.;
  692.         double dUdGaCos = 0.;
  693.         double dUdGaSin = 0.;

  694.         // I^m
  695.         @SuppressWarnings("unused")
  696.         final int Im = I > 0 ? 1 : (m % 2 == 0 ? 1 : -1);

  697.         // jacobi v, w, indices from 2.7.1-(15)
  698.         final int v = FastMath.abs(m - s);
  699.         final int w = FastMath.abs(m + s);

  700.         // Initialise lower degree nmin = (Max(2, m, |s|)) for summation over n
  701.         final int nmin = FastMath.max(FastMath.max(2, m), FastMath.abs(s));

  702.         //Get the corresponding Hansen object
  703.         final int sIndex = maxDegree + (j < 0 ? -s : s);
  704.         final int jIndex = FastMath.abs(j);
  705.         final HansenTesseralLinear hans = hansenObjects.getHansenObjects()[sIndex][jIndex];

  706.         // n-SUM from nmin to N
  707.         for (int n = nmin; n <= maxN; n++) {
  708.             // If (n - s) is odd, the contribution is null because of Vmns
  709.             if ((n - s) % 2 == 0) {

  710.                 // Vmns coefficient
  711.                 final double vMNS   = CoefficientsFactory.getVmns(m, n, s);

  712.                 // Inclination function Gamma and derivative
  713.                 final double gaMNS  = gammaMNS.getValue(m, n, s);
  714.                 final double dGaMNS = gammaMNS.getDerivative(m, n, s);

  715.                 // Hansen kernel value and derivative
  716.                 final double kJNS   = hans.getValue(-n - 1, context.getChi());
  717.                 final double dkJNS  = hans.getDerivative(-n - 1, context.getChi());

  718.                 // Gjms, Hjms polynomials and derivatives
  719.                 final double gMSJ   = ghMSJ.getGmsj(m, s, j);
  720.                 final double hMSJ   = ghMSJ.getHmsj(m, s, j);
  721.                 final double dGdh   = ghMSJ.getdGmsdh(m, s, j);
  722.                 final double dGdk   = ghMSJ.getdGmsdk(m, s, j);
  723.                 final double dGdA   = ghMSJ.getdGmsdAlpha(m, s, j);
  724.                 final double dGdB   = ghMSJ.getdGmsdBeta(m, s, j);
  725.                 final double dHdh   = ghMSJ.getdHmsdh(m, s, j);
  726.                 final double dHdk   = ghMSJ.getdHmsdk(m, s, j);
  727.                 final double dHdA   = ghMSJ.getdHmsdAlpha(m, s, j);
  728.                 final double dHdB   = ghMSJ.getdHmsdBeta(m, s, j);

  729.                 // Jacobi l-index from 2.7.1-(15)
  730.                 final int l = FastMath.min(n - m, n - FastMath.abs(s));
  731.                 // Jacobi polynomial and derivative
  732.                 final double[] jacobi = JacobiPolynomials.getValueAndDerivative(l, v, w, auxiliaryElements.getGamma());

  733.                 // Geopotential coefficients
  734.                 final double cnm = harmonics.getUnnormalizedCnm(n, m);
  735.                 final double snm = harmonics.getUnnormalizedSnm(n, m);

  736.                 // Common factors from expansion of equations 3.3-4
  737.                 final double cf_0      = roaPow[n] * Im * vMNS;
  738.                 final double cf_1      = cf_0 * gaMNS * jacobi[0]; //jacobi.getValue();
  739.                 final double cf_2      = cf_1 * kJNS;
  740.                 final double gcPhs     = gMSJ * cnm + hMSJ * snm;
  741.                 final double gsMhc     = gMSJ * snm - hMSJ * cnm;
  742.                 final double dKgcPhsx2 = 2. * dkJNS * gcPhs;
  743.                 final double dKgsMhcx2 = 2. * dkJNS * gsMhc;
  744.                 final double dUdaCoef  = (n + 1) * cf_2;
  745.                 final double dUdlCoef  = j * cf_2;
  746.                 //final double dUdGaCoef = cf_0 * kJNS * (jacobi.getValue() * dGaMNS + gaMNS * jacobi.getGradient()[0]);
  747.                 final double dUdGaCoef = cf_0 * kJNS * (jacobi[0] * dGaMNS + gaMNS * jacobi[1]);

  748.                 // dU / da components
  749.                 dUdaCos  += dUdaCoef * gcPhs;
  750.                 dUdaSin  += dUdaCoef * gsMhc;

  751.                 // dU / dh components
  752.                 dUdhCos  += cf_1 * (kJNS * (cnm * dGdh + snm * dHdh) + auxiliaryElements.getH() * dKgcPhsx2);
  753.                 dUdhSin  += cf_1 * (kJNS * (snm * dGdh - cnm * dHdh) + auxiliaryElements.getH() * dKgsMhcx2);

  754.                 // dU / dk components
  755.                 dUdkCos  += cf_1 * (kJNS * (cnm * dGdk + snm * dHdk) + auxiliaryElements.getK() * dKgcPhsx2);
  756.                 dUdkSin  += cf_1 * (kJNS * (snm * dGdk - cnm * dHdk) + auxiliaryElements.getK() * dKgsMhcx2);

  757.                 // dU / dLambda components
  758.                 dUdlCos  +=  dUdlCoef * gsMhc;
  759.                 dUdlSin  += -dUdlCoef * gcPhs;

  760.                 // dU / alpha components
  761.                 dUdAlCos += cf_2 * (dGdA * cnm + dHdA * snm);
  762.                 dUdAlSin += cf_2 * (dGdA * snm - dHdA * cnm);

  763.                 // dU / dBeta components
  764.                 dUdBeCos += cf_2 * (dGdB * cnm + dHdB * snm);
  765.                 dUdBeSin += cf_2 * (dGdB * snm - dHdB * cnm);

  766.                 // dU / dGamma components
  767.                 dUdGaCos += dUdGaCoef * gcPhs;
  768.                 dUdGaSin += dUdGaCoef * gsMhc;
  769.             }
  770.         }

  771.         return new double[][] { { dUdaCos,  dUdaSin  },
  772.                                 { dUdhCos,  dUdhSin  },
  773.                                 { dUdkCos,  dUdkSin  },
  774.                                 { dUdlCos,  dUdlSin  },
  775.                                 { dUdAlCos, dUdAlSin },
  776.                                 { dUdBeCos, dUdBeSin },
  777.                                 { dUdGaCos, dUdGaSin } };
  778.     }

  779.     /** Compute the n-SUM for potential derivatives components.
  780.      *  @param <T> the type of the field elements
  781.      *  @param date current date
  782.      *  @param j resonant index <i>j</i>
  783.      *  @param m resonant order <i>m</i>
  784.      *  @param s d'Alembert characteristic <i>s</i>
  785.      *  @param maxN maximum possible value for <i>n</i> index
  786.      *  @param roaPow powers of R/a up to degree <i>n</i>
  787.      *  @param ghMSJ G<sup>j</sup><sub>m,s</sub> and H<sup>j</sup><sub>m,s</sub> polynomials
  788.      *  @param gammaMNS &Gamma;<sup>m</sup><sub>n,s</sub>(γ) function
  789.      *  @param context container for attributes
  790.      *  @param hansenObjects initialization of hansen objects
  791.      *  @return Components of U<sub>n</sub> derivatives for fixed j, m, s
  792.      */
  793.     private <T extends CalculusFieldElement<T>> T[][] computeNSum(final FieldAbsoluteDate<T> date,
  794.                                                               final int j, final int m, final int s, final int maxN,
  795.                                                               final T[] roaPow,
  796.                                                               final FieldGHmsjPolynomials<T> ghMSJ,
  797.                                                               final FieldGammaMnsFunction<T> gammaMNS,
  798.                                                               final FieldDSSTTesseralContext<T> context,
  799.                                                               final FieldHansenObjects<T> hansenObjects) {

  800.         // Auxiliary elements related to the current orbit
  801.         final FieldAuxiliaryElements<T> auxiliaryElements = context.getFieldAuxiliaryElements();
  802.         // Zero for initialization
  803.         final Field<T> field = date.getField();
  804.         final T zero = field.getZero();

  805.         //spherical harmonics
  806.         final UnnormalizedSphericalHarmonics harmonics = provider.onDate(date.toAbsoluteDate());

  807.         // Potential derivatives components
  808.         T dUdaCos  = zero;
  809.         T dUdaSin  = zero;
  810.         T dUdhCos  = zero;
  811.         T dUdhSin  = zero;
  812.         T dUdkCos  = zero;
  813.         T dUdkSin  = zero;
  814.         T dUdlCos  = zero;
  815.         T dUdlSin  = zero;
  816.         T dUdAlCos = zero;
  817.         T dUdAlSin = zero;
  818.         T dUdBeCos = zero;
  819.         T dUdBeSin = zero;
  820.         T dUdGaCos = zero;
  821.         T dUdGaSin = zero;

  822.         // I^m
  823.         @SuppressWarnings("unused")
  824.         final int Im = I > 0 ? 1 : (m % 2 == 0 ? 1 : -1);

  825.         // jacobi v, w, indices from 2.7.1-(15)
  826.         final int v = FastMath.abs(m - s);
  827.         final int w = FastMath.abs(m + s);

  828.         // Initialise lower degree nmin = (Max(2, m, |s|)) for summation over n
  829.         final int nmin = FastMath.max(FastMath.max(2, m), FastMath.abs(s));

  830.         //Get the corresponding Hansen object
  831.         final int sIndex = maxDegree + (j < 0 ? -s : s);
  832.         final int jIndex = FastMath.abs(j);
  833.         final FieldHansenTesseralLinear<T> hans = hansenObjects.getHansenObjects()[sIndex][jIndex];

  834.         // n-SUM from nmin to N
  835.         for (int n = nmin; n <= maxN; n++) {
  836.             // If (n - s) is odd, the contribution is null because of Vmns
  837.             if ((n - s) % 2 == 0) {

  838.                 // Vmns coefficient
  839.                 final T vMNS   = zero.add(CoefficientsFactory.getVmns(m, n, s));

  840.                 // Inclination function Gamma and derivative
  841.                 final T gaMNS  = gammaMNS.getValue(m, n, s);
  842.                 final T dGaMNS = gammaMNS.getDerivative(m, n, s);

  843.                 // Hansen kernel value and derivative
  844.                 final T kJNS   = hans.getValue(-n - 1, context.getChi());
  845.                 final T dkJNS  = hans.getDerivative(-n - 1, context.getChi());

  846.                 // Gjms, Hjms polynomials and derivatives
  847.                 final T gMSJ   = ghMSJ.getGmsj(m, s, j);
  848.                 final T hMSJ   = ghMSJ.getHmsj(m, s, j);
  849.                 final T dGdh   = ghMSJ.getdGmsdh(m, s, j);
  850.                 final T dGdk   = ghMSJ.getdGmsdk(m, s, j);
  851.                 final T dGdA   = ghMSJ.getdGmsdAlpha(m, s, j);
  852.                 final T dGdB   = ghMSJ.getdGmsdBeta(m, s, j);
  853.                 final T dHdh   = ghMSJ.getdHmsdh(m, s, j);
  854.                 final T dHdk   = ghMSJ.getdHmsdk(m, s, j);
  855.                 final T dHdA   = ghMSJ.getdHmsdAlpha(m, s, j);
  856.                 final T dHdB   = ghMSJ.getdHmsdBeta(m, s, j);

  857.                 // Jacobi l-index from 2.7.1-(15)
  858.                 final int l = FastMath.min(n - m, n - FastMath.abs(s));
  859.                 // Jacobi polynomial and derivative
  860.                 final FieldGradient<T> jacobi =
  861.                         JacobiPolynomials.getValue(l, v, w, FieldGradient.variable(1, 0, auxiliaryElements.getGamma()));

  862.                 // Geopotential coefficients
  863.                 final T cnm = zero.add(harmonics.getUnnormalizedCnm(n, m));
  864.                 final T snm = zero.add(harmonics.getUnnormalizedSnm(n, m));

  865.                 // Common factors from expansion of equations 3.3-4
  866.                 final T cf_0      = roaPow[n].multiply(Im).multiply(vMNS);
  867.                 final T cf_1      = cf_0.multiply(gaMNS).multiply(jacobi.getValue());
  868.                 final T cf_2      = cf_1.multiply(kJNS);
  869.                 final T gcPhs     = gMSJ.multiply(cnm).add(hMSJ.multiply(snm));
  870.                 final T gsMhc     = gMSJ.multiply(snm).subtract(hMSJ.multiply(cnm));
  871.                 final T dKgcPhsx2 = dkJNS.multiply(gcPhs).multiply(2.);
  872.                 final T dKgsMhcx2 = dkJNS.multiply(gsMhc).multiply(2.);
  873.                 final T dUdaCoef  = cf_2.multiply(n + 1);
  874.                 final T dUdlCoef  = cf_2.multiply(j);
  875.                 final T dUdGaCoef = cf_0.multiply(kJNS).multiply(dGaMNS.multiply(jacobi.getValue()).add(gaMNS.multiply(jacobi.getGradient()[0])));

  876.                 // dU / da components
  877.                 dUdaCos  = dUdaCos.add(dUdaCoef.multiply(gcPhs));
  878.                 dUdaSin  = dUdaSin.add(dUdaCoef.multiply(gsMhc));

  879.                 // dU / dh components
  880.                 dUdhCos  = dUdhCos.add(cf_1.multiply(kJNS.multiply(cnm.multiply(dGdh).add(snm.multiply(dHdh))).add(dKgcPhsx2.multiply(auxiliaryElements.getH()))));
  881.                 dUdhSin  = dUdhSin.add(cf_1.multiply(kJNS.multiply(snm.multiply(dGdh).subtract(cnm.multiply(dHdh))).add(dKgsMhcx2.multiply(auxiliaryElements.getH()))));

  882.                 // dU / dk components
  883.                 dUdkCos  = dUdkCos.add(cf_1.multiply(kJNS.multiply(cnm.multiply(dGdk).add(snm.multiply(dHdk))).add(dKgcPhsx2.multiply(auxiliaryElements.getK()))));
  884.                 dUdkSin  = dUdkSin.add(cf_1.multiply(kJNS.multiply(snm.multiply(dGdk).subtract(cnm.multiply(dHdk))).add(dKgsMhcx2.multiply(auxiliaryElements.getK()))));

  885.                 // dU / dLambda components
  886.                 dUdlCos  = dUdlCos.add(dUdlCoef.multiply(gsMhc));
  887.                 dUdlSin  = dUdlSin.add(dUdlCoef.multiply(gcPhs).negate());

  888.                 // dU / alpha components
  889.                 dUdAlCos = dUdAlCos.add(cf_2.multiply(dGdA.multiply(cnm).add(dHdA.multiply(snm))));
  890.                 dUdAlSin = dUdAlSin.add(cf_2.multiply(dGdA.multiply(snm).subtract(dHdA.multiply(cnm))));

  891.                 // dU / dBeta components
  892.                 dUdBeCos = dUdBeCos.add(cf_2.multiply(dGdB.multiply(cnm).add(dHdB.multiply(snm))));
  893.                 dUdBeSin = dUdBeSin.add(cf_2.multiply(dGdB.multiply(snm).subtract(dHdB.multiply(cnm))));

  894.                 // dU / dGamma components
  895.                 dUdGaCos = dUdGaCos.add(dUdGaCoef.multiply(gcPhs));
  896.                 dUdGaSin = dUdGaSin.add(dUdGaCoef.multiply(gsMhc));
  897.             }
  898.         }

  899.         final T[][] derivatives = MathArrays.buildArray(field, 7, 2);
  900.         derivatives[0][0] = dUdaCos;
  901.         derivatives[0][1] = dUdaSin;
  902.         derivatives[1][0] = dUdhCos;
  903.         derivatives[1][1] = dUdhSin;
  904.         derivatives[2][0] = dUdkCos;
  905.         derivatives[2][1] = dUdkSin;
  906.         derivatives[3][0] = dUdlCos;
  907.         derivatives[3][1] = dUdlSin;
  908.         derivatives[4][0] = dUdAlCos;
  909.         derivatives[4][1] = dUdAlSin;
  910.         derivatives[5][0] = dUdBeCos;
  911.         derivatives[5][1] = dUdBeSin;
  912.         derivatives[6][0] = dUdGaCos;
  913.         derivatives[6][1] = dUdGaSin;

  914.         return derivatives;

  915.     }

  916.     /** {@inheritDoc} */
  917.     @Override
  918.     public void registerAttitudeProvider(final AttitudeProvider attitudeProvider) {
  919.         //nothing is done since this contribution is not sensitive to attitude
  920.     }

  921.     /** Compute the C<sup>j</sup> and the S<sup>j</sup> coefficients.
  922.      *  <p>
  923.      *  Those coefficients are given in Danielson paper by substituting the
  924.      *  disturbing function (2.7.1-16) with m != 0 into (2.2-10)
  925.      *  </p>
  926.      */
  927.     private class FourierCjSjCoefficients {

  928.         /** Absolute limit for j ( -jMax <= j <= jMax ).  */
  929.         private final int jMax;

  930.         /** The C<sub>i</sub><sup>jm</sup> coefficients.
  931.          * <p>
  932.          * The index order is [m][j][i] <br/>
  933.          * The i index corresponds to the C<sub>i</sub><sup>jm</sup> coefficients used to
  934.          * compute the following: <br/>
  935.          * - da/dt <br/>
  936.          * - dk/dt <br/>
  937.          * - dh/dt / dk <br/>
  938.          * - dq/dt <br/>
  939.          * - dp/dt / dα <br/>
  940.          * - dλ/dt / dβ <br/>
  941.          * </p>
  942.          */
  943.         private final double[][][] cCoef;

  944.         /** The S<sub>i</sub><sup>jm</sup> coefficients.
  945.          * <p>
  946.          * The index order is [m][j][i] <br/>
  947.          * The i index corresponds to the C<sub>i</sub><sup>jm</sup> coefficients used to
  948.          * compute the following: <br/>
  949.          * - da/dt <br/>
  950.          * - dk/dt <br/>
  951.          * - dh/dt / dk <br/>
  952.          * - dq/dt <br/>
  953.          * - dp/dt / dα <br/>
  954.          * - dλ/dt / dβ <br/>
  955.          * </p>
  956.          */
  957.         private final double[][][] sCoef;

  958.         /** G<sub>ms</sub><sup>j</sup> and H<sub>ms</sub><sup>j</sup> polynomials. */
  959.         private GHmsjPolynomials ghMSJ;

  960.         /** &Gamma;<sub>ns</sub><sup>m</sup> function. */
  961.         private GammaMnsFunction gammaMNS;

  962.         /** R / a up to power degree. */
  963.         private final double[] roaPow;

  964.         /** Create a set of C<sub>i</sub><sup>jm</sup> and S<sub>i</sub><sup>jm</sup> coefficients.
  965.          *  @param jMax absolute limit for j ( -jMax <= j <= jMax )
  966.          *  @param mMax maximum value for m
  967.          */
  968.         FourierCjSjCoefficients(final int jMax, final int mMax) {
  969.             // initialise fields
  970.             final int rows    = mMax + 1;
  971.             final int columns = 2 * jMax + 1;
  972.             this.jMax         = jMax;
  973.             this.cCoef        = new double[rows][columns][6];
  974.             this.sCoef        = new double[rows][columns][6];
  975.             this.roaPow       = new double[maxDegree + 1];
  976.             roaPow[0] = 1.;
  977.         }

  978.         /**
  979.          * Generate the coefficients.
  980.          * @param date the current date
  981.          * @param context container for attributes
  982.          * @param hansenObjects initialization of hansen objects
  983.          */
  984.         public void generateCoefficients(final AbsoluteDate date, final DSSTTesseralContext context,
  985.                                          final HansenObjects hansenObjects) {

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

  987.             // Compute only if there is at least one non-resonant tesseral
  988.             if (!nonResOrders.isEmpty() || maxDegreeTesseralSP < 0) {
  989.                 // Gmsj and Hmsj polynomials
  990.                 ghMSJ = new GHmsjPolynomials(auxiliaryElements.getK(), auxiliaryElements.getH(), auxiliaryElements.getAlpha(), auxiliaryElements.getBeta(), I);

  991.                 // GAMMAmns function
  992.                 gammaMNS = new GammaMnsFunction(maxDegree, auxiliaryElements.getGamma(), I);

  993.                 final int maxRoaPower = FastMath.max(maxDegreeTesseralSP, maxDegreeMdailyTesseralSP);

  994.                 // R / a up to power degree
  995.                 for (int i = 1; i <= maxRoaPower; i++) {
  996.                     roaPow[i] = context.getRoa() * roaPow[i - 1];
  997.                 }

  998.                 //generate the m-daily coefficients
  999.                 for (int m = 1; m <= maxOrderMdailyTesseralSP; m++) {
  1000.                     buildFourierCoefficients(date, m, 0, maxDegreeMdailyTesseralSP, context, hansenObjects);
  1001.                 }

  1002.                 // generate the other coefficients only if required
  1003.                 if (maxDegreeTesseralSP >= 0) {
  1004.                     for (int m: nonResOrders.keySet()) {
  1005.                         final List<Integer> listJ = nonResOrders.get(m);

  1006.                         for (int j: listJ) {
  1007.                             buildFourierCoefficients(date, m, j, maxDegreeTesseralSP, context, hansenObjects);
  1008.                         }
  1009.                     }
  1010.                 }
  1011.             }
  1012.         }

  1013.         /** Build a set of fourier coefficients for a given m and j.
  1014.          *
  1015.          * @param date the date of the coefficients
  1016.          * @param m m index
  1017.          * @param j j index
  1018.          * @param maxN  maximum value for n index
  1019.          * @param context container for attributes
  1020.          * @param hansenObjects initialization of hansen objects
  1021.          */
  1022.         private void buildFourierCoefficients(final AbsoluteDate date,
  1023.                final int m, final int j, final int maxN, final DSSTTesseralContext context,
  1024.                final HansenObjects hansenObjects) {

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

  1026.             // Potential derivatives components for a given non-resonant pair {j,m}
  1027.             double dRdaCos  = 0.;
  1028.             double dRdaSin  = 0.;
  1029.             double dRdhCos  = 0.;
  1030.             double dRdhSin  = 0.;
  1031.             double dRdkCos  = 0.;
  1032.             double dRdkSin  = 0.;
  1033.             double dRdlCos  = 0.;
  1034.             double dRdlSin  = 0.;
  1035.             double dRdAlCos = 0.;
  1036.             double dRdAlSin = 0.;
  1037.             double dRdBeCos = 0.;
  1038.             double dRdBeSin = 0.;
  1039.             double dRdGaCos = 0.;
  1040.             double dRdGaSin = 0.;

  1041.             // s-SUM from -sMin to sMax
  1042.             final int sMin = j == 0 ? maxEccPowMdailyTesseralSP : maxEccPowTesseralSP;
  1043.             final int sMax = j == 0 ? maxEccPowMdailyTesseralSP : maxEccPowTesseralSP;
  1044.             for (int s = 0; s <= sMax; s++) {

  1045.                 // n-SUM for s positive
  1046.                 final double[][] nSumSpos = computeNSum(date, j, m, s, maxN,
  1047.                                                         roaPow, ghMSJ, gammaMNS, context, hansenObjects);
  1048.                 dRdaCos  += nSumSpos[0][0];
  1049.                 dRdaSin  += nSumSpos[0][1];
  1050.                 dRdhCos  += nSumSpos[1][0];
  1051.                 dRdhSin  += nSumSpos[1][1];
  1052.                 dRdkCos  += nSumSpos[2][0];
  1053.                 dRdkSin  += nSumSpos[2][1];
  1054.                 dRdlCos  += nSumSpos[3][0];
  1055.                 dRdlSin  += nSumSpos[3][1];
  1056.                 dRdAlCos += nSumSpos[4][0];
  1057.                 dRdAlSin += nSumSpos[4][1];
  1058.                 dRdBeCos += nSumSpos[5][0];
  1059.                 dRdBeSin += nSumSpos[5][1];
  1060.                 dRdGaCos += nSumSpos[6][0];
  1061.                 dRdGaSin += nSumSpos[6][1];

  1062.                 // n-SUM for s negative
  1063.                 if (s > 0 && s <= sMin) {
  1064.                     final double[][] nSumSneg = computeNSum(date, j, m, -s, maxN,
  1065.                                                             roaPow, ghMSJ, gammaMNS, context, hansenObjects);
  1066.                     dRdaCos  += nSumSneg[0][0];
  1067.                     dRdaSin  += nSumSneg[0][1];
  1068.                     dRdhCos  += nSumSneg[1][0];
  1069.                     dRdhSin  += nSumSneg[1][1];
  1070.                     dRdkCos  += nSumSneg[2][0];
  1071.                     dRdkSin  += nSumSneg[2][1];
  1072.                     dRdlCos  += nSumSneg[3][0];
  1073.                     dRdlSin  += nSumSneg[3][1];
  1074.                     dRdAlCos += nSumSneg[4][0];
  1075.                     dRdAlSin += nSumSneg[4][1];
  1076.                     dRdBeCos += nSumSneg[5][0];
  1077.                     dRdBeSin += nSumSneg[5][1];
  1078.                     dRdGaCos += nSumSneg[6][0];
  1079.                     dRdGaSin += nSumSneg[6][1];
  1080.                 }
  1081.             }
  1082.             dRdaCos  *= -context.getMoa() / auxiliaryElements.getSma();
  1083.             dRdaSin  *= -context.getMoa() / auxiliaryElements.getSma();
  1084.             dRdhCos  *=  context.getMoa();
  1085.             dRdhSin  *=  context.getMoa();
  1086.             dRdkCos  *=  context.getMoa();
  1087.             dRdkSin  *=  context.getMoa();
  1088.             dRdlCos  *=  context.getMoa();
  1089.             dRdlSin  *=  context.getMoa();
  1090.             dRdAlCos *=  context.getMoa();
  1091.             dRdAlSin *=  context.getMoa();
  1092.             dRdBeCos *=  context.getMoa();
  1093.             dRdBeSin *=  context.getMoa();
  1094.             dRdGaCos *=  context.getMoa();
  1095.             dRdGaSin *=  context.getMoa();

  1096.             // Compute the cross derivative operator :
  1097.             final double RAlphaGammaCos   = auxiliaryElements.getAlpha() * dRdGaCos - auxiliaryElements.getGamma() * dRdAlCos;
  1098.             final double RAlphaGammaSin   = auxiliaryElements.getAlpha() * dRdGaSin - auxiliaryElements.getGamma() * dRdAlSin;
  1099.             final double RAlphaBetaCos    = auxiliaryElements.getAlpha() * dRdBeCos - auxiliaryElements.getBeta()  * dRdAlCos;
  1100.             final double RAlphaBetaSin    = auxiliaryElements.getAlpha() * dRdBeSin - auxiliaryElements.getBeta()  * dRdAlSin;
  1101.             final double RBetaGammaCos    =  auxiliaryElements.getBeta() * dRdGaCos - auxiliaryElements.getGamma() * dRdBeCos;
  1102.             final double RBetaGammaSin    =  auxiliaryElements.getBeta() * dRdGaSin - auxiliaryElements.getGamma() * dRdBeSin;
  1103.             final double RhkCos           =     auxiliaryElements.getH() * dRdkCos  -     auxiliaryElements.getK() * dRdhCos;
  1104.             final double RhkSin           =     auxiliaryElements.getH() * dRdkSin  -     auxiliaryElements.getK() * dRdhSin;
  1105.             final double pRagmIqRbgoABCos = (auxiliaryElements.getP() * RAlphaGammaCos - I * auxiliaryElements.getQ() * RBetaGammaCos) * context.getOoAB();
  1106.             final double pRagmIqRbgoABSin = (auxiliaryElements.getP() * RAlphaGammaSin - I * auxiliaryElements.getQ() * RBetaGammaSin) * context.getOoAB();
  1107.             final double RhkmRabmdRdlCos  =  RhkCos - RAlphaBetaCos - dRdlCos;
  1108.             final double RhkmRabmdRdlSin  =  RhkSin - RAlphaBetaSin - dRdlSin;

  1109.             // da/dt
  1110.             cCoef[m][j + jMax][0] = context.getAx2oA() * dRdlCos;
  1111.             sCoef[m][j + jMax][0] = context.getAx2oA() * dRdlSin;

  1112.             // dk/dt
  1113.             cCoef[m][j + jMax][1] = -(context.getBoA() * dRdhCos + auxiliaryElements.getH() * pRagmIqRbgoABCos + auxiliaryElements.getK() * context.getBoABpo() * dRdlCos);
  1114.             sCoef[m][j + jMax][1] = -(context.getBoA() * dRdhSin + auxiliaryElements.getH() * pRagmIqRbgoABSin + auxiliaryElements.getK() * context.getBoABpo() * dRdlSin);

  1115.             // dh/dt
  1116.             cCoef[m][j + jMax][2] = context.getBoA() * dRdkCos + auxiliaryElements.getK() * pRagmIqRbgoABCos - auxiliaryElements.getH() * context.getBoABpo() * dRdlCos;
  1117.             sCoef[m][j + jMax][2] = context.getBoA() * dRdkSin + auxiliaryElements.getK() * pRagmIqRbgoABSin - auxiliaryElements.getH() * context.getBoABpo() * dRdlSin;

  1118.             // dq/dt
  1119.             cCoef[m][j + jMax][3] = context.getCo2AB() * (auxiliaryElements.getQ() * RhkmRabmdRdlCos - I * RAlphaGammaCos);
  1120.             sCoef[m][j + jMax][3] = context.getCo2AB() * (auxiliaryElements.getQ() * RhkmRabmdRdlSin - I * RAlphaGammaSin);

  1121.             // dp/dt
  1122.             cCoef[m][j + jMax][4] = context.getCo2AB() * (auxiliaryElements.getP() * RhkmRabmdRdlCos - RBetaGammaCos);
  1123.             sCoef[m][j + jMax][4] = context.getCo2AB() * (auxiliaryElements.getP() * RhkmRabmdRdlSin - RBetaGammaSin);

  1124.             // dλ/dt
  1125.             cCoef[m][j + jMax][5] = -context.getAx2oA() * dRdaCos + context.getBoABpo() * (auxiliaryElements.getH() * dRdhCos + auxiliaryElements.getK() * dRdkCos) + pRagmIqRbgoABCos;
  1126.             sCoef[m][j + jMax][5] = -context.getAx2oA() * dRdaSin + context.getBoABpo() * (auxiliaryElements.getH() * dRdhSin + auxiliaryElements.getK() * dRdkSin) + pRagmIqRbgoABSin;
  1127.         }

  1128.         /** Get the coefficient C<sub>i</sub><sup>jm</sup>.
  1129.          * @param i i index - corresponds to the required variation
  1130.          * @param j j index
  1131.          * @param m m index
  1132.          * @return the coefficient C<sub>i</sub><sup>jm</sup>
  1133.          */
  1134.         public double getCijm(final int i, final int j, final int m) {
  1135.             return cCoef[m][j + jMax][i];
  1136.         }

  1137.         /** Get the coefficient S<sub>i</sub><sup>jm</sup>.
  1138.          * @param i i index - corresponds to the required variation
  1139.          * @param j j index
  1140.          * @param m m index
  1141.          * @return the coefficient S<sub>i</sub><sup>jm</sup>
  1142.          */
  1143.         public double getSijm(final int i, final int j, final int m) {
  1144.             return sCoef[m][j + jMax][i];
  1145.         }
  1146.     }

  1147.     /** Compute the C<sup>j</sup> and the S<sup>j</sup> coefficients.
  1148.      *  <p>
  1149.      *  Those coefficients are given in Danielson paper by substituting the
  1150.      *  disturbing function (2.7.1-16) with m != 0 into (2.2-10)
  1151.      *  </p>
  1152.      */
  1153.     private class FieldFourierCjSjCoefficients <T extends CalculusFieldElement<T>> {

  1154.         /** Absolute limit for j ( -jMax <= j <= jMax ).  */
  1155.         private final int jMax;

  1156.         /** The C<sub>i</sub><sup>jm</sup> coefficients.
  1157.          * <p>
  1158.          * The index order is [m][j][i] <br/>
  1159.          * The i index corresponds to the C<sub>i</sub><sup>jm</sup> coefficients used to
  1160.          * compute the following: <br/>
  1161.          * - da/dt <br/>
  1162.          * - dk/dt <br/>
  1163.          * - dh/dt / dk <br/>
  1164.          * - dq/dt <br/>
  1165.          * - dp/dt / dα <br/>
  1166.          * - dλ/dt / dβ <br/>
  1167.          * </p>
  1168.          */
  1169.         private final T[][][] cCoef;

  1170.         /** The S<sub>i</sub><sup>jm</sup> coefficients.
  1171.          * <p>
  1172.          * The index order is [m][j][i] <br/>
  1173.          * The i index corresponds to the C<sub>i</sub><sup>jm</sup> coefficients used to
  1174.          * compute the following: <br/>
  1175.          * - da/dt <br/>
  1176.          * - dk/dt <br/>
  1177.          * - dh/dt / dk <br/>
  1178.          * - dq/dt <br/>
  1179.          * - dp/dt / dα <br/>
  1180.          * - dλ/dt / dβ <br/>
  1181.          * </p>
  1182.          */
  1183.         private final T[][][] sCoef;

  1184.         /** G<sub>ms</sub><sup>j</sup> and H<sub>ms</sub><sup>j</sup> polynomials. */
  1185.         private FieldGHmsjPolynomials<T> ghMSJ;

  1186.         /** &Gamma;<sub>ns</sub><sup>m</sup> function. */
  1187.         private FieldGammaMnsFunction<T> gammaMNS;

  1188.         /** R / a up to power degree. */
  1189.         private final T[] roaPow;

  1190.         /** Create a set of C<sub>i</sub><sup>jm</sup> and S<sub>i</sub><sup>jm</sup> coefficients.
  1191.          *  @param jMax absolute limit for j ( -jMax <= j <= jMax )
  1192.          *  @param mMax maximum value for m
  1193.          *  @param field field used by default
  1194.          */
  1195.         FieldFourierCjSjCoefficients(final int jMax, final int mMax, final Field<T> field) {
  1196.             // initialise fields
  1197.             final T zero = field.getZero();
  1198.             final int rows    = mMax + 1;
  1199.             final int columns = 2 * jMax + 1;
  1200.             this.jMax         = jMax;
  1201.             this.cCoef        = MathArrays.buildArray(field, rows, columns, 6);
  1202.             this.sCoef        = MathArrays.buildArray(field, rows, columns, 6);
  1203.             this.roaPow       = MathArrays.buildArray(field, maxDegree + 1);
  1204.             roaPow[0] = zero.add(1.);
  1205.         }

  1206.         /**
  1207.          * Generate the coefficients.
  1208.          * @param date the current date
  1209.          * @param context container for attributes
  1210.          * @param hansenObjects initialization of hansen objects
  1211.          * @param field field used by default
  1212.          */
  1213.         public void generateCoefficients(final FieldAbsoluteDate<T> date,
  1214.                                          final FieldDSSTTesseralContext<T> context,
  1215.                                          final FieldHansenObjects<T> hansenObjects,
  1216.                                          final Field<T> field) {

  1217.             final FieldAuxiliaryElements<T> auxiliaryElements = context.getFieldAuxiliaryElements();
  1218.             // Compute only if there is at least one non-resonant tesseral
  1219.             if (!nonResOrders.isEmpty() || maxDegreeTesseralSP < 0) {
  1220.                 // Gmsj and Hmsj polynomials
  1221.                 ghMSJ = new FieldGHmsjPolynomials<>(auxiliaryElements.getK(), auxiliaryElements.getH(), auxiliaryElements.getAlpha(), auxiliaryElements.getBeta(), I, field);

  1222.                 // GAMMAmns function
  1223.                 gammaMNS = new FieldGammaMnsFunction<>(maxDegree, auxiliaryElements.getGamma(), I, field);

  1224.                 final int maxRoaPower = FastMath.max(maxDegreeTesseralSP, maxDegreeMdailyTesseralSP);

  1225.                 // R / a up to power degree
  1226.                 for (int i = 1; i <= maxRoaPower; i++) {
  1227.                     roaPow[i] = context.getRoa().multiply(roaPow[i - 1]);
  1228.                 }

  1229.                 //generate the m-daily coefficients
  1230.                 for (int m = 1; m <= maxOrderMdailyTesseralSP; m++) {
  1231.                     buildFourierCoefficients(date, m, 0, maxDegreeMdailyTesseralSP, context, hansenObjects, field);
  1232.                 }

  1233.                 // generate the other coefficients only if required
  1234.                 if (maxDegreeTesseralSP >= 0) {
  1235.                     for (int m: nonResOrders.keySet()) {
  1236.                         final List<Integer> listJ = nonResOrders.get(m);

  1237.                         for (int j: listJ) {
  1238.                             buildFourierCoefficients(date, m, j, maxDegreeTesseralSP, context, hansenObjects, field);
  1239.                         }
  1240.                     }
  1241.                 }
  1242.             }
  1243.         }

  1244.         /** Build a set of fourier coefficients for a given m and j.
  1245.          *
  1246.          * @param date the date of the coefficients
  1247.          * @param m m index
  1248.          * @param j j index
  1249.          * @param maxN  maximum value for n index
  1250.          * @param context container for attributes
  1251.          * @param hansenObjects initialization of hansen objects
  1252.          * @param field field used by default
  1253.          */
  1254.         private void buildFourierCoefficients(final FieldAbsoluteDate<T> date,
  1255.                                               final int m, final int j, final int maxN,
  1256.                                               final FieldDSSTTesseralContext<T> context,
  1257.                                               final FieldHansenObjects<T> hansenObjects,
  1258.                                               final Field<T> field) {

  1259.             // Zero
  1260.             final T zero = field.getZero();
  1261.             // Common parameters
  1262.             final FieldAuxiliaryElements<T> auxiliaryElements = context.getFieldAuxiliaryElements();

  1263.             // Potential derivatives components for a given non-resonant pair {j,m}
  1264.             T dRdaCos  = zero;
  1265.             T dRdaSin  = zero;
  1266.             T dRdhCos  = zero;
  1267.             T dRdhSin  = zero;
  1268.             T dRdkCos  = zero;
  1269.             T dRdkSin  = zero;
  1270.             T dRdlCos  = zero;
  1271.             T dRdlSin  = zero;
  1272.             T dRdAlCos = zero;
  1273.             T dRdAlSin = zero;
  1274.             T dRdBeCos = zero;
  1275.             T dRdBeSin = zero;
  1276.             T dRdGaCos = zero;
  1277.             T dRdGaSin = zero;

  1278.             // s-SUM from -sMin to sMax
  1279.             final int sMin = j == 0 ? maxEccPowMdailyTesseralSP : maxEccPowTesseralSP;
  1280.             final int sMax = j == 0 ? maxEccPowMdailyTesseralSP : maxEccPowTesseralSP;
  1281.             for (int s = 0; s <= sMax; s++) {

  1282.                 // n-SUM for s positive
  1283.                 final T[][] nSumSpos = computeNSum(date, j, m, s, maxN,
  1284.                                                         roaPow, ghMSJ, gammaMNS, context, hansenObjects);
  1285.                 dRdaCos  =  dRdaCos.add(nSumSpos[0][0]);
  1286.                 dRdaSin  =  dRdaSin.add(nSumSpos[0][1]);
  1287.                 dRdhCos  =  dRdhCos.add(nSumSpos[1][0]);
  1288.                 dRdhSin  =  dRdhSin.add(nSumSpos[1][1]);
  1289.                 dRdkCos  =  dRdkCos.add(nSumSpos[2][0]);
  1290.                 dRdkSin  =  dRdkSin.add(nSumSpos[2][1]);
  1291.                 dRdlCos  =  dRdlCos.add(nSumSpos[3][0]);
  1292.                 dRdlSin  =  dRdlSin.add(nSumSpos[3][1]);
  1293.                 dRdAlCos = dRdAlCos.add(nSumSpos[4][0]);
  1294.                 dRdAlSin = dRdAlSin.add(nSumSpos[4][1]);
  1295.                 dRdBeCos = dRdBeCos.add(nSumSpos[5][0]);
  1296.                 dRdBeSin = dRdBeSin.add(nSumSpos[5][1]);
  1297.                 dRdGaCos = dRdGaCos.add(nSumSpos[6][0]);
  1298.                 dRdGaSin = dRdGaSin.add(nSumSpos[6][1]);

  1299.                 // n-SUM for s negative
  1300.                 if (s > 0 && s <= sMin) {
  1301.                     final T[][] nSumSneg = computeNSum(date, j, m, -s, maxN,
  1302.                                                             roaPow, ghMSJ, gammaMNS, context, hansenObjects);
  1303.                     dRdaCos  =  dRdaCos.add(nSumSneg[0][0]);
  1304.                     dRdaSin  =  dRdaSin.add(nSumSneg[0][1]);
  1305.                     dRdhCos  =  dRdhCos.add(nSumSneg[1][0]);
  1306.                     dRdhSin  =  dRdhSin.add(nSumSneg[1][1]);
  1307.                     dRdkCos  =  dRdkCos.add(nSumSneg[2][0]);
  1308.                     dRdkSin  =  dRdkSin.add(nSumSneg[2][1]);
  1309.                     dRdlCos  =  dRdlCos.add(nSumSneg[3][0]);
  1310.                     dRdlSin  =  dRdlSin.add(nSumSneg[3][1]);
  1311.                     dRdAlCos = dRdAlCos.add(nSumSneg[4][0]);
  1312.                     dRdAlSin = dRdAlSin.add(nSumSneg[4][1]);
  1313.                     dRdBeCos = dRdBeCos.add(nSumSneg[5][0]);
  1314.                     dRdBeSin = dRdBeSin.add(nSumSneg[5][1]);
  1315.                     dRdGaCos = dRdGaCos.add(nSumSneg[6][0]);
  1316.                     dRdGaSin = dRdGaSin.add(nSumSneg[6][1]);
  1317.                 }
  1318.             }
  1319.             dRdaCos  =  dRdaCos.multiply(context.getMoa().negate().divide(auxiliaryElements.getSma()));
  1320.             dRdaSin  =  dRdaSin.multiply(context.getMoa().negate().divide(auxiliaryElements.getSma()));
  1321.             dRdhCos  =  dRdhCos.multiply(context.getMoa());
  1322.             dRdhSin  =  dRdhSin.multiply(context.getMoa());
  1323.             dRdkCos  =  dRdkCos.multiply(context.getMoa());
  1324.             dRdkSin  =  dRdkSin.multiply(context.getMoa());
  1325.             dRdlCos  =  dRdlCos.multiply(context.getMoa());
  1326.             dRdlSin  =  dRdlSin.multiply(context.getMoa());
  1327.             dRdAlCos = dRdAlCos.multiply(context.getMoa());
  1328.             dRdAlSin = dRdAlSin.multiply(context.getMoa());
  1329.             dRdBeCos = dRdBeCos.multiply(context.getMoa());
  1330.             dRdBeSin = dRdBeSin.multiply(context.getMoa());
  1331.             dRdGaCos = dRdGaCos.multiply(context.getMoa());
  1332.             dRdGaSin = dRdGaSin.multiply(context.getMoa());

  1333.             // Compute the cross derivative operator :
  1334.             final T RAlphaGammaCos   = auxiliaryElements.getAlpha().multiply(dRdGaCos).subtract(auxiliaryElements.getGamma().multiply(dRdAlCos));
  1335.             final T RAlphaGammaSin   = auxiliaryElements.getAlpha().multiply(dRdGaSin).subtract(auxiliaryElements.getGamma().multiply(dRdAlSin));
  1336.             final T RAlphaBetaCos    = auxiliaryElements.getAlpha().multiply(dRdBeCos).subtract(auxiliaryElements.getBeta().multiply(dRdAlCos));
  1337.             final T RAlphaBetaSin    = auxiliaryElements.getAlpha().multiply(dRdBeSin).subtract(auxiliaryElements.getBeta().multiply(dRdAlSin));
  1338.             final T RBetaGammaCos    =  auxiliaryElements.getBeta().multiply(dRdGaCos).subtract(auxiliaryElements.getGamma().multiply(dRdBeCos));
  1339.             final T RBetaGammaSin    =  auxiliaryElements.getBeta().multiply(dRdGaSin).subtract(auxiliaryElements.getGamma().multiply(dRdBeSin));
  1340.             final T RhkCos           =     auxiliaryElements.getH().multiply(dRdkCos).subtract(auxiliaryElements.getK().multiply(dRdhCos));
  1341.             final T RhkSin           =     auxiliaryElements.getH().multiply(dRdkSin).subtract(auxiliaryElements.getK().multiply(dRdhSin));
  1342.             final T pRagmIqRbgoABCos = (auxiliaryElements.getP().multiply(RAlphaGammaCos).subtract(auxiliaryElements.getQ().multiply(RBetaGammaCos).multiply(I))).multiply(context.getOoAB());
  1343.             final T pRagmIqRbgoABSin = (auxiliaryElements.getP().multiply(RAlphaGammaSin).subtract(auxiliaryElements.getQ().multiply(RBetaGammaSin).multiply(I))).multiply(context.getOoAB());
  1344.             final T RhkmRabmdRdlCos  =  RhkCos.subtract(RAlphaBetaCos).subtract(dRdlCos);
  1345.             final T RhkmRabmdRdlSin  =  RhkSin.subtract(RAlphaBetaSin).subtract(dRdlSin);

  1346.             // da/dt
  1347.             cCoef[m][j + jMax][0] = context.getAx2oA().multiply(dRdlCos);
  1348.             sCoef[m][j + jMax][0] = context.getAx2oA().multiply(dRdlSin);

  1349.             // dk/dt
  1350.             cCoef[m][j + jMax][1] = (context.getBoA().multiply(dRdhCos).add(auxiliaryElements.getH().multiply(pRagmIqRbgoABCos)).add(auxiliaryElements.getK().multiply(context.getBoABpo()).multiply(dRdlCos))).negate();
  1351.             sCoef[m][j + jMax][1] = (context.getBoA().multiply(dRdhSin).add(auxiliaryElements.getH().multiply(pRagmIqRbgoABSin)).add(auxiliaryElements.getK().multiply(context.getBoABpo()).multiply(dRdlSin))).negate();

  1352.             // dh/dt
  1353.             cCoef[m][j + jMax][2] = context.getBoA().multiply(dRdkCos).add(auxiliaryElements.getK().multiply(pRagmIqRbgoABCos)).subtract(auxiliaryElements.getH().multiply(context.getBoABpo()).multiply(dRdlCos));
  1354.             sCoef[m][j + jMax][2] = context.getBoA().multiply(dRdkSin).add(auxiliaryElements.getK().multiply(pRagmIqRbgoABSin)).subtract(auxiliaryElements.getH().multiply(context.getBoABpo()).multiply(dRdlSin));

  1355.             // dq/dt
  1356.             cCoef[m][j + jMax][3] = context.getCo2AB().multiply(auxiliaryElements.getQ().multiply(RhkmRabmdRdlCos).subtract(RAlphaGammaCos.multiply(I)));
  1357.             sCoef[m][j + jMax][3] = context.getCo2AB().multiply(auxiliaryElements.getQ().multiply(RhkmRabmdRdlSin).subtract(RAlphaGammaSin.multiply(I)));

  1358.             // dp/dt
  1359.             cCoef[m][j + jMax][4] = context.getCo2AB().multiply(auxiliaryElements.getP().multiply(RhkmRabmdRdlCos).subtract(RBetaGammaCos));
  1360.             sCoef[m][j + jMax][4] = context.getCo2AB().multiply(auxiliaryElements.getP().multiply(RhkmRabmdRdlSin).subtract(RBetaGammaSin));

  1361.             // dλ/dt
  1362.             cCoef[m][j + jMax][5] = context.getAx2oA().negate().multiply(dRdaCos).add(context.getBoABpo().multiply(auxiliaryElements.getH().multiply(dRdhCos).add(auxiliaryElements.getK().multiply(dRdkCos)))).add(pRagmIqRbgoABCos);
  1363.             sCoef[m][j + jMax][5] = context.getAx2oA().negate().multiply(dRdaSin).add(context.getBoABpo().multiply(auxiliaryElements.getH().multiply(dRdhSin).add(auxiliaryElements.getK().multiply(dRdkSin)))).add(pRagmIqRbgoABSin);
  1364.         }

  1365.         /** Get the coefficient C<sub>i</sub><sup>jm</sup>.
  1366.          * @param i i index - corresponds to the required variation
  1367.          * @param j j index
  1368.          * @param m m index
  1369.          * @return the coefficient C<sub>i</sub><sup>jm</sup>
  1370.          */
  1371.         public T getCijm(final int i, final int j, final int m) {
  1372.             return cCoef[m][j + jMax][i];
  1373.         }

  1374.         /** Get the coefficient S<sub>i</sub><sup>jm</sup>.
  1375.          * @param i i index - corresponds to the required variation
  1376.          * @param j j index
  1377.          * @param m m index
  1378.          * @return the coefficient S<sub>i</sub><sup>jm</sup>
  1379.          */
  1380.         public T getSijm(final int i, final int j, final int m) {
  1381.             return sCoef[m][j + jMax][i];
  1382.         }
  1383.     }

  1384.     /** The C<sup>i</sup><sub>m</sub><sub>t</sub> and S<sup>i</sup><sub>m</sub><sub>t</sub> coefficients used to compute
  1385.      * the short-periodic zonal contribution.
  1386.      *   <p>
  1387.      *  Those coefficients are given by expression 2.5.4-5 from the Danielson paper.
  1388.      *   </p>
  1389.      *
  1390.      * @author Sorin Scortan
  1391.      *
  1392.      */
  1393.     private static class TesseralShortPeriodicCoefficients implements ShortPeriodTerms {

  1394.         /** Retrograde factor I.
  1395.          *  <p>
  1396.          *  DSST model needs equinoctial orbit as internal representation.
  1397.          *  Classical equinoctial elements have discontinuities when inclination
  1398.          *  is close to zero. In this representation, I = +1. <br>
  1399.          *  To avoid this discontinuity, another representation exists and equinoctial
  1400.          *  elements can be expressed in a different way, called "retrograde" orbit.
  1401.          *  This implies I = -1. <br>
  1402.          *  As Orekit doesn't implement the retrograde orbit, I is always set to +1.
  1403.          *  But for the sake of consistency with the theory, the retrograde factor
  1404.          *  has been kept in the formulas.
  1405.          *  </p>
  1406.          */
  1407.         private static final int I = 1;

  1408.         /** Central body rotating frame. */
  1409.         private final Frame bodyFrame;

  1410.         /** Maximal order to consider for short periodics m-daily tesseral harmonics potential. */
  1411.         private final int maxOrderMdailyTesseralSP;

  1412.         /** Flag to take into account only M-dailies harmonic tesserals for short periodic perturbations.  */
  1413.         private final boolean mDailiesOnly;

  1414.         /** List of non resonant orders with j != 0. */
  1415.         private final SortedMap<Integer, List<Integer> > nonResOrders;

  1416.         /** Maximum value for m index. */
  1417.         private final int mMax;

  1418.         /** Maximum value for j index. */
  1419.         private final int jMax;

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

  1422.         /** All coefficients slots. */
  1423.         private final transient TimeSpanMap<Slot> slots;

  1424.         /** Constructor.
  1425.          * @param bodyFrame central body rotating frame
  1426.          * @param maxOrderMdailyTesseralSP maximal order to consider for short periodics m-daily tesseral harmonics potential
  1427.          * @param mDailiesOnly flag to take into account only M-dailies harmonic tesserals for short periodic perturbations
  1428.          * @param nonResOrders lst of non resonant orders with j != 0
  1429.          * @param mMax maximum value for m index
  1430.          * @param jMax maximum value for j index
  1431.          * @param interpolationPoints number of points used in the interpolation process
  1432.          * @param slots all coefficients slots
  1433.          */
  1434.         TesseralShortPeriodicCoefficients(final Frame bodyFrame, final int maxOrderMdailyTesseralSP,
  1435.                                           final boolean mDailiesOnly, final SortedMap<Integer, List<Integer> > nonResOrders,
  1436.                                           final int mMax, final int jMax, final int interpolationPoints,
  1437.                                           final TimeSpanMap<Slot> slots) {
  1438.             this.bodyFrame                = bodyFrame;
  1439.             this.maxOrderMdailyTesseralSP = maxOrderMdailyTesseralSP;
  1440.             this.mDailiesOnly             = mDailiesOnly;
  1441.             this.nonResOrders             = nonResOrders;
  1442.             this.mMax                     = mMax;
  1443.             this.jMax                     = jMax;
  1444.             this.interpolationPoints      = interpolationPoints;
  1445.             this.slots                    = slots;
  1446.         }

  1447.         /** Get the slot valid for some date.
  1448.          * @param meanStates mean states defining the slot
  1449.          * @return slot valid at the specified date
  1450.          */
  1451.         public Slot createSlot(final SpacecraftState... meanStates) {
  1452.             final Slot         slot  = new Slot(mMax, jMax, interpolationPoints);
  1453.             final AbsoluteDate first = meanStates[0].getDate();
  1454.             final AbsoluteDate last  = meanStates[meanStates.length - 1].getDate();
  1455.             final int compare = first.compareTo(last);
  1456.             if (compare < 0) {
  1457.                 slots.addValidAfter(slot, first, false);
  1458.             } else if (compare > 0) {
  1459.                 slots.addValidBefore(slot, first, false);
  1460.             } else {
  1461.                 // single date, valid for all time
  1462.                 slots.addValidAfter(slot, AbsoluteDate.PAST_INFINITY, false);
  1463.             }
  1464.             return slot;
  1465.         }

  1466.         /** {@inheritDoc} */
  1467.         @Override
  1468.         public double[] value(final Orbit meanOrbit) {

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

  1471.             // Initialise the short periodic variations
  1472.             final double[] shortPeriodicVariation = new double[6];

  1473.             // Compute only if there is at least one non-resonant tesseral or
  1474.             // only the m-daily tesseral should be taken into account
  1475.             if (!nonResOrders.isEmpty() || mDailiesOnly) {

  1476.                 //Build an auxiliary object
  1477.                 final AuxiliaryElements auxiliaryElements = new AuxiliaryElements(meanOrbit, I);

  1478.                 // Central body rotation angle from equation 2.7.1-(3)(4).
  1479.                 final StaticTransform t = bodyFrame.getStaticTransformTo(
  1480.                         auxiliaryElements.getFrame(),
  1481.                         auxiliaryElements.getDate());
  1482.                 final Vector3D xB = t.transformVector(Vector3D.PLUS_I);
  1483.                 final Vector3D yB = t.transformVector(Vector3D.PLUS_J);
  1484.                 final Vector3D  f = auxiliaryElements.getVectorF();
  1485.                 final Vector3D  g = auxiliaryElements.getVectorG();
  1486.                 final double currentTheta = FastMath.atan2(-f.dotProduct(yB) + I * g.dotProduct(xB),
  1487.                                                             f.dotProduct(xB) + I * g.dotProduct(yB));

  1488.                 //Add the m-daily contribution
  1489.                 for (int m = 1; m <= maxOrderMdailyTesseralSP; m++) {
  1490.                     // Phase angle
  1491.                     final double jlMmt  = -m * currentTheta;
  1492.                     final SinCos scPhi  = FastMath.sinCos(jlMmt);
  1493.                     final double sinPhi = scPhi.sin();
  1494.                     final double cosPhi = scPhi.cos();

  1495.                     // compute contribution for each element
  1496.                     final double[] c = slot.getCijm(0, m, meanOrbit.getDate());
  1497.                     final double[] s = slot.getSijm(0, m, meanOrbit.getDate());
  1498.                     for (int i = 0; i < 6; i++) {
  1499.                         shortPeriodicVariation[i] += c[i] * cosPhi + s[i] * sinPhi;
  1500.                     }
  1501.                 }

  1502.                 // loop through all non-resonant (j,m) pairs
  1503.                 for (final Map.Entry<Integer, List<Integer>> entry : nonResOrders.entrySet()) {
  1504.                     final int           m     = entry.getKey();
  1505.                     final List<Integer> listJ = entry.getValue();

  1506.                     for (int j : listJ) {
  1507.                         // Phase angle
  1508.                         final double jlMmt  = j * meanOrbit.getLM() - m * currentTheta;
  1509.                         final SinCos scPhi  = FastMath.sinCos(jlMmt);
  1510.                         final double sinPhi = scPhi.sin();
  1511.                         final double cosPhi = scPhi.cos();

  1512.                         // compute contribution for each element
  1513.                         final double[] c = slot.getCijm(j, m, meanOrbit.getDate());
  1514.                         final double[] s = slot.getSijm(j, m, meanOrbit.getDate());
  1515.                         for (int i = 0; i < 6; i++) {
  1516.                             shortPeriodicVariation[i] += c[i] * cosPhi + s[i] * sinPhi;
  1517.                         }

  1518.                     }
  1519.                 }
  1520.             }

  1521.             return shortPeriodicVariation;

  1522.         }

  1523.         /** {@inheritDoc} */
  1524.         @Override
  1525.         public String getCoefficientsKeyPrefix() {
  1526.             return DSSTTesseral.SHORT_PERIOD_PREFIX;
  1527.         }

  1528.         /** {@inheritDoc}
  1529.          * <p>
  1530.          * For tesseral terms contributions,there are maxOrderMdailyTesseralSP
  1531.          * m-daily cMm coefficients, maxOrderMdailyTesseralSP m-daily sMm
  1532.          * coefficients, nbNonResonant cjm coefficients and nbNonResonant
  1533.          * sjm coefficients, where maxOrderMdailyTesseralSP and nbNonResonant both
  1534.          * depend on the orbit. The j index is the integer multiplier for the true
  1535.          * longitude argument and the m index is the integer multiplier for m-dailies.
  1536.          * </p>
  1537.          */
  1538.         @Override
  1539.         public Map<String, double[]> getCoefficients(final AbsoluteDate date, final Set<String> selected) {

  1540.             // select the coefficients slot
  1541.             final Slot slot = slots.get(date);

  1542.             if (!nonResOrders.isEmpty() || mDailiesOnly) {
  1543.                 final Map<String, double[]> coefficients =
  1544.                                 new HashMap<String, double[]>(12 * maxOrderMdailyTesseralSP +
  1545.                                                               12 * nonResOrders.size());

  1546.                 for (int m = 1; m <= maxOrderMdailyTesseralSP; m++) {
  1547.                     storeIfSelected(coefficients, selected, slot.getCijm(0, m, date), DSSTTesseral.CM_COEFFICIENTS, m);
  1548.                     storeIfSelected(coefficients, selected, slot.getSijm(0, m, date), DSSTTesseral.SM_COEFFICIENTS, m);
  1549.                 }

  1550.                 for (final Map.Entry<Integer, List<Integer>> entry : nonResOrders.entrySet()) {
  1551.                     final int           m     = entry.getKey();
  1552.                     final List<Integer> listJ = entry.getValue();

  1553.                     for (int j : listJ) {
  1554.                         for (int i = 0; i < 6; ++i) {
  1555.                             storeIfSelected(coefficients, selected, slot.getCijm(j, m, date), "c", j, m);
  1556.                             storeIfSelected(coefficients, selected, slot.getSijm(j, m, date), "s", j, m);
  1557.                         }
  1558.                     }
  1559.                 }

  1560.                 return coefficients;

  1561.             } else {
  1562.                 return Collections.emptyMap();
  1563.             }

  1564.         }

  1565.         /** Put a coefficient in a map if selected.
  1566.          * @param map map to populate
  1567.          * @param selected set of coefficients that should be put in the map
  1568.          * (empty set means all coefficients are selected)
  1569.          * @param value coefficient value
  1570.          * @param id coefficient identifier
  1571.          * @param indices list of coefficient indices
  1572.          */
  1573.         private void storeIfSelected(final Map<String, double[]> map, final Set<String> selected,
  1574.                                      final double[] value, final String id, final int... indices) {
  1575.             final StringBuilder keyBuilder = new StringBuilder(getCoefficientsKeyPrefix());
  1576.             keyBuilder.append(id);
  1577.             for (int index : indices) {
  1578.                 keyBuilder.append('[').append(index).append(']');
  1579.             }
  1580.             final String key = keyBuilder.toString();
  1581.             if (selected.isEmpty() || selected.contains(key)) {
  1582.                 map.put(key, value);
  1583.             }
  1584.         }

  1585.     }

  1586.     /** The C<sup>i</sup><sub>m</sub><sub>t</sub> and S<sup>i</sup><sub>m</sub><sub>t</sub> coefficients used to compute
  1587.      * the short-periodic zonal contribution.
  1588.      *   <p>
  1589.      *  Those coefficients are given by expression 2.5.4-5 from the Danielson paper.
  1590.      *   </p>
  1591.      *
  1592.      * @author Sorin Scortan
  1593.      *
  1594.      */
  1595.     private static class FieldTesseralShortPeriodicCoefficients <T extends CalculusFieldElement<T>> implements FieldShortPeriodTerms<T> {

  1596.         /** Retrograde factor I.
  1597.          *  <p>
  1598.          *  DSST model needs equinoctial orbit as internal representation.
  1599.          *  Classical equinoctial elements have discontinuities when inclination
  1600.          *  is close to zero. In this representation, I = +1. <br>
  1601.          *  To avoid this discontinuity, another representation exists and equinoctial
  1602.          *  elements can be expressed in a different way, called "retrograde" orbit.
  1603.          *  This implies I = -1. <br>
  1604.          *  As Orekit doesn't implement the retrograde orbit, I is always set to +1.
  1605.          *  But for the sake of consistency with the theory, the retrograde factor
  1606.          *  has been kept in the formulas.
  1607.          *  </p>
  1608.          */
  1609.         private static final int I = 1;

  1610.         /** Central body rotating frame. */
  1611.         private final Frame bodyFrame;

  1612.         /** Maximal order to consider for short periodics m-daily tesseral harmonics potential. */
  1613.         private final int maxOrderMdailyTesseralSP;

  1614.         /** Flag to take into account only M-dailies harmonic tesserals for short periodic perturbations.  */
  1615.         private final boolean mDailiesOnly;

  1616.         /** List of non resonant orders with j != 0. */
  1617.         private final SortedMap<Integer, List<Integer> > nonResOrders;

  1618.         /** Maximum value for m index. */
  1619.         private final int mMax;

  1620.         /** Maximum value for j index. */
  1621.         private final int jMax;

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

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

  1626.         /** Constructor.
  1627.          * @param bodyFrame central body rotating frame
  1628.          * @param maxOrderMdailyTesseralSP maximal order to consider for short periodics m-daily tesseral harmonics potential
  1629.          * @param mDailiesOnly flag to take into account only M-dailies harmonic tesserals for short periodic perturbations
  1630.          * @param nonResOrders lst of non resonant orders with j != 0
  1631.          * @param mMax maximum value for m index
  1632.          * @param jMax maximum value for j index
  1633.          * @param interpolationPoints number of points used in the interpolation process
  1634.          * @param slots all coefficients slots
  1635.          */
  1636.         FieldTesseralShortPeriodicCoefficients(final Frame bodyFrame, final int maxOrderMdailyTesseralSP,
  1637.                                                final boolean mDailiesOnly, final SortedMap<Integer, List<Integer> > nonResOrders,
  1638.                                                final int mMax, final int jMax, final int interpolationPoints,
  1639.                                                final FieldTimeSpanMap<FieldSlot<T>, T> slots) {
  1640.             this.bodyFrame                = bodyFrame;
  1641.             this.maxOrderMdailyTesseralSP = maxOrderMdailyTesseralSP;
  1642.             this.mDailiesOnly             = mDailiesOnly;
  1643.             this.nonResOrders             = nonResOrders;
  1644.             this.mMax                     = mMax;
  1645.             this.jMax                     = jMax;
  1646.             this.interpolationPoints      = interpolationPoints;
  1647.             this.slots                    = slots;
  1648.         }

  1649.         /** Get the slot valid for some date.
  1650.          * @param meanStates mean states defining the slot
  1651.          * @return slot valid at the specified date
  1652.          */
  1653.         @SuppressWarnings("unchecked")
  1654.         public FieldSlot<T> createSlot(final FieldSpacecraftState<T>... meanStates) {
  1655.             final FieldSlot<T>         slot  = new FieldSlot<>(mMax, jMax, interpolationPoints);
  1656.             final FieldAbsoluteDate<T> first = meanStates[0].getDate();
  1657.             final FieldAbsoluteDate<T> last  = meanStates[meanStates.length - 1].getDate();
  1658.             if (first.compareTo(last) <= 0) {
  1659.                 slots.addValidAfter(slot, first);
  1660.             } else {
  1661.                 slots.addValidBefore(slot, first);
  1662.             }
  1663.             return slot;
  1664.         }

  1665.         /** {@inheritDoc} */
  1666.         @Override
  1667.         public T[] value(final FieldOrbit<T> meanOrbit) {

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

  1670.             // Initialise the short periodic variations
  1671.             final T[] shortPeriodicVariation = MathArrays.buildArray(meanOrbit.getDate().getField(), 6);

  1672.             // Compute only if there is at least one non-resonant tesseral or
  1673.             // only the m-daily tesseral should be taken into account
  1674.             if (!nonResOrders.isEmpty() || mDailiesOnly) {

  1675.                 //Build an auxiliary object
  1676.                 final FieldAuxiliaryElements<T> auxiliaryElements = new FieldAuxiliaryElements<>(meanOrbit, I);

  1677.                 // Central body rotation angle from equation 2.7.1-(3)(4).
  1678.                 final FieldStaticTransform<T> t = bodyFrame.getStaticTransformTo(auxiliaryElements.getFrame(), auxiliaryElements.getDate());
  1679.                 final FieldVector3D<T> xB = t.transformVector(Vector3D.PLUS_I);
  1680.                 final FieldVector3D<T> yB = t.transformVector(Vector3D.PLUS_J);
  1681.                 final FieldVector3D<T>  f = auxiliaryElements.getVectorF();
  1682.                 final FieldVector3D<T>  g = auxiliaryElements.getVectorG();
  1683.                 final T currentTheta = FastMath.atan2(f.dotProduct(yB).negate().add(g.dotProduct(xB).multiply(I)),
  1684.                                                       f.dotProduct(xB).add(g.dotProduct(yB).multiply(I)));

  1685.                 //Add the m-daily contribution
  1686.                 for (int m = 1; m <= maxOrderMdailyTesseralSP; m++) {
  1687.                     // Phase angle
  1688.                     final T jlMmt              = currentTheta.multiply(-m);
  1689.                     final FieldSinCos<T> scPhi = FastMath.sinCos(jlMmt);
  1690.                     final T sinPhi             = scPhi.sin();
  1691.                     final T cosPhi             = scPhi.cos();

  1692.                     // compute contribution for each element
  1693.                     final T[] c = slot.getCijm(0, m, meanOrbit.getDate());
  1694.                     final T[] s = slot.getSijm(0, m, meanOrbit.getDate());
  1695.                     for (int i = 0; i < 6; i++) {
  1696.                         shortPeriodicVariation[i] = shortPeriodicVariation[i].add(c[i].multiply(cosPhi).add(s[i].multiply(sinPhi)));
  1697.                     }
  1698.                 }

  1699.                 // loop through all non-resonant (j,m) pairs
  1700.                 for (final Map.Entry<Integer, List<Integer>> entry : nonResOrders.entrySet()) {
  1701.                     final int           m     = entry.getKey();
  1702.                     final List<Integer> listJ = entry.getValue();

  1703.                     for (int j : listJ) {
  1704.                         // Phase angle
  1705.                         final T jlMmt              = meanOrbit.getLM().multiply(j).subtract(currentTheta.multiply(m));
  1706.                         final FieldSinCos<T> scPhi = FastMath.sinCos(jlMmt);
  1707.                         final T sinPhi             = scPhi.sin();
  1708.                         final T cosPhi             = scPhi.cos();

  1709.                         // compute contribution for each element
  1710.                         final T[] c = slot.getCijm(j, m, meanOrbit.getDate());
  1711.                         final T[] s = slot.getSijm(j, m, meanOrbit.getDate());
  1712.                         for (int i = 0; i < 6; i++) {
  1713.                             shortPeriodicVariation[i] = shortPeriodicVariation[i].add(c[i].multiply(cosPhi).add(s[i].multiply(sinPhi)));
  1714.                         }

  1715.                     }
  1716.                 }
  1717.             }

  1718.             return shortPeriodicVariation;

  1719.         }

  1720.         /** {@inheritDoc} */
  1721.         @Override
  1722.         public String getCoefficientsKeyPrefix() {
  1723.             return DSSTTesseral.SHORT_PERIOD_PREFIX;
  1724.         }

  1725.         /** {@inheritDoc}
  1726.          * <p>
  1727.          * For tesseral terms contributions,there are maxOrderMdailyTesseralSP
  1728.          * m-daily cMm coefficients, maxOrderMdailyTesseralSP m-daily sMm
  1729.          * coefficients, nbNonResonant cjm coefficients and nbNonResonant
  1730.          * sjm coefficients, where maxOrderMdailyTesseralSP and nbNonResonant both
  1731.          * depend on the orbit. The j index is the integer multiplier for the true
  1732.          * longitude argument and the m index is the integer multiplier for m-dailies.
  1733.          * </p>
  1734.          */
  1735.         @Override
  1736.         public Map<String, T[]> getCoefficients(final FieldAbsoluteDate<T> date, final Set<String> selected) {

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

  1739.             if (!nonResOrders.isEmpty() || mDailiesOnly) {
  1740.                 final Map<String, T[]> coefficients =
  1741.                                 new HashMap<String, T[]>(12 * maxOrderMdailyTesseralSP +
  1742.                                                          12 * nonResOrders.size());

  1743.                 for (int m = 1; m <= maxOrderMdailyTesseralSP; m++) {
  1744.                     storeIfSelected(coefficients, selected, slot.getCijm(0, m, date), DSSTTesseral.CM_COEFFICIENTS, m);
  1745.                     storeIfSelected(coefficients, selected, slot.getSijm(0, m, date), DSSTTesseral.SM_COEFFICIENTS, m);
  1746.                 }

  1747.                 for (final Map.Entry<Integer, List<Integer>> entry : nonResOrders.entrySet()) {
  1748.                     final int           m     = entry.getKey();
  1749.                     final List<Integer> listJ = entry.getValue();

  1750.                     for (int j : listJ) {
  1751.                         for (int i = 0; i < 6; ++i) {
  1752.                             storeIfSelected(coefficients, selected, slot.getCijm(j, m, date), "c", j, m);
  1753.                             storeIfSelected(coefficients, selected, slot.getSijm(j, m, date), "s", j, m);
  1754.                         }
  1755.                     }
  1756.                 }

  1757.                 return coefficients;

  1758.             } else {
  1759.                 return Collections.emptyMap();
  1760.             }

  1761.         }

  1762.         /** Put a coefficient in a map if selected.
  1763.          * @param map map to populate
  1764.          * @param selected set of coefficients that should be put in the map
  1765.          * (empty set means all coefficients are selected)
  1766.          * @param value coefficient value
  1767.          * @param id coefficient identifier
  1768.          * @param indices list of coefficient indices
  1769.          */
  1770.         private void storeIfSelected(final Map<String, T[]> map, final Set<String> selected,
  1771.                                      final T[] value, final String id, final int... indices) {
  1772.             final StringBuilder keyBuilder = new StringBuilder(getCoefficientsKeyPrefix());
  1773.             keyBuilder.append(id);
  1774.             for (int index : indices) {
  1775.                 keyBuilder.append('[').append(index).append(']');
  1776.             }
  1777.             final String key = keyBuilder.toString();
  1778.             if (selected.isEmpty() || selected.contains(key)) {
  1779.                 map.put(key, value);
  1780.             }
  1781.         }
  1782.     }

  1783.     /** Coefficients valid for one time slot. */
  1784.     private static class Slot {

  1785.         /** The coefficients C<sub>i</sub><sup>j</sup><sup>m</sup>.
  1786.          * <p>
  1787.          * The index order is cijm[m][j][i] <br/>
  1788.          * i corresponds to the equinoctial element, as follows: <br/>
  1789.          * - i=0 for a <br/>
  1790.          * - i=1 for k <br/>
  1791.          * - i=2 for h <br/>
  1792.          * - i=3 for q <br/>
  1793.          * - i=4 for p <br/>
  1794.          * - i=5 for λ <br/>
  1795.          * </p>
  1796.          */
  1797.         private final ShortPeriodicsInterpolatedCoefficient[][] cijm;

  1798.         /** The coefficients S<sub>i</sub><sup>j</sup><sup>m</sup>.
  1799.          * <p>
  1800.          * The index order is sijm[m][j][i] <br/>
  1801.          * i corresponds to the equinoctial element, as follows: <br/>
  1802.          * - i=0 for a <br/>
  1803.          * - i=1 for k <br/>
  1804.          * - i=2 for h <br/>
  1805.          * - i=3 for q <br/>
  1806.          * - i=4 for p <br/>
  1807.          * - i=5 for λ <br/>
  1808.          * </p>
  1809.          */
  1810.         private final ShortPeriodicsInterpolatedCoefficient[][] sijm;

  1811.         /** Simple constructor.
  1812.          *  @param mMax maximum value for m index
  1813.          *  @param jMax maximum value for j index
  1814.          *  @param interpolationPoints number of points used in the interpolation process
  1815.          */
  1816.         Slot(final int mMax, final int jMax, final int interpolationPoints) {

  1817.             final int rows    = mMax + 1;
  1818.             final int columns = 2 * jMax + 1;
  1819.             cijm = new ShortPeriodicsInterpolatedCoefficient[rows][columns];
  1820.             sijm = new ShortPeriodicsInterpolatedCoefficient[rows][columns];
  1821.             for (int m = 1; m <= mMax; m++) {
  1822.                 for (int j = -jMax; j <= jMax; j++) {
  1823.                     cijm[m][j + jMax] = new ShortPeriodicsInterpolatedCoefficient(interpolationPoints);
  1824.                     sijm[m][j + jMax] = new ShortPeriodicsInterpolatedCoefficient(interpolationPoints);
  1825.                 }
  1826.             }

  1827.         }

  1828.         /** Get C<sub>i</sub><sup>j</sup><sup>m</sup>.
  1829.          *
  1830.          * @param j j index
  1831.          * @param m m index
  1832.          * @param date the date
  1833.          * @return C<sub>i</sub><sup>j</sup><sup>m</sup>
  1834.          */
  1835.         double[] getCijm(final int j, final int m, final AbsoluteDate date) {
  1836.             final int jMax = (cijm[m].length - 1) / 2;
  1837.             return cijm[m][j + jMax].value(date);
  1838.         }

  1839.         /** Get S<sub>i</sub><sup>j</sup><sup>m</sup>.
  1840.          *
  1841.          * @param j j index
  1842.          * @param m m index
  1843.          * @param date the date
  1844.          * @return S<sub>i</sub><sup>j</sup><sup>m</sup>
  1845.          */
  1846.         double[] getSijm(final int j, final int m, final AbsoluteDate date) {
  1847.             final int jMax = (cijm[m].length - 1) / 2;
  1848.             return sijm[m][j + jMax].value(date);
  1849.         }

  1850.     }

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

  1853.         /** The coefficients C<sub>i</sub><sup>j</sup><sup>m</sup>.
  1854.          * <p>
  1855.          * The index order is cijm[m][j][i] <br/>
  1856.          * i corresponds to the equinoctial element, as follows: <br/>
  1857.          * - i=0 for a <br/>
  1858.          * - i=1 for k <br/>
  1859.          * - i=2 for h <br/>
  1860.          * - i=3 for q <br/>
  1861.          * - i=4 for p <br/>
  1862.          * - i=5 for λ <br/>
  1863.          * </p>
  1864.          */
  1865.         private final FieldShortPeriodicsInterpolatedCoefficient<T>[][] cijm;

  1866.         /** The coefficients S<sub>i</sub><sup>j</sup><sup>m</sup>.
  1867.          * <p>
  1868.          * The index order is sijm[m][j][i] <br/>
  1869.          * i corresponds to the equinoctial element, as follows: <br/>
  1870.          * - i=0 for a <br/>
  1871.          * - i=1 for k <br/>
  1872.          * - i=2 for h <br/>
  1873.          * - i=3 for q <br/>
  1874.          * - i=4 for p <br/>
  1875.          * - i=5 for λ <br/>
  1876.          * </p>
  1877.          */
  1878.         private final FieldShortPeriodicsInterpolatedCoefficient<T>[][] sijm;

  1879.         /** Simple constructor.
  1880.          *  @param mMax maximum value for m index
  1881.          *  @param jMax maximum value for j index
  1882.          *  @param interpolationPoints number of points used in the interpolation process
  1883.          */
  1884.         @SuppressWarnings("unchecked")
  1885.         FieldSlot(final int mMax, final int jMax, final int interpolationPoints) {

  1886.             final int rows    = mMax + 1;
  1887.             final int columns = 2 * jMax + 1;
  1888.             cijm = (FieldShortPeriodicsInterpolatedCoefficient<T>[][]) Array.newInstance(FieldShortPeriodicsInterpolatedCoefficient.class, rows, columns);
  1889.             sijm = (FieldShortPeriodicsInterpolatedCoefficient<T>[][]) Array.newInstance(FieldShortPeriodicsInterpolatedCoefficient.class, rows, columns);
  1890.             for (int m = 1; m <= mMax; m++) {
  1891.                 for (int j = -jMax; j <= jMax; j++) {
  1892.                     cijm[m][j + jMax] = new FieldShortPeriodicsInterpolatedCoefficient<>(interpolationPoints);
  1893.                     sijm[m][j + jMax] = new FieldShortPeriodicsInterpolatedCoefficient<>(interpolationPoints);
  1894.                 }
  1895.             }

  1896.         }

  1897.         /** Get C<sub>i</sub><sup>j</sup><sup>m</sup>.
  1898.          *
  1899.          * @param j j index
  1900.          * @param m m index
  1901.          * @param date the date
  1902.          * @return C<sub>i</sub><sup>j</sup><sup>m</sup>
  1903.          */
  1904.         T[] getCijm(final int j, final int m, final FieldAbsoluteDate<T> date) {
  1905.             final int jMax = (cijm[m].length - 1) / 2;
  1906.             return cijm[m][j + jMax].value(date);
  1907.         }

  1908.         /** Get S<sub>i</sub><sup>j</sup><sup>m</sup>.
  1909.          *
  1910.          * @param j j index
  1911.          * @param m m index
  1912.          * @param date the date
  1913.          * @return S<sub>i</sub><sup>j</sup><sup>m</sup>
  1914.          */
  1915.         T[] getSijm(final int j, final int m, final FieldAbsoluteDate<T> date) {
  1916.             final int jMax = (cijm[m].length - 1) / 2;
  1917.             return sijm[m][j + jMax].value(date);
  1918.         }

  1919.     }

  1920.     /** Compute potential and potential derivatives with respect to orbital parameters.
  1921.      *  <p>The following elements are computed from expression 3.3 - (4).
  1922.      *  <pre>
  1923.      *  dU / da
  1924.      *  dU / dh
  1925.      *  dU / dk
  1926.      *  dU / dλ
  1927.      *  dU / dα
  1928.      *  dU / dβ
  1929.      *  dU / dγ
  1930.      *  </pre>
  1931.      *  </p>
  1932.      */
  1933.     private class UAnddU {

  1934.         /** dU / da. */
  1935.         private  double dUda;

  1936.         /** dU / dk. */
  1937.         private double dUdk;

  1938.         /** dU / dh. */
  1939.         private double dUdh;

  1940.         /** dU / dl. */
  1941.         private double dUdl;

  1942.         /** dU / dAlpha. */
  1943.         private double dUdAl;

  1944.         /** dU / dBeta. */
  1945.         private double dUdBe;

  1946.         /** dU / dGamma. */
  1947.         private double dUdGa;

  1948.         /** Simple constuctor.
  1949.          * @param date current date
  1950.          * @param context container for attributes
  1951.          * @param hansen hansen objects
  1952.          */
  1953.         UAnddU(final AbsoluteDate date, final DSSTTesseralContext context, final HansenObjects hansen) {

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

  1956.             // Potential derivatives
  1957.             dUda  = 0.;
  1958.             dUdh  = 0.;
  1959.             dUdk  = 0.;
  1960.             dUdl  = 0.;
  1961.             dUdAl = 0.;
  1962.             dUdBe = 0.;
  1963.             dUdGa = 0.;

  1964.             // Compute only if there is at least one resonant tesseral
  1965.             if (!resOrders.isEmpty()) {
  1966.                 // Gmsj and Hmsj polynomials
  1967.                 final GHmsjPolynomials ghMSJ = new GHmsjPolynomials(auxiliaryElements.getK(), auxiliaryElements.getH(), auxiliaryElements.getAlpha(), auxiliaryElements.getBeta(), I);

  1968.                 // GAMMAmns function
  1969.                 final GammaMnsFunction gammaMNS = new GammaMnsFunction(maxDegree, auxiliaryElements.getGamma(), I);

  1970.                 // R / a up to power degree
  1971.                 final double[] roaPow = new double[maxDegree + 1];
  1972.                 roaPow[0] = 1.;
  1973.                 for (int i = 1; i <= maxDegree; i++) {
  1974.                     roaPow[i] = context.getRoa() * roaPow[i - 1];
  1975.                 }

  1976.                 // SUM over resonant terms {j,m}
  1977.                 for (int m : resOrders) {

  1978.                     // Resonant index for the current resonant order
  1979.                     final int j = FastMath.max(1, (int) FastMath.round(context.getRatio() * m));

  1980.                     // Phase angle
  1981.                     final double jlMmt  = j * auxiliaryElements.getLM() - m * context.getTheta();
  1982.                     final SinCos scPhi  = FastMath.sinCos(jlMmt);
  1983.                     final double sinPhi = scPhi.sin();
  1984.                     final double cosPhi = scPhi.cos();

  1985.                     // Potential derivatives components for a given resonant pair {j,m}
  1986.                     double dUdaCos  = 0.;
  1987.                     double dUdaSin  = 0.;
  1988.                     double dUdhCos  = 0.;
  1989.                     double dUdhSin  = 0.;
  1990.                     double dUdkCos  = 0.;
  1991.                     double dUdkSin  = 0.;
  1992.                     double dUdlCos  = 0.;
  1993.                     double dUdlSin  = 0.;
  1994.                     double dUdAlCos = 0.;
  1995.                     double dUdAlSin = 0.;
  1996.                     double dUdBeCos = 0.;
  1997.                     double dUdBeSin = 0.;
  1998.                     double dUdGaCos = 0.;
  1999.                     double dUdGaSin = 0.;

  2000.                     // s-SUM from -sMin to sMax
  2001.                     final int sMin = FastMath.min(maxEccPow - j, maxDegree);
  2002.                     final int sMax = FastMath.min(maxEccPow + j, maxDegree);
  2003.                     for (int s = 0; s <= sMax; s++) {

  2004.                         //Compute the initial values for Hansen coefficients using newComb operators
  2005.                         hansen.computeHansenObjectsInitValues(context, s + maxDegree, j);

  2006.                         // n-SUM for s positive
  2007.                         final double[][] nSumSpos = computeNSum(date, j, m, s, maxDegree,
  2008.                                                                 roaPow, ghMSJ, gammaMNS, context, hansen);
  2009.                         dUdaCos  += nSumSpos[0][0];
  2010.                         dUdaSin  += nSumSpos[0][1];
  2011.                         dUdhCos  += nSumSpos[1][0];
  2012.                         dUdhSin  += nSumSpos[1][1];
  2013.                         dUdkCos  += nSumSpos[2][0];
  2014.                         dUdkSin  += nSumSpos[2][1];
  2015.                         dUdlCos  += nSumSpos[3][0];
  2016.                         dUdlSin  += nSumSpos[3][1];
  2017.                         dUdAlCos += nSumSpos[4][0];
  2018.                         dUdAlSin += nSumSpos[4][1];
  2019.                         dUdBeCos += nSumSpos[5][0];
  2020.                         dUdBeSin += nSumSpos[5][1];
  2021.                         dUdGaCos += nSumSpos[6][0];
  2022.                         dUdGaSin += nSumSpos[6][1];

  2023.                         // n-SUM for s negative
  2024.                         if (s > 0 && s <= sMin) {
  2025.                             //Compute the initial values for Hansen coefficients using newComb operators
  2026.                             hansen.computeHansenObjectsInitValues(context, maxDegree - s, j);

  2027.                             final double[][] nSumSneg = computeNSum(date, j, m, -s, maxDegree,
  2028.                                                                     roaPow, ghMSJ, gammaMNS, context, hansen);
  2029.                             dUdaCos  += nSumSneg[0][0];
  2030.                             dUdaSin  += nSumSneg[0][1];
  2031.                             dUdhCos  += nSumSneg[1][0];
  2032.                             dUdhSin  += nSumSneg[1][1];
  2033.                             dUdkCos  += nSumSneg[2][0];
  2034.                             dUdkSin  += nSumSneg[2][1];
  2035.                             dUdlCos  += nSumSneg[3][0];
  2036.                             dUdlSin  += nSumSneg[3][1];
  2037.                             dUdAlCos += nSumSneg[4][0];
  2038.                             dUdAlSin += nSumSneg[4][1];
  2039.                             dUdBeCos += nSumSneg[5][0];
  2040.                             dUdBeSin += nSumSneg[5][1];
  2041.                             dUdGaCos += nSumSneg[6][0];
  2042.                             dUdGaSin += nSumSneg[6][1];
  2043.                         }
  2044.                     }

  2045.                     // Assembly of potential derivatives componants
  2046.                     dUda  += cosPhi * dUdaCos  + sinPhi * dUdaSin;
  2047.                     dUdh  += cosPhi * dUdhCos  + sinPhi * dUdhSin;
  2048.                     dUdk  += cosPhi * dUdkCos  + sinPhi * dUdkSin;
  2049.                     dUdl  += cosPhi * dUdlCos  + sinPhi * dUdlSin;
  2050.                     dUdAl += cosPhi * dUdAlCos + sinPhi * dUdAlSin;
  2051.                     dUdBe += cosPhi * dUdBeCos + sinPhi * dUdBeSin;
  2052.                     dUdGa += cosPhi * dUdGaCos + sinPhi * dUdGaSin;
  2053.                 }

  2054.                 this.dUda  = dUda * (-context.getMoa() / auxiliaryElements.getSma());
  2055.                 this.dUdh  = dUdh * context.getMoa();
  2056.                 this.dUdk  = dUdk * context.getMoa();
  2057.                 this.dUdl  = dUdl * context.getMoa();
  2058.                 this.dUdAl = dUdAl * context.getMoa();
  2059.                 this.dUdBe = dUdBe * context.getMoa();
  2060.                 this.dUdGa = dUdGa * context.getMoa();
  2061.             }

  2062.         }

  2063.         /** Return value of dU / da.
  2064.          * @return dUda
  2065.          */
  2066.         public double getdUda() {
  2067.             return dUda;
  2068.         }

  2069.         /** Return value of dU / dk.
  2070.          * @return dUdk
  2071.          */
  2072.         public double getdUdk() {
  2073.             return dUdk;
  2074.         }

  2075.         /** Return value of dU / dh.
  2076.          * @return dUdh
  2077.          */
  2078.         public double getdUdh() {
  2079.             return dUdh;
  2080.         }

  2081.         /** Return value of dU / dl.
  2082.          * @return dUdl
  2083.          */
  2084.         public double getdUdl() {
  2085.             return dUdl;
  2086.         }

  2087.         /** Return value of dU / dAlpha.
  2088.          * @return dUdAl
  2089.          */
  2090.         public double getdUdAl() {
  2091.             return dUdAl;
  2092.         }

  2093.         /** Return value of dU / dBeta.
  2094.          * @return dUdBe
  2095.          */
  2096.         public double getdUdBe() {
  2097.             return dUdBe;
  2098.         }

  2099.         /** Return value of dU / dGamma.
  2100.          * @return dUdGa
  2101.          */
  2102.         public double getdUdGa() {
  2103.             return dUdGa;
  2104.         }

  2105.     }

  2106.     /**  Computes the potential U derivatives.
  2107.      *  <p>The following elements are computed from expression 3.3 - (4).
  2108.      *  <pre>
  2109.      *  dU / da
  2110.      *  dU / dh
  2111.      *  dU / dk
  2112.      *  dU / dλ
  2113.      *  dU / dα
  2114.      *  dU / dβ
  2115.      *  dU / dγ
  2116.      *  </pre>
  2117.      *  </p>
  2118.      */
  2119.     private class FieldUAnddU <T extends CalculusFieldElement<T>> {

  2120.         /** dU / da. */
  2121.         private T dUda;

  2122.         /** dU / dk. */
  2123.         private T dUdk;

  2124.         /** dU / dh. */
  2125.         private T dUdh;

  2126.         /** dU / dl. */
  2127.         private T dUdl;

  2128.         /** dU / dAlpha. */
  2129.         private T dUdAl;

  2130.         /** dU / dBeta. */
  2131.         private T dUdBe;

  2132.         /** dU / dGamma. */
  2133.         private T dUdGa;

  2134.         /** Simple constuctor.
  2135.          * @param date current date
  2136.          * @param context container for attributes
  2137.          * @param hansen hansen objects
  2138.          */
  2139.         FieldUAnddU(final FieldAbsoluteDate<T> date, final FieldDSSTTesseralContext<T> context,
  2140.                     final FieldHansenObjects<T> hansen) {

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

  2143.             // Zero for initialization
  2144.             final Field<T> field = date.getField();
  2145.             final T zero = field.getZero();

  2146.             // Potential derivatives
  2147.             dUda  = zero;
  2148.             dUdh  = zero;
  2149.             dUdk  = zero;
  2150.             dUdl  = zero;
  2151.             dUdAl = zero;
  2152.             dUdBe = zero;
  2153.             dUdGa = zero;

  2154.             // Compute only if there is at least one resonant tesseral
  2155.             if (!resOrders.isEmpty()) {
  2156.                 // Gmsj and Hmsj polynomials
  2157.                 final FieldGHmsjPolynomials<T> ghMSJ = new FieldGHmsjPolynomials<>(auxiliaryElements.getK(), auxiliaryElements.getH(), auxiliaryElements.getAlpha(), auxiliaryElements.getBeta(), I, field);

  2158.                 // GAMMAmns function
  2159.                 final FieldGammaMnsFunction<T> gammaMNS = new FieldGammaMnsFunction<>(maxDegree, auxiliaryElements.getGamma(), I, field);

  2160.                 // R / a up to power degree
  2161.                 final T[] roaPow = MathArrays.buildArray(field, maxDegree + 1);
  2162.                 roaPow[0] = zero.add(1.);
  2163.                 for (int i = 1; i <= maxDegree; i++) {
  2164.                     roaPow[i] = roaPow[i - 1].multiply(context.getRoa());
  2165.                 }

  2166.                 // SUM over resonant terms {j,m}
  2167.                 for (int m : resOrders) {

  2168.                     // Resonant index for the current resonant order
  2169.                     final int j = FastMath.max(1, (int) FastMath.round(context.getRatio().multiply(m)));

  2170.                     // Phase angle
  2171.                     final T jlMmt              = auxiliaryElements.getLM().multiply(j).subtract(context.getTheta().multiply(m));
  2172.                     final FieldSinCos<T> scPhi = FastMath.sinCos(jlMmt);
  2173.                     final T sinPhi             = scPhi.sin();
  2174.                     final T cosPhi             = scPhi.cos();

  2175.                     // Potential derivatives components for a given resonant pair {j,m}
  2176.                     T dUdaCos  = zero;
  2177.                     T dUdaSin  = zero;
  2178.                     T dUdhCos  = zero;
  2179.                     T dUdhSin  = zero;
  2180.                     T dUdkCos  = zero;
  2181.                     T dUdkSin  = zero;
  2182.                     T dUdlCos  = zero;
  2183.                     T dUdlSin  = zero;
  2184.                     T dUdAlCos = zero;
  2185.                     T dUdAlSin = zero;
  2186.                     T dUdBeCos = zero;
  2187.                     T dUdBeSin = zero;
  2188.                     T dUdGaCos = zero;
  2189.                     T dUdGaSin = zero;

  2190.                     // s-SUM from -sMin to sMax
  2191.                     final int sMin = FastMath.min(maxEccPow - j, maxDegree);
  2192.                     final int sMax = FastMath.min(maxEccPow + j, maxDegree);
  2193.                     for (int s = 0; s <= sMax; s++) {

  2194.                         //Compute the initial values for Hansen coefficients using newComb operators
  2195.                         hansen.computeHansenObjectsInitValues(context, s + maxDegree, j);

  2196.                         // n-SUM for s positive
  2197.                         final T[][] nSumSpos = computeNSum(date, j, m, s, maxDegree,
  2198.                                                                 roaPow, ghMSJ, gammaMNS, context, hansen);
  2199.                         dUdaCos  = dUdaCos.add(nSumSpos[0][0]);
  2200.                         dUdaSin  = dUdaSin.add(nSumSpos[0][1]);
  2201.                         dUdhCos  = dUdhCos.add(nSumSpos[1][0]);
  2202.                         dUdhSin  = dUdhSin.add(nSumSpos[1][1]);
  2203.                         dUdkCos  = dUdkCos.add(nSumSpos[2][0]);
  2204.                         dUdkSin  = dUdkSin.add(nSumSpos[2][1]);
  2205.                         dUdlCos  = dUdlCos.add(nSumSpos[3][0]);
  2206.                         dUdlSin  = dUdlSin.add(nSumSpos[3][1]);
  2207.                         dUdAlCos = dUdAlCos.add(nSumSpos[4][0]);
  2208.                         dUdAlSin = dUdAlSin.add(nSumSpos[4][1]);
  2209.                         dUdBeCos = dUdBeCos.add(nSumSpos[5][0]);
  2210.                         dUdBeSin = dUdBeSin.add(nSumSpos[5][1]);
  2211.                         dUdGaCos = dUdGaCos.add(nSumSpos[6][0]);
  2212.                         dUdGaSin = dUdGaSin.add(nSumSpos[6][1]);

  2213.                         // n-SUM for s negative
  2214.                         if (s > 0 && s <= sMin) {
  2215.                             //Compute the initial values for Hansen coefficients using newComb operators
  2216.                             hansen.computeHansenObjectsInitValues(context, maxDegree - s, j);

  2217.                             final T[][] nSumSneg = computeNSum(date, j, m, -s, maxDegree,
  2218.                                                                     roaPow, ghMSJ, gammaMNS, context, hansen);
  2219.                             dUdaCos  = dUdaCos.add(nSumSneg[0][0]);
  2220.                             dUdaSin  = dUdaSin.add(nSumSneg[0][1]);
  2221.                             dUdhCos  = dUdhCos.add(nSumSneg[1][0]);
  2222.                             dUdhSin  = dUdhSin.add(nSumSneg[1][1]);
  2223.                             dUdkCos  = dUdkCos.add(nSumSneg[2][0]);
  2224.                             dUdkSin  = dUdkSin.add(nSumSneg[2][1]);
  2225.                             dUdlCos  = dUdlCos.add(nSumSneg[3][0]);
  2226.                             dUdlSin  = dUdlSin.add(nSumSneg[3][1]);
  2227.                             dUdAlCos = dUdAlCos.add(nSumSneg[4][0]);
  2228.                             dUdAlSin = dUdAlSin.add(nSumSneg[4][1]);
  2229.                             dUdBeCos = dUdBeCos.add(nSumSneg[5][0]);
  2230.                             dUdBeSin = dUdBeSin.add(nSumSneg[5][1]);
  2231.                             dUdGaCos = dUdGaCos.add(nSumSneg[6][0]);
  2232.                             dUdGaSin = dUdGaSin.add(nSumSneg[6][1]);
  2233.                         }
  2234.                     }

  2235.                     // Assembly of potential derivatives componants
  2236.                     dUda  = dUda.add(dUdaCos.multiply(cosPhi).add(dUdaSin.multiply(sinPhi)));
  2237.                     dUdh  = dUdh.add(dUdhCos.multiply(cosPhi).add(dUdhSin.multiply(sinPhi)));
  2238.                     dUdk  = dUdk.add(dUdkCos.multiply(cosPhi).add(dUdkSin.multiply(sinPhi)));
  2239.                     dUdl  = dUdl.add(dUdlCos.multiply(cosPhi).add(dUdlSin.multiply(sinPhi)));
  2240.                     dUdAl = dUdAl.add(dUdAlCos.multiply(cosPhi).add(dUdAlSin.multiply(sinPhi)));
  2241.                     dUdBe = dUdBe.add(dUdBeCos.multiply(cosPhi).add(dUdBeSin.multiply(sinPhi)));
  2242.                     dUdGa = dUdGa.add(dUdGaCos.multiply(cosPhi).add(dUdGaSin.multiply(sinPhi)));
  2243.                 }

  2244.                 dUda  =  dUda.multiply(context.getMoa().divide(auxiliaryElements.getSma())).negate();
  2245.                 dUdh  =  dUdh.multiply(context.getMoa());
  2246.                 dUdk  =  dUdk.multiply(context.getMoa());
  2247.                 dUdl  =  dUdl.multiply(context.getMoa());
  2248.                 dUdAl =  dUdAl.multiply(context.getMoa());
  2249.                 dUdBe =  dUdBe.multiply(context.getMoa());
  2250.                 dUdGa =  dUdGa.multiply(context.getMoa());

  2251.             }

  2252.         }

  2253.         /** Return value of dU / da.
  2254.          * @return dUda
  2255.          */
  2256.         public T getdUda() {
  2257.             return dUda;
  2258.         }

  2259.         /** Return value of dU / dk.
  2260.          * @return dUdk
  2261.          */
  2262.         public T getdUdk() {
  2263.             return dUdk;
  2264.         }

  2265.         /** Return value of dU / dh.
  2266.          * @return dUdh
  2267.          */
  2268.         public T getdUdh() {
  2269.             return dUdh;
  2270.         }

  2271.         /** Return value of dU / dl.
  2272.          * @return dUdl
  2273.          */
  2274.         public T getdUdl() {
  2275.             return dUdl;
  2276.         }

  2277.         /** Return value of dU / dAlpha.
  2278.          * @return dUdAl
  2279.          */
  2280.         public T getdUdAl() {
  2281.             return dUdAl;
  2282.         }

  2283.         /** Return value of dU / dBeta.
  2284.          * @return dUdBe
  2285.          */
  2286.         public T getdUdBe() {
  2287.             return dUdBe;
  2288.         }

  2289.         /** Return value of dU / dGamma.
  2290.          * @return dUdGa
  2291.          */
  2292.         public T getdUdGa() {
  2293.             return dUdGa;
  2294.         }

  2295.     }

  2296.     /** Computes init values of the Hansen Objects. */
  2297.     private class HansenObjects {

  2298.         /** A two dimensional array that contains the objects needed to build the Hansen coefficients. <br/>
  2299.          * The indexes are s + maxDegree and j */
  2300.         private HansenTesseralLinear[][] hansenObjects;

  2301.         /** Simple constructor.
  2302.          * @param ratio Ratio of satellite period to central body rotation period
  2303.          * @param type type of the elements used during the propagation
  2304.          */
  2305.         HansenObjects(final double ratio,
  2306.                       final PropagationType type) {

  2307.             //Allocate the two dimensional array
  2308.             final int rows     = 2 * maxDegree + 1;
  2309.             final int columns  = maxFrequencyShortPeriodics + 1;
  2310.             this.hansenObjects = new HansenTesseralLinear[rows][columns];

  2311.             switch (type) {
  2312.                 case MEAN:
  2313.                     // loop through the resonant orders
  2314.                     for (int m : resOrders) {
  2315.                         //Compute the corresponding j term
  2316.                         final int j = FastMath.max(1, (int) FastMath.round(ratio * m));

  2317.                         //Compute the sMin and sMax values
  2318.                         final int sMin = FastMath.min(maxEccPow - j, maxDegree);
  2319.                         final int sMax = FastMath.min(maxEccPow + j, maxDegree);

  2320.                         //loop through the s values
  2321.                         for (int s = 0; s <= sMax; s++) {
  2322.                             //Compute the n0 value
  2323.                             final int n0 = FastMath.max(FastMath.max(2, m), s);

  2324.                             //Create the object for the pair j, s
  2325.                             this.hansenObjects[s + maxDegree][j] = new HansenTesseralLinear(maxDegree, s, j, n0, maxHansen);

  2326.                             if (s > 0 && s <= sMin) {
  2327.                                 //Also create the object for the pair j, -s
  2328.                                 this.hansenObjects[maxDegree - s][j] =  new HansenTesseralLinear(maxDegree, -s, j, n0, maxHansen);
  2329.                             }
  2330.                         }
  2331.                     }
  2332.                     break;

  2333.                 case OSCULATING:
  2334.                     // create all objects
  2335.                     for (int j = 0; j <= maxFrequencyShortPeriodics; j++) {
  2336.                         for (int s = -maxDegree; s <= maxDegree; s++) {
  2337.                             //Compute the n0 value
  2338.                             final int n0 = FastMath.max(2, FastMath.abs(s));
  2339.                             this.hansenObjects[s + maxDegree][j] = new HansenTesseralLinear(maxDegree, s, j, n0, maxHansen);
  2340.                         }
  2341.                     }
  2342.                     break;

  2343.                 default:
  2344.                     throw new OrekitInternalError(null);
  2345.             }

  2346.         }

  2347.         /** Compute init values for hansen objects.
  2348.          * @param context container for attributes
  2349.          * @param rows number of rows of the hansen matrix
  2350.          * @param columns columns number of columns of the hansen matrix
  2351.          */
  2352.         public void computeHansenObjectsInitValues(final DSSTTesseralContext context, final int rows, final int columns) {
  2353.             hansenObjects[rows][columns].computeInitValues(context.getE2(), context.getChi(), context.getChi2());
  2354.         }

  2355.         /** Get hansen object.
  2356.          * @return hansenObjects
  2357.          */
  2358.         public HansenTesseralLinear[][] getHansenObjects() {
  2359.             return hansenObjects;
  2360.         }

  2361.     }

  2362.     /** Computes init values of the Hansen Objects. */
  2363.     private class FieldHansenObjects<T extends CalculusFieldElement<T>> {

  2364.         /** A two dimensional array that contains the objects needed to build the Hansen coefficients. <br/>
  2365.          * The indexes are s + maxDegree and j */
  2366.         private FieldHansenTesseralLinear<T>[][] hansenObjects;

  2367.         /** Simple constructor.
  2368.          * @param ratio Ratio of satellite period to central body rotation period
  2369.          * @param type type of the elements used during the propagation
  2370.          */
  2371.         @SuppressWarnings("unchecked")
  2372.         FieldHansenObjects(final T ratio,
  2373.                            final PropagationType type) {

  2374.             // Set the maximum power of the eccentricity to use in Hansen coefficient Kernel expansion.
  2375.             maxHansen = maxEccPow / 2;

  2376.             //Allocate the two dimensional array
  2377.             final int rows     = 2 * maxDegree + 1;
  2378.             final int columns  = maxFrequencyShortPeriodics + 1;
  2379.             this.hansenObjects = (FieldHansenTesseralLinear<T>[][]) Array.newInstance(FieldHansenTesseralLinear.class, rows, columns);

  2380.             switch (type) {
  2381.                 case MEAN:
  2382.                  // loop through the resonant orders
  2383.                     for (int m : resOrders) {
  2384.                         //Compute the corresponding j term
  2385.                         final int j = FastMath.max(1, (int) FastMath.round(ratio.multiply(m)));

  2386.                         //Compute the sMin and sMax values
  2387.                         final int sMin = FastMath.min(maxEccPow - j, maxDegree);
  2388.                         final int sMax = FastMath.min(maxEccPow + j, maxDegree);

  2389.                         //loop through the s values
  2390.                         for (int s = 0; s <= sMax; s++) {
  2391.                             //Compute the n0 value
  2392.                             final int n0 = FastMath.max(FastMath.max(2, m), s);

  2393.                             //Create the object for the pair j, s
  2394.                             this.hansenObjects[s + maxDegree][j] = new FieldHansenTesseralLinear<>(maxDegree, s, j, n0, maxHansen, ratio.getField());

  2395.                             if (s > 0 && s <= sMin) {
  2396.                                 //Also create the object for the pair j, -s
  2397.                                 this.hansenObjects[maxDegree - s][j] =  new FieldHansenTesseralLinear<>(maxDegree, -s, j, n0, maxHansen, ratio.getField());
  2398.                             }
  2399.                         }
  2400.                     }
  2401.                     break;

  2402.                 case OSCULATING:
  2403.                     // create all objects
  2404.                     for (int j = 0; j <= maxFrequencyShortPeriodics; j++) {
  2405.                         for (int s = -maxDegree; s <= maxDegree; s++) {
  2406.                             //Compute the n0 value
  2407.                             final int n0 = FastMath.max(2, FastMath.abs(s));
  2408.                             this.hansenObjects[s + maxDegree][j] = new FieldHansenTesseralLinear<>(maxDegree, s, j, n0, maxHansen, ratio.getField());
  2409.                         }
  2410.                     }
  2411.                     break;

  2412.                 default:
  2413.                     throw new OrekitInternalError(null);
  2414.             }

  2415.         }

  2416.         /** Compute init values for hansen objects.
  2417.          * @param context container for attributes
  2418.          * @param rows number of rows of the hansen matrix
  2419.          * @param columns columns number of columns of the hansen matrix
  2420.          */
  2421.         public void computeHansenObjectsInitValues(final FieldDSSTTesseralContext<T> context,
  2422.                                                    final int rows, final int columns) {
  2423.             hansenObjects[rows][columns].computeInitValues(context.getE2(), context.getChi(), context.getChi2());
  2424.         }

  2425.         /** Get hansen object.
  2426.          * @return hansenObjects
  2427.          */
  2428.         public FieldHansenTesseralLinear<T>[][] getHansenObjects() {
  2429.             return hansenObjects;
  2430.         }

  2431.     }

  2432. }