DSSTTesseral.java

  1. /* Copyright 2002-2019 CS Systèmes d'Information
  2.  * Licensed to CS Systèmes d'Information (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.DSFactory;
  31. import org.hipparchus.analysis.differentiation.DerivativeStructure;
  32. import org.hipparchus.analysis.differentiation.FDSFactory;
  33. import org.hipparchus.analysis.differentiation.FieldDerivativeStructure;
  34. import org.hipparchus.exception.LocalizedCoreFormats;
  35. import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
  36. import org.hipparchus.geometry.euclidean.threed.Vector3D;
  37. import org.hipparchus.util.FastMath;
  38. import org.hipparchus.util.MathArrays;
  39. import org.hipparchus.util.MathUtils;
  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.     /** Factory for the DerivativeStructure instances. */
  162.     private DSFactory factory;

  163.     /** Factory for the DerivativeStructure instances. */
  164.     private Map<Field<?>, FDSFactory<?>> fieldFactory;

  165.     /** Flag for force model initialization with field elements. */
  166.     private boolean pendingInitialization;

  167.     /** Simple constructor.
  168.      * @param centralBodyFrame rotating body frame
  169.      * @param centralBodyRotationRate central body rotation rate (rad/s)
  170.      * @param provider provider for spherical harmonics
  171.      * @param maxDegreeTesseralSP maximal degree to consider for short periodics tesseral harmonics potential
  172.      *  (must be between 2 and {@code provider.getMaxDegree()})
  173.      * @param maxOrderTesseralSP maximal order to consider for short periodics tesseral harmonics potential
  174.      *  (must be between 0 and {@code provider.getMaxOrder()})
  175.      * @param maxEccPowTesseralSP maximum power of the eccentricity to use in summation over s for
  176.      * short periodic tesseral harmonics (without m-daily), should typically not exceed 4 as higher
  177.      * values will exceed computer capacity
  178.      * @param maxFrequencyShortPeriodics maximum frequency in mean longitude for short periodic computations
  179.      * (typically {@code maxDegreeTesseralSP} + {@code maxEccPowTesseralSP and no more than 12})
  180.      * @param maxDegreeMdailyTesseralSP maximal degree to consider for short periodics m-daily tesseral harmonics potential
  181.      *  (must be between 2 and {@code provider.getMaxDegree()})
  182.      * @param maxOrderMdailyTesseralSP maximal order to consider for short periodics m-daily tesseral harmonics potential
  183.      *  (must be between 0 and {@code provider.getMaxOrder()})
  184.      * @param maxEccPowMdailyTesseralSP maximum power of the eccentricity to use in summation over s for
  185.      * m-daily tesseral harmonics, (must be between 0 and {@code maxDegreeMdailyTesseralSP - 2},
  186.      * but should typically not exceed 4 as higher values will exceed computer capacity)
  187.      * @since 7.2
  188.      */
  189.     public DSSTTesseral(final Frame centralBodyFrame,
  190.                         final double centralBodyRotationRate,
  191.                         final UnnormalizedSphericalHarmonicsProvider provider,
  192.                         final int maxDegreeTesseralSP, final int maxOrderTesseralSP,
  193.                         final int maxEccPowTesseralSP, final int maxFrequencyShortPeriodics,
  194.                         final int maxDegreeMdailyTesseralSP, final int maxOrderMdailyTesseralSP,
  195.                         final int maxEccPowMdailyTesseralSP) {

  196.         gmParameterDriver = new ParameterDriver(DSSTNewtonianAttraction.CENTRAL_ATTRACTION_COEFFICIENT,
  197.                                                 provider.getMu(), MU_SCALE,
  198.                                                 0.0, Double.POSITIVE_INFINITY);

  199.         // Central body rotating frame
  200.         this.bodyFrame = centralBodyFrame;

  201.         //Save the rotation rate
  202.         this.centralBodyRotationRate = centralBodyRotationRate;

  203.         // Central body rotation period in seconds
  204.         this.bodyPeriod = MathUtils.TWO_PI / centralBodyRotationRate;

  205.         // Provider for spherical harmonics
  206.         this.provider      = provider;
  207.         this.maxDegree     = provider.getMaxDegree();
  208.         this.maxOrder      = provider.getMaxOrder();

  209.         //set the maximum degree order for short periodics
  210.         checkIndexRange(maxDegreeTesseralSP, 2, maxDegree);
  211.         this.maxDegreeTesseralSP       = maxDegreeTesseralSP;

  212.         checkIndexRange(maxDegreeMdailyTesseralSP, 2, maxDegree);
  213.         this.maxDegreeMdailyTesseralSP = maxDegreeMdailyTesseralSP;

  214.         checkIndexRange(maxOrderTesseralSP, 0, maxOrder);
  215.         this.maxOrderTesseralSP        = maxOrderTesseralSP;

  216.         checkIndexRange(maxOrderMdailyTesseralSP, 0, maxOrder);
  217.         this.maxOrderMdailyTesseralSP  = maxOrderMdailyTesseralSP;

  218.         // set the maximum value for eccentricity power
  219.         this.maxEccPowTesseralSP       = maxEccPowTesseralSP;

  220.         checkIndexRange(maxEccPowMdailyTesseralSP, 0, maxDegreeMdailyTesseralSP - 2);
  221.         this.maxEccPowMdailyTesseralSP = maxEccPowMdailyTesseralSP;

  222.         // set the maximum value for frequency
  223.         this.maxFrequencyShortPeriodics = maxFrequencyShortPeriodics;

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

  226.         pendingInitialization = true;

  227.         fieldShortPeriodTerms = new HashMap<>();
  228.         fieldHansen           = new HashMap<>();
  229.         fieldFactory          = new HashMap<>();

  230.     }

  231.     /** Check an index range.
  232.      * @param index index value
  233.      * @param min minimum value for index
  234.      * @param max maximum value for index
  235.      */
  236.     private void checkIndexRange(final int index, final int min, final int max) {
  237.         if (index < min || index > max) {
  238.             throw new OrekitException(LocalizedCoreFormats.OUT_OF_RANGE_SIMPLE, index, min, max);
  239.         }
  240.     }

  241.     /** {@inheritDoc} */
  242.     @Override
  243.     public List<ShortPeriodTerms> initialize(final AuxiliaryElements auxiliaryElements,
  244.                                              final PropagationType type,
  245.                                              final double[] parameters) {

  246.         // Initializes specific parameters.
  247.         final DSSTTesseralContext context = initializeStep(auxiliaryElements, parameters);

  248.         // The following terms are only used for hansen objects initialization
  249.         final double ratio            = context.getRatio();
  250.         final int maxEccPow           = context.getMaxEccPow();
  251.         final List<Integer> resOrders = context.getResOrders();

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

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

  255.         factory = new DSFactory(1, 1);

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

  257.         shortPeriodTerms = new TesseralShortPeriodicCoefficients(bodyFrame, maxOrderMdailyTesseralSP,
  258.                                                                  maxDegreeTesseralSP < 0, nonResOrders,
  259.                                                                  mMax, maxFrequencyShortPeriodics, INTERPOLATION_POINTS,
  260.                                                                  new TimeSpanMap<Slot>(new Slot(mMax, maxFrequencyShortPeriodics,
  261.                                                                                                 INTERPOLATION_POINTS)));

  262.         final List<ShortPeriodTerms> list = new ArrayList<ShortPeriodTerms>();
  263.         list.add(shortPeriodTerms);
  264.         return list;

  265.     }

  266.     /** {@inheritDoc} */
  267.     @Override
  268.     public <T extends RealFieldElement<T>> List<FieldShortPeriodTerms<T>> initialize(final FieldAuxiliaryElements<T> auxiliaryElements,
  269.                                                                                      final PropagationType type,
  270.                                                                                      final T[] parameters) {

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

  273.         if (pendingInitialization == true) {

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

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

  278.             // The following terms are only used for hansen objects initialization
  279.             final T      ratio            = context.getRatio();
  280.             final int maxEccPow           = context.getMaxEccPow();
  281.             final List<Integer> resOrders = context.getResOrders();


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

  283.             fieldFactory.put(field, new FDSFactory<>(field, 1, 1));
  284.             fieldHansen.put(field, new FieldHansenObjects<>(ratio, maxEccPow, resOrders, type, field));

  285.             pendingInitialization = false;
  286.         }

  287.         final FieldTesseralShortPeriodicCoefficients<T> ftspc =
  288.                         new FieldTesseralShortPeriodicCoefficients<>(bodyFrame, maxOrderMdailyTesseralSP,
  289.                                                                      maxDegreeTesseralSP < 0, nonResOrders,
  290.                                                                      mMax, maxFrequencyShortPeriodics, INTERPOLATION_POINTS,
  291.                                                                      new FieldTimeSpanMap<>(new FieldSlot<>(mMax,
  292.                                                                                                             maxFrequencyShortPeriodics,
  293.                                                                                                             INTERPOLATION_POINTS),
  294.                                                                                             field));

  295.         fieldShortPeriodTerms.put(field, ftspc);
  296.         return Collections.singletonList(ftspc);

  297.     }

  298.     /** Performs initialization at each integration step for the current force model.
  299.      *  <p>
  300.      *  This method aims at being called before mean elements rates computation.
  301.      *  </p>
  302.      *  @param auxiliaryElements auxiliary elements related to the current orbit
  303.      *  @param parameters values of the force model parameters
  304.      *  @return new force model context
  305.      */
  306.     private DSSTTesseralContext initializeStep(final AuxiliaryElements auxiliaryElements, final double[] parameters) {
  307.         return new DSSTTesseralContext(auxiliaryElements, bodyFrame, provider, maxFrequencyShortPeriodics, bodyPeriod, parameters);
  308.     }

  309.     /** Performs initialization at each integration step for the current force model.
  310.      *  <p>
  311.      *  This method aims at being called before mean elements rates computation.
  312.      *  </p>
  313.      *  @param <T> type of the elements
  314.      *  @param auxiliaryElements auxiliary elements related to the current orbit
  315.      *  @param parameters values of the force model parameters
  316.      *  @return new force model context
  317.      */
  318.     private <T extends RealFieldElement<T>> FieldDSSTTesseralContext<T> initializeStep(final FieldAuxiliaryElements<T> auxiliaryElements,
  319.                                                                                        final T[] parameters) {
  320.         return new FieldDSSTTesseralContext<>(auxiliaryElements, bodyFrame, provider, maxFrequencyShortPeriodics, bodyPeriod, parameters);
  321.     }

  322.     /** {@inheritDoc} */
  323.     @Override
  324.     public double[] getMeanElementRate(final SpacecraftState spacecraftState,
  325.                                        final AuxiliaryElements auxiliaryElements, final double[] parameters) {

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

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

  330.         // Compute the cross derivative operator :
  331.         final double UAlphaGamma   = auxiliaryElements.getAlpha() * udu.getdUdGa() - auxiliaryElements.getGamma() * udu.getdUdAl();
  332.         final double UAlphaBeta    = auxiliaryElements.getAlpha() * udu.getdUdBe() - auxiliaryElements.getBeta()  * udu.getdUdAl();
  333.         final double UBetaGamma    = auxiliaryElements.getBeta() * udu.getdUdGa() - auxiliaryElements.getGamma() * udu.getdUdBe();
  334.         final double Uhk           = auxiliaryElements.getH() * udu.getdUdk()  - auxiliaryElements.getK() * udu.getdUdh();
  335.         final double pUagmIqUbgoAB = (auxiliaryElements.getP() * UAlphaGamma - I * auxiliaryElements.getQ() * UBetaGamma) * context.getOoAB();
  336.         final double UhkmUabmdUdl  = Uhk - UAlphaBeta - udu.getdUdl();

  337.         final double da =  context.getAx2oA() * udu.getdUdl();
  338.         final double dh =  context.getBoA() * udu.getdUdk() + auxiliaryElements.getK() * pUagmIqUbgoAB - auxiliaryElements.getH() * context.getBoABpo() * udu.getdUdl();
  339.         final double dk =  -(context.getBoA() * udu.getdUdh() + auxiliaryElements.getH() * pUagmIqUbgoAB + auxiliaryElements.getK() * context.getBoABpo() * udu.getdUdl());
  340.         final double dp =  context.getCo2AB() * (auxiliaryElements.getP() * UhkmUabmdUdl - UBetaGamma);
  341.         final double dq =  context.getCo2AB() * (auxiliaryElements.getQ() * UhkmUabmdUdl - I * UAlphaGamma);
  342.         final double dM = -context.getAx2oA() * udu.getdUda() + context.getBoABpo() * (auxiliaryElements.getH() * udu.getdUdh() + auxiliaryElements.getK() * udu.getdUdk()) + pUagmIqUbgoAB;

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

  345.     /** {@inheritDoc} */
  346.     @Override
  347.     public <T extends RealFieldElement<T>> T[] getMeanElementRate(final FieldSpacecraftState<T> spacecraftState,
  348.                                                                   final FieldAuxiliaryElements<T> auxiliaryElements,
  349.                                                                   final T[] parameters) {

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

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

  354.         @SuppressWarnings("unchecked")
  355.         final FieldHansenObjects<T> fho = (FieldHansenObjects<T>) fieldHansen.get(field);
  356.         // Access to potential U derivatives
  357.         final FieldUAnddU<T> udu = new FieldUAnddU<>(spacecraftState.getDate(), context, fho);

  358.         // Compute the cross derivative operator :
  359.         final T UAlphaGamma   = udu.getdUdGa().multiply(auxiliaryElements.getAlpha()).subtract(udu.getdUdAl().multiply(auxiliaryElements.getGamma()));
  360.         final T UAlphaBeta    = udu.getdUdBe().multiply(auxiliaryElements.getAlpha()).subtract(udu.getdUdAl().multiply(auxiliaryElements.getBeta()));
  361.         final T UBetaGamma    = udu.getdUdGa().multiply(auxiliaryElements.getBeta()).subtract(udu.getdUdBe().multiply(auxiliaryElements.getGamma()));
  362.         final T Uhk           = udu.getdUdk().multiply(auxiliaryElements.getH()).subtract(udu.getdUdh().multiply(auxiliaryElements.getK()));
  363.         final T pUagmIqUbgoAB = (UAlphaGamma.multiply(auxiliaryElements.getP()).subtract(UBetaGamma.multiply(auxiliaryElements.getQ()).multiply(I))).multiply(context.getOoAB());
  364.         final T UhkmUabmdUdl  = Uhk.subtract(UAlphaBeta).subtract(udu.getdUdl());

  365.         final T da = udu.getdUdl().multiply(context.getAx2oA());
  366.         final T dh = udu.getdUdk().multiply(context.getBoA()).add(pUagmIqUbgoAB.multiply(auxiliaryElements.getK())).subtract(udu.getdUdl().multiply(auxiliaryElements.getH()).multiply(context.getBoABpo()));
  367.         final T dk = (udu.getdUdh().multiply(context.getBoA()).add(pUagmIqUbgoAB.multiply(auxiliaryElements.getH())).add(udu.getdUdl().multiply(context.getBoABpo()).multiply(auxiliaryElements.getK()))).negate();
  368.         final T dp = context.getCo2AB().multiply(auxiliaryElements.getP().multiply(UhkmUabmdUdl).subtract(UBetaGamma));
  369.         final T dq = context.getCo2AB().multiply(auxiliaryElements.getQ().multiply(UhkmUabmdUdl).subtract(UAlphaGamma.multiply(I)));
  370.         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()));

  371.         final T[] elements = MathArrays.buildArray(field, 6);
  372.         elements[0] = da;
  373.         elements[1] = dk;
  374.         elements[2] = dh;
  375.         elements[3] = dq;
  376.         elements[4] = dp;
  377.         elements[5] = dM;

  378.         return elements;

  379.     }

  380.     /** {@inheritDoc} */
  381.     @Override
  382.     public void updateShortPeriodTerms(final double[] parameters, final SpacecraftState... meanStates) {

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

  384.         for (final SpacecraftState meanState : meanStates) {

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

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

  387.             // Initialise the Hansen coefficients
  388.             for (int s = -maxDegree; s <= maxDegree; s++) {
  389.                 // coefficients with j == 0 are always needed
  390.                 hansen.computeHansenObjectsInitValues(context, s + maxDegree, 0);
  391.                 if (maxDegreeTesseralSP >= 0) {
  392.                     // initialize other objects only if required
  393.                     for (int j = 1; j <= maxFrequencyShortPeriodics; j++) {
  394.                         hansen.computeHansenObjectsInitValues(context, s + maxDegree, j);
  395.                     }
  396.                 }
  397.             }

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

  399.             // Compute coefficients
  400.             // Compute only if there is at least one non-resonant tesseral
  401.             if (!nonResOrders.isEmpty() || maxDegreeTesseralSP < 0) {
  402.                 // Generate the fourrier coefficients
  403.                 cjsjFourier.generateCoefficients(meanState.getDate(), context, hansen);

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

  406.                 // build the mDaily coefficients
  407.                 for (int m = 1; m <= maxOrderMdailyTesseralSP; m++) {
  408.                     // build the coefficients
  409.                     buildCoefficients(cjsjFourier, meanState.getDate(), slot, m, 0, tnota, context);
  410.                 }

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

  414.                         for (int j : entry.getValue()) {
  415.                             // build the coefficients
  416.                             buildCoefficients(cjsjFourier, meanState.getDate(), slot, entry.getKey(), j, tnota, context);
  417.                         }
  418.                     }
  419.                 }
  420.             }

  421.         }

  422.     }

  423.     /** {@inheritDoc} */
  424.     @Override
  425.     @SuppressWarnings("unchecked")
  426.     public <T extends RealFieldElement<T>> void updateShortPeriodTerms(final T[] parameters,
  427.                                                                        final FieldSpacecraftState<T>... meanStates) {

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

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

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

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

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

  435.             final FieldHansenObjects<T> fho = (FieldHansenObjects<T>) fieldHansen.get(field);
  436.             // Initialise the Hansen coefficients
  437.             for (int s = -maxDegree; s <= maxDegree; s++) {
  438.                 // coefficients with j == 0 are always needed
  439.                 fho.computeHansenObjectsInitValues(context, s + maxDegree, 0);
  440.                 if (maxDegreeTesseralSP >= 0) {
  441.                     // initialize other objects only if required
  442.                     for (int j = 1; j <= maxFrequencyShortPeriodics; j++) {
  443.                         fho.computeHansenObjectsInitValues(context, s + maxDegree, j);
  444.                     }
  445.                 }
  446.             }

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

  448.             // Compute coefficients
  449.             // Compute only if there is at least one non-resonant tesseral
  450.             if (!nonResOrders.isEmpty() || maxDegreeTesseralSP < 0) {
  451.                 // Generate the fourrier coefficients
  452.                 cjsjFourier.generateCoefficients(meanState.getDate(), context, fho, field);

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

  455.                 // build the mDaily coefficients
  456.                 for (int m = 1; m <= maxOrderMdailyTesseralSP; m++) {
  457.                     // build the coefficients
  458.                     buildCoefficients(cjsjFourier, meanState.getDate(), slot, m, 0, tnota, context, field);
  459.                 }

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

  463.                         for (int j : entry.getValue()) {
  464.                             // build the coefficients
  465.                             buildCoefficients(cjsjFourier, meanState.getDate(), slot, entry.getKey(), j, tnota, context, field);
  466.                         }
  467.                     }
  468.                 }
  469.             }

  470.         }

  471.     }

  472.     /** {@inheritDoc} */
  473.     public ParameterDriver[] getParametersDrivers() {
  474.         return new ParameterDriver[] {
  475.             gmParameterDriver
  476.         };
  477.     }

  478.     /** Build a set of coefficients.
  479.      * @param cjsjFourier the fourier coefficients C<sub>i</sub><sup>j</sup> and the S<sub>i</sub><sup>j</sup>
  480.      * @param date the current date
  481.      * @param slot slot to which the coefficients belong
  482.      * @param m m index
  483.      * @param j j index
  484.      * @param tnota 3n/2a
  485.      * @param context container for attributes
  486.      */
  487.     private void buildCoefficients(final FourierCjSjCoefficients cjsjFourier,
  488.                                    final AbsoluteDate date, final Slot slot,
  489.                                    final int m, final int j, final double tnota, final DSSTTesseralContext context) {

  490.         // Create local arrays
  491.         final double[] currentCijm = new double[] {0., 0., 0., 0., 0., 0.};
  492.         final double[] currentSijm = new double[] {0., 0., 0., 0., 0., 0.};

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

  495.         // initialise the coeficients
  496.         for (int i = 0; i < 6; i++) {
  497.             currentCijm[i] = -cjsjFourier.getSijm(i, j, m);
  498.             currentSijm[i] = cjsjFourier.getCijm(i, j, m);
  499.         }
  500.         // Add the separate part for δ<sub>6i</sub>
  501.         currentCijm[5] += tnota * oojnmt * cjsjFourier.getCijm(0, j, m);
  502.         currentSijm[5] += tnota * oojnmt * cjsjFourier.getSijm(0, j, m);

  503.         //Multiply by 1 / (jn - mθ<sup>.</sup>)
  504.         for (int i = 0; i < 6; i++) {
  505.             currentCijm[i] *= oojnmt;
  506.             currentSijm[i] *= oojnmt;
  507.         }

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

  511.     }

  512.      /** Build a set of coefficients.
  513.      * @param <T> the type of the field elements
  514.      * @param cjsjFourier the fourier coefficients C<sub>i</sub><sup>j</sup> and the S<sub>i</sub><sup>j</sup>
  515.      * @param date the current date
  516.      * @param slot slot to which the coefficients belong
  517.      * @param m m index
  518.      * @param j j index
  519.      * @param tnota 3n/2a
  520.      * @param context container for attributes
  521.      * @param field field used by default
  522.      */
  523.     private <T extends RealFieldElement<T>> void buildCoefficients(final FieldFourierCjSjCoefficients<T> cjsjFourier,
  524.                                                                    final FieldAbsoluteDate<T> date,
  525.                                                                    final FieldSlot<T> slot,
  526.                                                                    final int m, final int j, final T tnota,
  527.                                                                    final FieldDSSTTesseralContext<T> context,
  528.                                                                    final Field<T> field) {

  529.         // Zero
  530.         final T zero = field.getZero();

  531.         // Create local arrays
  532.         final T[] currentCijm = MathArrays.buildArray(field, 6);
  533.         final T[] currentSijm = MathArrays.buildArray(field, 6);

  534.         Arrays.fill(currentCijm, zero);
  535.         Arrays.fill(currentSijm, zero);

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

  538.         // initialise the coeficients
  539.         for (int i = 0; i < 6; i++) {
  540.             currentCijm[i] = cjsjFourier.getSijm(i, j, m).negate();
  541.             currentSijm[i] = cjsjFourier.getCijm(i, j, m);
  542.         }
  543.         // Add the separate part for δ<sub>6i</sub>
  544.         currentCijm[5] = currentCijm[5].add(tnota.multiply(oojnmt).multiply(cjsjFourier.getCijm(0, j, m)));
  545.         currentSijm[5] = currentSijm[5].add(tnota.multiply(oojnmt).multiply(cjsjFourier.getSijm(0, j, m)));

  546.         //Multiply by 1 / (jn - mθ<sup>.</sup>)
  547.         for (int i = 0; i < 6; i++) {
  548.             currentCijm[i] = currentCijm[i].multiply(oojnmt);
  549.             currentSijm[i] = currentSijm[i].multiply(oojnmt);
  550.         }

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

  554.     }

  555.     /** {@inheritDoc} */
  556.     @Override
  557.     public EventDetector[] getEventsDetectors() {
  558.         return null;
  559.     }

  560.     /** {@inheritDoc} */
  561.     @Override
  562.     public <T extends RealFieldElement<T>> FieldEventDetector<T>[] getFieldEventsDetectors(final Field<T> field) {
  563.         return null;
  564.     }

  565.      /**
  566.       * Get the non-resonant tesseral terms in the central body spherical harmonic field.
  567.       *
  568.       * @param type type of the elements used during the propagation
  569.       * @param context container for attributes
  570.       */
  571.     private void getNonResonantTerms(final PropagationType type, final DSSTTesseralContext context) {

  572.         // Compute natural resonant terms
  573.         final double tolerance = 1. / FastMath.max(MIN_PERIOD_IN_SAT_REV,
  574.                                                    MIN_PERIOD_IN_SECONDS / context.getOrbitPeriod());

  575.         nonResOrders.clear();
  576.         for (int m = 1; m <= maxOrder; m++) {
  577.             final double resonance = context.getRatio() * m;
  578.             int jRes = 0;
  579.             final int jComputedRes = (int) FastMath.round(resonance);
  580.             if (jComputedRes > 0 && jComputedRes <= maxFrequencyShortPeriodics && FastMath.abs(resonance - jComputedRes) <= tolerance) {
  581.                 // Store each resonant index and order
  582.                 jRes = jComputedRes;
  583.             }

  584.             if (type == PropagationType.OSCULATING && maxDegreeTesseralSP >= 0 && m <= maxOrderTesseralSP) {
  585.                 //compute non resonant orders in the tesseral harmonic field
  586.                 final List<Integer> listJofM = new ArrayList<Integer>();
  587.                 //for the moment we take only the pairs (j,m) with |j| <= maxDegree + maxEccPow (from |s-j| <= maxEccPow and |s| <= maxDegree)
  588.                 for (int j = -maxFrequencyShortPeriodics; j <= maxFrequencyShortPeriodics; j++) {
  589.                     if (j != 0 && j != jRes) {
  590.                         listJofM.add(j);
  591.                     }
  592.                 }

  593.                 nonResOrders.put(m, listJofM);
  594.             }
  595.         }
  596.     }

  597.     /**
  598.      * Get the non-resonant tesseral terms in the central body spherical harmonic field.
  599.      *
  600.      * @param <T> type of the elements
  601.      * @param type type of the elements used during the propagation
  602.      * @param context container for attributes
  603.      * @param field field used by default
  604.      */
  605.     private <T extends RealFieldElement<T>> void getNonResonantTerms(final PropagationType type,
  606.                                                                      final FieldDSSTTesseralContext<T> context,
  607.                                                                      final Field<T> field) {

  608.         final T zero = field.getZero();
  609.         // Compute natural resonant terms
  610.         final T tolerance = FastMath.max(zero.add(MIN_PERIOD_IN_SAT_REV),
  611.                                          context.getOrbitPeriod().divide(MIN_PERIOD_IN_SECONDS).reciprocal()).reciprocal();

  612.         nonResOrders.clear();
  613.         for (int m = 1; m <= maxOrder; m++) {
  614.             final T resonance = context.getRatio().multiply(m);
  615.             int jRes = 0;
  616.             final int jComputedRes = (int) FastMath.round(resonance);
  617.             if (jComputedRes > 0 && jComputedRes <= maxFrequencyShortPeriodics && FastMath.abs(resonance.subtract(jComputedRes)).getReal() <= tolerance.getReal()) {
  618.                 // Store each resonant index and order
  619.                 jRes = jComputedRes;
  620.             }

  621.             if (type == PropagationType.OSCULATING && maxDegreeTesseralSP >= 0 && m <= maxOrderTesseralSP) {
  622.                 //compute non resonant orders in the tesseral harmonic field
  623.                 final List<Integer> listJofM = new ArrayList<Integer>();
  624.                 //for the moment we take only the pairs (j,m) with |j| <= maxDegree + maxEccPow (from |s-j| <= maxEccPow and |s| <= maxDegree)
  625.                 for (int j = -maxFrequencyShortPeriodics; j <= maxFrequencyShortPeriodics; j++) {
  626.                     if (j != 0 && j != jRes) {
  627.                         listJofM.add(j);
  628.                     }
  629.                 }

  630.                 nonResOrders.put(m, listJofM);
  631.             }
  632.         }
  633.     }

  634.     /** Compute the n-SUM for potential derivatives components.
  635.      *  @param date current date
  636.      *  @param j resonant index <i>j</i>
  637.      *  @param m resonant order <i>m</i>
  638.      *  @param s d'Alembert characteristic <i>s</i>
  639.      *  @param maxN maximum possible value for <i>n</i> index
  640.      *  @param roaPow powers of R/a up to degree <i>n</i>
  641.      *  @param ghMSJ G<sup>j</sup><sub>m,s</sub> and H<sup>j</sup><sub>m,s</sub> polynomials
  642.      *  @param gammaMNS &Gamma;<sup>m</sup><sub>n,s</sub>(γ) function
  643.      *  @param context container for attributes
  644.      *  @param hansenObjects initialization of hansen objects
  645.      *  @return Components of U<sub>n</sub> derivatives for fixed j, m, s
  646.      */
  647.     private double[][] computeNSum(final AbsoluteDate date,
  648.                                    final int j, final int m, final int s, final int maxN, final double[] roaPow,
  649.                                    final GHmsjPolynomials ghMSJ, final GammaMnsFunction gammaMNS, final DSSTTesseralContext context,
  650.                                    final HansenObjects hansenObjects) {

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

  653.         //spherical harmonics
  654.         final UnnormalizedSphericalHarmonics harmonics = provider.onDate(date);

  655.         // Potential derivatives components
  656.         double dUdaCos  = 0.;
  657.         double dUdaSin  = 0.;
  658.         double dUdhCos  = 0.;
  659.         double dUdhSin  = 0.;
  660.         double dUdkCos  = 0.;
  661.         double dUdkSin  = 0.;
  662.         double dUdlCos  = 0.;
  663.         double dUdlSin  = 0.;
  664.         double dUdAlCos = 0.;
  665.         double dUdAlSin = 0.;
  666.         double dUdBeCos = 0.;
  667.         double dUdBeSin = 0.;
  668.         double dUdGaCos = 0.;
  669.         double dUdGaSin = 0.;

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

  673.         // jacobi v, w, indices from 2.7.1-(15)
  674.         final int v = FastMath.abs(m - s);
  675.         final int w = FastMath.abs(m + s);

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

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

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

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

  688.                 // Inclination function Gamma and derivative
  689.                 final double gaMNS  = gammaMNS.getValue(m, n, s);
  690.                 final double dGaMNS = gammaMNS.getDerivative(m, n, s);

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

  694.                 // Gjms, Hjms polynomials and derivatives
  695.                 final double gMSJ   = ghMSJ.getGmsj(m, s, j);
  696.                 final double hMSJ   = ghMSJ.getHmsj(m, s, j);
  697.                 final double dGdh   = ghMSJ.getdGmsdh(m, s, j);
  698.                 final double dGdk   = ghMSJ.getdGmsdk(m, s, j);
  699.                 final double dGdA   = ghMSJ.getdGmsdAlpha(m, s, j);
  700.                 final double dGdB   = ghMSJ.getdGmsdBeta(m, s, j);
  701.                 final double dHdh   = ghMSJ.getdHmsdh(m, s, j);
  702.                 final double dHdk   = ghMSJ.getdHmsdk(m, s, j);
  703.                 final double dHdA   = ghMSJ.getdHmsdAlpha(m, s, j);
  704.                 final double dHdB   = ghMSJ.getdHmsdBeta(m, s, j);

  705.                 // Jacobi l-index from 2.7.1-(15)
  706.                 final int l = FastMath.min(n - m, n - FastMath.abs(s));
  707.                 // Jacobi polynomial and derivative
  708.                 final DerivativeStructure jacobi =
  709.                         JacobiPolynomials.getValue(l, v, w, factory.variable(0, auxiliaryElements.getGamma()));

  710.                 // Geopotential coefficients
  711.                 final double cnm = harmonics.getUnnormalizedCnm(n, m);
  712.                 final double snm = harmonics.getUnnormalizedSnm(n, m);

  713.                 // Common factors from expansion of equations 3.3-4
  714.                 final double cf_0      = roaPow[n] * Im * vMNS;
  715.                 final double cf_1      = cf_0 * gaMNS * jacobi.getValue();
  716.                 final double cf_2      = cf_1 * kJNS;
  717.                 final double gcPhs     = gMSJ * cnm + hMSJ * snm;
  718.                 final double gsMhc     = gMSJ * snm - hMSJ * cnm;
  719.                 final double dKgcPhsx2 = 2. * dkJNS * gcPhs;
  720.                 final double dKgsMhcx2 = 2. * dkJNS * gsMhc;
  721.                 final double dUdaCoef  = (n + 1) * cf_2;
  722.                 final double dUdlCoef  = j * cf_2;
  723.                 final double dUdGaCoef = cf_0 * kJNS * (jacobi.getValue() * dGaMNS + gaMNS * jacobi.getPartialDerivative(1));

  724.                 // dU / da components
  725.                 dUdaCos  += dUdaCoef * gcPhs;
  726.                 dUdaSin  += dUdaCoef * gsMhc;

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

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

  733.                 // dU / dLambda components
  734.                 dUdlCos  +=  dUdlCoef * gsMhc;
  735.                 dUdlSin  += -dUdlCoef * gcPhs;

  736.                 // dU / alpha components
  737.                 dUdAlCos += cf_2 * (dGdA * cnm + dHdA * snm);
  738.                 dUdAlSin += cf_2 * (dGdA * snm - dHdA * cnm);

  739.                 // dU / dBeta components
  740.                 dUdBeCos += cf_2 * (dGdB * cnm + dHdB * snm);
  741.                 dUdBeSin += cf_2 * (dGdB * snm - dHdB * cnm);

  742.                 // dU / dGamma components
  743.                 dUdGaCos += dUdGaCoef * gcPhs;
  744.                 dUdGaSin += dUdGaCoef * gsMhc;
  745.             }
  746.         }

  747.         return new double[][] {{dUdaCos,  dUdaSin},
  748.                                {dUdhCos,  dUdhSin},
  749.                                {dUdkCos,  dUdkSin},
  750.                                {dUdlCos,  dUdlSin},
  751.                                {dUdAlCos, dUdAlSin},
  752.                                {dUdBeCos, dUdBeSin},
  753.                                {dUdGaCos, dUdGaSin}};
  754.     }

  755.     /** Compute the n-SUM for potential derivatives components.
  756.      *  @param <T> the type of the field elements
  757.      *  @param date current date
  758.      *  @param j resonant index <i>j</i>
  759.      *  @param m resonant order <i>m</i>
  760.      *  @param s d'Alembert characteristic <i>s</i>
  761.      *  @param maxN maximum possible value for <i>n</i> index
  762.      *  @param roaPow powers of R/a up to degree <i>n</i>
  763.      *  @param ghMSJ G<sup>j</sup><sub>m,s</sub> and H<sup>j</sup><sub>m,s</sub> polynomials
  764.      *  @param gammaMNS &Gamma;<sup>m</sup><sub>n,s</sub>(γ) function
  765.      *  @param context container for attributes
  766.      *  @param hansenObjects initialization of hansen objects
  767.      *  @return Components of U<sub>n</sub> derivatives for fixed j, m, s
  768.      */
  769.     private <T extends RealFieldElement<T>> T[][] computeNSum(final FieldAbsoluteDate<T> date,
  770.                                                               final int j, final int m, final int s, final int maxN,
  771.                                                               final T[] roaPow,
  772.                                                               final FieldGHmsjPolynomials<T> ghMSJ,
  773.                                                               final FieldGammaMnsFunction<T> gammaMNS,
  774.                                                               final FieldDSSTTesseralContext<T> context,
  775.                                                               final FieldHansenObjects<T> hansenObjects) {

  776.         // Auxiliary elements related to the current orbit
  777.         final FieldAuxiliaryElements<T> auxiliaryElements = context.getFieldAuxiliaryElements();
  778.         // Zero for initialization
  779.         final Field<T> field = date.getField();
  780.         final T zero = field.getZero();

  781.         //spherical harmonics
  782.         final UnnormalizedSphericalHarmonics harmonics = provider.onDate(date.toAbsoluteDate());

  783.         // Potential derivatives components
  784.         T dUdaCos  = zero;
  785.         T dUdaSin  = zero;
  786.         T dUdhCos  = zero;
  787.         T dUdhSin  = zero;
  788.         T dUdkCos  = zero;
  789.         T dUdkSin  = zero;
  790.         T dUdlCos  = zero;
  791.         T dUdlSin  = zero;
  792.         T dUdAlCos = zero;
  793.         T dUdAlSin = zero;
  794.         T dUdBeCos = zero;
  795.         T dUdBeSin = zero;
  796.         T dUdGaCos = zero;
  797.         T dUdGaSin = zero;

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

  801.         // jacobi v, w, indices from 2.7.1-(15)
  802.         final int v = FastMath.abs(m - s);
  803.         final int w = FastMath.abs(m + s);

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

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

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

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

  816.                 // Inclination function Gamma and derivative
  817.                 final T gaMNS  = gammaMNS.getValue(m, n, s);
  818.                 final T dGaMNS = gammaMNS.getDerivative(m, n, s);

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

  822.                 // Gjms, Hjms polynomials and derivatives
  823.                 final T gMSJ   = ghMSJ.getGmsj(m, s, j);
  824.                 final T hMSJ   = ghMSJ.getHmsj(m, s, j);
  825.                 final T dGdh   = ghMSJ.getdGmsdh(m, s, j);
  826.                 final T dGdk   = ghMSJ.getdGmsdk(m, s, j);
  827.                 final T dGdA   = ghMSJ.getdGmsdAlpha(m, s, j);
  828.                 final T dGdB   = ghMSJ.getdGmsdBeta(m, s, j);
  829.                 final T dHdh   = ghMSJ.getdHmsdh(m, s, j);
  830.                 final T dHdk   = ghMSJ.getdHmsdk(m, s, j);
  831.                 final T dHdA   = ghMSJ.getdHmsdAlpha(m, s, j);
  832.                 final T dHdB   = ghMSJ.getdHmsdBeta(m, s, j);

  833.                 // Jacobi l-index from 2.7.1-(15)
  834.                 final int l = FastMath.min(n - m, n - FastMath.abs(s));
  835.                 // Jacobi polynomial and derivative
  836.                 @SuppressWarnings("unchecked")
  837.                 final FDSFactory<T> fdsf = (FDSFactory<T>) fieldFactory.get(field);
  838.                 final FieldDerivativeStructure<T> jacobi =
  839.                         JacobiPolynomials.getValue(l, v, w, fdsf.variable(0, auxiliaryElements.getGamma()));

  840.                 // Geopotential coefficients
  841.                 final T cnm = zero.add(harmonics.getUnnormalizedCnm(n, m));
  842.                 final T snm = zero.add(harmonics.getUnnormalizedSnm(n, m));

  843.                 // Common factors from expansion of equations 3.3-4
  844.                 final T cf_0      = roaPow[n].multiply(Im).multiply(vMNS);
  845.                 final T cf_1      = cf_0.multiply(gaMNS).multiply(jacobi.getValue());
  846.                 final T cf_2      = cf_1.multiply(kJNS);
  847.                 final T gcPhs     = gMSJ.multiply(cnm).add(hMSJ.multiply(snm));
  848.                 final T gsMhc     = gMSJ.multiply(snm).subtract(hMSJ.multiply(cnm));
  849.                 final T dKgcPhsx2 = dkJNS.multiply(gcPhs).multiply(2.);
  850.                 final T dKgsMhcx2 = dkJNS.multiply(gsMhc).multiply(2.);
  851.                 final T dUdaCoef  = cf_2.multiply(n + 1);
  852.                 final T dUdlCoef  = cf_2.multiply(j);
  853.                 final T dUdGaCoef = cf_0.multiply(kJNS).multiply(dGaMNS.multiply(jacobi.getValue()).add(gaMNS.multiply(jacobi.getPartialDerivative(1))));

  854.                 // dU / da components
  855.                 dUdaCos  = dUdaCos.add(dUdaCoef.multiply(gcPhs));
  856.                 dUdaSin  = dUdaSin.add(dUdaCoef.multiply(gsMhc));

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

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

  863.                 // dU / dLambda components
  864.                 dUdlCos  = dUdlCos.add(dUdlCoef.multiply(gsMhc));
  865.                 dUdlSin  = dUdlSin.add(dUdlCoef.multiply(gcPhs).negate());

  866.                 // dU / alpha components
  867.                 dUdAlCos = dUdAlCos.add(cf_2.multiply(dGdA.multiply(cnm).add(dHdA.multiply(snm))));
  868.                 dUdAlSin = dUdAlSin.add(cf_2.multiply(dGdA.multiply(snm).subtract(dHdA.multiply(cnm))));

  869.                 // dU / dBeta components
  870.                 dUdBeCos = dUdBeCos.add(cf_2.multiply(dGdB.multiply(cnm).add(dHdB.multiply(snm))));
  871.                 dUdBeSin = dUdBeSin.add(cf_2.multiply(dGdB.multiply(snm).subtract(dHdB.multiply(cnm))));

  872.                 // dU / dGamma components
  873.                 dUdGaCos = dUdGaCos.add(dUdGaCoef.multiply(gcPhs));
  874.                 dUdGaSin = dUdGaSin.add(dUdGaCoef.multiply(gsMhc));
  875.             }
  876.         }

  877.         final T[][] derivatives = MathArrays.buildArray(field, 7, 2);
  878.         derivatives[0][0] = dUdaCos;
  879.         derivatives[0][1] = dUdaSin;
  880.         derivatives[1][0] = dUdhCos;
  881.         derivatives[1][1] = dUdhSin;
  882.         derivatives[2][0] = dUdkCos;
  883.         derivatives[2][1] = dUdkSin;
  884.         derivatives[3][0] = dUdlCos;
  885.         derivatives[3][1] = dUdlSin;
  886.         derivatives[4][0] = dUdAlCos;
  887.         derivatives[4][1] = dUdAlSin;
  888.         derivatives[5][0] = dUdBeCos;
  889.         derivatives[5][1] = dUdBeSin;
  890.         derivatives[6][0] = dUdGaCos;
  891.         derivatives[6][1] = dUdGaSin;

  892.         return derivatives;

  893.     }

  894.     /** {@inheritDoc} */
  895.     @Override
  896.     public void registerAttitudeProvider(final AttitudeProvider attitudeProvider) {
  897.         //nothing is done since this contribution is not sensitive to attitude
  898.     }

  899.     /** Compute the C<sup>j</sup> and the S<sup>j</sup> coefficients.
  900.      *  <p>
  901.      *  Those coefficients are given in Danielson paper by substituting the
  902.      *  disturbing function (2.7.1-16) with m != 0 into (2.2-10)
  903.      *  </p>
  904.      */
  905.     private class FourierCjSjCoefficients {

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

  908.         /** The C<sub>i</sub><sup>jm</sup> coefficients.
  909.          * <p>
  910.          * The index order is [m][j][i] <br/>
  911.          * The i index corresponds to the C<sub>i</sub><sup>jm</sup> coefficients used to
  912.          * compute the following: <br/>
  913.          * - da/dt <br/>
  914.          * - dk/dt <br/>
  915.          * - dh/dt / dk <br/>
  916.          * - dq/dt <br/>
  917.          * - dp/dt / dα <br/>
  918.          * - dλ/dt / dβ <br/>
  919.          * </p>
  920.          */
  921.         private final double[][][] cCoef;

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

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

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

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

  942.         /** Create a set of C<sub>i</sub><sup>jm</sup> and S<sub>i</sub><sup>jm</sup> coefficients.
  943.          *  @param jMax absolute limit for j ( -jMax <= j <= jMax )
  944.          *  @param mMax maximum value for m
  945.          */
  946.         FourierCjSjCoefficients(final int jMax, final int mMax) {
  947.             // initialise fields
  948.             final int rows    = mMax + 1;
  949.             final int columns = 2 * jMax + 1;
  950.             this.jMax         = jMax;
  951.             this.cCoef        = new double[rows][columns][6];
  952.             this.sCoef        = new double[rows][columns][6];
  953.             this.roaPow       = new double[maxDegree + 1];
  954.             roaPow[0] = 1.;
  955.         }

  956.         /**
  957.          * Generate the coefficients.
  958.          * @param date the current date
  959.          * @param context container for attributes
  960.          * @param hansenObjects initialization of hansen objects
  961.          */
  962.         public void generateCoefficients(final AbsoluteDate date, final DSSTTesseralContext context,
  963.                                          final HansenObjects hansenObjects) {

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

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

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

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

  972.                 // R / a up to power degree
  973.                 for (int i = 1; i <= maxRoaPower; i++) {
  974.                     roaPow[i] = context.getRoa() * roaPow[i - 1];
  975.                 }

  976.                 //generate the m-daily coefficients
  977.                 for (int m = 1; m <= maxOrderMdailyTesseralSP; m++) {
  978.                     buildFourierCoefficients(date, m, 0, maxDegreeMdailyTesseralSP, context, hansenObjects);
  979.                 }

  980.                 // generate the other coefficients only if required
  981.                 if (maxDegreeTesseralSP >= 0) {
  982.                     for (int m: nonResOrders.keySet()) {
  983.                         final List<Integer> listJ = nonResOrders.get(m);

  984.                         for (int j: listJ) {
  985.                             buildFourierCoefficients(date, m, j, maxDegreeTesseralSP, context, hansenObjects);
  986.                         }
  987.                     }
  988.                 }
  989.             }
  990.         }

  991.         /** Build a set of fourier coefficients for a given m and j.
  992.          *
  993.          * @param date the date of the coefficients
  994.          * @param m m index
  995.          * @param j j index
  996.          * @param maxN  maximum value for n index
  997.          * @param context container for attributes
  998.          * @param hansenObjects initialization of hansen objects
  999.          */
  1000.         private void buildFourierCoefficients(final AbsoluteDate date,
  1001.                final int m, final int j, final int maxN, final DSSTTesseralContext context,
  1002.                final HansenObjects hansenObjects) {

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

  1004.             // Potential derivatives components for a given non-resonant pair {j,m}
  1005.             double dRdaCos  = 0.;
  1006.             double dRdaSin  = 0.;
  1007.             double dRdhCos  = 0.;
  1008.             double dRdhSin  = 0.;
  1009.             double dRdkCos  = 0.;
  1010.             double dRdkSin  = 0.;
  1011.             double dRdlCos  = 0.;
  1012.             double dRdlSin  = 0.;
  1013.             double dRdAlCos = 0.;
  1014.             double dRdAlSin = 0.;
  1015.             double dRdBeCos = 0.;
  1016.             double dRdBeSin = 0.;
  1017.             double dRdGaCos = 0.;
  1018.             double dRdGaSin = 0.;

  1019.             // s-SUM from -sMin to sMax
  1020.             final int sMin = j == 0 ? maxEccPowMdailyTesseralSP : maxEccPowTesseralSP;
  1021.             final int sMax = j == 0 ? maxEccPowMdailyTesseralSP : maxEccPowTesseralSP;
  1022.             for (int s = 0; s <= sMax; s++) {

  1023.                 // n-SUM for s positive
  1024.                 final double[][] nSumSpos = computeNSum(date, j, m, s, maxN,
  1025.                                                         roaPow, ghMSJ, gammaMNS, context, hansenObjects);
  1026.                 dRdaCos  += nSumSpos[0][0];
  1027.                 dRdaSin  += nSumSpos[0][1];
  1028.                 dRdhCos  += nSumSpos[1][0];
  1029.                 dRdhSin  += nSumSpos[1][1];
  1030.                 dRdkCos  += nSumSpos[2][0];
  1031.                 dRdkSin  += nSumSpos[2][1];
  1032.                 dRdlCos  += nSumSpos[3][0];
  1033.                 dRdlSin  += nSumSpos[3][1];
  1034.                 dRdAlCos += nSumSpos[4][0];
  1035.                 dRdAlSin += nSumSpos[4][1];
  1036.                 dRdBeCos += nSumSpos[5][0];
  1037.                 dRdBeSin += nSumSpos[5][1];
  1038.                 dRdGaCos += nSumSpos[6][0];
  1039.                 dRdGaSin += nSumSpos[6][1];

  1040.                 // n-SUM for s negative
  1041.                 if (s > 0 && s <= sMin) {
  1042.                     final double[][] nSumSneg = computeNSum(date, j, m, -s, maxN,
  1043.                                                             roaPow, ghMSJ, gammaMNS, context, hansenObjects);
  1044.                     dRdaCos  += nSumSneg[0][0];
  1045.                     dRdaSin  += nSumSneg[0][1];
  1046.                     dRdhCos  += nSumSneg[1][0];
  1047.                     dRdhSin  += nSumSneg[1][1];
  1048.                     dRdkCos  += nSumSneg[2][0];
  1049.                     dRdkSin  += nSumSneg[2][1];
  1050.                     dRdlCos  += nSumSneg[3][0];
  1051.                     dRdlSin  += nSumSneg[3][1];
  1052.                     dRdAlCos += nSumSneg[4][0];
  1053.                     dRdAlSin += nSumSneg[4][1];
  1054.                     dRdBeCos += nSumSneg[5][0];
  1055.                     dRdBeSin += nSumSneg[5][1];
  1056.                     dRdGaCos += nSumSneg[6][0];
  1057.                     dRdGaSin += nSumSneg[6][1];
  1058.                 }
  1059.             }
  1060.             dRdaCos  *= -context.getMoa() / auxiliaryElements.getSma();
  1061.             dRdaSin  *= -context.getMoa() / auxiliaryElements.getSma();
  1062.             dRdhCos  *=  context.getMoa();
  1063.             dRdhSin  *=  context.getMoa();
  1064.             dRdkCos  *=  context.getMoa();
  1065.             dRdkSin  *=  context.getMoa();
  1066.             dRdlCos  *=  context.getMoa();
  1067.             dRdlSin  *=  context.getMoa();
  1068.             dRdAlCos *=  context.getMoa();
  1069.             dRdAlSin *=  context.getMoa();
  1070.             dRdBeCos *=  context.getMoa();
  1071.             dRdBeSin *=  context.getMoa();
  1072.             dRdGaCos *=  context.getMoa();
  1073.             dRdGaSin *=  context.getMoa();

  1074.             // Compute the cross derivative operator :
  1075.             final double RAlphaGammaCos   = auxiliaryElements.getAlpha() * dRdGaCos - auxiliaryElements.getGamma() * dRdAlCos;
  1076.             final double RAlphaGammaSin   = auxiliaryElements.getAlpha() * dRdGaSin - auxiliaryElements.getGamma() * dRdAlSin;
  1077.             final double RAlphaBetaCos    = auxiliaryElements.getAlpha() * dRdBeCos - auxiliaryElements.getBeta()  * dRdAlCos;
  1078.             final double RAlphaBetaSin    = auxiliaryElements.getAlpha() * dRdBeSin - auxiliaryElements.getBeta()  * dRdAlSin;
  1079.             final double RBetaGammaCos    =  auxiliaryElements.getBeta() * dRdGaCos - auxiliaryElements.getGamma() * dRdBeCos;
  1080.             final double RBetaGammaSin    =  auxiliaryElements.getBeta() * dRdGaSin - auxiliaryElements.getGamma() * dRdBeSin;
  1081.             final double RhkCos           =     auxiliaryElements.getH() * dRdkCos  -     auxiliaryElements.getK() * dRdhCos;
  1082.             final double RhkSin           =     auxiliaryElements.getH() * dRdkSin  -     auxiliaryElements.getK() * dRdhSin;
  1083.             final double pRagmIqRbgoABCos = (auxiliaryElements.getP() * RAlphaGammaCos - I * auxiliaryElements.getQ() * RBetaGammaCos) * context.getOoAB();
  1084.             final double pRagmIqRbgoABSin = (auxiliaryElements.getP() * RAlphaGammaSin - I * auxiliaryElements.getQ() * RBetaGammaSin) * context.getOoAB();
  1085.             final double RhkmRabmdRdlCos  =  RhkCos - RAlphaBetaCos - dRdlCos;
  1086.             final double RhkmRabmdRdlSin  =  RhkSin - RAlphaBetaSin - dRdlSin;

  1087.             // da/dt
  1088.             cCoef[m][j + jMax][0] = context.getAx2oA() * dRdlCos;
  1089.             sCoef[m][j + jMax][0] = context.getAx2oA() * dRdlSin;

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

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

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

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

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

  1106.         /** Get the coefficient C<sub>i</sub><sup>jm</sup>.
  1107.          * @param i i index - corresponds to the required variation
  1108.          * @param j j index
  1109.          * @param m m index
  1110.          * @return the coefficient C<sub>i</sub><sup>jm</sup>
  1111.          */
  1112.         public double getCijm(final int i, final int j, final int m) {
  1113.             return cCoef[m][j + jMax][i];
  1114.         }

  1115.         /** Get the coefficient S<sub>i</sub><sup>jm</sup>.
  1116.          * @param i i index - corresponds to the required variation
  1117.          * @param j j index
  1118.          * @param m m index
  1119.          * @return the coefficient S<sub>i</sub><sup>jm</sup>
  1120.          */
  1121.         public double getSijm(final int i, final int j, final int m) {
  1122.             return sCoef[m][j + jMax][i];
  1123.         }
  1124.     }

  1125.     /** Compute the C<sup>j</sup> and the S<sup>j</sup> coefficients.
  1126.      *  <p>
  1127.      *  Those coefficients are given in Danielson paper by substituting the
  1128.      *  disturbing function (2.7.1-16) with m != 0 into (2.2-10)
  1129.      *  </p>
  1130.      */
  1131.     private class FieldFourierCjSjCoefficients <T extends RealFieldElement<T>> {

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

  1134.         /** The C<sub>i</sub><sup>jm</sup> coefficients.
  1135.          * <p>
  1136.          * The index order is [m][j][i] <br/>
  1137.          * The i index corresponds to the C<sub>i</sub><sup>jm</sup> coefficients used to
  1138.          * compute the following: <br/>
  1139.          * - da/dt <br/>
  1140.          * - dk/dt <br/>
  1141.          * - dh/dt / dk <br/>
  1142.          * - dq/dt <br/>
  1143.          * - dp/dt / dα <br/>
  1144.          * - dλ/dt / dβ <br/>
  1145.          * </p>
  1146.          */
  1147.         private final T[][][] cCoef;

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

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

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

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

  1168.         /** Create a set of C<sub>i</sub><sup>jm</sup> and S<sub>i</sub><sup>jm</sup> coefficients.
  1169.          *  @param jMax absolute limit for j ( -jMax <= j <= jMax )
  1170.          *  @param mMax maximum value for m
  1171.          *  @param field field used by default
  1172.          */
  1173.         FieldFourierCjSjCoefficients(final int jMax, final int mMax, final Field<T> field) {
  1174.             // initialise fields
  1175.             final T zero = field.getZero();
  1176.             final int rows    = mMax + 1;
  1177.             final int columns = 2 * jMax + 1;
  1178.             this.jMax         = jMax;
  1179.             this.cCoef        = MathArrays.buildArray(field, rows, columns, 6);
  1180.             this.sCoef        = MathArrays.buildArray(field, rows, columns, 6);
  1181.             this.roaPow       = MathArrays.buildArray(field, maxDegree + 1);
  1182.             roaPow[0] = zero.add(1.);
  1183.         }

  1184.         /**
  1185.          * Generate the coefficients.
  1186.          * @param date the current date
  1187.          * @param context container for attributes
  1188.          * @param hansenObjects initialization of hansen objects
  1189.          * @param field field used by default
  1190.          */
  1191.         public void generateCoefficients(final FieldAbsoluteDate<T> date,
  1192.                                          final FieldDSSTTesseralContext<T> context,
  1193.                                          final FieldHansenObjects<T> hansenObjects,
  1194.                                          final Field<T> field) {

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

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

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

  1203.                 // R / a up to power degree
  1204.                 for (int i = 1; i <= maxRoaPower; i++) {
  1205.                     roaPow[i] = context.getRoa().multiply(roaPow[i - 1]);
  1206.                 }

  1207.                 //generate the m-daily coefficients
  1208.                 for (int m = 1; m <= maxOrderMdailyTesseralSP; m++) {
  1209.                     buildFourierCoefficients(date, m, 0, maxDegreeMdailyTesseralSP, context, hansenObjects, field);
  1210.                 }

  1211.                 // generate the other coefficients only if required
  1212.                 if (maxDegreeTesseralSP >= 0) {
  1213.                     for (int m: nonResOrders.keySet()) {
  1214.                         final List<Integer> listJ = nonResOrders.get(m);

  1215.                         for (int j: listJ) {
  1216.                             buildFourierCoefficients(date, m, j, maxDegreeTesseralSP, context, hansenObjects, field);
  1217.                         }
  1218.                     }
  1219.                 }
  1220.             }
  1221.         }

  1222.         /** Build a set of fourier coefficients for a given m and j.
  1223.          *
  1224.          * @param date the date of the coefficients
  1225.          * @param m m index
  1226.          * @param j j index
  1227.          * @param maxN  maximum value for n index
  1228.          * @param context container for attributes
  1229.          * @param hansenObjects initialization of hansen objects
  1230.          * @param field field used by default
  1231.          */
  1232.         private void buildFourierCoefficients(final FieldAbsoluteDate<T> date,
  1233.                                               final int m, final int j, final int maxN,
  1234.                                               final FieldDSSTTesseralContext<T> context,
  1235.                                               final FieldHansenObjects<T> hansenObjects,
  1236.                                               final Field<T> field) {

  1237.             // Zero
  1238.             final T zero = field.getZero();
  1239.             // Common parameters
  1240.             final FieldAuxiliaryElements<T> auxiliaryElements = context.getFieldAuxiliaryElements();

  1241.             // Potential derivatives components for a given non-resonant pair {j,m}
  1242.             T dRdaCos  = zero;
  1243.             T dRdaSin  = zero;
  1244.             T dRdhCos  = zero;
  1245.             T dRdhSin  = zero;
  1246.             T dRdkCos  = zero;
  1247.             T dRdkSin  = zero;
  1248.             T dRdlCos  = zero;
  1249.             T dRdlSin  = zero;
  1250.             T dRdAlCos = zero;
  1251.             T dRdAlSin = zero;
  1252.             T dRdBeCos = zero;
  1253.             T dRdBeSin = zero;
  1254.             T dRdGaCos = zero;
  1255.             T dRdGaSin = zero;

  1256.             // s-SUM from -sMin to sMax
  1257.             final int sMin = j == 0 ? maxEccPowMdailyTesseralSP : maxEccPowTesseralSP;
  1258.             final int sMax = j == 0 ? maxEccPowMdailyTesseralSP : maxEccPowTesseralSP;
  1259.             for (int s = 0; s <= sMax; s++) {

  1260.                 // n-SUM for s positive
  1261.                 final T[][] nSumSpos = computeNSum(date, j, m, s, maxN,
  1262.                                                         roaPow, ghMSJ, gammaMNS, context, hansenObjects);
  1263.                 dRdaCos  =  dRdaCos.add(nSumSpos[0][0]);
  1264.                 dRdaSin  =  dRdaSin.add(nSumSpos[0][1]);
  1265.                 dRdhCos  =  dRdhCos.add(nSumSpos[1][0]);
  1266.                 dRdhSin  =  dRdhSin.add(nSumSpos[1][1]);
  1267.                 dRdkCos  =  dRdkCos.add(nSumSpos[2][0]);
  1268.                 dRdkSin  =  dRdkSin.add(nSumSpos[2][1]);
  1269.                 dRdlCos  =  dRdlCos.add(nSumSpos[3][0]);
  1270.                 dRdlSin  =  dRdlSin.add(nSumSpos[3][1]);
  1271.                 dRdAlCos = dRdAlCos.add(nSumSpos[4][0]);
  1272.                 dRdAlSin = dRdAlSin.add(nSumSpos[4][1]);
  1273.                 dRdBeCos = dRdBeCos.add(nSumSpos[5][0]);
  1274.                 dRdBeSin = dRdBeSin.add(nSumSpos[5][1]);
  1275.                 dRdGaCos = dRdGaCos.add(nSumSpos[6][0]);
  1276.                 dRdGaSin = dRdGaSin.add(nSumSpos[6][1]);

  1277.                 // n-SUM for s negative
  1278.                 if (s > 0 && s <= sMin) {
  1279.                     final T[][] nSumSneg = computeNSum(date, j, m, -s, maxN,
  1280.                                                             roaPow, ghMSJ, gammaMNS, context, hansenObjects);
  1281.                     dRdaCos  =  dRdaCos.add(nSumSneg[0][0]);
  1282.                     dRdaSin  =  dRdaSin.add(nSumSneg[0][1]);
  1283.                     dRdhCos  =  dRdhCos.add(nSumSneg[1][0]);
  1284.                     dRdhSin  =  dRdhSin.add(nSumSneg[1][1]);
  1285.                     dRdkCos  =  dRdkCos.add(nSumSneg[2][0]);
  1286.                     dRdkSin  =  dRdkSin.add(nSumSneg[2][1]);
  1287.                     dRdlCos  =  dRdlCos.add(nSumSneg[3][0]);
  1288.                     dRdlSin  =  dRdlSin.add(nSumSneg[3][1]);
  1289.                     dRdAlCos = dRdAlCos.add(nSumSneg[4][0]);
  1290.                     dRdAlSin = dRdAlSin.add(nSumSneg[4][1]);
  1291.                     dRdBeCos = dRdBeCos.add(nSumSneg[5][0]);
  1292.                     dRdBeSin = dRdBeSin.add(nSumSneg[5][1]);
  1293.                     dRdGaCos = dRdGaCos.add(nSumSneg[6][0]);
  1294.                     dRdGaSin = dRdGaSin.add(nSumSneg[6][1]);
  1295.                 }
  1296.             }
  1297.             dRdaCos  =  dRdaCos.multiply(context.getMoa().negate().divide(auxiliaryElements.getSma()));
  1298.             dRdaSin  =  dRdaSin.multiply(context.getMoa().negate().divide(auxiliaryElements.getSma()));
  1299.             dRdhCos  =  dRdhCos.multiply(context.getMoa());
  1300.             dRdhSin  =  dRdhSin.multiply(context.getMoa());
  1301.             dRdkCos  =  dRdkCos.multiply(context.getMoa());
  1302.             dRdkSin  =  dRdkSin.multiply(context.getMoa());
  1303.             dRdlCos  =  dRdlCos.multiply(context.getMoa());
  1304.             dRdlSin  =  dRdlSin.multiply(context.getMoa());
  1305.             dRdAlCos = dRdAlCos.multiply(context.getMoa());
  1306.             dRdAlSin = dRdAlSin.multiply(context.getMoa());
  1307.             dRdBeCos = dRdBeCos.multiply(context.getMoa());
  1308.             dRdBeSin = dRdBeSin.multiply(context.getMoa());
  1309.             dRdGaCos = dRdGaCos.multiply(context.getMoa());
  1310.             dRdGaSin = dRdGaSin.multiply(context.getMoa());

  1311.             // Compute the cross derivative operator :
  1312.             final T RAlphaGammaCos   = auxiliaryElements.getAlpha().multiply(dRdGaCos).subtract(auxiliaryElements.getGamma().multiply(dRdAlCos));
  1313.             final T RAlphaGammaSin   = auxiliaryElements.getAlpha().multiply(dRdGaSin).subtract(auxiliaryElements.getGamma().multiply(dRdAlSin));
  1314.             final T RAlphaBetaCos    = auxiliaryElements.getAlpha().multiply(dRdBeCos).subtract(auxiliaryElements.getBeta().multiply(dRdAlCos));
  1315.             final T RAlphaBetaSin    = auxiliaryElements.getAlpha().multiply(dRdBeSin).subtract(auxiliaryElements.getBeta().multiply(dRdAlSin));
  1316.             final T RBetaGammaCos    =  auxiliaryElements.getBeta().multiply(dRdGaCos).subtract(auxiliaryElements.getGamma().multiply(dRdBeCos));
  1317.             final T RBetaGammaSin    =  auxiliaryElements.getBeta().multiply(dRdGaSin).subtract(auxiliaryElements.getGamma().multiply(dRdBeSin));
  1318.             final T RhkCos           =     auxiliaryElements.getH().multiply(dRdkCos).subtract(auxiliaryElements.getK().multiply(dRdhCos));
  1319.             final T RhkSin           =     auxiliaryElements.getH().multiply(dRdkSin).subtract(auxiliaryElements.getK().multiply(dRdhSin));
  1320.             final T pRagmIqRbgoABCos = (auxiliaryElements.getP().multiply(RAlphaGammaCos).subtract(auxiliaryElements.getQ().multiply(RBetaGammaCos).multiply(I))).multiply(context.getOoAB());
  1321.             final T pRagmIqRbgoABSin = (auxiliaryElements.getP().multiply(RAlphaGammaSin).subtract(auxiliaryElements.getQ().multiply(RBetaGammaSin).multiply(I))).multiply(context.getOoAB());
  1322.             final T RhkmRabmdRdlCos  =  RhkCos.subtract(RAlphaBetaCos).subtract(dRdlCos);
  1323.             final T RhkmRabmdRdlSin  =  RhkSin.subtract(RAlphaBetaSin).subtract(dRdlSin);

  1324.             // da/dt
  1325.             cCoef[m][j + jMax][0] = context.getAx2oA().multiply(dRdlCos);
  1326.             sCoef[m][j + jMax][0] = context.getAx2oA().multiply(dRdlSin);

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

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

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

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

  1339.             // dλ/dt
  1340.             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);
  1341.             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);
  1342.         }

  1343.         /** Get the coefficient C<sub>i</sub><sup>jm</sup>.
  1344.          * @param i i index - corresponds to the required variation
  1345.          * @param j j index
  1346.          * @param m m index
  1347.          * @return the coefficient C<sub>i</sub><sup>jm</sup>
  1348.          */
  1349.         public T getCijm(final int i, final int j, final int m) {
  1350.             return cCoef[m][j + jMax][i];
  1351.         }

  1352.         /** Get the coefficient S<sub>i</sub><sup>jm</sup>.
  1353.          * @param i i index - corresponds to the required variation
  1354.          * @param j j index
  1355.          * @param m m index
  1356.          * @return the coefficient S<sub>i</sub><sup>jm</sup>
  1357.          */
  1358.         public T getSijm(final int i, final int j, final int m) {
  1359.             return sCoef[m][j + jMax][i];
  1360.         }
  1361.     }

  1362.     /** 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
  1363.      * the short-periodic zonal contribution.
  1364.      *   <p>
  1365.      *  Those coefficients are given by expression 2.5.4-5 from the Danielson paper.
  1366.      *   </p>
  1367.      *
  1368.      * @author Sorin Scortan
  1369.      *
  1370.      */
  1371.     private static class TesseralShortPeriodicCoefficients implements ShortPeriodTerms {

  1372.         /** Retrograde factor I.
  1373.          *  <p>
  1374.          *  DSST model needs equinoctial orbit as internal representation.
  1375.          *  Classical equinoctial elements have discontinuities when inclination
  1376.          *  is close to zero. In this representation, I = +1. <br>
  1377.          *  To avoid this discontinuity, another representation exists and equinoctial
  1378.          *  elements can be expressed in a different way, called "retrograde" orbit.
  1379.          *  This implies I = -1. <br>
  1380.          *  As Orekit doesn't implement the retrograde orbit, I is always set to +1.
  1381.          *  But for the sake of consistency with the theory, the retrograde factor
  1382.          *  has been kept in the formulas.
  1383.          *  </p>
  1384.          */
  1385.         private static final int I = 1;

  1386.         /** Central body rotating frame. */
  1387.         private final Frame bodyFrame;

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

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

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

  1394.         /** Maximum value for m index. */
  1395.         private final int mMax;

  1396.         /** Maximum value for j index. */
  1397.         private final int jMax;

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

  1400.         /** All coefficients slots. */
  1401.         private final transient TimeSpanMap<Slot> slots;

  1402.         /** Constructor.
  1403.          * @param bodyFrame central body rotating frame
  1404.          * @param maxOrderMdailyTesseralSP maximal order to consider for short periodics m-daily tesseral harmonics potential
  1405.          * @param mDailiesOnly flag to take into account only M-dailies harmonic tesserals for short periodic perturbations
  1406.          * @param nonResOrders lst of non resonant orders with j != 0
  1407.          * @param mMax maximum value for m index
  1408.          * @param jMax maximum value for j index
  1409.          * @param interpolationPoints number of points used in the interpolation process
  1410.          * @param slots all coefficients slots
  1411.          */
  1412.         TesseralShortPeriodicCoefficients(final Frame bodyFrame, final int maxOrderMdailyTesseralSP,
  1413.                                           final boolean mDailiesOnly, final SortedMap<Integer, List<Integer> > nonResOrders,
  1414.                                           final int mMax, final int jMax, final int interpolationPoints,
  1415.                                           final TimeSpanMap<Slot> slots) {
  1416.             this.bodyFrame                = bodyFrame;
  1417.             this.maxOrderMdailyTesseralSP = maxOrderMdailyTesseralSP;
  1418.             this.mDailiesOnly             = mDailiesOnly;
  1419.             this.nonResOrders             = nonResOrders;
  1420.             this.mMax                     = mMax;
  1421.             this.jMax                     = jMax;
  1422.             this.interpolationPoints      = interpolationPoints;
  1423.             this.slots                    = slots;
  1424.         }

  1425.         /** Get the slot valid for some date.
  1426.          * @param meanStates mean states defining the slot
  1427.          * @return slot valid at the specified date
  1428.          */
  1429.         public Slot createSlot(final SpacecraftState... meanStates) {
  1430.             final Slot         slot  = new Slot(mMax, jMax, interpolationPoints);
  1431.             final AbsoluteDate first = meanStates[0].getDate();
  1432.             final AbsoluteDate last  = meanStates[meanStates.length - 1].getDate();
  1433.             if (first.compareTo(last) <= 0) {
  1434.                 slots.addValidAfter(slot, first);
  1435.             } else {
  1436.                 slots.addValidBefore(slot, first);
  1437.             }
  1438.             return slot;
  1439.         }

  1440.         /** {@inheritDoc} */
  1441.         @Override
  1442.         public double[] value(final Orbit meanOrbit) {

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

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

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

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

  1452.                 // Central body rotation angle from equation 2.7.1-(3)(4).
  1453.                 final Transform t = bodyFrame.getTransformTo(auxiliaryElements.getFrame(), auxiliaryElements.getDate());
  1454.                 final Vector3D xB = t.transformVector(Vector3D.PLUS_I);
  1455.                 final Vector3D yB = t.transformVector(Vector3D.PLUS_J);
  1456.                 final Vector3D  f = auxiliaryElements.getVectorF();
  1457.                 final Vector3D  g = auxiliaryElements.getVectorG();
  1458.                 final double currentTheta = FastMath.atan2(-f.dotProduct(yB) + I * g.dotProduct(xB),
  1459.                                                             f.dotProduct(xB) + I * g.dotProduct(yB));

  1460.                 //Add the m-daily contribution
  1461.                 for (int m = 1; m <= maxOrderMdailyTesseralSP; m++) {
  1462.                     // Phase angle
  1463.                     final double jlMmt  = -m * currentTheta;
  1464.                     final double sinPhi = FastMath.sin(jlMmt);
  1465.                     final double cosPhi = FastMath.cos(jlMmt);

  1466.                     // compute contribution for each element
  1467.                     final double[] c = slot.getCijm(0, m, meanOrbit.getDate());
  1468.                     final double[] s = slot.getSijm(0, m, meanOrbit.getDate());
  1469.                     for (int i = 0; i < 6; i++) {
  1470.                         shortPeriodicVariation[i] += c[i] * cosPhi + s[i] * sinPhi;
  1471.                     }
  1472.                 }

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

  1477.                     for (int j : listJ) {
  1478.                         // Phase angle
  1479.                         final double jlMmt  = j * meanOrbit.getLM() - m * currentTheta;
  1480.                         final double sinPhi = FastMath.sin(jlMmt);
  1481.                         final double cosPhi = FastMath.cos(jlMmt);

  1482.                         // compute contribution for each element
  1483.                         final double[] c = slot.getCijm(j, m, meanOrbit.getDate());
  1484.                         final double[] s = slot.getSijm(j, m, meanOrbit.getDate());
  1485.                         for (int i = 0; i < 6; i++) {
  1486.                             shortPeriodicVariation[i] += c[i] * cosPhi + s[i] * sinPhi;
  1487.                         }

  1488.                     }
  1489.                 }
  1490.             }

  1491.             return shortPeriodicVariation;

  1492.         }

  1493.         /** {@inheritDoc} */
  1494.         @Override
  1495.         public String getCoefficientsKeyPrefix() {
  1496.             return DSSTTesseral.SHORT_PERIOD_PREFIX;
  1497.         }

  1498.         /** {@inheritDoc}
  1499.          * <p>
  1500.          * For tesseral terms contributions,there are maxOrderMdailyTesseralSP
  1501.          * m-daily cMm coefficients, maxOrderMdailyTesseralSP m-daily sMm
  1502.          * coefficients, nbNonResonant cjm coefficients and nbNonResonant
  1503.          * sjm coefficients, where maxOrderMdailyTesseralSP and nbNonResonant both
  1504.          * depend on the orbit. The j index is the integer multiplier for the true
  1505.          * longitude argument and the m index is the integer multiplier for m-dailies.
  1506.          * </p>
  1507.          */
  1508.         @Override
  1509.         public Map<String, double[]> getCoefficients(final AbsoluteDate date, final Set<String> selected) {

  1510.             // select the coefficients slot
  1511.             final Slot slot = slots.get(date);

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

  1516.                 for (int m = 1; m <= maxOrderMdailyTesseralSP; m++) {
  1517.                     storeIfSelected(coefficients, selected, slot.getCijm(0, m, date), DSSTTesseral.CM_COEFFICIENTS, m);
  1518.                     storeIfSelected(coefficients, selected, slot.getSijm(0, m, date), DSSTTesseral.SM_COEFFICIENTS, m);
  1519.                 }

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

  1523.                     for (int j : listJ) {
  1524.                         for (int i = 0; i < 6; ++i) {
  1525.                             storeIfSelected(coefficients, selected, slot.getCijm(j, m, date), "c", j, m);
  1526.                             storeIfSelected(coefficients, selected, slot.getSijm(j, m, date), "s", j, m);
  1527.                         }
  1528.                     }
  1529.                 }

  1530.                 return coefficients;

  1531.             } else {
  1532.                 return Collections.emptyMap();
  1533.             }

  1534.         }

  1535.         /** Put a coefficient in a map if selected.
  1536.          * @param map map to populate
  1537.          * @param selected set of coefficients that should be put in the map
  1538.          * (empty set means all coefficients are selected)
  1539.          * @param value coefficient value
  1540.          * @param id coefficient identifier
  1541.          * @param indices list of coefficient indices
  1542.          */
  1543.         private void storeIfSelected(final Map<String, double[]> map, final Set<String> selected,
  1544.                                      final double[] value, final String id, final int... indices) {
  1545.             final StringBuilder keyBuilder = new StringBuilder(getCoefficientsKeyPrefix());
  1546.             keyBuilder.append(id);
  1547.             for (int index : indices) {
  1548.                 keyBuilder.append('[').append(index).append(']');
  1549.             }
  1550.             final String key = keyBuilder.toString();
  1551.             if (selected.isEmpty() || selected.contains(key)) {
  1552.                 map.put(key, value);
  1553.             }
  1554.         }

  1555.     }

  1556.     /** 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
  1557.      * the short-periodic zonal contribution.
  1558.      *   <p>
  1559.      *  Those coefficients are given by expression 2.5.4-5 from the Danielson paper.
  1560.      *   </p>
  1561.      *
  1562.      * @author Sorin Scortan
  1563.      *
  1564.      */
  1565.     private static class FieldTesseralShortPeriodicCoefficients <T extends RealFieldElement<T>> implements FieldShortPeriodTerms<T> {

  1566.         /** Retrograde factor I.
  1567.          *  <p>
  1568.          *  DSST model needs equinoctial orbit as internal representation.
  1569.          *  Classical equinoctial elements have discontinuities when inclination
  1570.          *  is close to zero. In this representation, I = +1. <br>
  1571.          *  To avoid this discontinuity, another representation exists and equinoctial
  1572.          *  elements can be expressed in a different way, called "retrograde" orbit.
  1573.          *  This implies I = -1. <br>
  1574.          *  As Orekit doesn't implement the retrograde orbit, I is always set to +1.
  1575.          *  But for the sake of consistency with the theory, the retrograde factor
  1576.          *  has been kept in the formulas.
  1577.          *  </p>
  1578.          */
  1579.         private static final int I = 1;

  1580.         /** Central body rotating frame. */
  1581.         private final Frame bodyFrame;

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

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

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

  1588.         /** Maximum value for m index. */
  1589.         private final int mMax;

  1590.         /** Maximum value for j index. */
  1591.         private final int jMax;

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

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

  1596.         /** Constructor.
  1597.          * @param bodyFrame central body rotating frame
  1598.          * @param maxOrderMdailyTesseralSP maximal order to consider for short periodics m-daily tesseral harmonics potential
  1599.          * @param mDailiesOnly flag to take into account only M-dailies harmonic tesserals for short periodic perturbations
  1600.          * @param nonResOrders lst of non resonant orders with j != 0
  1601.          * @param mMax maximum value for m index
  1602.          * @param jMax maximum value for j index
  1603.          * @param interpolationPoints number of points used in the interpolation process
  1604.          * @param slots all coefficients slots
  1605.          */
  1606.         FieldTesseralShortPeriodicCoefficients(final Frame bodyFrame, final int maxOrderMdailyTesseralSP,
  1607.                                                final boolean mDailiesOnly, final SortedMap<Integer, List<Integer> > nonResOrders,
  1608.                                                final int mMax, final int jMax, final int interpolationPoints,
  1609.                                                final FieldTimeSpanMap<FieldSlot<T>, T> slots) {
  1610.             this.bodyFrame                = bodyFrame;
  1611.             this.maxOrderMdailyTesseralSP = maxOrderMdailyTesseralSP;
  1612.             this.mDailiesOnly             = mDailiesOnly;
  1613.             this.nonResOrders             = nonResOrders;
  1614.             this.mMax                     = mMax;
  1615.             this.jMax                     = jMax;
  1616.             this.interpolationPoints      = interpolationPoints;
  1617.             this.slots                    = slots;
  1618.         }

  1619.         /** Get the slot valid for some date.
  1620.          * @param meanStates mean states defining the slot
  1621.          * @return slot valid at the specified date
  1622.          */
  1623.         @SuppressWarnings("unchecked")
  1624.         public FieldSlot<T> createSlot(final FieldSpacecraftState<T>... meanStates) {
  1625.             final FieldSlot<T>         slot  = new FieldSlot<>(mMax, jMax, interpolationPoints);
  1626.             final FieldAbsoluteDate<T> first = meanStates[0].getDate();
  1627.             final FieldAbsoluteDate<T> last  = meanStates[meanStates.length - 1].getDate();
  1628.             if (first.compareTo(last) <= 0) {
  1629.                 slots.addValidAfter(slot, first);
  1630.             } else {
  1631.                 slots.addValidBefore(slot, first);
  1632.             }
  1633.             return slot;
  1634.         }

  1635.         /** {@inheritDoc} */
  1636.         @Override
  1637.         public T[] value(final FieldOrbit<T> meanOrbit) {

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

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

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

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

  1647.                 // Central body rotation angle from equation 2.7.1-(3)(4).
  1648.                 final FieldTransform<T> t = bodyFrame.getTransformTo(auxiliaryElements.getFrame(), auxiliaryElements.getDate());
  1649.                 final FieldVector3D<T> xB = t.transformVector(Vector3D.PLUS_I);
  1650.                 final FieldVector3D<T> yB = t.transformVector(Vector3D.PLUS_J);
  1651.                 final FieldVector3D<T>  f = auxiliaryElements.getVectorF();
  1652.                 final FieldVector3D<T>  g = auxiliaryElements.getVectorG();
  1653.                 final T currentTheta = FastMath.atan2(f.dotProduct(yB).negate().add(g.dotProduct(xB).multiply(I)),
  1654.                                                       f.dotProduct(xB).add(g.dotProduct(yB).multiply(I)));

  1655.                 //Add the m-daily contribution
  1656.                 for (int m = 1; m <= maxOrderMdailyTesseralSP; m++) {
  1657.                     // Phase angle
  1658.                     final T jlMmt  = currentTheta.multiply(-m);
  1659.                     final T sinPhi = FastMath.sin(jlMmt);
  1660.                     final T cosPhi = FastMath.cos(jlMmt);

  1661.                     // compute contribution for each element
  1662.                     final T[] c = slot.getCijm(0, m, meanOrbit.getDate());
  1663.                     final T[] s = slot.getSijm(0, m, meanOrbit.getDate());
  1664.                     for (int i = 0; i < 6; i++) {
  1665.                         shortPeriodicVariation[i] = shortPeriodicVariation[i].add(c[i].multiply(cosPhi).add(s[i].multiply(sinPhi)));
  1666.                     }
  1667.                 }

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

  1672.                     for (int j : listJ) {
  1673.                         // Phase angle
  1674.                         final T jlMmt  = meanOrbit.getLM().multiply(j).subtract(currentTheta.multiply(m));
  1675.                         final T sinPhi = FastMath.sin(jlMmt);
  1676.                         final T cosPhi = FastMath.cos(jlMmt);

  1677.                         // compute contribution for each element
  1678.                         final T[] c = slot.getCijm(j, m, meanOrbit.getDate());
  1679.                         final T[] s = slot.getSijm(j, m, meanOrbit.getDate());
  1680.                         for (int i = 0; i < 6; i++) {
  1681.                             shortPeriodicVariation[i] = shortPeriodicVariation[i].add(c[i].multiply(cosPhi).add(s[i].multiply(sinPhi)));
  1682.                         }

  1683.                     }
  1684.                 }
  1685.             }

  1686.             return shortPeriodicVariation;

  1687.         }

  1688.         /** {@inheritDoc} */
  1689.         @Override
  1690.         public String getCoefficientsKeyPrefix() {
  1691.             return DSSTTesseral.SHORT_PERIOD_PREFIX;
  1692.         }

  1693.         /** {@inheritDoc}
  1694.          * <p>
  1695.          * For tesseral terms contributions,there are maxOrderMdailyTesseralSP
  1696.          * m-daily cMm coefficients, maxOrderMdailyTesseralSP m-daily sMm
  1697.          * coefficients, nbNonResonant cjm coefficients and nbNonResonant
  1698.          * sjm coefficients, where maxOrderMdailyTesseralSP and nbNonResonant both
  1699.          * depend on the orbit. The j index is the integer multiplier for the true
  1700.          * longitude argument and the m index is the integer multiplier for m-dailies.
  1701.          * </p>
  1702.          */
  1703.         @Override
  1704.         public Map<String, T[]> getCoefficients(final FieldAbsoluteDate<T> date, final Set<String> selected) {

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

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

  1711.                 for (int m = 1; m <= maxOrderMdailyTesseralSP; m++) {
  1712.                     storeIfSelected(coefficients, selected, slot.getCijm(0, m, date), DSSTTesseral.CM_COEFFICIENTS, m);
  1713.                     storeIfSelected(coefficients, selected, slot.getSijm(0, m, date), DSSTTesseral.SM_COEFFICIENTS, m);
  1714.                 }

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

  1718.                     for (int j : listJ) {
  1719.                         for (int i = 0; i < 6; ++i) {
  1720.                             storeIfSelected(coefficients, selected, slot.getCijm(j, m, date), "c", j, m);
  1721.                             storeIfSelected(coefficients, selected, slot.getSijm(j, m, date), "s", j, m);
  1722.                         }
  1723.                     }
  1724.                 }

  1725.                 return coefficients;

  1726.             } else {
  1727.                 return Collections.emptyMap();
  1728.             }

  1729.         }

  1730.         /** Put a coefficient in a map if selected.
  1731.          * @param map map to populate
  1732.          * @param selected set of coefficients that should be put in the map
  1733.          * (empty set means all coefficients are selected)
  1734.          * @param value coefficient value
  1735.          * @param id coefficient identifier
  1736.          * @param indices list of coefficient indices
  1737.          */
  1738.         private void storeIfSelected(final Map<String, T[]> map, final Set<String> selected,
  1739.                                      final T[] value, final String id, final int... indices) {
  1740.             final StringBuilder keyBuilder = new StringBuilder(getCoefficientsKeyPrefix());
  1741.             keyBuilder.append(id);
  1742.             for (int index : indices) {
  1743.                 keyBuilder.append('[').append(index).append(']');
  1744.             }
  1745.             final String key = keyBuilder.toString();
  1746.             if (selected.isEmpty() || selected.contains(key)) {
  1747.                 map.put(key, value);
  1748.             }
  1749.         }
  1750.     }

  1751.     /** Coefficients valid for one time slot. */
  1752.     private static class Slot {

  1753.         /** The coefficients C<sub>i</sub><sup>j</sup><sup>m</sup>.
  1754.          * <p>
  1755.          * The index order is cijm[m][j][i] <br/>
  1756.          * i corresponds to the equinoctial element, as follows: <br/>
  1757.          * - i=0 for a <br/>
  1758.          * - i=1 for k <br/>
  1759.          * - i=2 for h <br/>
  1760.          * - i=3 for q <br/>
  1761.          * - i=4 for p <br/>
  1762.          * - i=5 for λ <br/>
  1763.          * </p>
  1764.          */
  1765.         private final ShortPeriodicsInterpolatedCoefficient[][] cijm;

  1766.         /** The coefficients S<sub>i</sub><sup>j</sup><sup>m</sup>.
  1767.          * <p>
  1768.          * The index order is sijm[m][j][i] <br/>
  1769.          * i corresponds to the equinoctial element, as follows: <br/>
  1770.          * - i=0 for a <br/>
  1771.          * - i=1 for k <br/>
  1772.          * - i=2 for h <br/>
  1773.          * - i=3 for q <br/>
  1774.          * - i=4 for p <br/>
  1775.          * - i=5 for λ <br/>
  1776.          * </p>
  1777.          */
  1778.         private final ShortPeriodicsInterpolatedCoefficient[][] sijm;

  1779.         /** Simple constructor.
  1780.          *  @param mMax maximum value for m index
  1781.          *  @param jMax maximum value for j index
  1782.          *  @param interpolationPoints number of points used in the interpolation process
  1783.          */
  1784.         Slot(final int mMax, final int jMax, final int interpolationPoints) {

  1785.             final int rows    = mMax + 1;
  1786.             final int columns = 2 * jMax + 1;
  1787.             cijm = new ShortPeriodicsInterpolatedCoefficient[rows][columns];
  1788.             sijm = new ShortPeriodicsInterpolatedCoefficient[rows][columns];
  1789.             for (int m = 1; m <= mMax; m++) {
  1790.                 for (int j = -jMax; j <= jMax; j++) {
  1791.                     cijm[m][j + jMax] = new ShortPeriodicsInterpolatedCoefficient(interpolationPoints);
  1792.                     sijm[m][j + jMax] = new ShortPeriodicsInterpolatedCoefficient(interpolationPoints);
  1793.                 }
  1794.             }

  1795.         }

  1796.         /** Get C<sub>i</sub><sup>j</sup><sup>m</sup>.
  1797.          *
  1798.          * @param j j index
  1799.          * @param m m index
  1800.          * @param date the date
  1801.          * @return C<sub>i</sub><sup>j</sup><sup>m</sup>
  1802.          */
  1803.         double[] getCijm(final int j, final int m, final AbsoluteDate date) {
  1804.             final int jMax = (cijm[m].length - 1) / 2;
  1805.             return cijm[m][j + jMax].value(date);
  1806.         }

  1807.         /** Get S<sub>i</sub><sup>j</sup><sup>m</sup>.
  1808.          *
  1809.          * @param j j index
  1810.          * @param m m index
  1811.          * @param date the date
  1812.          * @return S<sub>i</sub><sup>j</sup><sup>m</sup>
  1813.          */
  1814.         double[] getSijm(final int j, final int m, final AbsoluteDate date) {
  1815.             final int jMax = (cijm[m].length - 1) / 2;
  1816.             return sijm[m][j + jMax].value(date);
  1817.         }

  1818.     }

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

  1821.         /** The coefficients C<sub>i</sub><sup>j</sup><sup>m</sup>.
  1822.          * <p>
  1823.          * The index order is cijm[m][j][i] <br/>
  1824.          * i corresponds to the equinoctial element, as follows: <br/>
  1825.          * - i=0 for a <br/>
  1826.          * - i=1 for k <br/>
  1827.          * - i=2 for h <br/>
  1828.          * - i=3 for q <br/>
  1829.          * - i=4 for p <br/>
  1830.          * - i=5 for λ <br/>
  1831.          * </p>
  1832.          */
  1833.         private final FieldShortPeriodicsInterpolatedCoefficient<T>[][] cijm;

  1834.         /** The coefficients S<sub>i</sub><sup>j</sup><sup>m</sup>.
  1835.          * <p>
  1836.          * The index order is sijm[m][j][i] <br/>
  1837.          * i corresponds to the equinoctial element, as follows: <br/>
  1838.          * - i=0 for a <br/>
  1839.          * - i=1 for k <br/>
  1840.          * - i=2 for h <br/>
  1841.          * - i=3 for q <br/>
  1842.          * - i=4 for p <br/>
  1843.          * - i=5 for λ <br/>
  1844.          * </p>
  1845.          */
  1846.         private final FieldShortPeriodicsInterpolatedCoefficient<T>[][] sijm;

  1847.         /** Simple constructor.
  1848.          *  @param mMax maximum value for m index
  1849.          *  @param jMax maximum value for j index
  1850.          *  @param interpolationPoints number of points used in the interpolation process
  1851.          */
  1852.         @SuppressWarnings("unchecked")
  1853.         FieldSlot(final int mMax, final int jMax, final int interpolationPoints) {

  1854.             final int rows    = mMax + 1;
  1855.             final int columns = 2 * jMax + 1;
  1856.             cijm = (FieldShortPeriodicsInterpolatedCoefficient<T>[][]) Array.newInstance(FieldShortPeriodicsInterpolatedCoefficient.class, rows, columns);
  1857.             sijm = (FieldShortPeriodicsInterpolatedCoefficient<T>[][]) Array.newInstance(FieldShortPeriodicsInterpolatedCoefficient.class, rows, columns);
  1858.             for (int m = 1; m <= mMax; m++) {
  1859.                 for (int j = -jMax; j <= jMax; j++) {
  1860.                     cijm[m][j + jMax] = new FieldShortPeriodicsInterpolatedCoefficient<>(interpolationPoints);
  1861.                     sijm[m][j + jMax] = new FieldShortPeriodicsInterpolatedCoefficient<>(interpolationPoints);
  1862.                 }
  1863.             }

  1864.         }

  1865.         /** Get C<sub>i</sub><sup>j</sup><sup>m</sup>.
  1866.          *
  1867.          * @param j j index
  1868.          * @param m m index
  1869.          * @param date the date
  1870.          * @return C<sub>i</sub><sup>j</sup><sup>m</sup>
  1871.          */
  1872.         T[] getCijm(final int j, final int m, final FieldAbsoluteDate<T> date) {
  1873.             final int jMax = (cijm[m].length - 1) / 2;
  1874.             return cijm[m][j + jMax].value(date);
  1875.         }

  1876.         /** Get S<sub>i</sub><sup>j</sup><sup>m</sup>.
  1877.          *
  1878.          * @param j j index
  1879.          * @param m m index
  1880.          * @param date the date
  1881.          * @return S<sub>i</sub><sup>j</sup><sup>m</sup>
  1882.          */
  1883.         T[] getSijm(final int j, final int m, final FieldAbsoluteDate<T> date) {
  1884.             final int jMax = (cijm[m].length - 1) / 2;
  1885.             return sijm[m][j + jMax].value(date);
  1886.         }

  1887.     }

  1888.     /** Compute potential and potential derivatives with respect to orbital parameters.
  1889.      *  <p>The following elements are computed from expression 3.3 - (4).
  1890.      *  <pre>
  1891.      *  dU / da
  1892.      *  dU / dh
  1893.      *  dU / dk
  1894.      *  dU / dλ
  1895.      *  dU / dα
  1896.      *  dU / dβ
  1897.      *  dU / dγ
  1898.      *  </pre>
  1899.      *  </p>
  1900.      */
  1901.     private class UAnddU {

  1902.         /** dU / da. */
  1903.         private  double dUda;

  1904.         /** dU / dk. */
  1905.         private double dUdk;

  1906.         /** dU / dh. */
  1907.         private double dUdh;

  1908.         /** dU / dl. */
  1909.         private double dUdl;

  1910.         /** dU / dAlpha. */
  1911.         private double dUdAl;

  1912.         /** dU / dBeta. */
  1913.         private double dUdBe;

  1914.         /** dU / dGamma. */
  1915.         private double dUdGa;

  1916.         /** Simple constuctor.
  1917.          * @param date current date
  1918.          * @param context container for attributes
  1919.          * @param hansen hansen objects
  1920.          */
  1921.         UAnddU(final AbsoluteDate date, final DSSTTesseralContext context, final HansenObjects hansen) {

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

  1924.             // Potential derivatives
  1925.             dUda  = 0.;
  1926.             dUdh  = 0.;
  1927.             dUdk  = 0.;
  1928.             dUdl  = 0.;
  1929.             dUdAl = 0.;
  1930.             dUdBe = 0.;
  1931.             dUdGa = 0.;

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

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

  1938.                 // R / a up to power degree
  1939.                 final double[] roaPow = new double[maxDegree + 1];
  1940.                 roaPow[0] = 1.;
  1941.                 for (int i = 1; i <= maxDegree; i++) {
  1942.                     roaPow[i] = context.getRoa() * roaPow[i - 1];
  1943.                 }

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

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

  1948.                     // Phase angle
  1949.                     final double jlMmt  = j * auxiliaryElements.getLM() - m * context.getTheta();
  1950.                     final double sinPhi = FastMath.sin(jlMmt);
  1951.                     final double cosPhi = FastMath.cos(jlMmt);

  1952.                     // Potential derivatives components for a given resonant pair {j,m}
  1953.                     double dUdaCos  = 0.;
  1954.                     double dUdaSin  = 0.;
  1955.                     double dUdhCos  = 0.;
  1956.                     double dUdhSin  = 0.;
  1957.                     double dUdkCos  = 0.;
  1958.                     double dUdkSin  = 0.;
  1959.                     double dUdlCos  = 0.;
  1960.                     double dUdlSin  = 0.;
  1961.                     double dUdAlCos = 0.;
  1962.                     double dUdAlSin = 0.;
  1963.                     double dUdBeCos = 0.;
  1964.                     double dUdBeSin = 0.;
  1965.                     double dUdGaCos = 0.;
  1966.                     double dUdGaSin = 0.;

  1967.                     // s-SUM from -sMin to sMax
  1968.                     final int sMin = FastMath.min(context.getMaxEccPow() - j, maxDegree);
  1969.                     final int sMax = FastMath.min(context.getMaxEccPow() + j, maxDegree);
  1970.                     for (int s = 0; s <= sMax; s++) {

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

  1973.                         // n-SUM for s positive
  1974.                         final double[][] nSumSpos = computeNSum(date, j, m, s, maxDegree,
  1975.                                                                 roaPow, ghMSJ, gammaMNS, context, hansen);
  1976.                         dUdaCos  += nSumSpos[0][0];
  1977.                         dUdaSin  += nSumSpos[0][1];
  1978.                         dUdhCos  += nSumSpos[1][0];
  1979.                         dUdhSin  += nSumSpos[1][1];
  1980.                         dUdkCos  += nSumSpos[2][0];
  1981.                         dUdkSin  += nSumSpos[2][1];
  1982.                         dUdlCos  += nSumSpos[3][0];
  1983.                         dUdlSin  += nSumSpos[3][1];
  1984.                         dUdAlCos += nSumSpos[4][0];
  1985.                         dUdAlSin += nSumSpos[4][1];
  1986.                         dUdBeCos += nSumSpos[5][0];
  1987.                         dUdBeSin += nSumSpos[5][1];
  1988.                         dUdGaCos += nSumSpos[6][0];
  1989.                         dUdGaSin += nSumSpos[6][1];

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

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

  2012.                     // Assembly of potential derivatives componants
  2013.                     dUda  += cosPhi * dUdaCos  + sinPhi * dUdaSin;
  2014.                     dUdh  += cosPhi * dUdhCos  + sinPhi * dUdhSin;
  2015.                     dUdk  += cosPhi * dUdkCos  + sinPhi * dUdkSin;
  2016.                     dUdl  += cosPhi * dUdlCos  + sinPhi * dUdlSin;
  2017.                     dUdAl += cosPhi * dUdAlCos + sinPhi * dUdAlSin;
  2018.                     dUdBe += cosPhi * dUdBeCos + sinPhi * dUdBeSin;
  2019.                     dUdGa += cosPhi * dUdGaCos + sinPhi * dUdGaSin;
  2020.                 }

  2021.                 this.dUda  = dUda * (-context.getMoa() / auxiliaryElements.getSma());
  2022.                 this.dUdh  = dUdh * context.getMoa();
  2023.                 this.dUdk  = dUdk * context.getMoa();
  2024.                 this.dUdl  = dUdl * context.getMoa();
  2025.                 this.dUdAl = dUdAl * context.getMoa();
  2026.                 this.dUdBe = dUdBe * context.getMoa();
  2027.                 this.dUdGa = dUdGa * context.getMoa();
  2028.             }

  2029.         }

  2030.         /** Return value of dU / da.
  2031.          * @return dUda
  2032.          */
  2033.         public double getdUda() {
  2034.             return dUda;
  2035.         }

  2036.         /** Return value of dU / dk.
  2037.          * @return dUdk
  2038.          */
  2039.         public double getdUdk() {
  2040.             return dUdk;
  2041.         }

  2042.         /** Return value of dU / dh.
  2043.          * @return dUdh
  2044.          */
  2045.         public double getdUdh() {
  2046.             return dUdh;
  2047.         }

  2048.         /** Return value of dU / dl.
  2049.          * @return dUdl
  2050.          */
  2051.         public double getdUdl() {
  2052.             return dUdl;
  2053.         }

  2054.         /** Return value of dU / dAlpha.
  2055.          * @return dUdAl
  2056.          */
  2057.         public double getdUdAl() {
  2058.             return dUdAl;
  2059.         }

  2060.         /** Return value of dU / dBeta.
  2061.          * @return dUdBe
  2062.          */
  2063.         public double getdUdBe() {
  2064.             return dUdBe;
  2065.         }

  2066.         /** Return value of dU / dGamma.
  2067.          * @return dUdGa
  2068.          */
  2069.         public double getdUdGa() {
  2070.             return dUdGa;
  2071.         }

  2072.     }

  2073.     /**  Computes the potential U derivatives.
  2074.      *  <p>The following elements are computed from expression 3.3 - (4).
  2075.      *  <pre>
  2076.      *  dU / da
  2077.      *  dU / dh
  2078.      *  dU / dk
  2079.      *  dU / dλ
  2080.      *  dU / dα
  2081.      *  dU / dβ
  2082.      *  dU / dγ
  2083.      *  </pre>
  2084.      *  </p>
  2085.      */
  2086.     private class FieldUAnddU <T extends RealFieldElement<T>> {

  2087.         /** dU / da. */
  2088.         private T dUda;

  2089.         /** dU / dk. */
  2090.         private T dUdk;

  2091.         /** dU / dh. */
  2092.         private T dUdh;

  2093.         /** dU / dl. */
  2094.         private T dUdl;

  2095.         /** dU / dAlpha. */
  2096.         private T dUdAl;

  2097.         /** dU / dBeta. */
  2098.         private T dUdBe;

  2099.         /** dU / dGamma. */
  2100.         private T dUdGa;

  2101.         /** Simple constuctor.
  2102.          * @param date current date
  2103.          * @param context container for attributes
  2104.          * @param hansen hansen objects
  2105.          */
  2106.         FieldUAnddU(final FieldAbsoluteDate<T> date, final FieldDSSTTesseralContext<T> context,
  2107.                     final FieldHansenObjects<T> hansen) {

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

  2110.             // Zero for initialization
  2111.             final Field<T> field = date.getField();
  2112.             final T zero = field.getZero();

  2113.             // Potential derivatives
  2114.             dUda  = zero;
  2115.             dUdh  = zero;
  2116.             dUdk  = zero;
  2117.             dUdl  = zero;
  2118.             dUdAl = zero;
  2119.             dUdBe = zero;
  2120.             dUdGa = zero;

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

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

  2127.                 // R / a up to power degree
  2128.                 final T[] roaPow = MathArrays.buildArray(field, maxDegree + 1);
  2129.                 roaPow[0] = zero.add(1.);
  2130.                 for (int i = 1; i <= maxDegree; i++) {
  2131.                     roaPow[i] = roaPow[i - 1].multiply(context.getRoa());
  2132.                 }

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

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

  2137.                     // Phase angle
  2138.                     final T jlMmt  = auxiliaryElements.getLM().multiply(j).subtract(context.getTheta().multiply(m));
  2139.                     final T sinPhi = FastMath.sin(jlMmt);
  2140.                     final T cosPhi = FastMath.cos(jlMmt);

  2141.                     // Potential derivatives components for a given resonant pair {j,m}
  2142.                     T dUdaCos  = zero;
  2143.                     T dUdaSin  = zero;
  2144.                     T dUdhCos  = zero;
  2145.                     T dUdhSin  = zero;
  2146.                     T dUdkCos  = zero;
  2147.                     T dUdkSin  = zero;
  2148.                     T dUdlCos  = zero;
  2149.                     T dUdlSin  = zero;
  2150.                     T dUdAlCos = zero;
  2151.                     T dUdAlSin = zero;
  2152.                     T dUdBeCos = zero;
  2153.                     T dUdBeSin = zero;
  2154.                     T dUdGaCos = zero;
  2155.                     T dUdGaSin = zero;

  2156.                     // s-SUM from -sMin to sMax
  2157.                     final int sMin = FastMath.min(context.getMaxEccPow() - j, maxDegree);
  2158.                     final int sMax = FastMath.min(context.getMaxEccPow() + j, maxDegree);
  2159.                     for (int s = 0; s <= sMax; s++) {

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

  2162.                         // n-SUM for s positive
  2163.                         final T[][] nSumSpos = computeNSum(date, j, m, s, maxDegree,
  2164.                                                                 roaPow, ghMSJ, gammaMNS, context, hansen);
  2165.                         dUdaCos  = dUdaCos.add(nSumSpos[0][0]);
  2166.                         dUdaSin  = dUdaSin.add(nSumSpos[0][1]);
  2167.                         dUdhCos  = dUdhCos.add(nSumSpos[1][0]);
  2168.                         dUdhSin  = dUdhSin.add(nSumSpos[1][1]);
  2169.                         dUdkCos  = dUdkCos.add(nSumSpos[2][0]);
  2170.                         dUdkSin  = dUdkSin.add(nSumSpos[2][1]);
  2171.                         dUdlCos  = dUdlCos.add(nSumSpos[3][0]);
  2172.                         dUdlSin  = dUdlSin.add(nSumSpos[3][1]);
  2173.                         dUdAlCos = dUdAlCos.add(nSumSpos[4][0]);
  2174.                         dUdAlSin = dUdAlSin.add(nSumSpos[4][1]);
  2175.                         dUdBeCos = dUdBeCos.add(nSumSpos[5][0]);
  2176.                         dUdBeSin = dUdBeSin.add(nSumSpos[5][1]);
  2177.                         dUdGaCos = dUdGaCos.add(nSumSpos[6][0]);
  2178.                         dUdGaSin = dUdGaSin.add(nSumSpos[6][1]);

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

  2183.                             final T[][] nSumSneg = computeNSum(date, j, m, -s, maxDegree,
  2184.                                                                     roaPow, ghMSJ, gammaMNS, context, hansen);
  2185.                             dUdaCos  = dUdaCos.add(nSumSneg[0][0]);
  2186.                             dUdaSin  = dUdaSin.add(nSumSneg[0][1]);
  2187.                             dUdhCos  = dUdhCos.add(nSumSneg[1][0]);
  2188.                             dUdhSin  = dUdhSin.add(nSumSneg[1][1]);
  2189.                             dUdkCos  = dUdkCos.add(nSumSneg[2][0]);
  2190.                             dUdkSin  = dUdkSin.add(nSumSneg[2][1]);
  2191.                             dUdlCos  = dUdlCos.add(nSumSneg[3][0]);
  2192.                             dUdlSin  = dUdlSin.add(nSumSneg[3][1]);
  2193.                             dUdAlCos = dUdAlCos.add(nSumSneg[4][0]);
  2194.                             dUdAlSin = dUdAlSin.add(nSumSneg[4][1]);
  2195.                             dUdBeCos = dUdBeCos.add(nSumSneg[5][0]);
  2196.                             dUdBeSin = dUdBeSin.add(nSumSneg[5][1]);
  2197.                             dUdGaCos = dUdGaCos.add(nSumSneg[6][0]);
  2198.                             dUdGaSin = dUdGaSin.add(nSumSneg[6][1]);
  2199.                         }
  2200.                     }

  2201.                     // Assembly of potential derivatives componants
  2202.                     dUda  = dUda.add(dUdaCos.multiply(cosPhi).add(dUdaSin.multiply(sinPhi)));
  2203.                     dUdh  = dUdh.add(dUdhCos.multiply(cosPhi).add(dUdhSin.multiply(sinPhi)));
  2204.                     dUdk  = dUdk.add(dUdkCos.multiply(cosPhi).add(dUdkSin.multiply(sinPhi)));
  2205.                     dUdl  = dUdl.add(dUdlCos.multiply(cosPhi).add(dUdlSin.multiply(sinPhi)));
  2206.                     dUdAl = dUdAl.add(dUdAlCos.multiply(cosPhi).add(dUdAlSin.multiply(sinPhi)));
  2207.                     dUdBe = dUdBe.add(dUdBeCos.multiply(cosPhi).add(dUdBeSin.multiply(sinPhi)));
  2208.                     dUdGa = dUdGa.add(dUdGaCos.multiply(cosPhi).add(dUdGaSin.multiply(sinPhi)));
  2209.                 }

  2210.                 dUda  =  dUda.multiply(context.getMoa().divide(auxiliaryElements.getSma())).negate();
  2211.                 dUdh  =  dUdh.multiply(context.getMoa());
  2212.                 dUdk  =  dUdk.multiply(context.getMoa());
  2213.                 dUdl  =  dUdl.multiply(context.getMoa());
  2214.                 dUdAl =  dUdAl.multiply(context.getMoa());
  2215.                 dUdBe =  dUdBe.multiply(context.getMoa());
  2216.                 dUdGa =  dUdGa.multiply(context.getMoa());

  2217.             }

  2218.         }

  2219.         /** Return value of dU / da.
  2220.          * @return dUda
  2221.          */
  2222.         public T getdUda() {
  2223.             return dUda;
  2224.         }

  2225.         /** Return value of dU / dk.
  2226.          * @return dUdk
  2227.          */
  2228.         public T getdUdk() {
  2229.             return dUdk;
  2230.         }

  2231.         /** Return value of dU / dh.
  2232.          * @return dUdh
  2233.          */
  2234.         public T getdUdh() {
  2235.             return dUdh;
  2236.         }

  2237.         /** Return value of dU / dl.
  2238.          * @return dUdl
  2239.          */
  2240.         public T getdUdl() {
  2241.             return dUdl;
  2242.         }

  2243.         /** Return value of dU / dAlpha.
  2244.          * @return dUdAl
  2245.          */
  2246.         public T getdUdAl() {
  2247.             return dUdAl;
  2248.         }

  2249.         /** Return value of dU / dBeta.
  2250.          * @return dUdBe
  2251.          */
  2252.         public T getdUdBe() {
  2253.             return dUdBe;
  2254.         }

  2255.         /** Return value of dU / dGamma.
  2256.          * @return dUdGa
  2257.          */
  2258.         public T getdUdGa() {
  2259.             return dUdGa;
  2260.         }

  2261.     }

  2262.     /** Computes init values of the Hansen Objects. */
  2263.     private class HansenObjects {

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

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

  2269.         /** Simple constructor.
  2270.          * @param ratio Ratio of satellite period to central body rotation period
  2271.          * @param maxEccPow Maximum power of the eccentricity to use in summation over s.
  2272.          * @param resOrders List of resonant orders
  2273.          * @param type type of the elements used during the propagation
  2274.          */
  2275.         HansenObjects(final double ratio,
  2276.                       final int maxEccPow,
  2277.                       final List<Integer> resOrders,
  2278.                       final PropagationType type) {

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

  2281.             //Allocate the two dimensional array
  2282.             final int rows     = 2 * maxDegree + 1;
  2283.             final int columns  = maxFrequencyShortPeriodics + 1;
  2284.             this.hansenObjects = new HansenTesseralLinear[rows][columns];

  2285.             switch (type) {
  2286.                 case MEAN:
  2287.                     // loop through the resonant orders
  2288.                     for (int m : resOrders) {
  2289.                         //Compute the corresponding j term
  2290.                         final int j = FastMath.max(1, (int) FastMath.round(ratio * m));

  2291.                         //Compute the sMin and sMax values
  2292.                         final int sMin = FastMath.min(maxEccPow - j, maxDegree);
  2293.                         final int sMax = FastMath.min(maxEccPow + j, maxDegree);

  2294.                         //loop through the s values
  2295.                         for (int s = 0; s <= sMax; s++) {
  2296.                             //Compute the n0 value
  2297.                             final int n0 = FastMath.max(FastMath.max(2, m), s);

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

  2300.                             if (s > 0 && s <= sMin) {
  2301.                                 //Also create the object for the pair j, -s
  2302.                                 this.hansenObjects[maxDegree - s][j] =  new HansenTesseralLinear(maxDegree, -s, j, n0, maxHansen);
  2303.                             }
  2304.                         }
  2305.                     }
  2306.                     break;

  2307.                 case OSCULATING:
  2308.                     // create all objects
  2309.                     for (int j = 0; j <= maxFrequencyShortPeriodics; j++) {
  2310.                         for (int s = -maxDegree; s <= maxDegree; s++) {
  2311.                             //Compute the n0 value
  2312.                             final int n0 = FastMath.max(2, FastMath.abs(s));
  2313.                             this.hansenObjects[s + maxDegree][j] = new HansenTesseralLinear(maxDegree, s, j, n0, maxHansen);
  2314.                         }
  2315.                     }
  2316.                     break;

  2317.                 default:
  2318.                     throw new OrekitInternalError(null);
  2319.             }

  2320.         }

  2321.         /** Compute init values for hansen objects.
  2322.          * @param context container for attributes
  2323.          * @param rows number of rows of the hansen matrix
  2324.          * @param columns columns number of columns of the hansen matrix
  2325.          */
  2326.         public void computeHansenObjectsInitValues(final DSSTTesseralContext context, final int rows, final int columns) {
  2327.             hansenObjects[rows][columns].computeInitValues(context.getE2(), context.getChi(), context.getChi2());
  2328.         }

  2329.         /** Get hansen object.
  2330.          * @return hansenObjects
  2331.          */
  2332.         public HansenTesseralLinear[][] getHansenObjects() {
  2333.             return hansenObjects;
  2334.         }

  2335.     }

  2336.     /** Computes init values of the Hansen Objects. */
  2337.     private class FieldHansenObjects<T extends RealFieldElement<T>> {

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

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

  2343.         /** Simple constructor.
  2344.          * @param ratio Ratio of satellite period to central body rotation period
  2345.          * @param maxEccPow Maximum power of the eccentricity to use in summation over s.
  2346.          * @param resOrders List of resonant orders
  2347.          * @param type type of the elements used during the propagation
  2348.          * @param field field used by default.
  2349.          */
  2350.         @SuppressWarnings("unchecked")
  2351.         FieldHansenObjects(final T ratio,
  2352.                            final int maxEccPow,
  2353.                            final List<Integer> resOrders,
  2354.                            final PropagationType type,
  2355.                            final Field<T> field) {

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

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

  2362.             switch (type) {
  2363.                 case MEAN:
  2364.                  // loop through the resonant orders
  2365.                     for (int m : resOrders) {
  2366.                         //Compute the corresponding j term
  2367.                         final int j = FastMath.max(1, (int) FastMath.round(ratio.multiply(m)));

  2368.                         //Compute the sMin and sMax values
  2369.                         final int sMin = FastMath.min(maxEccPow - j, maxDegree);
  2370.                         final int sMax = FastMath.min(maxEccPow + j, maxDegree);

  2371.                         //loop through the s values
  2372.                         for (int s = 0; s <= sMax; s++) {
  2373.                             //Compute the n0 value
  2374.                             final int n0 = FastMath.max(FastMath.max(2, m), s);

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

  2377.                             if (s > 0 && s <= sMin) {
  2378.                                 //Also create the object for the pair j, -s
  2379.                                 this.hansenObjects[maxDegree - s][j] =  new FieldHansenTesseralLinear<>(maxDegree, -s, j, n0, maxHansen, field);
  2380.                             }
  2381.                         }
  2382.                     }
  2383.                     break;

  2384.                 case OSCULATING:
  2385.                     // create all objects
  2386.                     for (int j = 0; j <= maxFrequencyShortPeriodics; j++) {
  2387.                         for (int s = -maxDegree; s <= maxDegree; s++) {
  2388.                             //Compute the n0 value
  2389.                             final int n0 = FastMath.max(2, FastMath.abs(s));
  2390.                             this.hansenObjects[s + maxDegree][j] = new FieldHansenTesseralLinear<>(maxDegree, s, j, n0, maxHansen, field);
  2391.                         }
  2392.                     }
  2393.                     break;

  2394.                 default:
  2395.                     throw new OrekitInternalError(null);
  2396.             }

  2397.         }

  2398.         /** Compute init values for hansen objects.
  2399.          * @param context container for attributes
  2400.          * @param rows number of rows of the hansen matrix
  2401.          * @param columns columns number of columns of the hansen matrix
  2402.          */
  2403.         public void computeHansenObjectsInitValues(final FieldDSSTTesseralContext<T> context,
  2404.                                                    final int rows, final int columns) {
  2405.             hansenObjects[rows][columns].computeInitValues(context.getE2(), context.getChi(), context.getChi2());
  2406.         }

  2407.         /** Get hansen object.
  2408.          * @return hansenObjects
  2409.          */
  2410.         public FieldHansenTesseralLinear<T>[][] getHansenObjects() {
  2411.             return hansenObjects;
  2412.         }

  2413.     }

  2414. }