DSSTTesseral.java

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

  18. import 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<>();
  257.         this.nonResOrders = new TreeMap<>();

  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<>(new Slot(mMax, maxFrequencyShortPeriodics, INTERPOLATION_POINTS)));

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

  300.     }

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

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

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

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

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

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

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

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

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

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

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

  333.     }

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

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

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

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

  389.         // Container for attributes

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

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

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

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

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

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

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

  415.         // Container for attributes

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

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

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

  428.         final T da = udu.getdUdl().multiply(context.getAx2oA());
  429.         final T dh = udu.getdUdk().multiply(context.getBoA()).add(pUagmIqUbgoAB.multiply(auxiliaryElements.getK())).subtract(udu.getdUdl().multiply(auxiliaryElements.getH()).multiply(context.getBoABpo()));
  430.         final T dk = (udu.getdUdh().multiply(context.getBoA()).add(pUagmIqUbgoAB.multiply(auxiliaryElements.getH())).add(udu.getdUdl().multiply(context.getBoABpo()).multiply(auxiliaryElements.getK()))).negate();
  431.         final T dp = context.getCo2AB().multiply(auxiliaryElements.getP().multiply(UhkmUabmdUdl).subtract(UBetaGamma));
  432.         final T dq = context.getCo2AB().multiply(auxiliaryElements.getQ().multiply(UhkmUabmdUdl).subtract(UAlphaGamma.multiply(I)));
  433.         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()));

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

  441.         return elements;

  442.     }

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

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

  447.         for (final SpacecraftState meanState : meanStates) {

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

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

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

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

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

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

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

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

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

  486.         }

  487.     }

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

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

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

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

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

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

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

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

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

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

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

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

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

  537.         }

  538.     }

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

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

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

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

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

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

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

  576.     }

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

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

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

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

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

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

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

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

  619.     }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  913.         return derivatives;

  914.     }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  1096.             // Compute the cross derivative operator :
  1097.             final double RAlphaGammaCos   = context.getAlpha() * dRdGaCos - context.getGamma() * dRdAlCos;
  1098.             final double RAlphaGammaSin   = context.getAlpha() * dRdGaSin - context.getGamma() * dRdAlSin;
  1099.             final double RAlphaBetaCos    = context.getAlpha() * dRdBeCos - context.getBeta()  * dRdAlCos;
  1100.             final double RAlphaBetaSin    = context.getAlpha() * dRdBeSin - context.getBeta()  * dRdAlSin;
  1101.             final double RBetaGammaCos    =  context.getBeta() * dRdGaCos - context.getGamma() * dRdBeCos;
  1102.             final double RBetaGammaSin    =  context.getBeta() * dRdGaSin - context.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.newInstance(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(), context.getAlpha(), context.getBeta(), I, field);

  1222.                 // GAMMAmns function
  1223.                 gammaMNS = new FieldGammaMnsFunction<>(maxDegree, context.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.             final T muOnA = context.getMuoa();
  1320.             dRdaCos  =  dRdaCos.multiply(muOnA.negate().divide(auxiliaryElements.getSma()));
  1321.             dRdaSin  =  dRdaSin.multiply(muOnA.negate().divide(auxiliaryElements.getSma()));
  1322.             dRdhCos  =  dRdhCos.multiply(muOnA);
  1323.             dRdhSin  =  dRdhSin.multiply(muOnA);
  1324.             dRdkCos  =  dRdkCos.multiply(muOnA);
  1325.             dRdkSin  =  dRdkSin.multiply(muOnA);
  1326.             dRdlCos  =  dRdlCos.multiply(muOnA);
  1327.             dRdlSin  =  dRdlSin.multiply(muOnA);
  1328.             dRdAlCos = dRdAlCos.multiply(muOnA);
  1329.             dRdAlSin = dRdAlSin.multiply(muOnA);
  1330.             dRdBeCos = dRdBeCos.multiply(muOnA);
  1331.             dRdBeSin = dRdBeSin.multiply(muOnA);
  1332.             dRdGaCos = dRdGaCos.multiply(muOnA);
  1333.             dRdGaSin = dRdGaSin.multiply(muOnA);

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

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

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

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

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

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

  1362.             // dλ/dt
  1363.             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);
  1364.             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);
  1365.         }

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

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

  1385.     /** 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
  1386.      * the short-periodic zonal contribution.
  1387.      *   <p>
  1388.      *  Those coefficients are given by expression 2.5.4-5 from the Danielson paper.
  1389.      *   </p>
  1390.      *
  1391.      * @author Sorin Scortan
  1392.      *
  1393.      */
  1394.     private static class TesseralShortPeriodicCoefficients implements ShortPeriodTerms {

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  1519.                     }
  1520.                 }
  1521.             }

  1522.             return shortPeriodicVariation;

  1523.         }

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

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

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

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

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

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

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

  1559.                 return coefficients;

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

  1563.         }

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

  1584.     }

  1585.     /** 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
  1586.      * the short-periodic zonal contribution.
  1587.      *   <p>
  1588.      *  Those coefficients are given by expression 2.5.4-5 from the Danielson paper.
  1589.      *   </p>
  1590.      *
  1591.      * @author Sorin Scortan
  1592.      *
  1593.      */
  1594.     private static class FieldTesseralShortPeriodicCoefficients <T extends CalculusFieldElement<T>> implements FieldShortPeriodTerms<T> {

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  1714.                     }
  1715.                 }
  1716.             }

  1717.             return shortPeriodicVariation;

  1718.         }

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

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

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

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

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

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

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

  1754.                 return coefficients;

  1755.             } else {
  1756.                 return Collections.emptyMap();
  1757.             }

  1758.         }

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

  1780.     /** Coefficients valid for one time slot. */
  1781.     private static class Slot {

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

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

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

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

  1824.         }

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

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

  1847.     }

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

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

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

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

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

  1893.         }

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

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

  1916.     }

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

  1931.         /** dU / da. */
  1932.         private  double dUda;

  1933.         /** dU / dk. */
  1934.         private double dUdk;

  1935.         /** dU / dh. */
  1936.         private double dUdh;

  1937.         /** dU / dl. */
  1938.         private double dUdl;

  1939.         /** dU / dAlpha. */
  1940.         private double dUdAl;

  1941.         /** dU / dBeta. */
  1942.         private double dUdBe;

  1943.         /** dU / dGamma. */
  1944.         private double dUdGa;

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

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

  1953.             // Potential derivatives
  1954.             dUda  = 0.;
  1955.             dUdh  = 0.;
  1956.             dUdk  = 0.;
  1957.             dUdl  = 0.;
  1958.             dUdAl = 0.;
  1959.             dUdBe = 0.;
  1960.             dUdGa = 0.;

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

  1965.                 // GAMMAmns function
  1966.                 final GammaMnsFunction gammaMNS = new GammaMnsFunction(maxDegree, context.getGamma(), I);

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

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

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

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

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

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

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

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

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

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

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

  2051.                 final double muOnA = context.getMuoa();
  2052.                 this.dUda  = dUda * (-muOnA / auxiliaryElements.getSma());
  2053.                 this.dUdh  = dUdh * muOnA;
  2054.                 this.dUdk  = dUdk * muOnA;
  2055.                 this.dUdl  = dUdl * muOnA;
  2056.                 this.dUdAl = dUdAl * muOnA;
  2057.                 this.dUdBe = dUdBe * muOnA;
  2058.                 this.dUdGa = dUdGa * muOnA;
  2059.             }

  2060.         }

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

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

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

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

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

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

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

  2103.     }

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

  2118.         /** dU / da. */
  2119.         private T dUda;

  2120.         /** dU / dk. */
  2121.         private T dUdk;

  2122.         /** dU / dh. */
  2123.         private T dUdh;

  2124.         /** dU / dl. */
  2125.         private T dUdl;

  2126.         /** dU / dAlpha. */
  2127.         private T dUdAl;

  2128.         /** dU / dBeta. */
  2129.         private T dUdBe;

  2130.         /** dU / dGamma. */
  2131.         private T dUdGa;

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

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

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

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

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

  2156.                 // GAMMAmns function
  2157.                 final FieldGammaMnsFunction<T> gammaMNS = new FieldGammaMnsFunction<>(maxDegree, context.getGamma(), I, field);

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

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

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

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

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

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

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

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

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

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

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

  2242.                 final T muOnA = context.getMuoa();
  2243.                 dUda  =  dUda.multiply(muOnA.divide(auxiliaryElements.getSma())).negate();
  2244.                 dUdh  =  dUdh.multiply(muOnA);
  2245.                 dUdk  =  dUdk.multiply(muOnA);
  2246.                 dUdl  =  dUdl.multiply(muOnA);
  2247.                 dUdAl =  dUdAl.multiply(muOnA);
  2248.                 dUdBe =  dUdBe.multiply(muOnA);
  2249.                 dUdGa =  dUdGa.multiply(muOnA);
  2250.             }
  2251.         }

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

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

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

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

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

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

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

  2294.     }

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

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

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

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

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

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

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

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

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

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

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

  2345.         }

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

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

  2360.     }

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

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

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

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

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

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

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

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

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

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

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

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

  2414.         }

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

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

  2430.     }

  2431. }