DSSTTesseral.java

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

  18. import java.lang.reflect.Array;
  19. import java.util.ArrayList;
  20. import java.util.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.Field;
  29. import org.hipparchus.RealFieldElement;
  30. import org.hipparchus.analysis.differentiation.FieldGradient;
  31. import org.hipparchus.analysis.differentiation.Gradient;
  32. import org.hipparchus.exception.LocalizedCoreFormats;
  33. import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
  34. import org.hipparchus.geometry.euclidean.threed.Vector3D;
  35. import org.hipparchus.util.FastMath;
  36. import org.hipparchus.util.FieldSinCos;
  37. import org.hipparchus.util.MathArrays;
  38. import org.hipparchus.util.MathUtils;
  39. import org.hipparchus.util.SinCos;
  40. import org.orekit.attitudes.AttitudeProvider;
  41. import org.orekit.errors.OrekitException;
  42. import org.orekit.errors.OrekitInternalError;
  43. import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider;
  44. import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider.UnnormalizedSphericalHarmonics;
  45. import org.orekit.frames.FieldTransform;
  46. import org.orekit.frames.Frame;
  47. import org.orekit.frames.Transform;
  48. import org.orekit.orbits.FieldOrbit;
  49. import org.orekit.orbits.Orbit;
  50. import org.orekit.propagation.FieldSpacecraftState;
  51. import org.orekit.propagation.PropagationType;
  52. import org.orekit.propagation.SpacecraftState;
  53. import org.orekit.propagation.events.EventDetector;
  54. import org.orekit.propagation.events.FieldEventDetector;
  55. import org.orekit.propagation.semianalytical.dsst.utilities.AuxiliaryElements;
  56. import org.orekit.propagation.semianalytical.dsst.utilities.CoefficientsFactory;
  57. import org.orekit.propagation.semianalytical.dsst.utilities.FieldAuxiliaryElements;
  58. import org.orekit.propagation.semianalytical.dsst.utilities.FieldGHmsjPolynomials;
  59. import org.orekit.propagation.semianalytical.dsst.utilities.FieldGammaMnsFunction;
  60. import org.orekit.propagation.semianalytical.dsst.utilities.FieldShortPeriodicsInterpolatedCoefficient;
  61. import org.orekit.propagation.semianalytical.dsst.utilities.GHmsjPolynomials;
  62. import org.orekit.propagation.semianalytical.dsst.utilities.GammaMnsFunction;
  63. import org.orekit.propagation.semianalytical.dsst.utilities.JacobiPolynomials;
  64. import org.orekit.propagation.semianalytical.dsst.utilities.ShortPeriodicsInterpolatedCoefficient;
  65. import org.orekit.propagation.semianalytical.dsst.utilities.hansen.FieldHansenTesseralLinear;
  66. import org.orekit.propagation.semianalytical.dsst.utilities.hansen.HansenTesseralLinear;
  67. import org.orekit.time.AbsoluteDate;
  68. import org.orekit.time.FieldAbsoluteDate;
  69. import org.orekit.utils.FieldTimeSpanMap;
  70. import org.orekit.utils.ParameterDriver;
  71. import org.orekit.utils.TimeSpanMap;

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

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

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

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

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

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

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

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

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

  119.     /** Provider for spherical harmonics. */
  120.     private final UnnormalizedSphericalHarmonicsProvider provider;

  121.     /** Central body rotating frame. */
  122.     private final Frame bodyFrame;

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

  125.     /** Central body rotation period (seconds). */
  126.     private final double bodyPeriod;

  127.     /** Maximal degree to consider for harmonics potential. */
  128.     private final int maxDegree;

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

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

  133.     /** Maximal order to consider for harmonics potential. */
  134.     private final int maxOrder;

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

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

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

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

  145.     /** Maximum value for j. */
  146.     private final int maxFrequencyShortPeriodics;

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

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

  151.     /** Short period terms. */
  152.     private TesseralShortPeriodicCoefficients shortPeriodTerms;

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

  155.     /** Driver for gravitational parameter. */
  156.     private final ParameterDriver gmParameterDriver;

  157.     /** Hansen objects. */
  158.     private HansenObjects hansen;

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

  161.     /** Flag for force model initialization with field elements. */
  162.     private boolean pendingInitialization;

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

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

  220.         gmParameterDriver = new ParameterDriver(DSSTNewtonianAttraction.CENTRAL_ATTRACTION_COEFFICIENT,
  221.                                                 provider.getMu(), MU_SCALE,
  222.                                                 0.0, Double.POSITIVE_INFINITY);

  223.         // Central body rotating frame
  224.         this.bodyFrame = centralBodyFrame;

  225.         //Save the rotation rate
  226.         this.centralBodyRotationRate = centralBodyRotationRate;

  227.         // Central body rotation period in seconds
  228.         this.bodyPeriod = MathUtils.TWO_PI / centralBodyRotationRate;

  229.         // Provider for spherical harmonics
  230.         this.provider      = provider;
  231.         this.maxDegree     = provider.getMaxDegree();
  232.         this.maxOrder      = provider.getMaxOrder();

  233.         //set the maximum degree order for short periodics
  234.         checkIndexRange(maxDegreeTesseralSP, 2, maxDegree);
  235.         this.maxDegreeTesseralSP       = maxDegreeTesseralSP;

  236.         checkIndexRange(maxDegreeMdailyTesseralSP, 2, maxDegree);
  237.         this.maxDegreeMdailyTesseralSP = maxDegreeMdailyTesseralSP;

  238.         checkIndexRange(maxOrderTesseralSP, 0, maxOrder);
  239.         this.maxOrderTesseralSP        = maxOrderTesseralSP;

  240.         checkIndexRange(maxOrderMdailyTesseralSP, 0, maxOrder);
  241.         this.maxOrderMdailyTesseralSP  = maxOrderMdailyTesseralSP;

  242.         // set the maximum value for eccentricity power
  243.         this.maxEccPowTesseralSP       = maxEccPowTesseralSP;

  244.         checkIndexRange(maxEccPowMdailyTesseralSP, 0, maxDegreeMdailyTesseralSP - 2);
  245.         this.maxEccPowMdailyTesseralSP = maxEccPowMdailyTesseralSP;

  246.         // set the maximum value for frequency
  247.         this.maxFrequencyShortPeriodics = maxFrequencyShortPeriodics;

  248.         // Initialize default values
  249.         this.nonResOrders = new TreeMap<Integer, List <Integer> >();

  250.         pendingInitialization = true;

  251.         fieldShortPeriodTerms = new HashMap<>();
  252.         fieldHansen           = new HashMap<>();

  253.     }

  254.     /** Check an index range.
  255.      * @param index index value
  256.      * @param min minimum value for index
  257.      * @param max maximum value for index
  258.      */
  259.     private void checkIndexRange(final int index, final int min, final int max) {
  260.         if (index < min || index > max) {
  261.             throw new OrekitException(LocalizedCoreFormats.OUT_OF_RANGE_SIMPLE, index, min, max);
  262.         }
  263.     }

  264.     /** {@inheritDoc} */
  265.     @Override
  266.     public List<ShortPeriodTerms> initialize(final AuxiliaryElements auxiliaryElements,
  267.                                              final PropagationType type,
  268.                                              final double[] parameters) {

  269.         // Initializes specific parameters.
  270.         final DSSTTesseralContext context = initializeStep(auxiliaryElements, parameters);

  271.         // The following terms are only used for hansen objects initialization
  272.         final double ratio            = context.getRatio();
  273.         final int maxEccPow           = context.getMaxEccPow();
  274.         final List<Integer> resOrders = context.getResOrders();

  275.         // Compute the non resonant tesseral harmonic terms if not set by the user
  276.         getNonResonantTerms(type, context);

  277.         hansen = new HansenObjects(ratio, maxEccPow, resOrders, type);

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

  279.         shortPeriodTerms = new TesseralShortPeriodicCoefficients(bodyFrame, maxOrderMdailyTesseralSP,
  280.                                                                  maxDegreeTesseralSP < 0, nonResOrders,
  281.                                                                  mMax, maxFrequencyShortPeriodics, INTERPOLATION_POINTS,
  282.                                                                  new TimeSpanMap<Slot>(new Slot(mMax, maxFrequencyShortPeriodics,
  283.                                                                                                 INTERPOLATION_POINTS)));

  284.         final List<ShortPeriodTerms> list = new ArrayList<ShortPeriodTerms>();
  285.         list.add(shortPeriodTerms);
  286.         return list;

  287.     }

  288.     /** {@inheritDoc} */
  289.     @Override
  290.     public <T extends RealFieldElement<T>> List<FieldShortPeriodTerms<T>> initialize(final FieldAuxiliaryElements<T> auxiliaryElements,
  291.                                                                                      final PropagationType type,
  292.                                                                                      final T[] parameters) {

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

  295.         if (pendingInitialization == true) {

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

  298.             // Compute the non resonant tesseral harmonic terms if not set by the user
  299.             getNonResonantTerms(type, context, field);

  300.             // The following terms are only used for hansen objects initialization
  301.             final T      ratio            = context.getRatio();
  302.             final int maxEccPow           = context.getMaxEccPow();
  303.             final List<Integer> resOrders = context.getResOrders();


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

  305.             fieldHansen.put(field, new FieldHansenObjects<>(ratio, maxEccPow, resOrders, type, field));

  306.             pendingInitialization = false;
  307.         }

  308.         final FieldTesseralShortPeriodicCoefficients<T> ftspc =
  309.                         new FieldTesseralShortPeriodicCoefficients<>(bodyFrame, maxOrderMdailyTesseralSP,
  310.                                                                      maxDegreeTesseralSP < 0, nonResOrders,
  311.                                                                      mMax, maxFrequencyShortPeriodics, INTERPOLATION_POINTS,
  312.                                                                      new FieldTimeSpanMap<>(new FieldSlot<>(mMax,
  313.                                                                                                             maxFrequencyShortPeriodics,
  314.                                                                                                             INTERPOLATION_POINTS),
  315.                                                                                             field));

  316.         fieldShortPeriodTerms.put(field, ftspc);
  317.         return Collections.singletonList(ftspc);

  318.     }

  319.     /** Performs initialization at each integration step for the current force model.
  320.      *  <p>
  321.      *  This method aims at being called before mean elements rates computation.
  322.      *  </p>
  323.      *  @param auxiliaryElements auxiliary elements related to the current orbit
  324.      *  @param parameters values of the force model parameters
  325.      *  @return new force model context
  326.      */
  327.     private DSSTTesseralContext initializeStep(final AuxiliaryElements auxiliaryElements, final double[] parameters) {
  328.         return new DSSTTesseralContext(auxiliaryElements, bodyFrame, provider, maxFrequencyShortPeriodics, bodyPeriod, parameters);
  329.     }

  330.     /** Performs initialization at each integration step for the current force model.
  331.      *  <p>
  332.      *  This method aims at being called before mean elements rates computation.
  333.      *  </p>
  334.      *  @param <T> type of the elements
  335.      *  @param auxiliaryElements auxiliary elements related to the current orbit
  336.      *  @param parameters values of the force model parameters
  337.      *  @return new force model context
  338.      */
  339.     private <T extends RealFieldElement<T>> FieldDSSTTesseralContext<T> initializeStep(final FieldAuxiliaryElements<T> auxiliaryElements,
  340.                                                                                        final T[] parameters) {
  341.         return new FieldDSSTTesseralContext<>(auxiliaryElements, bodyFrame, provider, maxFrequencyShortPeriodics, bodyPeriod, parameters);
  342.     }

  343.     /** {@inheritDoc} */
  344.     @Override
  345.     public double[] getMeanElementRate(final SpacecraftState spacecraftState,
  346.                                        final AuxiliaryElements auxiliaryElements, final double[] parameters) {

  347.         // Container for attributes
  348.         final DSSTTesseralContext context = initializeStep(auxiliaryElements, parameters);

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

  351.         // Compute the cross derivative operator :
  352.         final double UAlphaGamma   = auxiliaryElements.getAlpha() * udu.getdUdGa() - auxiliaryElements.getGamma() * udu.getdUdAl();
  353.         final double UAlphaBeta    = auxiliaryElements.getAlpha() * udu.getdUdBe() - auxiliaryElements.getBeta()  * udu.getdUdAl();
  354.         final double UBetaGamma    = auxiliaryElements.getBeta() * udu.getdUdGa() - auxiliaryElements.getGamma() * udu.getdUdBe();
  355.         final double Uhk           = auxiliaryElements.getH() * udu.getdUdk()  - auxiliaryElements.getK() * udu.getdUdh();
  356.         final double pUagmIqUbgoAB = (auxiliaryElements.getP() * UAlphaGamma - I * auxiliaryElements.getQ() * UBetaGamma) * context.getOoAB();
  357.         final double UhkmUabmdUdl  = Uhk - UAlphaBeta - udu.getdUdl();

  358.         final double da =  context.getAx2oA() * udu.getdUdl();
  359.         final double dh =  context.getBoA() * udu.getdUdk() + auxiliaryElements.getK() * pUagmIqUbgoAB - auxiliaryElements.getH() * context.getBoABpo() * udu.getdUdl();
  360.         final double dk =  -(context.getBoA() * udu.getdUdh() + auxiliaryElements.getH() * pUagmIqUbgoAB + auxiliaryElements.getK() * context.getBoABpo() * udu.getdUdl());
  361.         final double dp =  context.getCo2AB() * (auxiliaryElements.getP() * UhkmUabmdUdl - UBetaGamma);
  362.         final double dq =  context.getCo2AB() * (auxiliaryElements.getQ() * UhkmUabmdUdl - I * UAlphaGamma);
  363.         final double dM = -context.getAx2oA() * udu.getdUda() + context.getBoABpo() * (auxiliaryElements.getH() * udu.getdUdh() + auxiliaryElements.getK() * udu.getdUdk()) + pUagmIqUbgoAB;

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

  366.     /** {@inheritDoc} */
  367.     @Override
  368.     public <T extends RealFieldElement<T>> T[] getMeanElementRate(final FieldSpacecraftState<T> spacecraftState,
  369.                                                                   final FieldAuxiliaryElements<T> auxiliaryElements,
  370.                                                                   final T[] parameters) {

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

  373.         // Container for attributes
  374.         final FieldDSSTTesseralContext<T> context = initializeStep(auxiliaryElements, parameters);

  375.         @SuppressWarnings("unchecked")
  376.         final FieldHansenObjects<T> fho = (FieldHansenObjects<T>) fieldHansen.get(field);
  377.         // Access to potential U derivatives
  378.         final FieldUAnddU<T> udu = new FieldUAnddU<>(spacecraftState.getDate(), context, fho);

  379.         // Compute the cross derivative operator :
  380.         final T UAlphaGamma   = udu.getdUdGa().multiply(auxiliaryElements.getAlpha()).subtract(udu.getdUdAl().multiply(auxiliaryElements.getGamma()));
  381.         final T UAlphaBeta    = udu.getdUdBe().multiply(auxiliaryElements.getAlpha()).subtract(udu.getdUdAl().multiply(auxiliaryElements.getBeta()));
  382.         final T UBetaGamma    = udu.getdUdGa().multiply(auxiliaryElements.getBeta()).subtract(udu.getdUdBe().multiply(auxiliaryElements.getGamma()));
  383.         final T Uhk           = udu.getdUdk().multiply(auxiliaryElements.getH()).subtract(udu.getdUdh().multiply(auxiliaryElements.getK()));
  384.         final T pUagmIqUbgoAB = (UAlphaGamma.multiply(auxiliaryElements.getP()).subtract(UBetaGamma.multiply(auxiliaryElements.getQ()).multiply(I))).multiply(context.getOoAB());
  385.         final T UhkmUabmdUdl  = Uhk.subtract(UAlphaBeta).subtract(udu.getdUdl());

  386.         final T da = udu.getdUdl().multiply(context.getAx2oA());
  387.         final T dh = udu.getdUdk().multiply(context.getBoA()).add(pUagmIqUbgoAB.multiply(auxiliaryElements.getK())).subtract(udu.getdUdl().multiply(auxiliaryElements.getH()).multiply(context.getBoABpo()));
  388.         final T dk = (udu.getdUdh().multiply(context.getBoA()).add(pUagmIqUbgoAB.multiply(auxiliaryElements.getH())).add(udu.getdUdl().multiply(context.getBoABpo()).multiply(auxiliaryElements.getK()))).negate();
  389.         final T dp = context.getCo2AB().multiply(auxiliaryElements.getP().multiply(UhkmUabmdUdl).subtract(UBetaGamma));
  390.         final T dq = context.getCo2AB().multiply(auxiliaryElements.getQ().multiply(UhkmUabmdUdl).subtract(UAlphaGamma.multiply(I)));
  391.         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()));

  392.         final T[] elements = MathArrays.buildArray(field, 6);
  393.         elements[0] = da;
  394.         elements[1] = dk;
  395.         elements[2] = dh;
  396.         elements[3] = dq;
  397.         elements[4] = dp;
  398.         elements[5] = dM;

  399.         return elements;

  400.     }

  401.     /** {@inheritDoc} */
  402.     @Override
  403.     public void updateShortPeriodTerms(final double[] parameters, final SpacecraftState... meanStates) {

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

  405.         for (final SpacecraftState meanState : meanStates) {

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

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

  408.             // Initialise the Hansen coefficients
  409.             for (int s = -maxDegree; s <= maxDegree; s++) {
  410.                 // coefficients with j == 0 are always needed
  411.                 hansen.computeHansenObjectsInitValues(context, s + maxDegree, 0);
  412.                 if (maxDegreeTesseralSP >= 0) {
  413.                     // initialize other objects only if required
  414.                     for (int j = 1; j <= maxFrequencyShortPeriodics; j++) {
  415.                         hansen.computeHansenObjectsInitValues(context, s + maxDegree, j);
  416.                     }
  417.                 }
  418.             }

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

  420.             // Compute coefficients
  421.             // Compute only if there is at least one non-resonant tesseral
  422.             if (!nonResOrders.isEmpty() || maxDegreeTesseralSP < 0) {
  423.                 // Generate the fourrier coefficients
  424.                 cjsjFourier.generateCoefficients(meanState.getDate(), context, hansen);

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

  427.                 // build the mDaily coefficients
  428.                 for (int m = 1; m <= maxOrderMdailyTesseralSP; m++) {
  429.                     // build the coefficients
  430.                     buildCoefficients(cjsjFourier, meanState.getDate(), slot, m, 0, tnota, context);
  431.                 }

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

  435.                         for (int j : entry.getValue()) {
  436.                             // build the coefficients
  437.                             buildCoefficients(cjsjFourier, meanState.getDate(), slot, entry.getKey(), j, tnota, context);
  438.                         }
  439.                     }
  440.                 }
  441.             }

  442.         }

  443.     }

  444.     /** {@inheritDoc} */
  445.     @Override
  446.     @SuppressWarnings("unchecked")
  447.     public <T extends RealFieldElement<T>> void updateShortPeriodTerms(final T[] parameters,
  448.                                                                        final FieldSpacecraftState<T>... meanStates) {

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

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

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

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

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

  456.             final FieldHansenObjects<T> fho = (FieldHansenObjects<T>) fieldHansen.get(field);
  457.             // Initialise the Hansen coefficients
  458.             for (int s = -maxDegree; s <= maxDegree; s++) {
  459.                 // coefficients with j == 0 are always needed
  460.                 fho.computeHansenObjectsInitValues(context, s + maxDegree, 0);
  461.                 if (maxDegreeTesseralSP >= 0) {
  462.                     // initialize other objects only if required
  463.                     for (int j = 1; j <= maxFrequencyShortPeriodics; j++) {
  464.                         fho.computeHansenObjectsInitValues(context, s + maxDegree, j);
  465.                     }
  466.                 }
  467.             }

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

  469.             // Compute coefficients
  470.             // Compute only if there is at least one non-resonant tesseral
  471.             if (!nonResOrders.isEmpty() || maxDegreeTesseralSP < 0) {
  472.                 // Generate the fourrier coefficients
  473.                 cjsjFourier.generateCoefficients(meanState.getDate(), context, fho, field);

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

  476.                 // build the mDaily coefficients
  477.                 for (int m = 1; m <= maxOrderMdailyTesseralSP; m++) {
  478.                     // build the coefficients
  479.                     buildCoefficients(cjsjFourier, meanState.getDate(), slot, m, 0, tnota, context, field);
  480.                 }

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

  484.                         for (int j : entry.getValue()) {
  485.                             // build the coefficients
  486.                             buildCoefficients(cjsjFourier, meanState.getDate(), slot, entry.getKey(), j, tnota, context, field);
  487.                         }
  488.                     }
  489.                 }
  490.             }

  491.         }

  492.     }

  493.     /** {@inheritDoc} */
  494.     public ParameterDriver[] getParametersDrivers() {
  495.         return new ParameterDriver[] {
  496.             gmParameterDriver
  497.         };
  498.     }

  499.     /** Build a set of coefficients.
  500.      * @param cjsjFourier the fourier coefficients C<sub>i</sub><sup>j</sup> and the S<sub>i</sub><sup>j</sup>
  501.      * @param date the current date
  502.      * @param slot slot to which the coefficients belong
  503.      * @param m m index
  504.      * @param j j index
  505.      * @param tnota 3n/2a
  506.      * @param context container for attributes
  507.      */
  508.     private void buildCoefficients(final FourierCjSjCoefficients cjsjFourier,
  509.                                    final AbsoluteDate date, final Slot slot,
  510.                                    final int m, final int j, final double tnota, final DSSTTesseralContext context) {

  511.         // Create local arrays
  512.         final double[] currentCijm = new double[] {0., 0., 0., 0., 0., 0.};
  513.         final double[] currentSijm = new double[] {0., 0., 0., 0., 0., 0.};

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

  516.         // initialise the coeficients
  517.         for (int i = 0; i < 6; i++) {
  518.             currentCijm[i] = -cjsjFourier.getSijm(i, j, m);
  519.             currentSijm[i] = cjsjFourier.getCijm(i, j, m);
  520.         }
  521.         // Add the separate part for δ<sub>6i</sub>
  522.         currentCijm[5] += tnota * oojnmt * cjsjFourier.getCijm(0, j, m);
  523.         currentSijm[5] += tnota * oojnmt * cjsjFourier.getSijm(0, j, m);

  524.         //Multiply by 1 / (jn - mθ<sup>.</sup>)
  525.         for (int i = 0; i < 6; i++) {
  526.             currentCijm[i] *= oojnmt;
  527.             currentSijm[i] *= oojnmt;
  528.         }

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

  532.     }

  533.      /** Build a set of coefficients.
  534.      * @param <T> the type of the field elements
  535.      * @param cjsjFourier the fourier coefficients C<sub>i</sub><sup>j</sup> and the S<sub>i</sub><sup>j</sup>
  536.      * @param date the current date
  537.      * @param slot slot to which the coefficients belong
  538.      * @param m m index
  539.      * @param j j index
  540.      * @param tnota 3n/2a
  541.      * @param context container for attributes
  542.      * @param field field used by default
  543.      */
  544.     private <T extends RealFieldElement<T>> void buildCoefficients(final FieldFourierCjSjCoefficients<T> cjsjFourier,
  545.                                                                    final FieldAbsoluteDate<T> date,
  546.                                                                    final FieldSlot<T> slot,
  547.                                                                    final int m, final int j, final T tnota,
  548.                                                                    final FieldDSSTTesseralContext<T> context,
  549.                                                                    final Field<T> field) {

  550.         // Zero
  551.         final T zero = field.getZero();

  552.         // Create local arrays
  553.         final T[] currentCijm = MathArrays.buildArray(field, 6);
  554.         final T[] currentSijm = MathArrays.buildArray(field, 6);

  555.         Arrays.fill(currentCijm, zero);
  556.         Arrays.fill(currentSijm, zero);

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

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

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

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

  575.     }

  576.     /** {@inheritDoc} */
  577.     @Override
  578.     public EventDetector[] getEventsDetectors() {
  579.         return null;
  580.     }

  581.     /** {@inheritDoc} */
  582.     @Override
  583.     public <T extends RealFieldElement<T>> FieldEventDetector<T>[] getFieldEventsDetectors(final Field<T> field) {
  584.         return null;
  585.     }

  586.      /**
  587.       * Get the non-resonant tesseral terms in the central body spherical harmonic field.
  588.       *
  589.       * @param type type of the elements used during the propagation
  590.       * @param context container for attributes
  591.       */
  592.     private void getNonResonantTerms(final PropagationType type, final DSSTTesseralContext context) {

  593.         // Compute natural resonant terms
  594.         final double tolerance = 1. / FastMath.max(MIN_PERIOD_IN_SAT_REV,
  595.                                                    MIN_PERIOD_IN_SECONDS / context.getOrbitPeriod());

  596.         nonResOrders.clear();
  597.         for (int m = 1; m <= maxOrder; m++) {
  598.             final double resonance = context.getRatio() * m;
  599.             int jRes = 0;
  600.             final int jComputedRes = (int) FastMath.round(resonance);
  601.             if (jComputedRes > 0 && jComputedRes <= maxFrequencyShortPeriodics && FastMath.abs(resonance - jComputedRes) <= tolerance) {
  602.                 // Store each resonant index and order
  603.                 jRes = jComputedRes;
  604.             }

  605.             if (type == PropagationType.OSCULATING && maxDegreeTesseralSP >= 0 && m <= maxOrderTesseralSP) {
  606.                 //compute non resonant orders in the tesseral harmonic field
  607.                 final List<Integer> listJofM = new ArrayList<Integer>();
  608.                 //for the moment we take only the pairs (j,m) with |j| <= maxDegree + maxEccPow (from |s-j| <= maxEccPow and |s| <= maxDegree)
  609.                 for (int j = -maxFrequencyShortPeriodics; j <= maxFrequencyShortPeriodics; j++) {
  610.                     if (j != 0 && j != jRes) {
  611.                         listJofM.add(j);
  612.                     }
  613.                 }

  614.                 nonResOrders.put(m, listJofM);
  615.             }
  616.         }
  617.     }

  618.     /**
  619.      * Get the non-resonant tesseral terms in the central body spherical harmonic field.
  620.      *
  621.      * @param <T> type of the elements
  622.      * @param type type of the elements used during the propagation
  623.      * @param context container for attributes
  624.      * @param field field used by default
  625.      */
  626.     private <T extends RealFieldElement<T>> void getNonResonantTerms(final PropagationType type,
  627.                                                                      final FieldDSSTTesseralContext<T> context,
  628.                                                                      final Field<T> field) {

  629.         final T zero = field.getZero();
  630.         // Compute natural resonant terms
  631.         final T tolerance = FastMath.max(zero.add(MIN_PERIOD_IN_SAT_REV),
  632.                                          context.getOrbitPeriod().divide(MIN_PERIOD_IN_SECONDS).reciprocal()).reciprocal();

  633.         nonResOrders.clear();
  634.         for (int m = 1; m <= maxOrder; m++) {
  635.             final T resonance = context.getRatio().multiply(m);
  636.             int jRes = 0;
  637.             final int jComputedRes = (int) FastMath.round(resonance);
  638.             if (jComputedRes > 0 && jComputedRes <= maxFrequencyShortPeriodics && FastMath.abs(resonance.subtract(jComputedRes)).getReal() <= tolerance.getReal()) {
  639.                 // Store each resonant index and order
  640.                 jRes = jComputedRes;
  641.             }

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

  651.                 nonResOrders.put(m, listJofM);
  652.             }
  653.         }
  654.     }

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

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

  674.         //spherical harmonics
  675.         final UnnormalizedSphericalHarmonics harmonics = provider.onDate(date);

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

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

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

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

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

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

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

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

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

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

  726.                 // Jacobi l-index from 2.7.1-(15)
  727.                 final int l = FastMath.min(n - m, n - FastMath.abs(s));
  728.                 // Jacobi polynomial and derivative
  729.                 final Gradient jacobi =
  730.                         JacobiPolynomials.getValue(l, v, w, Gradient.variable(1, 0, auxiliaryElements.getGamma()));

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

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

  745.                 // dU / da components
  746.                 dUdaCos  += dUdaCoef * gcPhs;
  747.                 dUdaSin  += dUdaCoef * gsMhc;

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

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

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

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

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

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

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

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

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

  802.         //spherical harmonics
  803.         final UnnormalizedSphericalHarmonics harmonics = provider.onDate(date.toAbsoluteDate());

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

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

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

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

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

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

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

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

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

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

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

  859.                 // Geopotential coefficients
  860.                 final T cnm = zero.add(harmonics.getUnnormalizedCnm(n, m));
  861.                 final T snm = zero.add(harmonics.getUnnormalizedSnm(n, m));

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

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

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

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

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

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

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

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

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

  911.         return derivatives;

  912.     }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  1106.             // da/dt
  1107.             cCoef[m][j + jMax][0] = context.getAx2oA() * dRdlCos;
  1108.             sCoef[m][j + jMax][0] = context.getAx2oA() * dRdlSin;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  1234.                         for (int j: listJ) {
  1235.                             buildFourierCoefficients(date, m, j, maxDegreeTesseralSP, context, hansenObjects, field);
  1236.                         }
  1237.                     }
  1238.                 }
  1239.             }
  1240.         }

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

  1256.             // Zero
  1257.             final T zero = field.getZero();
  1258.             // Common parameters
  1259.             final FieldAuxiliaryElements<T> auxiliaryElements = context.getFieldAuxiliaryElements();

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

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

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

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

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

  1343.             // da/dt
  1344.             cCoef[m][j + jMax][0] = context.getAx2oA().multiply(dRdlCos);
  1345.             sCoef[m][j + jMax][0] = context.getAx2oA().multiply(dRdlSin);

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

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

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

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

  1358.             // dλ/dt
  1359.             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);
  1360.             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);
  1361.         }

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

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

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

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

  1405.         /** Central body rotating frame. */
  1406.         private final Frame bodyFrame;

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

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

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

  1413.         /** Maximum value for m index. */
  1414.         private final int mMax;

  1415.         /** Maximum value for j index. */
  1416.         private final int jMax;

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

  1419.         /** All coefficients slots. */
  1420.         private final transient TimeSpanMap<Slot> slots;

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

  1444.         /** Get the slot valid for some date.
  1445.          * @param meanStates mean states defining the slot
  1446.          * @return slot valid at the specified date
  1447.          */
  1448.         public Slot createSlot(final SpacecraftState... meanStates) {
  1449.             final Slot         slot  = new Slot(mMax, jMax, interpolationPoints);
  1450.             final AbsoluteDate first = meanStates[0].getDate();
  1451.             final AbsoluteDate last  = meanStates[meanStates.length - 1].getDate();
  1452.             if (first.compareTo(last) <= 0) {
  1453.                 slots.addValidAfter(slot, first);
  1454.             } else {
  1455.                 slots.addValidBefore(slot, first);
  1456.             }
  1457.             return slot;
  1458.         }

  1459.         /** {@inheritDoc} */
  1460.         @Override
  1461.         public double[] value(final Orbit meanOrbit) {

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

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

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

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

  1471.                 // Central body rotation angle from equation 2.7.1-(3)(4).
  1472.                 final Transform t = bodyFrame.getTransformTo(auxiliaryElements.getFrame(), auxiliaryElements.getDate());
  1473.                 final Vector3D xB = t.transformVector(Vector3D.PLUS_I);
  1474.                 final Vector3D yB = t.transformVector(Vector3D.PLUS_J);
  1475.                 final Vector3D  f = auxiliaryElements.getVectorF();
  1476.                 final Vector3D  g = auxiliaryElements.getVectorG();
  1477.                 final double currentTheta = FastMath.atan2(-f.dotProduct(yB) + I * g.dotProduct(xB),
  1478.                                                             f.dotProduct(xB) + I * g.dotProduct(yB));

  1479.                 //Add the m-daily contribution
  1480.                 for (int m = 1; m <= maxOrderMdailyTesseralSP; m++) {
  1481.                     // Phase angle
  1482.                     final double jlMmt  = -m * currentTheta;
  1483.                     final SinCos scPhi  = FastMath.sinCos(jlMmt);
  1484.                     final double sinPhi = scPhi.sin();
  1485.                     final double cosPhi = scPhi.cos();

  1486.                     // compute contribution for each element
  1487.                     final double[] c = slot.getCijm(0, m, meanOrbit.getDate());
  1488.                     final double[] s = slot.getSijm(0, m, meanOrbit.getDate());
  1489.                     for (int i = 0; i < 6; i++) {
  1490.                         shortPeriodicVariation[i] += c[i] * cosPhi + s[i] * sinPhi;
  1491.                     }
  1492.                 }

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

  1497.                     for (int j : listJ) {
  1498.                         // Phase angle
  1499.                         final double jlMmt  = j * meanOrbit.getLM() - m * currentTheta;
  1500.                         final SinCos scPhi  = FastMath.sinCos(jlMmt);
  1501.                         final double sinPhi = scPhi.sin();
  1502.                         final double cosPhi = scPhi.cos();

  1503.                         // compute contribution for each element
  1504.                         final double[] c = slot.getCijm(j, m, meanOrbit.getDate());
  1505.                         final double[] s = slot.getSijm(j, m, meanOrbit.getDate());
  1506.                         for (int i = 0; i < 6; i++) {
  1507.                             shortPeriodicVariation[i] += c[i] * cosPhi + s[i] * sinPhi;
  1508.                         }

  1509.                     }
  1510.                 }
  1511.             }

  1512.             return shortPeriodicVariation;

  1513.         }

  1514.         /** {@inheritDoc} */
  1515.         @Override
  1516.         public String getCoefficientsKeyPrefix() {
  1517.             return DSSTTesseral.SHORT_PERIOD_PREFIX;
  1518.         }

  1519.         /** {@inheritDoc}
  1520.          * <p>
  1521.          * For tesseral terms contributions,there are maxOrderMdailyTesseralSP
  1522.          * m-daily cMm coefficients, maxOrderMdailyTesseralSP m-daily sMm
  1523.          * coefficients, nbNonResonant cjm coefficients and nbNonResonant
  1524.          * sjm coefficients, where maxOrderMdailyTesseralSP and nbNonResonant both
  1525.          * depend on the orbit. The j index is the integer multiplier for the true
  1526.          * longitude argument and the m index is the integer multiplier for m-dailies.
  1527.          * </p>
  1528.          */
  1529.         @Override
  1530.         public Map<String, double[]> getCoefficients(final AbsoluteDate date, final Set<String> selected) {

  1531.             // select the coefficients slot
  1532.             final Slot slot = slots.get(date);

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

  1537.                 for (int m = 1; m <= maxOrderMdailyTesseralSP; m++) {
  1538.                     storeIfSelected(coefficients, selected, slot.getCijm(0, m, date), DSSTTesseral.CM_COEFFICIENTS, m);
  1539.                     storeIfSelected(coefficients, selected, slot.getSijm(0, m, date), DSSTTesseral.SM_COEFFICIENTS, m);
  1540.                 }

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

  1544.                     for (int j : listJ) {
  1545.                         for (int i = 0; i < 6; ++i) {
  1546.                             storeIfSelected(coefficients, selected, slot.getCijm(j, m, date), "c", j, m);
  1547.                             storeIfSelected(coefficients, selected, slot.getSijm(j, m, date), "s", j, m);
  1548.                         }
  1549.                     }
  1550.                 }

  1551.                 return coefficients;

  1552.             } else {
  1553.                 return Collections.emptyMap();
  1554.             }

  1555.         }

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

  1576.     }

  1577.     /** 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
  1578.      * the short-periodic zonal contribution.
  1579.      *   <p>
  1580.      *  Those coefficients are given by expression 2.5.4-5 from the Danielson paper.
  1581.      *   </p>
  1582.      *
  1583.      * @author Sorin Scortan
  1584.      *
  1585.      */
  1586.     private static class FieldTesseralShortPeriodicCoefficients <T extends RealFieldElement<T>> implements FieldShortPeriodTerms<T> {

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

  1601.         /** Central body rotating frame. */
  1602.         private final Frame bodyFrame;

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

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

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

  1609.         /** Maximum value for m index. */
  1610.         private final int mMax;

  1611.         /** Maximum value for j index. */
  1612.         private final int jMax;

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

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

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

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

  1656.         /** {@inheritDoc} */
  1657.         @Override
  1658.         public T[] value(final FieldOrbit<T> meanOrbit) {

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

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

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

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

  1668.                 // Central body rotation angle from equation 2.7.1-(3)(4).
  1669.                 final FieldTransform<T> t = bodyFrame.getTransformTo(auxiliaryElements.getFrame(), auxiliaryElements.getDate());
  1670.                 final FieldVector3D<T> xB = t.transformVector(Vector3D.PLUS_I);
  1671.                 final FieldVector3D<T> yB = t.transformVector(Vector3D.PLUS_J);
  1672.                 final FieldVector3D<T>  f = auxiliaryElements.getVectorF();
  1673.                 final FieldVector3D<T>  g = auxiliaryElements.getVectorG();
  1674.                 final T currentTheta = FastMath.atan2(f.dotProduct(yB).negate().add(g.dotProduct(xB).multiply(I)),
  1675.                                                       f.dotProduct(xB).add(g.dotProduct(yB).multiply(I)));

  1676.                 //Add the m-daily contribution
  1677.                 for (int m = 1; m <= maxOrderMdailyTesseralSP; m++) {
  1678.                     // Phase angle
  1679.                     final T jlMmt              = currentTheta.multiply(-m);
  1680.                     final FieldSinCos<T> scPhi = FastMath.sinCos(jlMmt);
  1681.                     final T sinPhi             = scPhi.sin();
  1682.                     final T cosPhi             = scPhi.cos();

  1683.                     // compute contribution for each element
  1684.                     final T[] c = slot.getCijm(0, m, meanOrbit.getDate());
  1685.                     final T[] s = slot.getSijm(0, m, meanOrbit.getDate());
  1686.                     for (int i = 0; i < 6; i++) {
  1687.                         shortPeriodicVariation[i] = shortPeriodicVariation[i].add(c[i].multiply(cosPhi).add(s[i].multiply(sinPhi)));
  1688.                     }
  1689.                 }

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

  1694.                     for (int j : listJ) {
  1695.                         // Phase angle
  1696.                         final T jlMmt              = meanOrbit.getLM().multiply(j).subtract(currentTheta.multiply(m));
  1697.                         final FieldSinCos<T> scPhi = FastMath.sinCos(jlMmt);
  1698.                         final T sinPhi             = scPhi.sin();
  1699.                         final T cosPhi             = scPhi.cos();

  1700.                         // compute contribution for each element
  1701.                         final T[] c = slot.getCijm(j, m, meanOrbit.getDate());
  1702.                         final T[] s = slot.getSijm(j, m, meanOrbit.getDate());
  1703.                         for (int i = 0; i < 6; i++) {
  1704.                             shortPeriodicVariation[i] = shortPeriodicVariation[i].add(c[i].multiply(cosPhi).add(s[i].multiply(sinPhi)));
  1705.                         }

  1706.                     }
  1707.                 }
  1708.             }

  1709.             return shortPeriodicVariation;

  1710.         }

  1711.         /** {@inheritDoc} */
  1712.         @Override
  1713.         public String getCoefficientsKeyPrefix() {
  1714.             return DSSTTesseral.SHORT_PERIOD_PREFIX;
  1715.         }

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

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

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

  1734.                 for (int m = 1; m <= maxOrderMdailyTesseralSP; m++) {
  1735.                     storeIfSelected(coefficients, selected, slot.getCijm(0, m, date), DSSTTesseral.CM_COEFFICIENTS, m);
  1736.                     storeIfSelected(coefficients, selected, slot.getSijm(0, m, date), DSSTTesseral.SM_COEFFICIENTS, m);
  1737.                 }

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

  1741.                     for (int j : listJ) {
  1742.                         for (int i = 0; i < 6; ++i) {
  1743.                             storeIfSelected(coefficients, selected, slot.getCijm(j, m, date), "c", j, m);
  1744.                             storeIfSelected(coefficients, selected, slot.getSijm(j, m, date), "s", j, m);
  1745.                         }
  1746.                     }
  1747.                 }

  1748.                 return coefficients;

  1749.             } else {
  1750.                 return Collections.emptyMap();
  1751.             }

  1752.         }

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

  1774.     /** Coefficients valid for one time slot. */
  1775.     private static class Slot {

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

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

  1802.         /** Simple constructor.
  1803.          *  @param mMax maximum value for m index
  1804.          *  @param jMax maximum value for j index
  1805.          *  @param interpolationPoints number of points used in the interpolation process
  1806.          */
  1807.         Slot(final int mMax, final int jMax, final int interpolationPoints) {

  1808.             final int rows    = mMax + 1;
  1809.             final int columns = 2 * jMax + 1;
  1810.             cijm = new ShortPeriodicsInterpolatedCoefficient[rows][columns];
  1811.             sijm = new ShortPeriodicsInterpolatedCoefficient[rows][columns];
  1812.             for (int m = 1; m <= mMax; m++) {
  1813.                 for (int j = -jMax; j <= jMax; j++) {
  1814.                     cijm[m][j + jMax] = new ShortPeriodicsInterpolatedCoefficient(interpolationPoints);
  1815.                     sijm[m][j + jMax] = new ShortPeriodicsInterpolatedCoefficient(interpolationPoints);
  1816.                 }
  1817.             }

  1818.         }

  1819.         /** Get C<sub>i</sub><sup>j</sup><sup>m</sup>.
  1820.          *
  1821.          * @param j j index
  1822.          * @param m m index
  1823.          * @param date the date
  1824.          * @return C<sub>i</sub><sup>j</sup><sup>m</sup>
  1825.          */
  1826.         double[] getCijm(final int j, final int m, final AbsoluteDate date) {
  1827.             final int jMax = (cijm[m].length - 1) / 2;
  1828.             return cijm[m][j + jMax].value(date);
  1829.         }

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

  1841.     }

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

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

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

  1870.         /** Simple constructor.
  1871.          *  @param mMax maximum value for m index
  1872.          *  @param jMax maximum value for j index
  1873.          *  @param interpolationPoints number of points used in the interpolation process
  1874.          */
  1875.         @SuppressWarnings("unchecked")
  1876.         FieldSlot(final int mMax, final int jMax, final int interpolationPoints) {

  1877.             final int rows    = mMax + 1;
  1878.             final int columns = 2 * jMax + 1;
  1879.             cijm = (FieldShortPeriodicsInterpolatedCoefficient<T>[][]) Array.newInstance(FieldShortPeriodicsInterpolatedCoefficient.class, rows, columns);
  1880.             sijm = (FieldShortPeriodicsInterpolatedCoefficient<T>[][]) Array.newInstance(FieldShortPeriodicsInterpolatedCoefficient.class, rows, columns);
  1881.             for (int m = 1; m <= mMax; m++) {
  1882.                 for (int j = -jMax; j <= jMax; j++) {
  1883.                     cijm[m][j + jMax] = new FieldShortPeriodicsInterpolatedCoefficient<>(interpolationPoints);
  1884.                     sijm[m][j + jMax] = new FieldShortPeriodicsInterpolatedCoefficient<>(interpolationPoints);
  1885.                 }
  1886.             }

  1887.         }

  1888.         /** Get C<sub>i</sub><sup>j</sup><sup>m</sup>.
  1889.          *
  1890.          * @param j j index
  1891.          * @param m m index
  1892.          * @param date the date
  1893.          * @return C<sub>i</sub><sup>j</sup><sup>m</sup>
  1894.          */
  1895.         T[] getCijm(final int j, final int m, final FieldAbsoluteDate<T> date) {
  1896.             final int jMax = (cijm[m].length - 1) / 2;
  1897.             return cijm[m][j + jMax].value(date);
  1898.         }

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

  1910.     }

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

  1925.         /** dU / da. */
  1926.         private  double dUda;

  1927.         /** dU / dk. */
  1928.         private double dUdk;

  1929.         /** dU / dh. */
  1930.         private double dUdh;

  1931.         /** dU / dl. */
  1932.         private double dUdl;

  1933.         /** dU / dAlpha. */
  1934.         private double dUdAl;

  1935.         /** dU / dBeta. */
  1936.         private double dUdBe;

  1937.         /** dU / dGamma. */
  1938.         private double dUdGa;

  1939.         /** Simple constuctor.
  1940.          * @param date current date
  1941.          * @param context container for attributes
  1942.          * @param hansen hansen objects
  1943.          */
  1944.         UAnddU(final AbsoluteDate date, final DSSTTesseralContext context, final HansenObjects hansen) {

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

  1947.             // Potential derivatives
  1948.             dUda  = 0.;
  1949.             dUdh  = 0.;
  1950.             dUdk  = 0.;
  1951.             dUdl  = 0.;
  1952.             dUdAl = 0.;
  1953.             dUdBe = 0.;
  1954.             dUdGa = 0.;

  1955.             // Compute only if there is at least one resonant tesseral
  1956.             if (!context.getResOrders().isEmpty()) {
  1957.                 // Gmsj and Hmsj polynomials
  1958.                 final GHmsjPolynomials ghMSJ = new GHmsjPolynomials(auxiliaryElements.getK(), auxiliaryElements.getH(), auxiliaryElements.getAlpha(), auxiliaryElements.getBeta(), I);

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

  1961.                 // R / a up to power degree
  1962.                 final double[] roaPow = new double[maxDegree + 1];
  1963.                 roaPow[0] = 1.;
  1964.                 for (int i = 1; i <= maxDegree; i++) {
  1965.                     roaPow[i] = context.getRoa() * roaPow[i - 1];
  1966.                 }

  1967.                 // SUM over resonant terms {j,m}
  1968.                 for (int m : context.getResOrders()) {

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

  1971.                     // Phase angle
  1972.                     final double jlMmt  = j * auxiliaryElements.getLM() - m * context.getTheta();
  1973.                     final SinCos scPhi  = FastMath.sinCos(jlMmt);
  1974.                     final double sinPhi = scPhi.sin();
  1975.                     final double cosPhi = scPhi.cos();

  1976.                     // Potential derivatives components for a given resonant pair {j,m}
  1977.                     double dUdaCos  = 0.;
  1978.                     double dUdaSin  = 0.;
  1979.                     double dUdhCos  = 0.;
  1980.                     double dUdhSin  = 0.;
  1981.                     double dUdkCos  = 0.;
  1982.                     double dUdkSin  = 0.;
  1983.                     double dUdlCos  = 0.;
  1984.                     double dUdlSin  = 0.;
  1985.                     double dUdAlCos = 0.;
  1986.                     double dUdAlSin = 0.;
  1987.                     double dUdBeCos = 0.;
  1988.                     double dUdBeSin = 0.;
  1989.                     double dUdGaCos = 0.;
  1990.                     double dUdGaSin = 0.;

  1991.                     // s-SUM from -sMin to sMax
  1992.                     final int sMin = FastMath.min(context.getMaxEccPow() - j, maxDegree);
  1993.                     final int sMax = FastMath.min(context.getMaxEccPow() + j, maxDegree);
  1994.                     for (int s = 0; s <= sMax; s++) {

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

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

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

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

  2036.                     // Assembly of potential derivatives componants
  2037.                     dUda  += cosPhi * dUdaCos  + sinPhi * dUdaSin;
  2038.                     dUdh  += cosPhi * dUdhCos  + sinPhi * dUdhSin;
  2039.                     dUdk  += cosPhi * dUdkCos  + sinPhi * dUdkSin;
  2040.                     dUdl  += cosPhi * dUdlCos  + sinPhi * dUdlSin;
  2041.                     dUdAl += cosPhi * dUdAlCos + sinPhi * dUdAlSin;
  2042.                     dUdBe += cosPhi * dUdBeCos + sinPhi * dUdBeSin;
  2043.                     dUdGa += cosPhi * dUdGaCos + sinPhi * dUdGaSin;
  2044.                 }

  2045.                 this.dUda  = dUda * (-context.getMoa() / auxiliaryElements.getSma());
  2046.                 this.dUdh  = dUdh * context.getMoa();
  2047.                 this.dUdk  = dUdk * context.getMoa();
  2048.                 this.dUdl  = dUdl * context.getMoa();
  2049.                 this.dUdAl = dUdAl * context.getMoa();
  2050.                 this.dUdBe = dUdBe * context.getMoa();
  2051.                 this.dUdGa = dUdGa * context.getMoa();
  2052.             }

  2053.         }

  2054.         /** Return value of dU / da.
  2055.          * @return dUda
  2056.          */
  2057.         public double getdUda() {
  2058.             return dUda;
  2059.         }

  2060.         /** Return value of dU / dk.
  2061.          * @return dUdk
  2062.          */
  2063.         public double getdUdk() {
  2064.             return dUdk;
  2065.         }

  2066.         /** Return value of dU / dh.
  2067.          * @return dUdh
  2068.          */
  2069.         public double getdUdh() {
  2070.             return dUdh;
  2071.         }

  2072.         /** Return value of dU / dl.
  2073.          * @return dUdl
  2074.          */
  2075.         public double getdUdl() {
  2076.             return dUdl;
  2077.         }

  2078.         /** Return value of dU / dAlpha.
  2079.          * @return dUdAl
  2080.          */
  2081.         public double getdUdAl() {
  2082.             return dUdAl;
  2083.         }

  2084.         /** Return value of dU / dBeta.
  2085.          * @return dUdBe
  2086.          */
  2087.         public double getdUdBe() {
  2088.             return dUdBe;
  2089.         }

  2090.         /** Return value of dU / dGamma.
  2091.          * @return dUdGa
  2092.          */
  2093.         public double getdUdGa() {
  2094.             return dUdGa;
  2095.         }

  2096.     }

  2097.     /**  Computes the potential U derivatives.
  2098.      *  <p>The following elements are computed from expression 3.3 - (4).
  2099.      *  <pre>
  2100.      *  dU / da
  2101.      *  dU / dh
  2102.      *  dU / dk
  2103.      *  dU / dλ
  2104.      *  dU / dα
  2105.      *  dU / dβ
  2106.      *  dU / dγ
  2107.      *  </pre>
  2108.      *  </p>
  2109.      */
  2110.     private class FieldUAnddU <T extends RealFieldElement<T>> {

  2111.         /** dU / da. */
  2112.         private T dUda;

  2113.         /** dU / dk. */
  2114.         private T dUdk;

  2115.         /** dU / dh. */
  2116.         private T dUdh;

  2117.         /** dU / dl. */
  2118.         private T dUdl;

  2119.         /** dU / dAlpha. */
  2120.         private T dUdAl;

  2121.         /** dU / dBeta. */
  2122.         private T dUdBe;

  2123.         /** dU / dGamma. */
  2124.         private T dUdGa;

  2125.         /** Simple constuctor.
  2126.          * @param date current date
  2127.          * @param context container for attributes
  2128.          * @param hansen hansen objects
  2129.          */
  2130.         FieldUAnddU(final FieldAbsoluteDate<T> date, final FieldDSSTTesseralContext<T> context,
  2131.                     final FieldHansenObjects<T> hansen) {

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

  2134.             // Zero for initialization
  2135.             final Field<T> field = date.getField();
  2136.             final T zero = field.getZero();

  2137.             // Potential derivatives
  2138.             dUda  = zero;
  2139.             dUdh  = zero;
  2140.             dUdk  = zero;
  2141.             dUdl  = zero;
  2142.             dUdAl = zero;
  2143.             dUdBe = zero;
  2144.             dUdGa = zero;

  2145.             // Compute only if there is at least one resonant tesseral
  2146.             if (!context.getResOrders().isEmpty()) {
  2147.                 // Gmsj and Hmsj polynomials
  2148.                 final FieldGHmsjPolynomials<T> ghMSJ = new FieldGHmsjPolynomials<>(auxiliaryElements.getK(), auxiliaryElements.getH(), auxiliaryElements.getAlpha(), auxiliaryElements.getBeta(), I, field);

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

  2151.                 // R / a up to power degree
  2152.                 final T[] roaPow = MathArrays.buildArray(field, maxDegree + 1);
  2153.                 roaPow[0] = zero.add(1.);
  2154.                 for (int i = 1; i <= maxDegree; i++) {
  2155.                     roaPow[i] = roaPow[i - 1].multiply(context.getRoa());
  2156.                 }

  2157.                 // SUM over resonant terms {j,m}
  2158.                 for (int m : context.getResOrders()) {

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

  2161.                     // Phase angle
  2162.                     final T jlMmt              = auxiliaryElements.getLM().multiply(j).subtract(context.getTheta().multiply(m));
  2163.                     final FieldSinCos<T> scPhi = FastMath.sinCos(jlMmt);
  2164.                     final T sinPhi             = scPhi.sin();
  2165.                     final T cosPhi             = scPhi.cos();

  2166.                     // Potential derivatives components for a given resonant pair {j,m}
  2167.                     T dUdaCos  = zero;
  2168.                     T dUdaSin  = zero;
  2169.                     T dUdhCos  = zero;
  2170.                     T dUdhSin  = zero;
  2171.                     T dUdkCos  = zero;
  2172.                     T dUdkSin  = zero;
  2173.                     T dUdlCos  = zero;
  2174.                     T dUdlSin  = zero;
  2175.                     T dUdAlCos = zero;
  2176.                     T dUdAlSin = zero;
  2177.                     T dUdBeCos = zero;
  2178.                     T dUdBeSin = zero;
  2179.                     T dUdGaCos = zero;
  2180.                     T dUdGaSin = zero;

  2181.                     // s-SUM from -sMin to sMax
  2182.                     final int sMin = FastMath.min(context.getMaxEccPow() - j, maxDegree);
  2183.                     final int sMax = FastMath.min(context.getMaxEccPow() + j, maxDegree);
  2184.                     for (int s = 0; s <= sMax; s++) {

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

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

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

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

  2226.                     // Assembly of potential derivatives componants
  2227.                     dUda  = dUda.add(dUdaCos.multiply(cosPhi).add(dUdaSin.multiply(sinPhi)));
  2228.                     dUdh  = dUdh.add(dUdhCos.multiply(cosPhi).add(dUdhSin.multiply(sinPhi)));
  2229.                     dUdk  = dUdk.add(dUdkCos.multiply(cosPhi).add(dUdkSin.multiply(sinPhi)));
  2230.                     dUdl  = dUdl.add(dUdlCos.multiply(cosPhi).add(dUdlSin.multiply(sinPhi)));
  2231.                     dUdAl = dUdAl.add(dUdAlCos.multiply(cosPhi).add(dUdAlSin.multiply(sinPhi)));
  2232.                     dUdBe = dUdBe.add(dUdBeCos.multiply(cosPhi).add(dUdBeSin.multiply(sinPhi)));
  2233.                     dUdGa = dUdGa.add(dUdGaCos.multiply(cosPhi).add(dUdGaSin.multiply(sinPhi)));
  2234.                 }

  2235.                 dUda  =  dUda.multiply(context.getMoa().divide(auxiliaryElements.getSma())).negate();
  2236.                 dUdh  =  dUdh.multiply(context.getMoa());
  2237.                 dUdk  =  dUdk.multiply(context.getMoa());
  2238.                 dUdl  =  dUdl.multiply(context.getMoa());
  2239.                 dUdAl =  dUdAl.multiply(context.getMoa());
  2240.                 dUdBe =  dUdBe.multiply(context.getMoa());
  2241.                 dUdGa =  dUdGa.multiply(context.getMoa());

  2242.             }

  2243.         }

  2244.         /** Return value of dU / da.
  2245.          * @return dUda
  2246.          */
  2247.         public T getdUda() {
  2248.             return dUda;
  2249.         }

  2250.         /** Return value of dU / dk.
  2251.          * @return dUdk
  2252.          */
  2253.         public T getdUdk() {
  2254.             return dUdk;
  2255.         }

  2256.         /** Return value of dU / dh.
  2257.          * @return dUdh
  2258.          */
  2259.         public T getdUdh() {
  2260.             return dUdh;
  2261.         }

  2262.         /** Return value of dU / dl.
  2263.          * @return dUdl
  2264.          */
  2265.         public T getdUdl() {
  2266.             return dUdl;
  2267.         }

  2268.         /** Return value of dU / dAlpha.
  2269.          * @return dUdAl
  2270.          */
  2271.         public T getdUdAl() {
  2272.             return dUdAl;
  2273.         }

  2274.         /** Return value of dU / dBeta.
  2275.          * @return dUdBe
  2276.          */
  2277.         public T getdUdBe() {
  2278.             return dUdBe;
  2279.         }

  2280.         /** Return value of dU / dGamma.
  2281.          * @return dUdGa
  2282.          */
  2283.         public T getdUdGa() {
  2284.             return dUdGa;
  2285.         }

  2286.     }

  2287.     /** Computes init values of the Hansen Objects. */
  2288.     private class HansenObjects {

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

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

  2294.         /** Simple constructor.
  2295.          * @param ratio Ratio of satellite period to central body rotation period
  2296.          * @param maxEccPow Maximum power of the eccentricity to use in summation over s.
  2297.          * @param resOrders List of resonant orders
  2298.          * @param type type of the elements used during the propagation
  2299.          */
  2300.         HansenObjects(final double ratio,
  2301.                       final int maxEccPow,
  2302.                       final List<Integer> resOrders,
  2303.                       final PropagationType type) {

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

  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 RealFieldElement<T>> {

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

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

  2368.         /** Simple constructor.
  2369.          * @param ratio Ratio of satellite period to central body rotation period
  2370.          * @param maxEccPow Maximum power of the eccentricity to use in summation over s.
  2371.          * @param resOrders List of resonant orders
  2372.          * @param type type of the elements used during the propagation
  2373.          * @param field field used by default.
  2374.          */
  2375.         @SuppressWarnings("unchecked")
  2376.         FieldHansenObjects(final T ratio,
  2377.                            final int maxEccPow,
  2378.                            final List<Integer> resOrders,
  2379.                            final PropagationType type,
  2380.                            final Field<T> field) {

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

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

  2387.             switch (type) {
  2388.                 case MEAN:
  2389.                  // loop through the resonant orders
  2390.                     for (int m : resOrders) {
  2391.                         //Compute the corresponding j term
  2392.                         final int j = FastMath.max(1, (int) FastMath.round(ratio.multiply(m)));

  2393.                         //Compute the sMin and sMax values
  2394.                         final int sMin = FastMath.min(maxEccPow - j, maxDegree);
  2395.                         final int sMax = FastMath.min(maxEccPow + j, maxDegree);

  2396.                         //loop through the s values
  2397.                         for (int s = 0; s <= sMax; s++) {
  2398.                             //Compute the n0 value
  2399.                             final int n0 = FastMath.max(FastMath.max(2, m), s);

  2400.                             //Create the object for the pair j, s
  2401.                             this.hansenObjects[s + maxDegree][j] = new FieldHansenTesseralLinear<>(maxDegree, s, j, n0, maxHansen, field);

  2402.                             if (s > 0 && s <= sMin) {
  2403.                                 //Also create the object for the pair j, -s
  2404.                                 this.hansenObjects[maxDegree - s][j] =  new FieldHansenTesseralLinear<>(maxDegree, -s, j, n0, maxHansen, field);
  2405.                             }
  2406.                         }
  2407.                     }
  2408.                     break;

  2409.                 case OSCULATING:
  2410.                     // create all objects
  2411.                     for (int j = 0; j <= maxFrequencyShortPeriodics; j++) {
  2412.                         for (int s = -maxDegree; s <= maxDegree; s++) {
  2413.                             //Compute the n0 value
  2414.                             final int n0 = FastMath.max(2, FastMath.abs(s));
  2415.                             this.hansenObjects[s + maxDegree][j] = new FieldHansenTesseralLinear<>(maxDegree, s, j, n0, maxHansen, field);
  2416.                         }
  2417.                     }
  2418.                     break;

  2419.                 default:
  2420.                     throw new OrekitInternalError(null);
  2421.             }

  2422.         }

  2423.         /** Compute init values for hansen objects.
  2424.          * @param context container for attributes
  2425.          * @param rows number of rows of the hansen matrix
  2426.          * @param columns columns number of columns of the hansen matrix
  2427.          */
  2428.         public void computeHansenObjectsInitValues(final FieldDSSTTesseralContext<T> context,
  2429.                                                    final int rows, final int columns) {
  2430.             hansenObjects[rows][columns].computeInitValues(context.getE2(), context.getChi(), context.getChi2());
  2431.         }

  2432.         /** Get hansen object.
  2433.          * @return hansenObjects
  2434.          */
  2435.         public FieldHansenTesseralLinear<T>[][] getHansenObjects() {
  2436.             return hansenObjects;
  2437.         }

  2438.     }

  2439. }