1   /* Copyright 2002-2024 CS GROUP
2    * Licensed to CS GROUP (CS) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * CS licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *   http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.orekit.propagation.semianalytical.dsst.forces;
18  
19  import org.hipparchus.CalculusFieldElement;
20  import org.hipparchus.Field;
21  import org.hipparchus.analysis.differentiation.FieldGradient;
22  import org.hipparchus.exception.LocalizedCoreFormats;
23  import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
24  import org.hipparchus.geometry.euclidean.threed.Vector3D;
25  import org.hipparchus.util.FastMath;
26  import org.hipparchus.util.FieldSinCos;
27  import org.hipparchus.util.MathArrays;
28  import org.hipparchus.util.MathUtils;
29  import org.hipparchus.util.SinCos;
30  import org.orekit.attitudes.AttitudeProvider;
31  import org.orekit.errors.OrekitException;
32  import org.orekit.errors.OrekitInternalError;
33  import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider;
34  import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider.UnnormalizedSphericalHarmonics;
35  import org.orekit.frames.FieldStaticTransform;
36  import org.orekit.frames.Frame;
37  import org.orekit.frames.StaticTransform;
38  import org.orekit.orbits.FieldOrbit;
39  import org.orekit.orbits.Orbit;
40  import org.orekit.propagation.FieldSpacecraftState;
41  import org.orekit.propagation.PropagationType;
42  import org.orekit.propagation.SpacecraftState;
43  import org.orekit.propagation.semianalytical.dsst.utilities.AuxiliaryElements;
44  import org.orekit.propagation.semianalytical.dsst.utilities.CoefficientsFactory;
45  import org.orekit.propagation.semianalytical.dsst.utilities.FieldAuxiliaryElements;
46  import org.orekit.propagation.semianalytical.dsst.utilities.FieldGHmsjPolynomials;
47  import org.orekit.propagation.semianalytical.dsst.utilities.FieldGammaMnsFunction;
48  import org.orekit.propagation.semianalytical.dsst.utilities.FieldShortPeriodicsInterpolatedCoefficient;
49  import org.orekit.propagation.semianalytical.dsst.utilities.GHmsjPolynomials;
50  import org.orekit.propagation.semianalytical.dsst.utilities.GammaMnsFunction;
51  import org.orekit.propagation.semianalytical.dsst.utilities.JacobiPolynomials;
52  import org.orekit.propagation.semianalytical.dsst.utilities.ShortPeriodicsInterpolatedCoefficient;
53  import org.orekit.propagation.semianalytical.dsst.utilities.hansen.FieldHansenTesseralLinear;
54  import org.orekit.propagation.semianalytical.dsst.utilities.hansen.HansenTesseralLinear;
55  import org.orekit.time.AbsoluteDate;
56  import org.orekit.time.FieldAbsoluteDate;
57  import org.orekit.utils.FieldTimeSpanMap;
58  import org.orekit.utils.ParameterDriver;
59  import org.orekit.utils.TimeSpanMap;
60  
61  import java.lang.reflect.Array;
62  import java.util.ArrayList;
63  import java.util.Arrays;
64  import java.util.Collections;
65  import java.util.HashMap;
66  import java.util.List;
67  import java.util.Map;
68  import java.util.Set;
69  import java.util.SortedMap;
70  import java.util.TreeMap;
71  
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  
83      /**  Name of the prefix for short period coefficients keys. */
84      public static final String SHORT_PERIOD_PREFIX = "DSST-central-body-tesseral-";
85  
86      /** Identifier for cMm coefficients. */
87      public static final String CM_COEFFICIENTS = "cM";
88  
89      /** Identifier for sMm coefficients. */
90      public static final String SM_COEFFICIENTS = "sM";
91  
92      /** Retrograde factor I.
93       *  <p>
94       *  DSST model needs equinoctial orbit as internal representation.
95       *  Classical equinoctial elements have discontinuities when inclination
96       *  is close to zero. In this representation, I = +1. <br>
97       *  To avoid this discontinuity, another representation exists and equinoctial
98       *  elements can be expressed in a different way, called "retrograde" orbit.
99       *  This implies I = -1. <br>
100      *  As Orekit doesn't implement the retrograde orbit, I is always set to +1.
101      *  But for the sake of consistency with the theory, the retrograde factor
102      *  has been kept in the formulas.
103      *  </p>
104      */
105     private static final int I = 1;
106 
107     /** Central attraction scaling factor.
108      * <p>
109      * We use a power of 2 to avoid numeric noise introduction
110      * in the multiplications/divisions sequences.
111      * </p>
112      */
113     private static final double MU_SCALE = FastMath.scalb(1.0, 32);
114 
115     /** Minimum period for analytically averaged high-order resonant
116      *  central body spherical harmonics in seconds.
117      */
118     private static final double MIN_PERIOD_IN_SECONDS = 864000.;
119 
120     /** Minimum period for analytically averaged high-order resonant
121      *  central body spherical harmonics in satellite revolutions.
122      */
123     private static final double MIN_PERIOD_IN_SAT_REV = 10.;
124 
125     /** Number of points for interpolation. */
126     private static final int INTERPOLATION_POINTS = 3;
127 
128     /** Provider for spherical harmonics. */
129     private final UnnormalizedSphericalHarmonicsProvider provider;
130 
131     /** Central body rotating frame. */
132     private final Frame bodyFrame;
133 
134     /** Central body rotation rate (rad/s). */
135     private final double centralBodyRotationRate;
136 
137     /** Central body rotation period (seconds). */
138     private final double bodyPeriod;
139 
140     /** Maximal degree to consider for harmonics potential. */
141     private final int maxDegree;
142 
143     /** Maximal degree to consider for short periodics tesseral harmonics potential (without m-daily). */
144     private final int maxDegreeTesseralSP;
145 
146     /** Maximal degree to consider for short periodics m-daily tesseral harmonics potential . */
147     private final int maxDegreeMdailyTesseralSP;
148 
149     /** Maximal order to consider for harmonics potential. */
150     private final int maxOrder;
151 
152     /** Maximal order to consider for short periodics tesseral harmonics potential (without m-daily). */
153     private final int maxOrderTesseralSP;
154 
155     /** Maximal order to consider for short periodics m-daily tesseral harmonics potential . */
156     private final int maxOrderMdailyTesseralSP;
157 
158     /** Maximum power of the eccentricity to use in summation over s for
159      * short periodic tesseral harmonics (without m-daily). */
160     private final int maxEccPowTesseralSP;
161 
162     /** Maximum power of the eccentricity to use in summation over s for
163      * m-daily tesseral harmonics. */
164     private final int maxEccPowMdailyTesseralSP;
165 
166     /** Maximum value for j. */
167     private final int maxFrequencyShortPeriodics;
168 
169     /** Maximum power of the eccentricity to use in summation over s. */
170     private int maxEccPow;
171 
172     /** Maximum power of the eccentricity to use in Hansen coefficient Kernel expansion. */
173     private int maxHansen;
174 
175     /** Maximum value between maxOrderMdailyTesseralSP and maxOrderTesseralSP. */
176     private int mMax;
177 
178     /** List of non resonant orders with j != 0. */
179     private final SortedMap<Integer, List<Integer> > nonResOrders;
180 
181     /** List of resonant orders. */
182     private final List<Integer> resOrders;
183 
184     /** Short period terms. */
185     private TesseralShortPeriodicCoefficients shortPeriodTerms;
186 
187     /** Short period terms. */
188     private Map<Field<?>, FieldTesseralShortPeriodicCoefficients<?>> fieldShortPeriodTerms;
189 
190     /** Driver for gravitational parameter. */
191     private final ParameterDriver gmParameterDriver;
192 
193     /** Hansen objects. */
194     private HansenObjects hansen;
195 
196     /** Hansen objects for field elements. */
197     private Map<Field<?>, FieldHansenObjects<?>> fieldHansen;
198 
199     /** Simple constructor with default reference values.
200      * <p>
201      * When this constructor is used, maximum allowed values are used
202      * for the short periodic coefficients:
203      * </p>
204      * <ul>
205      *    <li> {@link #maxDegreeTesseralSP} is set to {@code provider.getMaxDegree()} </li>
206      *    <li> {@link #maxOrderTesseralSP} is set to {@code provider.getMaxOrder()}. </li>
207      *    <li> {@link #maxEccPowTesseralSP} is set to {@code min(4, provider.getMaxOrder())} </li>
208      *    <li> {@link #maxFrequencyShortPeriodics} is set to {@code min(provider.getMaxDegree() + 4, 12)}.
209      *         This parameter should not exceed 12 as higher values will exceed computer capacity </li>
210      *    <li> {@link #maxDegreeMdailyTesseralSP} is set to {@code provider.getMaxDegree()} </li>
211      *    <li> {@link #maxOrderMdailyTesseralSP} is set to {@code provider.getMaxOrder()} </li>
212      *    <li> {@link #maxEccPowMdailyTesseralSP} is set to min(provider.getMaxDegree() - 2, 4).
213      *         This parameter should not exceed 4 as higher values will exceed computer capacity </li>
214      * </ul>
215      * @param centralBodyFrame rotating body frame
216      * @param centralBodyRotationRate central body rotation rate (rad/s)
217      * @param provider provider for spherical harmonics
218      * @since 10.1
219      */
220     public DSSTTesseral(final Frame centralBodyFrame,
221                         final double centralBodyRotationRate,
222                         final UnnormalizedSphericalHarmonicsProvider provider) {
223         this(centralBodyFrame, centralBodyRotationRate, provider, provider.getMaxDegree(),
224              provider.getMaxOrder(), FastMath.min(4, provider.getMaxOrder()),  FastMath.min(12, provider.getMaxDegree() + 4),
225              provider.getMaxDegree(), provider.getMaxOrder(), FastMath.min(4, provider.getMaxDegree() - 2));
226     }
227 
228     /** Simple constructor.
229      * @param centralBodyFrame rotating body frame
230      * @param centralBodyRotationRate central body rotation rate (rad/s)
231      * @param provider provider for spherical harmonics
232      * @param maxDegreeTesseralSP maximal degree to consider for short periodics tesseral harmonics potential
233      *  (must be between 2 and {@code provider.getMaxDegree()})
234      * @param maxOrderTesseralSP maximal order to consider for short periodics tesseral harmonics potential
235      *  (must be between 0 and {@code provider.getMaxOrder()})
236      * @param maxEccPowTesseralSP maximum power of the eccentricity to use in summation over s for
237      * short periodic tesseral harmonics (without m-daily), should typically not exceed 4 as higher
238      * values will exceed computer capacity
239      * (must be between 0 and {@code provider.getMaxOrder()} though, however if order = 0 the value can be anything
240      *  since it won't be used in the code)
241      * @param maxFrequencyShortPeriodics maximum frequency in mean longitude for short periodic computations
242      * (typically {@code maxDegreeTesseralSP} + {@code maxEccPowTesseralSP and no more than 12})
243      * @param maxDegreeMdailyTesseralSP maximal degree to consider for short periodics m-daily tesseral harmonics potential
244      *  (must be between 2 and {@code provider.getMaxDegree()})
245      * @param maxOrderMdailyTesseralSP maximal order to consider for short periodics m-daily tesseral harmonics potential
246      *  (must be between 0 and {@code provider.getMaxOrder()})
247      * @param maxEccPowMdailyTesseralSP maximum power of the eccentricity to use in summation over s for
248      * m-daily tesseral harmonics, (must be between 0 and {@code maxDegreeMdailyTesseralSP - 2},
249      * but should typically not exceed 4 as higher values will exceed computer capacity)
250      * @since 7.2
251      */
252     public DSSTTesseral(final Frame centralBodyFrame,
253                         final double centralBodyRotationRate,
254                         final UnnormalizedSphericalHarmonicsProvider provider,
255                         final int maxDegreeTesseralSP, final int maxOrderTesseralSP,
256                         final int maxEccPowTesseralSP, final int maxFrequencyShortPeriodics,
257                         final int maxDegreeMdailyTesseralSP, final int maxOrderMdailyTesseralSP,
258                         final int maxEccPowMdailyTesseralSP) {
259 
260         gmParameterDriver = new ParameterDriver(DSSTNewtonianAttraction.CENTRAL_ATTRACTION_COEFFICIENT,
261                                                 provider.getMu(), MU_SCALE,
262                                                 0.0, Double.POSITIVE_INFINITY);
263 
264         // Central body rotating frame
265         this.bodyFrame = centralBodyFrame;
266 
267         //Save the rotation rate
268         this.centralBodyRotationRate = centralBodyRotationRate;
269 
270         // Central body rotation period in seconds
271         this.bodyPeriod = MathUtils.TWO_PI / centralBodyRotationRate;
272 
273         // Provider for spherical harmonics
274         this.provider      = provider;
275         this.maxDegree     = provider.getMaxDegree();
276         this.maxOrder      = provider.getMaxOrder();
277 
278         //set the maximum degree order for short periodics
279         checkIndexRange(maxDegreeTesseralSP, 2, maxDegree);
280         this.maxDegreeTesseralSP       = maxDegreeTesseralSP;
281 
282         checkIndexRange(maxDegreeMdailyTesseralSP, 2, maxDegree);
283         this.maxDegreeMdailyTesseralSP = maxDegreeMdailyTesseralSP;
284 
285         checkIndexRange(maxOrderTesseralSP, 0, maxOrder);
286         this.maxOrderTesseralSP        = maxOrderTesseralSP;
287 
288         checkIndexRange(maxOrderMdailyTesseralSP, 0, maxOrder);
289         this.maxOrderMdailyTesseralSP  = maxOrderMdailyTesseralSP;
290 
291         // set the maximum value for eccentricity power
292         if (maxOrder > 0) {
293             // Range check can be silently ignored if order = 0
294             checkIndexRange(maxEccPowTesseralSP, 0, maxOrder);
295         }
296         this.maxEccPowTesseralSP       = maxEccPowTesseralSP;
297 
298         checkIndexRange(maxEccPowMdailyTesseralSP, 0, maxDegreeMdailyTesseralSP - 2);
299         this.maxEccPowMdailyTesseralSP = maxEccPowMdailyTesseralSP;
300 
301         // set the maximum value for frequency
302         this.maxFrequencyShortPeriodics = maxFrequencyShortPeriodics;
303 
304         // Initialize default values
305         this.resOrders    = new ArrayList<>();
306         this.nonResOrders = new TreeMap<>();
307 
308         // Initialize default values
309         this.fieldShortPeriodTerms = new HashMap<>();
310         this.fieldHansen           = new HashMap<>();
311         this.maxEccPow             = 0;
312         this.maxHansen             = 0;
313 
314     }
315 
316     /** Check an index range.
317      * @param index index value
318      * @param min minimum value for index
319      * @param max maximum value for index
320      */
321     private void checkIndexRange(final int index, final int min, final int max) {
322         if (index < min || index > max) {
323             throw new OrekitException(LocalizedCoreFormats.OUT_OF_RANGE_SIMPLE, index, min, max);
324         }
325     }
326 
327     /** {@inheritDoc} */
328     @Override
329     public List<ShortPeriodTerms> initializeShortPeriodTerms(final AuxiliaryElements auxiliaryElements,
330                                              final PropagationType type,
331                                              final double[] parameters) {
332 
333         // Initializes specific parameters.
334 
335         final DSSTTesseralContext context = initializeStep(auxiliaryElements, parameters);
336 
337         // Set the highest power of the eccentricity in the analytical power
338         // series expansion for the averaged high order resonant central body
339         // spherical harmonic perturbation
340         maxEccPow = getMaxEccPow(auxiliaryElements.getEcc());
341 
342         // Set the maximum power of the eccentricity to use in Hansen coefficient Kernel expansion.
343         maxHansen = maxEccPow / 2;
344 
345         // The following terms are only used for hansen objects initialization
346         final double ratio = context.getRatio();
347 
348         // Compute the non resonant tesseral harmonic terms if not set by the user
349         getResonantAndNonResonantTerms(type, context.getOrbitPeriod(), ratio);
350 
351         hansen = new HansenObjects(ratio, type);
352 
353         mMax = FastMath.max(maxOrderTesseralSP, maxOrderMdailyTesseralSP);
354 
355         shortPeriodTerms = new TesseralShortPeriodicCoefficients(bodyFrame, maxOrderMdailyTesseralSP,
356                                                                  maxDegreeTesseralSP < 0, nonResOrders,
357                                                                  mMax, maxFrequencyShortPeriodics, INTERPOLATION_POINTS,
358                                                                  new TimeSpanMap<>(new Slot(mMax, maxFrequencyShortPeriodics, INTERPOLATION_POINTS)));
359 
360         final List<ShortPeriodTerms> list = new ArrayList<>();
361         list.add(shortPeriodTerms);
362         return list;
363 
364     }
365 
366     /** {@inheritDoc} */
367     @Override
368     public <T extends CalculusFieldElement<T>> List<FieldShortPeriodTerms<T>> initializeShortPeriodTerms(final FieldAuxiliaryElements<T> auxiliaryElements,
369                                                                                      final PropagationType type,
370                                                                                      final T[] parameters) {
371 
372         // Field used by default
373         final Field<T> field = auxiliaryElements.getDate().getField();
374 
375         // Initializes specific parameters.
376         final FieldDSSTTesseralContext<T> context = initializeStep(auxiliaryElements, parameters);
377 
378         // Set the highest power of the eccentricity in the analytical power
379         // series expansion for the averaged high order resonant central body
380         // spherical harmonic perturbation
381         maxEccPow = getMaxEccPow(auxiliaryElements.getEcc().getReal());
382 
383         // Set the maximum power of the eccentricity to use in Hansen coefficient Kernel expansion.
384         maxHansen = maxEccPow / 2;
385 
386         // The following terms are only used for hansen objects initialization
387         final T ratio = context.getRatio();
388 
389         // Compute the non resonant tesseral harmonic terms if not set by the user
390         // Field information is not important here
391         getResonantAndNonResonantTerms(type, context.getOrbitPeriod().getReal(), ratio.getReal());
392 
393         mMax = FastMath.max(maxOrderTesseralSP, maxOrderMdailyTesseralSP);
394 
395         fieldHansen.put(field, new FieldHansenObjects<>(ratio, type));
396 
397         final FieldTesseralShortPeriodicCoefficients<T> ftspc =
398                         new FieldTesseralShortPeriodicCoefficients<>(bodyFrame, maxOrderMdailyTesseralSP,
399                                                                      maxDegreeTesseralSP < 0, nonResOrders,
400                                                                      mMax, maxFrequencyShortPeriodics, INTERPOLATION_POINTS,
401                                                                      new FieldTimeSpanMap<>(new FieldSlot<>(mMax,
402                                                                                                             maxFrequencyShortPeriodics,
403                                                                                                             INTERPOLATION_POINTS),
404                                                                                             field));
405 
406         fieldShortPeriodTerms.put(field, ftspc);
407         return Collections.singletonList(ftspc);
408 
409     }
410 
411     /**
412      * Get the maximum power of the eccentricity to use in summation over s.
413      * @param e eccentricity
414      * @return the maximum power of the eccentricity
415      */
416     private int getMaxEccPow(final double e) {
417         // maxEccPow depends on satellite eccentricity
418         if (e <= 0.005) {
419             return 3;
420         } else if (e <= 0.02) {
421             return 4;
422         } else if (e <= 0.1) {
423             return 7;
424         } else if (e <= 0.2) {
425             return 10;
426         } else if (e <= 0.3) {
427             return 12;
428         } else if (e <= 0.4) {
429             return 15;
430         } else {
431             return 20;
432         }
433     }
434 
435     /** Performs initialization at each integration step for the current force model.
436      *  <p>
437      *  This method aims at being called before mean elements rates computation.
438      *  </p>
439      *  @param auxiliaryElements auxiliary elements related to the current orbit
440      *  @param parameters values of the force model parameters (only 1 value for each parameter)
441      *  that is to say that the extract parameter method {@link #extractParameters(double[], AbsoluteDate)}
442      *  should have be called before or the parameters list given in argument must correspond
443      *  to the extraction of parameter for a precise date {@link #getParameters(AbsoluteDate)}.
444      *  @return new force model context
445      */
446     private DSSTTesseralContext initializeStep(final AuxiliaryElements auxiliaryElements, final double[] parameters) {
447         return new DSSTTesseralContext(auxiliaryElements, bodyFrame, provider, maxFrequencyShortPeriodics, bodyPeriod, parameters);
448     }
449 
450     /** Performs initialization at each integration step for the current force model.
451      *  <p>
452      *  This method aims at being called before mean elements rates computation.
453      *  </p>
454      *  @param <T> type of the elements
455      *  @param auxiliaryElements auxiliary elements related to the current orbit
456      *  @param parameters list of each estimated values for each driver of the force model parameters
457          *                (each span of each driver)
458      *  @return new force model context
459      */
460     private <T extends CalculusFieldElement<T>> FieldDSSTTesseralContext<T> initializeStep(final FieldAuxiliaryElements<T> auxiliaryElements,
461                                                                                        final T[] parameters) {
462         return new FieldDSSTTesseralContext<>(auxiliaryElements, bodyFrame, provider, maxFrequencyShortPeriodics, bodyPeriod, parameters);
463     }
464 
465     /** {@inheritDoc} */
466     @Override
467     public double[] getMeanElementRate(final SpacecraftState spacecraftState,
468                                        final AuxiliaryElements auxiliaryElements, final double[] parameters) {
469 
470         // Container for attributes
471 
472         final DSSTTesseralContext context = initializeStep(auxiliaryElements, parameters);
473 
474         // Access to potential U derivatives
475         final UAnddU udu = new UAnddU(spacecraftState.getDate(), context, hansen);
476 
477         // Compute the cross derivative operator :
478         final double UAlphaGamma   = auxiliaryElements.getAlpha() * udu.getdUdGa() - auxiliaryElements.getGamma() * udu.getdUdAl();
479         final double UAlphaBeta    = auxiliaryElements.getAlpha() * udu.getdUdBe() - auxiliaryElements.getBeta()  * udu.getdUdAl();
480         final double UBetaGamma    = auxiliaryElements.getBeta() * udu.getdUdGa() - auxiliaryElements.getGamma() * udu.getdUdBe();
481         final double Uhk           = auxiliaryElements.getH() * udu.getdUdk()  - auxiliaryElements.getK() * udu.getdUdh();
482         final double pUagmIqUbgoAB = (auxiliaryElements.getP() * UAlphaGamma - I * auxiliaryElements.getQ() * UBetaGamma) * context.getOoAB();
483         final double UhkmUabmdUdl  = Uhk - UAlphaBeta - udu.getdUdl();
484 
485         final double da =  context.getAx2oA() * udu.getdUdl();
486         final double dh =  context.getBoA() * udu.getdUdk() + auxiliaryElements.getK() * pUagmIqUbgoAB - auxiliaryElements.getH() * context.getBoABpo() * udu.getdUdl();
487         final double dk =  -(context.getBoA() * udu.getdUdh() + auxiliaryElements.getH() * pUagmIqUbgoAB + auxiliaryElements.getK() * context.getBoABpo() * udu.getdUdl());
488         final double dp =  context.getCo2AB() * (auxiliaryElements.getP() * UhkmUabmdUdl - UBetaGamma);
489         final double dq =  context.getCo2AB() * (auxiliaryElements.getQ() * UhkmUabmdUdl - I * UAlphaGamma);
490         final double dM = -context.getAx2oA() * udu.getdUda() + context.getBoABpo() * (auxiliaryElements.getH() * udu.getdUdh() + auxiliaryElements.getK() * udu.getdUdk()) + pUagmIqUbgoAB;
491 
492         return new double[] {da, dk, dh, dq, dp, dM};
493     }
494 
495     /** {@inheritDoc} */
496     @Override
497     public <T extends CalculusFieldElement<T>> T[] getMeanElementRate(final FieldSpacecraftState<T> spacecraftState,
498                                                                   final FieldAuxiliaryElements<T> auxiliaryElements,
499                                                                   final T[] parameters) {
500 
501         // Field used by default
502         final Field<T> field = auxiliaryElements.getDate().getField();
503 
504         // Container for attributes
505 
506         final FieldDSSTTesseralContext<T> context = initializeStep(auxiliaryElements, parameters);
507 
508         @SuppressWarnings("unchecked")
509         final FieldHansenObjects<T> fho = (FieldHansenObjects<T>) fieldHansen.get(field);
510         // Access to potential U derivatives
511         final FieldUAnddU<T> udu = new FieldUAnddU<>(spacecraftState.getDate(), context, fho);
512 
513         // Compute the cross derivative operator :
514         final T UAlphaGamma   = udu.getdUdGa().multiply(auxiliaryElements.getAlpha()).subtract(udu.getdUdAl().multiply(auxiliaryElements.getGamma()));
515         final T UAlphaBeta    = udu.getdUdBe().multiply(auxiliaryElements.getAlpha()).subtract(udu.getdUdAl().multiply(auxiliaryElements.getBeta()));
516         final T UBetaGamma    = udu.getdUdGa().multiply(auxiliaryElements.getBeta()).subtract(udu.getdUdBe().multiply(auxiliaryElements.getGamma()));
517         final T Uhk           = udu.getdUdk().multiply(auxiliaryElements.getH()).subtract(udu.getdUdh().multiply(auxiliaryElements.getK()));
518         final T pUagmIqUbgoAB = (UAlphaGamma.multiply(auxiliaryElements.getP()).subtract(UBetaGamma.multiply(auxiliaryElements.getQ()).multiply(I))).multiply(context.getOoAB());
519         final T UhkmUabmdUdl  = Uhk.subtract(UAlphaBeta).subtract(udu.getdUdl());
520 
521         final T da = udu.getdUdl().multiply(context.getAx2oA());
522         final T dh = udu.getdUdk().multiply(context.getBoA()).add(pUagmIqUbgoAB.multiply(auxiliaryElements.getK())).subtract(udu.getdUdl().multiply(auxiliaryElements.getH()).multiply(context.getBoABpo()));
523         final T dk = (udu.getdUdh().multiply(context.getBoA()).add(pUagmIqUbgoAB.multiply(auxiliaryElements.getH())).add(udu.getdUdl().multiply(context.getBoABpo()).multiply(auxiliaryElements.getK()))).negate();
524         final T dp = context.getCo2AB().multiply(auxiliaryElements.getP().multiply(UhkmUabmdUdl).subtract(UBetaGamma));
525         final T dq = context.getCo2AB().multiply(auxiliaryElements.getQ().multiply(UhkmUabmdUdl).subtract(UAlphaGamma.multiply(I)));
526         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()));
527 
528         final T[] elements = MathArrays.buildArray(field, 6);
529         elements[0] = da;
530         elements[1] = dk;
531         elements[2] = dh;
532         elements[3] = dq;
533         elements[4] = dp;
534         elements[5] = dM;
535 
536         return elements;
537 
538     }
539 
540     /** {@inheritDoc} */
541     @Override
542     public void updateShortPeriodTerms(final double[] parameters, final SpacecraftState... meanStates) {
543 
544         final Slot slot = shortPeriodTerms.createSlot(meanStates);
545 
546         for (final SpacecraftState meanState : meanStates) {
547 
548             final AuxiliaryElements auxiliaryElements = new AuxiliaryElements(meanState.getOrbit(), I);
549 
550             // Extract the proper parameters valid at date from the input array
551             final double[] extractedParameters = this.extractParameters(parameters, auxiliaryElements.getDate());
552             final DSSTTesseralContext context = initializeStep(auxiliaryElements, extractedParameters);
553 
554             // Initialise the Hansen coefficients
555             for (int s = -maxDegree; s <= maxDegree; s++) {
556                 // coefficients with j == 0 are always needed
557                 hansen.computeHansenObjectsInitValues(context, s + maxDegree, 0);
558                 if (maxDegreeTesseralSP >= 0) {
559                     // initialize other objects only if required
560                     for (int j = 1; j <= maxFrequencyShortPeriodics; j++) {
561                         hansen.computeHansenObjectsInitValues(context, s + maxDegree, j);
562                     }
563                 }
564             }
565 
566             final FourierCjSjCoefficients cjsjFourier = new FourierCjSjCoefficients(maxFrequencyShortPeriodics, mMax);
567 
568             // Compute coefficients
569             // Compute only if there is at least one non-resonant tesseral
570             if (!nonResOrders.isEmpty() || maxDegreeTesseralSP < 0) {
571                 // Generate the fourrier coefficients
572                 cjsjFourier.generateCoefficients(meanState.getDate(), context, hansen);
573 
574                 // the coefficient 3n / 2a
575                 final double tnota = 1.5 * context.getMeanMotion() / auxiliaryElements.getSma();
576 
577                 // build the mDaily coefficients
578                 for (int m = 1; m <= maxOrderMdailyTesseralSP; m++) {
579                     // build the coefficients
580                     buildCoefficients(cjsjFourier, meanState.getDate(), slot, m, 0, tnota, context);
581                 }
582 
583                 if (maxDegreeTesseralSP >= 0) {
584                     // generate the other coefficients, if required
585                     for (final Map.Entry<Integer, List<Integer>> entry : nonResOrders.entrySet()) {
586 
587                         for (int j : entry.getValue()) {
588                             // build the coefficients
589                             buildCoefficients(cjsjFourier, meanState.getDate(), slot, entry.getKey(), j, tnota, context);
590                         }
591                     }
592                 }
593             }
594 
595         }
596 
597     }
598 
599     /** {@inheritDoc} */
600     @Override
601     @SuppressWarnings("unchecked")
602     public <T extends CalculusFieldElement<T>> void updateShortPeriodTerms(final T[] parameters,
603                                                                        final FieldSpacecraftState<T>... meanStates) {
604 
605         // Field used by default
606         final Field<T> field = meanStates[0].getDate().getField();
607 
608         final FieldTesseralShortPeriodicCoefficients<T> ftspc = (FieldTesseralShortPeriodicCoefficients<T>) fieldShortPeriodTerms.get(field);
609         final FieldSlot<T> slot = ftspc.createSlot(meanStates);
610 
611         for (final FieldSpacecraftState<T> meanState : meanStates) {
612 
613             final FieldAuxiliaryElements<T> auxiliaryElements = new FieldAuxiliaryElements<>(meanState.getOrbit(), I);
614 
615             // Extract the proper parameters valid at date from the input array
616             final T[] extractedParameters = this.extractParameters(parameters, auxiliaryElements.getDate());
617             final FieldDSSTTesseralContext<T> context = initializeStep(auxiliaryElements, extractedParameters);
618 
619             final FieldHansenObjects<T> fho = (FieldHansenObjects<T>) fieldHansen.get(field);
620             // Initialise the Hansen coefficients
621             for (int s = -maxDegree; s <= maxDegree; s++) {
622                 // coefficients with j == 0 are always needed
623                 fho.computeHansenObjectsInitValues(context, s + maxDegree, 0);
624                 if (maxDegreeTesseralSP >= 0) {
625                     // initialize other objects only if required
626                     for (int j = 1; j <= maxFrequencyShortPeriodics; j++) {
627                         fho.computeHansenObjectsInitValues(context, s + maxDegree, j);
628                     }
629                 }
630             }
631 
632             final FieldFourierCjSjCoefficients<T> cjsjFourier = new FieldFourierCjSjCoefficients<>(maxFrequencyShortPeriodics, mMax, field);
633 
634             // Compute coefficients
635             // Compute only if there is at least one non-resonant tesseral
636             if (!nonResOrders.isEmpty() || maxDegreeTesseralSP < 0) {
637                 // Generate the fourrier coefficients
638                 cjsjFourier.generateCoefficients(meanState.getDate(), context, fho, field);
639 
640                 // the coefficient 3n / 2a
641                 final T tnota = context.getMeanMotion().multiply(1.5).divide(auxiliaryElements.getSma());
642 
643                 // build the mDaily coefficients
644                 for (int m = 1; m <= maxOrderMdailyTesseralSP; m++) {
645                     // build the coefficients
646                     buildCoefficients(cjsjFourier, meanState.getDate(), slot, m, 0, tnota, context, field);
647                 }
648 
649                 if (maxDegreeTesseralSP >= 0) {
650                     // generate the other coefficients, if required
651                     for (final Map.Entry<Integer, List<Integer>> entry : nonResOrders.entrySet()) {
652 
653                         for (int j : entry.getValue()) {
654                             // build the coefficients
655                             buildCoefficients(cjsjFourier, meanState.getDate(), slot, entry.getKey(), j, tnota, context, field);
656                         }
657                     }
658                 }
659             }
660 
661         }
662 
663     }
664 
665     /** {@inheritDoc} */
666     public List<ParameterDriver> getParametersDrivers() {
667         return Collections.singletonList(gmParameterDriver);
668     }
669 
670     /** Build a set of coefficients.
671      * @param cjsjFourier the fourier coefficients C<sub>i</sub><sup>j</sup> and the S<sub>i</sub><sup>j</sup>
672      * @param date the current date
673      * @param slot slot to which the coefficients belong
674      * @param m m index
675      * @param j j index
676      * @param tnota 3n/2a
677      * @param context container for attributes
678      */
679     private void buildCoefficients(final FourierCjSjCoefficients cjsjFourier,
680                                    final AbsoluteDate date, final Slot slot,
681                                    final int m, final int j, final double tnota, final DSSTTesseralContext context) {
682 
683         // Create local arrays
684         final double[] currentCijm = new double[] {0., 0., 0., 0., 0., 0.};
685         final double[] currentSijm = new double[] {0., 0., 0., 0., 0., 0.};
686 
687         // compute the term 1 / (jn - mθ<sup>.</sup>)
688         final double oojnmt = 1. / (j * context.getMeanMotion() - m * centralBodyRotationRate);
689 
690         // initialise the coeficients
691         for (int i = 0; i < 6; i++) {
692             currentCijm[i] = -cjsjFourier.getSijm(i, j, m);
693             currentSijm[i] = cjsjFourier.getCijm(i, j, m);
694         }
695         // Add the separate part for δ<sub>6i</sub>
696         currentCijm[5] += tnota * oojnmt * cjsjFourier.getCijm(0, j, m);
697         currentSijm[5] += tnota * oojnmt * cjsjFourier.getSijm(0, j, m);
698 
699         //Multiply by 1 / (jn - mθ<sup>.</sup>)
700         for (int i = 0; i < 6; i++) {
701             currentCijm[i] *= oojnmt;
702             currentSijm[i] *= oojnmt;
703         }
704 
705         // Add the coefficients to the interpolation grid
706         slot.cijm[m][j + maxFrequencyShortPeriodics].addGridPoint(date, currentCijm);
707         slot.sijm[m][j + maxFrequencyShortPeriodics].addGridPoint(date, currentSijm);
708 
709     }
710 
711      /** Build a set of coefficients.
712      * @param <T> the type of the field elements
713      * @param cjsjFourier the fourier coefficients C<sub>i</sub><sup>j</sup> and the S<sub>i</sub><sup>j</sup>
714      * @param date the current date
715      * @param slot slot to which the coefficients belong
716      * @param m m index
717      * @param j j index
718      * @param tnota 3n/2a
719      * @param context container for attributes
720      * @param field field used by default
721      */
722     private <T extends CalculusFieldElement<T>> void buildCoefficients(final FieldFourierCjSjCoefficients<T> cjsjFourier,
723                                                                    final FieldAbsoluteDate<T> date,
724                                                                    final FieldSlot<T> slot,
725                                                                    final int m, final int j, final T tnota,
726                                                                    final FieldDSSTTesseralContext<T> context,
727                                                                    final Field<T> field) {
728 
729         // Zero
730         final T zero = field.getZero();
731 
732         // Create local arrays
733         final T[] currentCijm = MathArrays.buildArray(field, 6);
734         final T[] currentSijm = MathArrays.buildArray(field, 6);
735 
736         Arrays.fill(currentCijm, zero);
737         Arrays.fill(currentSijm, zero);
738 
739         // compute the term 1 / (jn - mθ<sup>.</sup>)
740         final T oojnmt = (context.getMeanMotion().multiply(j).subtract(m * centralBodyRotationRate)).reciprocal();
741 
742         // initialise the coeficients
743         for (int i = 0; i < 6; i++) {
744             currentCijm[i] = cjsjFourier.getSijm(i, j, m).negate();
745             currentSijm[i] = cjsjFourier.getCijm(i, j, m);
746         }
747         // Add the separate part for δ<sub>6i</sub>
748         currentCijm[5] = currentCijm[5].add(tnota.multiply(oojnmt).multiply(cjsjFourier.getCijm(0, j, m)));
749         currentSijm[5] = currentSijm[5].add(tnota.multiply(oojnmt).multiply(cjsjFourier.getSijm(0, j, m)));
750 
751         //Multiply by 1 / (jn - mθ<sup>.</sup>)
752         for (int i = 0; i < 6; i++) {
753             currentCijm[i] = currentCijm[i].multiply(oojnmt);
754             currentSijm[i] = currentSijm[i].multiply(oojnmt);
755         }
756 
757         // Add the coefficients to the interpolation grid
758         slot.cijm[m][j + maxFrequencyShortPeriodics].addGridPoint(date, currentCijm);
759         slot.sijm[m][j + maxFrequencyShortPeriodics].addGridPoint(date, currentSijm);
760 
761     }
762 
763      /**
764       * Get the resonant and non-resonant tesseral terms in the central body spherical harmonic field.
765       *
766       * @param type type of the elements used during the propagation
767       * @param orbitPeriod Keplerian period
768       * @param ratio ratio of satellite period to central body rotation period
769       */
770     private void getResonantAndNonResonantTerms(final PropagationType type, final double orbitPeriod,
771                                                 final double ratio) {
772 
773         // Compute natural resonant terms
774         final double tolerance = 1. / FastMath.max(MIN_PERIOD_IN_SAT_REV,
775                                                    MIN_PERIOD_IN_SECONDS / orbitPeriod);
776 
777         // Search the resonant orders in the tesseral harmonic field
778         resOrders.clear();
779         nonResOrders.clear();
780         for (int m = 1; m <= maxOrder; m++) {
781             final double resonance = ratio * m;
782             int jRes = 0;
783             final int jComputedRes = (int) FastMath.round(resonance);
784             if (jComputedRes > 0 && jComputedRes <= maxFrequencyShortPeriodics && FastMath.abs(resonance - jComputedRes) <= tolerance) {
785                 // Store each resonant index and order
786                 resOrders.add(m);
787                 jRes = jComputedRes;
788             }
789 
790             if (type == PropagationType.OSCULATING && maxDegreeTesseralSP >= 0 && m <= maxOrderTesseralSP) {
791                 //compute non resonant orders in the tesseral harmonic field
792                 final List<Integer> listJofM = new ArrayList<>();
793                 //for the moment we take only the pairs (j,m) with |j| <= maxDegree + maxEccPow (from |s-j| <= maxEccPow and |s| <= maxDegree)
794                 for (int j = -maxFrequencyShortPeriodics; j <= maxFrequencyShortPeriodics; j++) {
795                     if (j != 0 && j != jRes) {
796                         listJofM.add(j);
797                     }
798                 }
799 
800                 nonResOrders.put(m, listJofM);
801             }
802         }
803     }
804 
805     /** Compute the n-SUM for potential derivatives components.
806      *  @param date current date
807      *  @param j resonant index <i>j</i>
808      *  @param m resonant order <i>m</i>
809      *  @param s d'Alembert characteristic <i>s</i>
810      *  @param maxN maximum possible value for <i>n</i> index
811      *  @param roaPow powers of R/a up to degree <i>n</i>
812      *  @param ghMSJ G<sup>j</sup><sub>m,s</sub> and H<sup>j</sup><sub>m,s</sub> polynomials
813      *  @param gammaMNS &Gamma;<sup>m</sup><sub>n,s</sub>(γ) function
814      *  @param context container for attributes
815      *  @param hansenObjects initialization of hansen objects
816      *  @return Components of U<sub>n</sub> derivatives for fixed j, m, s
817      */
818     private double[][] computeNSum(final AbsoluteDate date,
819                                    final int j, final int m, final int s, final int maxN, final double[] roaPow,
820                                    final GHmsjPolynomials ghMSJ, final GammaMnsFunction gammaMNS, final DSSTTesseralContext context,
821                                    final HansenObjects hansenObjects) {
822 
823         // Auxiliary elements related to the current orbit
824         final AuxiliaryElements auxiliaryElements = context.getAuxiliaryElements();
825 
826         //spherical harmonics
827         final UnnormalizedSphericalHarmonics harmonics = provider.onDate(date);
828 
829         // Potential derivatives components
830         double dUdaCos  = 0.;
831         double dUdaSin  = 0.;
832         double dUdhCos  = 0.;
833         double dUdhSin  = 0.;
834         double dUdkCos  = 0.;
835         double dUdkSin  = 0.;
836         double dUdlCos  = 0.;
837         double dUdlSin  = 0.;
838         double dUdAlCos = 0.;
839         double dUdAlSin = 0.;
840         double dUdBeCos = 0.;
841         double dUdBeSin = 0.;
842         double dUdGaCos = 0.;
843         double dUdGaSin = 0.;
844 
845         // I^m
846         @SuppressWarnings("unused")
847         final int Im = I > 0 ? 1 : (m % 2 == 0 ? 1 : -1);
848 
849         // jacobi v, w, indices from 2.7.1-(15)
850         final int v = FastMath.abs(m - s);
851         final int w = FastMath.abs(m + s);
852 
853         // Initialise lower degree nmin = (Max(2, m, |s|)) for summation over n
854         final int nmin = FastMath.max(FastMath.max(2, m), FastMath.abs(s));
855 
856         //Get the corresponding Hansen object
857         final int sIndex = maxDegree + (j < 0 ? -s : s);
858         final int jIndex = FastMath.abs(j);
859         final HansenTesseralLinear hans = hansenObjects.getHansenObjects()[sIndex][jIndex];
860 
861         // n-SUM from nmin to N
862         for (int n = nmin; n <= maxN; n++) {
863             // If (n - s) is odd, the contribution is null because of Vmns
864             if ((n - s) % 2 == 0) {
865 
866                 // Vmns coefficient
867                 final double vMNS   = CoefficientsFactory.getVmns(m, n, s);
868 
869                 // Inclination function Gamma and derivative
870                 final double gaMNS  = gammaMNS.getValue(m, n, s);
871                 final double dGaMNS = gammaMNS.getDerivative(m, n, s);
872 
873                 // Hansen kernel value and derivative
874                 final double kJNS   = hans.getValue(-n - 1, context.getChi());
875                 final double dkJNS  = hans.getDerivative(-n - 1, context.getChi());
876 
877                 // Gjms, Hjms polynomials and derivatives
878                 final double gMSJ   = ghMSJ.getGmsj(m, s, j);
879                 final double hMSJ   = ghMSJ.getHmsj(m, s, j);
880                 final double dGdh   = ghMSJ.getdGmsdh(m, s, j);
881                 final double dGdk   = ghMSJ.getdGmsdk(m, s, j);
882                 final double dGdA   = ghMSJ.getdGmsdAlpha(m, s, j);
883                 final double dGdB   = ghMSJ.getdGmsdBeta(m, s, j);
884                 final double dHdh   = ghMSJ.getdHmsdh(m, s, j);
885                 final double dHdk   = ghMSJ.getdHmsdk(m, s, j);
886                 final double dHdA   = ghMSJ.getdHmsdAlpha(m, s, j);
887                 final double dHdB   = ghMSJ.getdHmsdBeta(m, s, j);
888 
889                 // Jacobi l-index from 2.7.1-(15)
890                 final int l = FastMath.min(n - m, n - FastMath.abs(s));
891                 // Jacobi polynomial and derivative
892                 final double[] jacobi = JacobiPolynomials.getValueAndDerivative(l, v, w, auxiliaryElements.getGamma());
893 
894                 // Geopotential coefficients
895                 final double cnm = harmonics.getUnnormalizedCnm(n, m);
896                 final double snm = harmonics.getUnnormalizedSnm(n, m);
897 
898                 // Common factors from expansion of equations 3.3-4
899                 final double cf_0      = roaPow[n] * Im * vMNS;
900                 final double cf_1      = cf_0 * gaMNS * jacobi[0]; //jacobi.getValue();
901                 final double cf_2      = cf_1 * kJNS;
902                 final double gcPhs     = gMSJ * cnm + hMSJ * snm;
903                 final double gsMhc     = gMSJ * snm - hMSJ * cnm;
904                 final double dKgcPhsx2 = 2. * dkJNS * gcPhs;
905                 final double dKgsMhcx2 = 2. * dkJNS * gsMhc;
906                 final double dUdaCoef  = (n + 1) * cf_2;
907                 final double dUdlCoef  = j * cf_2;
908                 //final double dUdGaCoef = cf_0 * kJNS * (jacobi.getValue() * dGaMNS + gaMNS * jacobi.getGradient()[0]);
909                 final double dUdGaCoef = cf_0 * kJNS * (jacobi[0] * dGaMNS + gaMNS * jacobi[1]);
910 
911                 // dU / da components
912                 dUdaCos  += dUdaCoef * gcPhs;
913                 dUdaSin  += dUdaCoef * gsMhc;
914 
915                 // dU / dh components
916                 dUdhCos  += cf_1 * (kJNS * (cnm * dGdh + snm * dHdh) + auxiliaryElements.getH() * dKgcPhsx2);
917                 dUdhSin  += cf_1 * (kJNS * (snm * dGdh - cnm * dHdh) + auxiliaryElements.getH() * dKgsMhcx2);
918 
919                 // dU / dk components
920                 dUdkCos  += cf_1 * (kJNS * (cnm * dGdk + snm * dHdk) + auxiliaryElements.getK() * dKgcPhsx2);
921                 dUdkSin  += cf_1 * (kJNS * (snm * dGdk - cnm * dHdk) + auxiliaryElements.getK() * dKgsMhcx2);
922 
923                 // dU / dLambda components
924                 dUdlCos  +=  dUdlCoef * gsMhc;
925                 dUdlSin  += -dUdlCoef * gcPhs;
926 
927                 // dU / alpha components
928                 dUdAlCos += cf_2 * (dGdA * cnm + dHdA * snm);
929                 dUdAlSin += cf_2 * (dGdA * snm - dHdA * cnm);
930 
931                 // dU / dBeta components
932                 dUdBeCos += cf_2 * (dGdB * cnm + dHdB * snm);
933                 dUdBeSin += cf_2 * (dGdB * snm - dHdB * cnm);
934 
935                 // dU / dGamma components
936                 dUdGaCos += dUdGaCoef * gcPhs;
937                 dUdGaSin += dUdGaCoef * gsMhc;
938             }
939         }
940 
941         return new double[][] { { dUdaCos,  dUdaSin  },
942                                 { dUdhCos,  dUdhSin  },
943                                 { dUdkCos,  dUdkSin  },
944                                 { dUdlCos,  dUdlSin  },
945                                 { dUdAlCos, dUdAlSin },
946                                 { dUdBeCos, dUdBeSin },
947                                 { dUdGaCos, dUdGaSin } };
948     }
949 
950     /** Compute the n-SUM for potential derivatives components.
951      *  @param <T> the type of the field elements
952      *  @param date current date
953      *  @param j resonant index <i>j</i>
954      *  @param m resonant order <i>m</i>
955      *  @param s d'Alembert characteristic <i>s</i>
956      *  @param maxN maximum possible value for <i>n</i> index
957      *  @param roaPow powers of R/a up to degree <i>n</i>
958      *  @param ghMSJ G<sup>j</sup><sub>m,s</sub> and H<sup>j</sup><sub>m,s</sub> polynomials
959      *  @param gammaMNS &Gamma;<sup>m</sup><sub>n,s</sub>(γ) function
960      *  @param context container for attributes
961      *  @param hansenObjects initialization of hansen objects
962      *  @return Components of U<sub>n</sub> derivatives for fixed j, m, s
963      */
964     private <T extends CalculusFieldElement<T>> T[][] computeNSum(final FieldAbsoluteDate<T> date,
965                                                               final int j, final int m, final int s, final int maxN,
966                                                               final T[] roaPow,
967                                                               final FieldGHmsjPolynomials<T> ghMSJ,
968                                                               final FieldGammaMnsFunction<T> gammaMNS,
969                                                               final FieldDSSTTesseralContext<T> context,
970                                                               final FieldHansenObjects<T> hansenObjects) {
971 
972         // Auxiliary elements related to the current orbit
973         final FieldAuxiliaryElements<T> auxiliaryElements = context.getFieldAuxiliaryElements();
974         // Zero for initialization
975         final Field<T> field = date.getField();
976         final T zero = field.getZero();
977 
978         //spherical harmonics
979         final UnnormalizedSphericalHarmonics harmonics = provider.onDate(date.toAbsoluteDate());
980 
981         // Potential derivatives components
982         T dUdaCos  = zero;
983         T dUdaSin  = zero;
984         T dUdhCos  = zero;
985         T dUdhSin  = zero;
986         T dUdkCos  = zero;
987         T dUdkSin  = zero;
988         T dUdlCos  = zero;
989         T dUdlSin  = zero;
990         T dUdAlCos = zero;
991         T dUdAlSin = zero;
992         T dUdBeCos = zero;
993         T dUdBeSin = zero;
994         T dUdGaCos = zero;
995         T dUdGaSin = zero;
996 
997         // I^m
998         @SuppressWarnings("unused")
999         final int Im = I > 0 ? 1 : (m % 2 == 0 ? 1 : -1);
1000 
1001         // jacobi v, w, indices from 2.7.1-(15)
1002         final int v = FastMath.abs(m - s);
1003         final int w = FastMath.abs(m + s);
1004 
1005         // Initialise lower degree nmin = (Max(2, m, |s|)) for summation over n
1006         final int nmin = FastMath.max(FastMath.max(2, m), FastMath.abs(s));
1007 
1008         //Get the corresponding Hansen object
1009         final int sIndex = maxDegree + (j < 0 ? -s : s);
1010         final int jIndex = FastMath.abs(j);
1011         final FieldHansenTesseralLinear<T> hans = hansenObjects.getHansenObjects()[sIndex][jIndex];
1012 
1013         // n-SUM from nmin to N
1014         for (int n = nmin; n <= maxN; n++) {
1015             // If (n - s) is odd, the contribution is null because of Vmns
1016             if ((n - s) % 2 == 0) {
1017 
1018                 // Vmns coefficient
1019                 final T vMNS   = zero.newInstance(CoefficientsFactory.getVmns(m, n, s));
1020 
1021                 // Inclination function Gamma and derivative
1022                 final T gaMNS  = gammaMNS.getValue(m, n, s);
1023                 final T dGaMNS = gammaMNS.getDerivative(m, n, s);
1024 
1025                 // Hansen kernel value and derivative
1026                 final T kJNS   = hans.getValue(-n - 1, context.getChi());
1027                 final T dkJNS  = hans.getDerivative(-n - 1, context.getChi());
1028 
1029                 // Gjms, Hjms polynomials and derivatives
1030                 final T gMSJ   = ghMSJ.getGmsj(m, s, j);
1031                 final T hMSJ   = ghMSJ.getHmsj(m, s, j);
1032                 final T dGdh   = ghMSJ.getdGmsdh(m, s, j);
1033                 final T dGdk   = ghMSJ.getdGmsdk(m, s, j);
1034                 final T dGdA   = ghMSJ.getdGmsdAlpha(m, s, j);
1035                 final T dGdB   = ghMSJ.getdGmsdBeta(m, s, j);
1036                 final T dHdh   = ghMSJ.getdHmsdh(m, s, j);
1037                 final T dHdk   = ghMSJ.getdHmsdk(m, s, j);
1038                 final T dHdA   = ghMSJ.getdHmsdAlpha(m, s, j);
1039                 final T dHdB   = ghMSJ.getdHmsdBeta(m, s, j);
1040 
1041                 // Jacobi l-index from 2.7.1-(15)
1042                 final int l = FastMath.min(n - m, n - FastMath.abs(s));
1043                 // Jacobi polynomial and derivative
1044                 final FieldGradient<T> jacobi =
1045                         JacobiPolynomials.getValue(l, v, w, FieldGradient.variable(1, 0, auxiliaryElements.getGamma()));
1046 
1047                 // Geopotential coefficients
1048                 final T cnm = zero.newInstance(harmonics.getUnnormalizedCnm(n, m));
1049                 final T snm = zero.newInstance(harmonics.getUnnormalizedSnm(n, m));
1050 
1051                 // Common factors from expansion of equations 3.3-4
1052                 final T cf_0      = roaPow[n].multiply(Im).multiply(vMNS);
1053                 final T cf_1      = cf_0.multiply(gaMNS).multiply(jacobi.getValue());
1054                 final T cf_2      = cf_1.multiply(kJNS);
1055                 final T gcPhs     = gMSJ.multiply(cnm).add(hMSJ.multiply(snm));
1056                 final T gsMhc     = gMSJ.multiply(snm).subtract(hMSJ.multiply(cnm));
1057                 final T dKgcPhsx2 = dkJNS.multiply(gcPhs).multiply(2.);
1058                 final T dKgsMhcx2 = dkJNS.multiply(gsMhc).multiply(2.);
1059                 final T dUdaCoef  = cf_2.multiply(n + 1);
1060                 final T dUdlCoef  = cf_2.multiply(j);
1061                 final T dUdGaCoef = cf_0.multiply(kJNS).multiply(dGaMNS.multiply(jacobi.getValue()).add(gaMNS.multiply(jacobi.getGradient()[0])));
1062 
1063                 // dU / da components
1064                 dUdaCos  = dUdaCos.add(dUdaCoef.multiply(gcPhs));
1065                 dUdaSin  = dUdaSin.add(dUdaCoef.multiply(gsMhc));
1066 
1067                 // dU / dh components
1068                 dUdhCos  = dUdhCos.add(cf_1.multiply(kJNS.multiply(cnm.multiply(dGdh).add(snm.multiply(dHdh))).add(dKgcPhsx2.multiply(auxiliaryElements.getH()))));
1069                 dUdhSin  = dUdhSin.add(cf_1.multiply(kJNS.multiply(snm.multiply(dGdh).subtract(cnm.multiply(dHdh))).add(dKgsMhcx2.multiply(auxiliaryElements.getH()))));
1070 
1071                 // dU / dk components
1072                 dUdkCos  = dUdkCos.add(cf_1.multiply(kJNS.multiply(cnm.multiply(dGdk).add(snm.multiply(dHdk))).add(dKgcPhsx2.multiply(auxiliaryElements.getK()))));
1073                 dUdkSin  = dUdkSin.add(cf_1.multiply(kJNS.multiply(snm.multiply(dGdk).subtract(cnm.multiply(dHdk))).add(dKgsMhcx2.multiply(auxiliaryElements.getK()))));
1074 
1075                 // dU / dLambda components
1076                 dUdlCos  = dUdlCos.add(dUdlCoef.multiply(gsMhc));
1077                 dUdlSin  = dUdlSin.add(dUdlCoef.multiply(gcPhs).negate());
1078 
1079                 // dU / alpha components
1080                 dUdAlCos = dUdAlCos.add(cf_2.multiply(dGdA.multiply(cnm).add(dHdA.multiply(snm))));
1081                 dUdAlSin = dUdAlSin.add(cf_2.multiply(dGdA.multiply(snm).subtract(dHdA.multiply(cnm))));
1082 
1083                 // dU / dBeta components
1084                 dUdBeCos = dUdBeCos.add(cf_2.multiply(dGdB.multiply(cnm).add(dHdB.multiply(snm))));
1085                 dUdBeSin = dUdBeSin.add(cf_2.multiply(dGdB.multiply(snm).subtract(dHdB.multiply(cnm))));
1086 
1087                 // dU / dGamma components
1088                 dUdGaCos = dUdGaCos.add(dUdGaCoef.multiply(gcPhs));
1089                 dUdGaSin = dUdGaSin.add(dUdGaCoef.multiply(gsMhc));
1090             }
1091         }
1092 
1093         final T[][] derivatives = MathArrays.buildArray(field, 7, 2);
1094         derivatives[0][0] = dUdaCos;
1095         derivatives[0][1] = dUdaSin;
1096         derivatives[1][0] = dUdhCos;
1097         derivatives[1][1] = dUdhSin;
1098         derivatives[2][0] = dUdkCos;
1099         derivatives[2][1] = dUdkSin;
1100         derivatives[3][0] = dUdlCos;
1101         derivatives[3][1] = dUdlSin;
1102         derivatives[4][0] = dUdAlCos;
1103         derivatives[4][1] = dUdAlSin;
1104         derivatives[5][0] = dUdBeCos;
1105         derivatives[5][1] = dUdBeSin;
1106         derivatives[6][0] = dUdGaCos;
1107         derivatives[6][1] = dUdGaSin;
1108 
1109         return derivatives;
1110 
1111     }
1112 
1113     /** {@inheritDoc} */
1114     @Override
1115     public void registerAttitudeProvider(final AttitudeProvider attitudeProvider) {
1116         //nothing is done since this contribution is not sensitive to attitude
1117     }
1118 
1119     /** Compute the C<sup>j</sup> and the S<sup>j</sup> coefficients.
1120      *  <p>
1121      *  Those coefficients are given in Danielson paper by substituting the
1122      *  disturbing function (2.7.1-16) with m != 0 into (2.2-10)
1123      *  </p>
1124      */
1125     private class FourierCjSjCoefficients {
1126 
1127         /** Absolute limit for j ( -jMax <= j <= jMax ).  */
1128         private final int jMax;
1129 
1130         /** The C<sub>i</sub><sup>jm</sup> coefficients.
1131          * <p>
1132          * The index order is [m][j][i] <br/>
1133          * The i index corresponds to the C<sub>i</sub><sup>jm</sup> coefficients used to
1134          * compute the following: <br/>
1135          * - da/dt <br/>
1136          * - dk/dt <br/>
1137          * - dh/dt / dk <br/>
1138          * - dq/dt <br/>
1139          * - dp/dt / dα <br/>
1140          * - dλ/dt / dβ <br/>
1141          * </p>
1142          */
1143         private final double[][][] cCoef;
1144 
1145         /** The S<sub>i</sub><sup>jm</sup> coefficients.
1146          * <p>
1147          * The index order is [m][j][i] <br/>
1148          * The i index corresponds to the C<sub>i</sub><sup>jm</sup> coefficients used to
1149          * compute the following: <br/>
1150          * - da/dt <br/>
1151          * - dk/dt <br/>
1152          * - dh/dt / dk <br/>
1153          * - dq/dt <br/>
1154          * - dp/dt / dα <br/>
1155          * - dλ/dt / dβ <br/>
1156          * </p>
1157          */
1158         private final double[][][] sCoef;
1159 
1160         /** G<sub>ms</sub><sup>j</sup> and H<sub>ms</sub><sup>j</sup> polynomials. */
1161         private GHmsjPolynomials ghMSJ;
1162 
1163         /** &Gamma;<sub>ns</sub><sup>m</sup> function. */
1164         private GammaMnsFunction gammaMNS;
1165 
1166         /** R / a up to power degree. */
1167         private final double[] roaPow;
1168 
1169         /** Create a set of C<sub>i</sub><sup>jm</sup> and S<sub>i</sub><sup>jm</sup> coefficients.
1170          *  @param jMax absolute limit for j ( -jMax <= j <= jMax )
1171          *  @param mMax maximum value for m
1172          */
1173         FourierCjSjCoefficients(final int jMax, final int mMax) {
1174             // initialise fields
1175             final int rows    = mMax + 1;
1176             final int columns = 2 * jMax + 1;
1177             this.jMax         = jMax;
1178             this.cCoef        = new double[rows][columns][6];
1179             this.sCoef        = new double[rows][columns][6];
1180             this.roaPow       = new double[maxDegree + 1];
1181             roaPow[0] = 1.;
1182         }
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          */
1190         public void generateCoefficients(final AbsoluteDate date, final DSSTTesseralContext context,
1191                                          final HansenObjects hansenObjects) {
1192 
1193             final AuxiliaryElements auxiliaryElements = context.getAuxiliaryElements();
1194 
1195             // Compute only if there is at least one non-resonant tesseral
1196             if (!nonResOrders.isEmpty() || maxDegreeTesseralSP < 0) {
1197                 // Gmsj and Hmsj polynomials
1198                 ghMSJ = new GHmsjPolynomials(auxiliaryElements.getK(), auxiliaryElements.getH(), auxiliaryElements.getAlpha(), auxiliaryElements.getBeta(), I);
1199 
1200                 // GAMMAmns function
1201                 gammaMNS = new GammaMnsFunction(maxDegree, auxiliaryElements.getGamma(), I);
1202 
1203                 final int maxRoaPower = FastMath.max(maxDegreeTesseralSP, maxDegreeMdailyTesseralSP);
1204 
1205                 // R / a up to power degree
1206                 for (int i = 1; i <= maxRoaPower; i++) {
1207                     roaPow[i] = context.getRoa() * roaPow[i - 1];
1208                 }
1209 
1210                 //generate the m-daily coefficients
1211                 for (int m = 1; m <= maxOrderMdailyTesseralSP; m++) {
1212                     buildFourierCoefficients(date, m, 0, maxDegreeMdailyTesseralSP, context, hansenObjects);
1213                 }
1214 
1215                 // generate the other coefficients only if required
1216                 if (maxDegreeTesseralSP >= 0) {
1217                     for (int m: nonResOrders.keySet()) {
1218                         final List<Integer> listJ = nonResOrders.get(m);
1219 
1220                         for (int j: listJ) {
1221                             buildFourierCoefficients(date, m, j, maxDegreeTesseralSP, context, hansenObjects);
1222                         }
1223                     }
1224                 }
1225             }
1226         }
1227 
1228         /** Build a set of fourier coefficients for a given m and j.
1229          *
1230          * @param date the date of the coefficients
1231          * @param m m index
1232          * @param j j index
1233          * @param maxN  maximum value for n index
1234          * @param context container for attributes
1235          * @param hansenObjects initialization of hansen objects
1236          */
1237         private void buildFourierCoefficients(final AbsoluteDate date,
1238                final int m, final int j, final int maxN, final DSSTTesseralContext context,
1239                final HansenObjects hansenObjects) {
1240 
1241             final AuxiliaryElements auxiliaryElements = context.getAuxiliaryElements();
1242 
1243             // Potential derivatives components for a given non-resonant pair {j,m}
1244             double dRdaCos  = 0.;
1245             double dRdaSin  = 0.;
1246             double dRdhCos  = 0.;
1247             double dRdhSin  = 0.;
1248             double dRdkCos  = 0.;
1249             double dRdkSin  = 0.;
1250             double dRdlCos  = 0.;
1251             double dRdlSin  = 0.;
1252             double dRdAlCos = 0.;
1253             double dRdAlSin = 0.;
1254             double dRdBeCos = 0.;
1255             double dRdBeSin = 0.;
1256             double dRdGaCos = 0.;
1257             double dRdGaSin = 0.;
1258 
1259             // s-SUM from -sMin to sMax
1260             final int sMin = j == 0 ? maxEccPowMdailyTesseralSP : maxEccPowTesseralSP;
1261             final int sMax = j == 0 ? maxEccPowMdailyTesseralSP : maxEccPowTesseralSP;
1262             for (int s = 0; s <= sMax; s++) {
1263 
1264                 // n-SUM for s positive
1265                 final double[][] nSumSpos = computeNSum(date, j, m, s, maxN,
1266                                                         roaPow, ghMSJ, gammaMNS, context, hansenObjects);
1267                 dRdaCos  += nSumSpos[0][0];
1268                 dRdaSin  += nSumSpos[0][1];
1269                 dRdhCos  += nSumSpos[1][0];
1270                 dRdhSin  += nSumSpos[1][1];
1271                 dRdkCos  += nSumSpos[2][0];
1272                 dRdkSin  += nSumSpos[2][1];
1273                 dRdlCos  += nSumSpos[3][0];
1274                 dRdlSin  += nSumSpos[3][1];
1275                 dRdAlCos += nSumSpos[4][0];
1276                 dRdAlSin += nSumSpos[4][1];
1277                 dRdBeCos += nSumSpos[5][0];
1278                 dRdBeSin += nSumSpos[5][1];
1279                 dRdGaCos += nSumSpos[6][0];
1280                 dRdGaSin += nSumSpos[6][1];
1281 
1282                 // n-SUM for s negative
1283                 if (s > 0 && s <= sMin) {
1284                     final double[][] nSumSneg = computeNSum(date, j, m, -s, maxN,
1285                                                             roaPow, ghMSJ, gammaMNS, context, hansenObjects);
1286                     dRdaCos  += nSumSneg[0][0];
1287                     dRdaSin  += nSumSneg[0][1];
1288                     dRdhCos  += nSumSneg[1][0];
1289                     dRdhSin  += nSumSneg[1][1];
1290                     dRdkCos  += nSumSneg[2][0];
1291                     dRdkSin  += nSumSneg[2][1];
1292                     dRdlCos  += nSumSneg[3][0];
1293                     dRdlSin  += nSumSneg[3][1];
1294                     dRdAlCos += nSumSneg[4][0];
1295                     dRdAlSin += nSumSneg[4][1];
1296                     dRdBeCos += nSumSneg[5][0];
1297                     dRdBeSin += nSumSneg[5][1];
1298                     dRdGaCos += nSumSneg[6][0];
1299                     dRdGaSin += nSumSneg[6][1];
1300                 }
1301             }
1302             dRdaCos  *= -context.getMoa() / auxiliaryElements.getSma();
1303             dRdaSin  *= -context.getMoa() / auxiliaryElements.getSma();
1304             dRdhCos  *=  context.getMoa();
1305             dRdhSin  *=  context.getMoa();
1306             dRdkCos  *=  context.getMoa();
1307             dRdkSin  *=  context.getMoa();
1308             dRdlCos  *=  context.getMoa();
1309             dRdlSin  *=  context.getMoa();
1310             dRdAlCos *=  context.getMoa();
1311             dRdAlSin *=  context.getMoa();
1312             dRdBeCos *=  context.getMoa();
1313             dRdBeSin *=  context.getMoa();
1314             dRdGaCos *=  context.getMoa();
1315             dRdGaSin *=  context.getMoa();
1316 
1317             // Compute the cross derivative operator :
1318             final double RAlphaGammaCos   = auxiliaryElements.getAlpha() * dRdGaCos - auxiliaryElements.getGamma() * dRdAlCos;
1319             final double RAlphaGammaSin   = auxiliaryElements.getAlpha() * dRdGaSin - auxiliaryElements.getGamma() * dRdAlSin;
1320             final double RAlphaBetaCos    = auxiliaryElements.getAlpha() * dRdBeCos - auxiliaryElements.getBeta()  * dRdAlCos;
1321             final double RAlphaBetaSin    = auxiliaryElements.getAlpha() * dRdBeSin - auxiliaryElements.getBeta()  * dRdAlSin;
1322             final double RBetaGammaCos    =  auxiliaryElements.getBeta() * dRdGaCos - auxiliaryElements.getGamma() * dRdBeCos;
1323             final double RBetaGammaSin    =  auxiliaryElements.getBeta() * dRdGaSin - auxiliaryElements.getGamma() * dRdBeSin;
1324             final double RhkCos           =     auxiliaryElements.getH() * dRdkCos  -     auxiliaryElements.getK() * dRdhCos;
1325             final double RhkSin           =     auxiliaryElements.getH() * dRdkSin  -     auxiliaryElements.getK() * dRdhSin;
1326             final double pRagmIqRbgoABCos = (auxiliaryElements.getP() * RAlphaGammaCos - I * auxiliaryElements.getQ() * RBetaGammaCos) * context.getOoAB();
1327             final double pRagmIqRbgoABSin = (auxiliaryElements.getP() * RAlphaGammaSin - I * auxiliaryElements.getQ() * RBetaGammaSin) * context.getOoAB();
1328             final double RhkmRabmdRdlCos  =  RhkCos - RAlphaBetaCos - dRdlCos;
1329             final double RhkmRabmdRdlSin  =  RhkSin - RAlphaBetaSin - dRdlSin;
1330 
1331             // da/dt
1332             cCoef[m][j + jMax][0] = context.getAx2oA() * dRdlCos;
1333             sCoef[m][j + jMax][0] = context.getAx2oA() * dRdlSin;
1334 
1335             // dk/dt
1336             cCoef[m][j + jMax][1] = -(context.getBoA() * dRdhCos + auxiliaryElements.getH() * pRagmIqRbgoABCos + auxiliaryElements.getK() * context.getBoABpo() * dRdlCos);
1337             sCoef[m][j + jMax][1] = -(context.getBoA() * dRdhSin + auxiliaryElements.getH() * pRagmIqRbgoABSin + auxiliaryElements.getK() * context.getBoABpo() * dRdlSin);
1338 
1339             // dh/dt
1340             cCoef[m][j + jMax][2] = context.getBoA() * dRdkCos + auxiliaryElements.getK() * pRagmIqRbgoABCos - auxiliaryElements.getH() * context.getBoABpo() * dRdlCos;
1341             sCoef[m][j + jMax][2] = context.getBoA() * dRdkSin + auxiliaryElements.getK() * pRagmIqRbgoABSin - auxiliaryElements.getH() * context.getBoABpo() * dRdlSin;
1342 
1343             // dq/dt
1344             cCoef[m][j + jMax][3] = context.getCo2AB() * (auxiliaryElements.getQ() * RhkmRabmdRdlCos - I * RAlphaGammaCos);
1345             sCoef[m][j + jMax][3] = context.getCo2AB() * (auxiliaryElements.getQ() * RhkmRabmdRdlSin - I * RAlphaGammaSin);
1346 
1347             // dp/dt
1348             cCoef[m][j + jMax][4] = context.getCo2AB() * (auxiliaryElements.getP() * RhkmRabmdRdlCos - RBetaGammaCos);
1349             sCoef[m][j + jMax][4] = context.getCo2AB() * (auxiliaryElements.getP() * RhkmRabmdRdlSin - RBetaGammaSin);
1350 
1351             // dλ/dt
1352             cCoef[m][j + jMax][5] = -context.getAx2oA() * dRdaCos + context.getBoABpo() * (auxiliaryElements.getH() * dRdhCos + auxiliaryElements.getK() * dRdkCos) + pRagmIqRbgoABCos;
1353             sCoef[m][j + jMax][5] = -context.getAx2oA() * dRdaSin + context.getBoABpo() * (auxiliaryElements.getH() * dRdhSin + auxiliaryElements.getK() * dRdkSin) + pRagmIqRbgoABSin;
1354         }
1355 
1356         /** Get the coefficient C<sub>i</sub><sup>jm</sup>.
1357          * @param i i index - corresponds to the required variation
1358          * @param j j index
1359          * @param m m index
1360          * @return the coefficient C<sub>i</sub><sup>jm</sup>
1361          */
1362         public double getCijm(final int i, final int j, final int m) {
1363             return cCoef[m][j + jMax][i];
1364         }
1365 
1366         /** Get the coefficient S<sub>i</sub><sup>jm</sup>.
1367          * @param i i index - corresponds to the required variation
1368          * @param j j index
1369          * @param m m index
1370          * @return the coefficient S<sub>i</sub><sup>jm</sup>
1371          */
1372         public double getSijm(final int i, final int j, final int m) {
1373             return sCoef[m][j + jMax][i];
1374         }
1375     }
1376 
1377     /** Compute the C<sup>j</sup> and the S<sup>j</sup> coefficients.
1378      *  <p>
1379      *  Those coefficients are given in Danielson paper by substituting the
1380      *  disturbing function (2.7.1-16) with m != 0 into (2.2-10)
1381      *  </p>
1382      */
1383     private class FieldFourierCjSjCoefficients <T extends CalculusFieldElement<T>> {
1384 
1385         /** Absolute limit for j ( -jMax <= j <= jMax ).  */
1386         private final int jMax;
1387 
1388         /** The C<sub>i</sub><sup>jm</sup> coefficients.
1389          * <p>
1390          * The index order is [m][j][i] <br/>
1391          * The i index corresponds to the C<sub>i</sub><sup>jm</sup> coefficients used to
1392          * compute the following: <br/>
1393          * - da/dt <br/>
1394          * - dk/dt <br/>
1395          * - dh/dt / dk <br/>
1396          * - dq/dt <br/>
1397          * - dp/dt / dα <br/>
1398          * - dλ/dt / dβ <br/>
1399          * </p>
1400          */
1401         private final T[][][] cCoef;
1402 
1403         /** The S<sub>i</sub><sup>jm</sup> coefficients.
1404          * <p>
1405          * The index order is [m][j][i] <br/>
1406          * The i index corresponds to the C<sub>i</sub><sup>jm</sup> coefficients used to
1407          * compute the following: <br/>
1408          * - da/dt <br/>
1409          * - dk/dt <br/>
1410          * - dh/dt / dk <br/>
1411          * - dq/dt <br/>
1412          * - dp/dt / dα <br/>
1413          * - dλ/dt / dβ <br/>
1414          * </p>
1415          */
1416         private final T[][][] sCoef;
1417 
1418         /** G<sub>ms</sub><sup>j</sup> and H<sub>ms</sub><sup>j</sup> polynomials. */
1419         private FieldGHmsjPolynomials<T> ghMSJ;
1420 
1421         /** &Gamma;<sub>ns</sub><sup>m</sup> function. */
1422         private FieldGammaMnsFunction<T> gammaMNS;
1423 
1424         /** R / a up to power degree. */
1425         private final T[] roaPow;
1426 
1427         /** Create a set of C<sub>i</sub><sup>jm</sup> and S<sub>i</sub><sup>jm</sup> coefficients.
1428          *  @param jMax absolute limit for j ( -jMax <= j <= jMax )
1429          *  @param mMax maximum value for m
1430          *  @param field field used by default
1431          */
1432         FieldFourierCjSjCoefficients(final int jMax, final int mMax, final Field<T> field) {
1433             // initialise fields
1434             final T zero = field.getZero();
1435             final int rows    = mMax + 1;
1436             final int columns = 2 * jMax + 1;
1437             this.jMax         = jMax;
1438             this.cCoef        = MathArrays.buildArray(field, rows, columns, 6);
1439             this.sCoef        = MathArrays.buildArray(field, rows, columns, 6);
1440             this.roaPow       = MathArrays.buildArray(field, maxDegree + 1);
1441             roaPow[0] = zero.newInstance(1.);
1442         }
1443 
1444         /**
1445          * Generate the coefficients.
1446          * @param date the current date
1447          * @param context container for attributes
1448          * @param hansenObjects initialization of hansen objects
1449          * @param field field used by default
1450          */
1451         public void generateCoefficients(final FieldAbsoluteDate<T> date,
1452                                          final FieldDSSTTesseralContext<T> context,
1453                                          final FieldHansenObjects<T> hansenObjects,
1454                                          final Field<T> field) {
1455 
1456             final FieldAuxiliaryElements<T> auxiliaryElements = context.getFieldAuxiliaryElements();
1457             // Compute only if there is at least one non-resonant tesseral
1458             if (!nonResOrders.isEmpty() || maxDegreeTesseralSP < 0) {
1459                 // Gmsj and Hmsj polynomials
1460                 ghMSJ = new FieldGHmsjPolynomials<>(auxiliaryElements.getK(), auxiliaryElements.getH(), auxiliaryElements.getAlpha(), auxiliaryElements.getBeta(), I, field);
1461 
1462                 // GAMMAmns function
1463                 gammaMNS = new FieldGammaMnsFunction<>(maxDegree, auxiliaryElements.getGamma(), I, field);
1464 
1465                 final int maxRoaPower = FastMath.max(maxDegreeTesseralSP, maxDegreeMdailyTesseralSP);
1466 
1467                 // R / a up to power degree
1468                 for (int i = 1; i <= maxRoaPower; i++) {
1469                     roaPow[i] = context.getRoa().multiply(roaPow[i - 1]);
1470                 }
1471 
1472                 //generate the m-daily coefficients
1473                 for (int m = 1; m <= maxOrderMdailyTesseralSP; m++) {
1474                     buildFourierCoefficients(date, m, 0, maxDegreeMdailyTesseralSP, context, hansenObjects, field);
1475                 }
1476 
1477                 // generate the other coefficients only if required
1478                 if (maxDegreeTesseralSP >= 0) {
1479                     for (int m: nonResOrders.keySet()) {
1480                         final List<Integer> listJ = nonResOrders.get(m);
1481 
1482                         for (int j: listJ) {
1483                             buildFourierCoefficients(date, m, j, maxDegreeTesseralSP, context, hansenObjects, field);
1484                         }
1485                     }
1486                 }
1487             }
1488         }
1489 
1490         /** Build a set of fourier coefficients for a given m and j.
1491          *
1492          * @param date the date of the coefficients
1493          * @param m m index
1494          * @param j j index
1495          * @param maxN  maximum value for n index
1496          * @param context container for attributes
1497          * @param hansenObjects initialization of hansen objects
1498          * @param field field used by default
1499          */
1500         private void buildFourierCoefficients(final FieldAbsoluteDate<T> date,
1501                                               final int m, final int j, final int maxN,
1502                                               final FieldDSSTTesseralContext<T> context,
1503                                               final FieldHansenObjects<T> hansenObjects,
1504                                               final Field<T> field) {
1505 
1506             // Zero
1507             final T zero = field.getZero();
1508             // Common parameters
1509             final FieldAuxiliaryElements<T> auxiliaryElements = context.getFieldAuxiliaryElements();
1510 
1511             // Potential derivatives components for a given non-resonant pair {j,m}
1512             T dRdaCos  = zero;
1513             T dRdaSin  = zero;
1514             T dRdhCos  = zero;
1515             T dRdhSin  = zero;
1516             T dRdkCos  = zero;
1517             T dRdkSin  = zero;
1518             T dRdlCos  = zero;
1519             T dRdlSin  = zero;
1520             T dRdAlCos = zero;
1521             T dRdAlSin = zero;
1522             T dRdBeCos = zero;
1523             T dRdBeSin = zero;
1524             T dRdGaCos = zero;
1525             T dRdGaSin = zero;
1526 
1527             // s-SUM from -sMin to sMax
1528             final int sMin = j == 0 ? maxEccPowMdailyTesseralSP : maxEccPowTesseralSP;
1529             final int sMax = j == 0 ? maxEccPowMdailyTesseralSP : maxEccPowTesseralSP;
1530             for (int s = 0; s <= sMax; s++) {
1531 
1532                 // n-SUM for s positive
1533                 final T[][] nSumSpos = computeNSum(date, j, m, s, maxN,
1534                                                         roaPow, ghMSJ, gammaMNS, context, hansenObjects);
1535                 dRdaCos  =  dRdaCos.add(nSumSpos[0][0]);
1536                 dRdaSin  =  dRdaSin.add(nSumSpos[0][1]);
1537                 dRdhCos  =  dRdhCos.add(nSumSpos[1][0]);
1538                 dRdhSin  =  dRdhSin.add(nSumSpos[1][1]);
1539                 dRdkCos  =  dRdkCos.add(nSumSpos[2][0]);
1540                 dRdkSin  =  dRdkSin.add(nSumSpos[2][1]);
1541                 dRdlCos  =  dRdlCos.add(nSumSpos[3][0]);
1542                 dRdlSin  =  dRdlSin.add(nSumSpos[3][1]);
1543                 dRdAlCos = dRdAlCos.add(nSumSpos[4][0]);
1544                 dRdAlSin = dRdAlSin.add(nSumSpos[4][1]);
1545                 dRdBeCos = dRdBeCos.add(nSumSpos[5][0]);
1546                 dRdBeSin = dRdBeSin.add(nSumSpos[5][1]);
1547                 dRdGaCos = dRdGaCos.add(nSumSpos[6][0]);
1548                 dRdGaSin = dRdGaSin.add(nSumSpos[6][1]);
1549 
1550                 // n-SUM for s negative
1551                 if (s > 0 && s <= sMin) {
1552                     final T[][] nSumSneg = computeNSum(date, j, m, -s, maxN,
1553                                                             roaPow, ghMSJ, gammaMNS, context, hansenObjects);
1554                     dRdaCos  =  dRdaCos.add(nSumSneg[0][0]);
1555                     dRdaSin  =  dRdaSin.add(nSumSneg[0][1]);
1556                     dRdhCos  =  dRdhCos.add(nSumSneg[1][0]);
1557                     dRdhSin  =  dRdhSin.add(nSumSneg[1][1]);
1558                     dRdkCos  =  dRdkCos.add(nSumSneg[2][0]);
1559                     dRdkSin  =  dRdkSin.add(nSumSneg[2][1]);
1560                     dRdlCos  =  dRdlCos.add(nSumSneg[3][0]);
1561                     dRdlSin  =  dRdlSin.add(nSumSneg[3][1]);
1562                     dRdAlCos = dRdAlCos.add(nSumSneg[4][0]);
1563                     dRdAlSin = dRdAlSin.add(nSumSneg[4][1]);
1564                     dRdBeCos = dRdBeCos.add(nSumSneg[5][0]);
1565                     dRdBeSin = dRdBeSin.add(nSumSneg[5][1]);
1566                     dRdGaCos = dRdGaCos.add(nSumSneg[6][0]);
1567                     dRdGaSin = dRdGaSin.add(nSumSneg[6][1]);
1568                 }
1569             }
1570             dRdaCos  =  dRdaCos.multiply(context.getMoa().negate().divide(auxiliaryElements.getSma()));
1571             dRdaSin  =  dRdaSin.multiply(context.getMoa().negate().divide(auxiliaryElements.getSma()));
1572             dRdhCos  =  dRdhCos.multiply(context.getMoa());
1573             dRdhSin  =  dRdhSin.multiply(context.getMoa());
1574             dRdkCos  =  dRdkCos.multiply(context.getMoa());
1575             dRdkSin  =  dRdkSin.multiply(context.getMoa());
1576             dRdlCos  =  dRdlCos.multiply(context.getMoa());
1577             dRdlSin  =  dRdlSin.multiply(context.getMoa());
1578             dRdAlCos = dRdAlCos.multiply(context.getMoa());
1579             dRdAlSin = dRdAlSin.multiply(context.getMoa());
1580             dRdBeCos = dRdBeCos.multiply(context.getMoa());
1581             dRdBeSin = dRdBeSin.multiply(context.getMoa());
1582             dRdGaCos = dRdGaCos.multiply(context.getMoa());
1583             dRdGaSin = dRdGaSin.multiply(context.getMoa());
1584 
1585             // Compute the cross derivative operator :
1586             final T RAlphaGammaCos   = auxiliaryElements.getAlpha().multiply(dRdGaCos).subtract(auxiliaryElements.getGamma().multiply(dRdAlCos));
1587             final T RAlphaGammaSin   = auxiliaryElements.getAlpha().multiply(dRdGaSin).subtract(auxiliaryElements.getGamma().multiply(dRdAlSin));
1588             final T RAlphaBetaCos    = auxiliaryElements.getAlpha().multiply(dRdBeCos).subtract(auxiliaryElements.getBeta().multiply(dRdAlCos));
1589             final T RAlphaBetaSin    = auxiliaryElements.getAlpha().multiply(dRdBeSin).subtract(auxiliaryElements.getBeta().multiply(dRdAlSin));
1590             final T RBetaGammaCos    =  auxiliaryElements.getBeta().multiply(dRdGaCos).subtract(auxiliaryElements.getGamma().multiply(dRdBeCos));
1591             final T RBetaGammaSin    =  auxiliaryElements.getBeta().multiply(dRdGaSin).subtract(auxiliaryElements.getGamma().multiply(dRdBeSin));
1592             final T RhkCos           =     auxiliaryElements.getH().multiply(dRdkCos).subtract(auxiliaryElements.getK().multiply(dRdhCos));
1593             final T RhkSin           =     auxiliaryElements.getH().multiply(dRdkSin).subtract(auxiliaryElements.getK().multiply(dRdhSin));
1594             final T pRagmIqRbgoABCos = (auxiliaryElements.getP().multiply(RAlphaGammaCos).subtract(auxiliaryElements.getQ().multiply(RBetaGammaCos).multiply(I))).multiply(context.getOoAB());
1595             final T pRagmIqRbgoABSin = (auxiliaryElements.getP().multiply(RAlphaGammaSin).subtract(auxiliaryElements.getQ().multiply(RBetaGammaSin).multiply(I))).multiply(context.getOoAB());
1596             final T RhkmRabmdRdlCos  =  RhkCos.subtract(RAlphaBetaCos).subtract(dRdlCos);
1597             final T RhkmRabmdRdlSin  =  RhkSin.subtract(RAlphaBetaSin).subtract(dRdlSin);
1598 
1599             // da/dt
1600             cCoef[m][j + jMax][0] = context.getAx2oA().multiply(dRdlCos);
1601             sCoef[m][j + jMax][0] = context.getAx2oA().multiply(dRdlSin);
1602 
1603             // dk/dt
1604             cCoef[m][j + jMax][1] = (context.getBoA().multiply(dRdhCos).add(auxiliaryElements.getH().multiply(pRagmIqRbgoABCos)).add(auxiliaryElements.getK().multiply(context.getBoABpo()).multiply(dRdlCos))).negate();
1605             sCoef[m][j + jMax][1] = (context.getBoA().multiply(dRdhSin).add(auxiliaryElements.getH().multiply(pRagmIqRbgoABSin)).add(auxiliaryElements.getK().multiply(context.getBoABpo()).multiply(dRdlSin))).negate();
1606 
1607             // dh/dt
1608             cCoef[m][j + jMax][2] = context.getBoA().multiply(dRdkCos).add(auxiliaryElements.getK().multiply(pRagmIqRbgoABCos)).subtract(auxiliaryElements.getH().multiply(context.getBoABpo()).multiply(dRdlCos));
1609             sCoef[m][j + jMax][2] = context.getBoA().multiply(dRdkSin).add(auxiliaryElements.getK().multiply(pRagmIqRbgoABSin)).subtract(auxiliaryElements.getH().multiply(context.getBoABpo()).multiply(dRdlSin));
1610 
1611             // dq/dt
1612             cCoef[m][j + jMax][3] = context.getCo2AB().multiply(auxiliaryElements.getQ().multiply(RhkmRabmdRdlCos).subtract(RAlphaGammaCos.multiply(I)));
1613             sCoef[m][j + jMax][3] = context.getCo2AB().multiply(auxiliaryElements.getQ().multiply(RhkmRabmdRdlSin).subtract(RAlphaGammaSin.multiply(I)));
1614 
1615             // dp/dt
1616             cCoef[m][j + jMax][4] = context.getCo2AB().multiply(auxiliaryElements.getP().multiply(RhkmRabmdRdlCos).subtract(RBetaGammaCos));
1617             sCoef[m][j + jMax][4] = context.getCo2AB().multiply(auxiliaryElements.getP().multiply(RhkmRabmdRdlSin).subtract(RBetaGammaSin));
1618 
1619             // dλ/dt
1620             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);
1621             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);
1622         }
1623 
1624         /** Get the coefficient C<sub>i</sub><sup>jm</sup>.
1625          * @param i i index - corresponds to the required variation
1626          * @param j j index
1627          * @param m m index
1628          * @return the coefficient C<sub>i</sub><sup>jm</sup>
1629          */
1630         public T getCijm(final int i, final int j, final int m) {
1631             return cCoef[m][j + jMax][i];
1632         }
1633 
1634         /** Get the coefficient S<sub>i</sub><sup>jm</sup>.
1635          * @param i i index - corresponds to the required variation
1636          * @param j j index
1637          * @param m m index
1638          * @return the coefficient S<sub>i</sub><sup>jm</sup>
1639          */
1640         public T getSijm(final int i, final int j, final int m) {
1641             return sCoef[m][j + jMax][i];
1642         }
1643     }
1644 
1645     /** 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
1646      * the short-periodic zonal contribution.
1647      *   <p>
1648      *  Those coefficients are given by expression 2.5.4-5 from the Danielson paper.
1649      *   </p>
1650      *
1651      * @author Sorin Scortan
1652      *
1653      */
1654     private static class TesseralShortPeriodicCoefficients implements ShortPeriodTerms {
1655 
1656         /** Retrograde factor I.
1657          *  <p>
1658          *  DSST model needs equinoctial orbit as internal representation.
1659          *  Classical equinoctial elements have discontinuities when inclination
1660          *  is close to zero. In this representation, I = +1. <br>
1661          *  To avoid this discontinuity, another representation exists and equinoctial
1662          *  elements can be expressed in a different way, called "retrograde" orbit.
1663          *  This implies I = -1. <br>
1664          *  As Orekit doesn't implement the retrograde orbit, I is always set to +1.
1665          *  But for the sake of consistency with the theory, the retrograde factor
1666          *  has been kept in the formulas.
1667          *  </p>
1668          */
1669         private static final int I = 1;
1670 
1671         /** Central body rotating frame. */
1672         private final Frame bodyFrame;
1673 
1674         /** Maximal order to consider for short periodics m-daily tesseral harmonics potential. */
1675         private final int maxOrderMdailyTesseralSP;
1676 
1677         /** Flag to take into account only M-dailies harmonic tesserals for short periodic perturbations.  */
1678         private final boolean mDailiesOnly;
1679 
1680         /** List of non resonant orders with j != 0. */
1681         private final SortedMap<Integer, List<Integer> > nonResOrders;
1682 
1683         /** Maximum value for m index. */
1684         private final int mMax;
1685 
1686         /** Maximum value for j index. */
1687         private final int jMax;
1688 
1689         /** Number of points used in the interpolation process. */
1690         private final int interpolationPoints;
1691 
1692         /** All coefficients slots. */
1693         private final transient TimeSpanMap<Slot> slots;
1694 
1695         /** Constructor.
1696          * @param bodyFrame central body rotating frame
1697          * @param maxOrderMdailyTesseralSP maximal order to consider for short periodics m-daily tesseral harmonics potential
1698          * @param mDailiesOnly flag to take into account only M-dailies harmonic tesserals for short periodic perturbations
1699          * @param nonResOrders lst of non resonant orders with j != 0
1700          * @param mMax maximum value for m index
1701          * @param jMax maximum value for j index
1702          * @param interpolationPoints number of points used in the interpolation process
1703          * @param slots all coefficients slots
1704          */
1705         TesseralShortPeriodicCoefficients(final Frame bodyFrame, final int maxOrderMdailyTesseralSP,
1706                                           final boolean mDailiesOnly, final SortedMap<Integer, List<Integer> > nonResOrders,
1707                                           final int mMax, final int jMax, final int interpolationPoints,
1708                                           final TimeSpanMap<Slot> slots) {
1709             this.bodyFrame                = bodyFrame;
1710             this.maxOrderMdailyTesseralSP = maxOrderMdailyTesseralSP;
1711             this.mDailiesOnly             = mDailiesOnly;
1712             this.nonResOrders             = nonResOrders;
1713             this.mMax                     = mMax;
1714             this.jMax                     = jMax;
1715             this.interpolationPoints      = interpolationPoints;
1716             this.slots                    = slots;
1717         }
1718 
1719         /** Get the slot valid for some date.
1720          * @param meanStates mean states defining the slot
1721          * @return slot valid at the specified date
1722          */
1723         public Slot createSlot(final SpacecraftState... meanStates) {
1724             final Slot         slot  = new Slot(mMax, jMax, interpolationPoints);
1725             final AbsoluteDate first = meanStates[0].getDate();
1726             final AbsoluteDate last  = meanStates[meanStates.length - 1].getDate();
1727             final int compare = first.compareTo(last);
1728             if (compare < 0) {
1729                 slots.addValidAfter(slot, first, false);
1730             } else if (compare > 0) {
1731                 slots.addValidBefore(slot, first, false);
1732             } else {
1733                 // single date, valid for all time
1734                 slots.addValidAfter(slot, AbsoluteDate.PAST_INFINITY, false);
1735             }
1736             return slot;
1737         }
1738 
1739         /** {@inheritDoc} */
1740         @Override
1741         public double[] value(final Orbit meanOrbit) {
1742 
1743             // select the coefficients slot
1744             final Slot slot = slots.get(meanOrbit.getDate());
1745 
1746             // Initialise the short periodic variations
1747             final double[] shortPeriodicVariation = new double[6];
1748 
1749             // Compute only if there is at least one non-resonant tesseral or
1750             // only the m-daily tesseral should be taken into account
1751             if (!nonResOrders.isEmpty() || mDailiesOnly) {
1752 
1753                 //Build an auxiliary object
1754                 final AuxiliaryElements auxiliaryElements = new AuxiliaryElements(meanOrbit, I);
1755 
1756                 // Central body rotation angle from equation 2.7.1-(3)(4).
1757                 final StaticTransform t = bodyFrame.getStaticTransformTo(
1758                         auxiliaryElements.getFrame(),
1759                         auxiliaryElements.getDate());
1760                 final Vector3D xB = t.transformVector(Vector3D.PLUS_I);
1761                 final Vector3D yB = t.transformVector(Vector3D.PLUS_J);
1762                 final Vector3D  f = auxiliaryElements.getVectorF();
1763                 final Vector3D  g = auxiliaryElements.getVectorG();
1764                 final double currentTheta = FastMath.atan2(-f.dotProduct(yB) + I * g.dotProduct(xB),
1765                                                             f.dotProduct(xB) + I * g.dotProduct(yB));
1766 
1767                 //Add the m-daily contribution
1768                 for (int m = 1; m <= maxOrderMdailyTesseralSP; m++) {
1769                     // Phase angle
1770                     final double jlMmt  = -m * currentTheta;
1771                     final SinCos scPhi  = FastMath.sinCos(jlMmt);
1772                     final double sinPhi = scPhi.sin();
1773                     final double cosPhi = scPhi.cos();
1774 
1775                     // compute contribution for each element
1776                     final double[] c = slot.getCijm(0, m, meanOrbit.getDate());
1777                     final double[] s = slot.getSijm(0, m, meanOrbit.getDate());
1778                     for (int i = 0; i < 6; i++) {
1779                         shortPeriodicVariation[i] += c[i] * cosPhi + s[i] * sinPhi;
1780                     }
1781                 }
1782 
1783                 // loop through all non-resonant (j,m) pairs
1784                 for (final Map.Entry<Integer, List<Integer>> entry : nonResOrders.entrySet()) {
1785                     final int           m     = entry.getKey();
1786                     final List<Integer> listJ = entry.getValue();
1787 
1788                     for (int j : listJ) {
1789                         // Phase angle
1790                         final double jlMmt  = j * meanOrbit.getLM() - m * currentTheta;
1791                         final SinCos scPhi  = FastMath.sinCos(jlMmt);
1792                         final double sinPhi = scPhi.sin();
1793                         final double cosPhi = scPhi.cos();
1794 
1795                         // compute contribution for each element
1796                         final double[] c = slot.getCijm(j, m, meanOrbit.getDate());
1797                         final double[] s = slot.getSijm(j, m, meanOrbit.getDate());
1798                         for (int i = 0; i < 6; i++) {
1799                             shortPeriodicVariation[i] += c[i] * cosPhi + s[i] * sinPhi;
1800                         }
1801 
1802                     }
1803                 }
1804             }
1805 
1806             return shortPeriodicVariation;
1807 
1808         }
1809 
1810         /** {@inheritDoc} */
1811         @Override
1812         public String getCoefficientsKeyPrefix() {
1813             return DSSTTesseral.SHORT_PERIOD_PREFIX;
1814         }
1815 
1816         /** {@inheritDoc}
1817          * <p>
1818          * For tesseral terms contributions,there are maxOrderMdailyTesseralSP
1819          * m-daily cMm coefficients, maxOrderMdailyTesseralSP m-daily sMm
1820          * coefficients, nbNonResonant cjm coefficients and nbNonResonant
1821          * sjm coefficients, where maxOrderMdailyTesseralSP and nbNonResonant both
1822          * depend on the orbit. The j index is the integer multiplier for the true
1823          * longitude argument and the m index is the integer multiplier for m-dailies.
1824          * </p>
1825          */
1826         @Override
1827         public Map<String, double[]> getCoefficients(final AbsoluteDate date, final Set<String> selected) {
1828 
1829             // select the coefficients slot
1830             final Slot slot = slots.get(date);
1831 
1832             if (!nonResOrders.isEmpty() || mDailiesOnly) {
1833                 final Map<String, double[]> coefficients = new HashMap<>(12 * maxOrderMdailyTesseralSP + 12 * nonResOrders.size());
1834 
1835                 for (int m = 1; m <= maxOrderMdailyTesseralSP; m++) {
1836                     storeIfSelected(coefficients, selected, slot.getCijm(0, m, date), DSSTTesseral.CM_COEFFICIENTS, m);
1837                     storeIfSelected(coefficients, selected, slot.getSijm(0, m, date), DSSTTesseral.SM_COEFFICIENTS, m);
1838                 }
1839 
1840                 for (final Map.Entry<Integer, List<Integer>> entry : nonResOrders.entrySet()) {
1841                     final int           m     = entry.getKey();
1842                     final List<Integer> listJ = entry.getValue();
1843 
1844                     for (int j : listJ) {
1845                         for (int i = 0; i < 6; ++i) {
1846                             storeIfSelected(coefficients, selected, slot.getCijm(j, m, date), "c", j, m);
1847                             storeIfSelected(coefficients, selected, slot.getSijm(j, m, date), "s", j, m);
1848                         }
1849                     }
1850                 }
1851 
1852                 return coefficients;
1853 
1854             } else {
1855                 return Collections.emptyMap();
1856             }
1857 
1858         }
1859 
1860         /** Put a coefficient in a map if selected.
1861          * @param map map to populate
1862          * @param selected set of coefficients that should be put in the map
1863          * (empty set means all coefficients are selected)
1864          * @param value coefficient value
1865          * @param id coefficient identifier
1866          * @param indices list of coefficient indices
1867          */
1868         private void storeIfSelected(final Map<String, double[]> map, final Set<String> selected,
1869                                      final double[] value, final String id, final int... indices) {
1870             final StringBuilder keyBuilder = new StringBuilder(getCoefficientsKeyPrefix());
1871             keyBuilder.append(id);
1872             for (int index : indices) {
1873                 keyBuilder.append('[').append(index).append(']');
1874             }
1875             final String key = keyBuilder.toString();
1876             if (selected.isEmpty() || selected.contains(key)) {
1877                 map.put(key, value);
1878             }
1879         }
1880 
1881     }
1882 
1883     /** 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
1884      * the short-periodic zonal contribution.
1885      *   <p>
1886      *  Those coefficients are given by expression 2.5.4-5 from the Danielson paper.
1887      *   </p>
1888      *
1889      * @author Sorin Scortan
1890      *
1891      */
1892     private static class FieldTesseralShortPeriodicCoefficients <T extends CalculusFieldElement<T>> implements FieldShortPeriodTerms<T> {
1893 
1894         /** Retrograde factor I.
1895          *  <p>
1896          *  DSST model needs equinoctial orbit as internal representation.
1897          *  Classical equinoctial elements have discontinuities when inclination
1898          *  is close to zero. In this representation, I = +1. <br>
1899          *  To avoid this discontinuity, another representation exists and equinoctial
1900          *  elements can be expressed in a different way, called "retrograde" orbit.
1901          *  This implies I = -1. <br>
1902          *  As Orekit doesn't implement the retrograde orbit, I is always set to +1.
1903          *  But for the sake of consistency with the theory, the retrograde factor
1904          *  has been kept in the formulas.
1905          *  </p>
1906          */
1907         private static final int I = 1;
1908 
1909         /** Central body rotating frame. */
1910         private final Frame bodyFrame;
1911 
1912         /** Maximal order to consider for short periodics m-daily tesseral harmonics potential. */
1913         private final int maxOrderMdailyTesseralSP;
1914 
1915         /** Flag to take into account only M-dailies harmonic tesserals for short periodic perturbations.  */
1916         private final boolean mDailiesOnly;
1917 
1918         /** List of non resonant orders with j != 0. */
1919         private final SortedMap<Integer, List<Integer> > nonResOrders;
1920 
1921         /** Maximum value for m index. */
1922         private final int mMax;
1923 
1924         /** Maximum value for j index. */
1925         private final int jMax;
1926 
1927         /** Number of points used in the interpolation process. */
1928         private final int interpolationPoints;
1929 
1930         /** All coefficients slots. */
1931         private final transient FieldTimeSpanMap<FieldSlot<T>, T> slots;
1932 
1933         /** Constructor.
1934          * @param bodyFrame central body rotating frame
1935          * @param maxOrderMdailyTesseralSP maximal order to consider for short periodics m-daily tesseral harmonics potential
1936          * @param mDailiesOnly flag to take into account only M-dailies harmonic tesserals for short periodic perturbations
1937          * @param nonResOrders lst of non resonant orders with j != 0
1938          * @param mMax maximum value for m index
1939          * @param jMax maximum value for j index
1940          * @param interpolationPoints number of points used in the interpolation process
1941          * @param slots all coefficients slots
1942          */
1943         FieldTesseralShortPeriodicCoefficients(final Frame bodyFrame, final int maxOrderMdailyTesseralSP,
1944                                                final boolean mDailiesOnly, final SortedMap<Integer, List<Integer> > nonResOrders,
1945                                                final int mMax, final int jMax, final int interpolationPoints,
1946                                                final FieldTimeSpanMap<FieldSlot<T>, T> slots) {
1947             this.bodyFrame                = bodyFrame;
1948             this.maxOrderMdailyTesseralSP = maxOrderMdailyTesseralSP;
1949             this.mDailiesOnly             = mDailiesOnly;
1950             this.nonResOrders             = nonResOrders;
1951             this.mMax                     = mMax;
1952             this.jMax                     = jMax;
1953             this.interpolationPoints      = interpolationPoints;
1954             this.slots                    = slots;
1955         }
1956 
1957         /** Get the slot valid for some date.
1958          * @param meanStates mean states defining the slot
1959          * @return slot valid at the specified date
1960          */
1961         @SuppressWarnings("unchecked")
1962         public FieldSlot<T> createSlot(final FieldSpacecraftState<T>... meanStates) {
1963             final FieldSlot<T>         slot  = new FieldSlot<>(mMax, jMax, interpolationPoints);
1964             final FieldAbsoluteDate<T> first = meanStates[0].getDate();
1965             final FieldAbsoluteDate<T> last  = meanStates[meanStates.length - 1].getDate();
1966             if (first.compareTo(last) <= 0) {
1967                 slots.addValidAfter(slot, first);
1968             } else {
1969                 slots.addValidBefore(slot, first);
1970             }
1971             return slot;
1972         }
1973 
1974         /** {@inheritDoc} */
1975         @Override
1976         public T[] value(final FieldOrbit<T> meanOrbit) {
1977 
1978             // select the coefficients slot
1979             final FieldSlot<T> slot = slots.get(meanOrbit.getDate());
1980 
1981             // Initialise the short periodic variations
1982             final T[] shortPeriodicVariation = MathArrays.buildArray(meanOrbit.getDate().getField(), 6);
1983 
1984             // Compute only if there is at least one non-resonant tesseral or
1985             // only the m-daily tesseral should be taken into account
1986             if (!nonResOrders.isEmpty() || mDailiesOnly) {
1987 
1988                 //Build an auxiliary object
1989                 final FieldAuxiliaryElements<T> auxiliaryElements = new FieldAuxiliaryElements<>(meanOrbit, I);
1990 
1991                 // Central body rotation angle from equation 2.7.1-(3)(4).
1992                 final FieldStaticTransform<T> t = bodyFrame.getStaticTransformTo(auxiliaryElements.getFrame(), auxiliaryElements.getDate());
1993                 final FieldVector3D<T> xB = t.transformVector(Vector3D.PLUS_I);
1994                 final FieldVector3D<T> yB = t.transformVector(Vector3D.PLUS_J);
1995                 final FieldVector3D<T>  f = auxiliaryElements.getVectorF();
1996                 final FieldVector3D<T>  g = auxiliaryElements.getVectorG();
1997                 final T currentTheta = FastMath.atan2(f.dotProduct(yB).negate().add(g.dotProduct(xB).multiply(I)),
1998                                                       f.dotProduct(xB).add(g.dotProduct(yB).multiply(I)));
1999 
2000                 //Add the m-daily contribution
2001                 for (int m = 1; m <= maxOrderMdailyTesseralSP; m++) {
2002                     // Phase angle
2003                     final T jlMmt              = currentTheta.multiply(-m);
2004                     final FieldSinCos<T> scPhi = FastMath.sinCos(jlMmt);
2005                     final T sinPhi             = scPhi.sin();
2006                     final T cosPhi             = scPhi.cos();
2007 
2008                     // compute contribution for each element
2009                     final T[] c = slot.getCijm(0, m, meanOrbit.getDate());
2010                     final T[] s = slot.getSijm(0, m, meanOrbit.getDate());
2011                     for (int i = 0; i < 6; i++) {
2012                         shortPeriodicVariation[i] = shortPeriodicVariation[i].add(c[i].multiply(cosPhi).add(s[i].multiply(sinPhi)));
2013                     }
2014                 }
2015 
2016                 // loop through all non-resonant (j,m) pairs
2017                 for (final Map.Entry<Integer, List<Integer>> entry : nonResOrders.entrySet()) {
2018                     final int           m     = entry.getKey();
2019                     final List<Integer> listJ = entry.getValue();
2020 
2021                     for (int j : listJ) {
2022                         // Phase angle
2023                         final T jlMmt              = meanOrbit.getLM().multiply(j).subtract(currentTheta.multiply(m));
2024                         final FieldSinCos<T> scPhi = FastMath.sinCos(jlMmt);
2025                         final T sinPhi             = scPhi.sin();
2026                         final T cosPhi             = scPhi.cos();
2027 
2028                         // compute contribution for each element
2029                         final T[] c = slot.getCijm(j, m, meanOrbit.getDate());
2030                         final T[] s = slot.getSijm(j, m, meanOrbit.getDate());
2031                         for (int i = 0; i < 6; i++) {
2032                             shortPeriodicVariation[i] = shortPeriodicVariation[i].add(c[i].multiply(cosPhi).add(s[i].multiply(sinPhi)));
2033                         }
2034 
2035                     }
2036                 }
2037             }
2038 
2039             return shortPeriodicVariation;
2040 
2041         }
2042 
2043         /** {@inheritDoc} */
2044         @Override
2045         public String getCoefficientsKeyPrefix() {
2046             return DSSTTesseral.SHORT_PERIOD_PREFIX;
2047         }
2048 
2049         /** {@inheritDoc}
2050          * <p>
2051          * For tesseral terms contributions,there are maxOrderMdailyTesseralSP
2052          * m-daily cMm coefficients, maxOrderMdailyTesseralSP m-daily sMm
2053          * coefficients, nbNonResonant cjm coefficients and nbNonResonant
2054          * sjm coefficients, where maxOrderMdailyTesseralSP and nbNonResonant both
2055          * depend on the orbit. The j index is the integer multiplier for the true
2056          * longitude argument and the m index is the integer multiplier for m-dailies.
2057          * </p>
2058          */
2059         @Override
2060         public Map<String, T[]> getCoefficients(final FieldAbsoluteDate<T> date, final Set<String> selected) {
2061 
2062             // select the coefficients slot
2063             final FieldSlot<T> slot = slots.get(date);
2064 
2065             if (!nonResOrders.isEmpty() || mDailiesOnly) {
2066                 final Map<String, T[]> coefficients = new HashMap<>(12 * maxOrderMdailyTesseralSP + 12 * nonResOrders.size());
2067 
2068                 for (int m = 1; m <= maxOrderMdailyTesseralSP; m++) {
2069                     storeIfSelected(coefficients, selected, slot.getCijm(0, m, date), DSSTTesseral.CM_COEFFICIENTS, m);
2070                     storeIfSelected(coefficients, selected, slot.getSijm(0, m, date), DSSTTesseral.SM_COEFFICIENTS, m);
2071                 }
2072 
2073                 for (final Map.Entry<Integer, List<Integer>> entry : nonResOrders.entrySet()) {
2074                     final int           m     = entry.getKey();
2075                     final List<Integer> listJ = entry.getValue();
2076 
2077                     for (int j : listJ) {
2078                         for (int i = 0; i < 6; ++i) {
2079                             storeIfSelected(coefficients, selected, slot.getCijm(j, m, date), "c", j, m);
2080                             storeIfSelected(coefficients, selected, slot.getSijm(j, m, date), "s", j, m);
2081                         }
2082                     }
2083                 }
2084 
2085                 return coefficients;
2086 
2087             } else {
2088                 return Collections.emptyMap();
2089             }
2090 
2091         }
2092 
2093         /** Put a coefficient in a map if selected.
2094          * @param map map to populate
2095          * @param selected set of coefficients that should be put in the map
2096          * (empty set means all coefficients are selected)
2097          * @param value coefficient value
2098          * @param id coefficient identifier
2099          * @param indices list of coefficient indices
2100          */
2101         private void storeIfSelected(final Map<String, T[]> map, final Set<String> selected,
2102                                      final T[] value, final String id, final int... indices) {
2103             final StringBuilder keyBuilder = new StringBuilder(getCoefficientsKeyPrefix());
2104             keyBuilder.append(id);
2105             for (int index : indices) {
2106                 keyBuilder.append('[').append(index).append(']');
2107             }
2108             final String key = keyBuilder.toString();
2109             if (selected.isEmpty() || selected.contains(key)) {
2110                 map.put(key, value);
2111             }
2112         }
2113     }
2114 
2115     /** Coefficients valid for one time slot. */
2116     private static class Slot {
2117 
2118         /** The coefficients C<sub>i</sub><sup>j</sup><sup>m</sup>.
2119          * <p>
2120          * The index order is cijm[m][j][i] <br/>
2121          * i corresponds to the equinoctial element, as follows: <br/>
2122          * - i=0 for a <br/>
2123          * - i=1 for k <br/>
2124          * - i=2 for h <br/>
2125          * - i=3 for q <br/>
2126          * - i=4 for p <br/>
2127          * - i=5 for λ <br/>
2128          * </p>
2129          */
2130         private final ShortPeriodicsInterpolatedCoefficient[][] cijm;
2131 
2132         /** The coefficients S<sub>i</sub><sup>j</sup><sup>m</sup>.
2133          * <p>
2134          * The index order is sijm[m][j][i] <br/>
2135          * i corresponds to the equinoctial element, as follows: <br/>
2136          * - i=0 for a <br/>
2137          * - i=1 for k <br/>
2138          * - i=2 for h <br/>
2139          * - i=3 for q <br/>
2140          * - i=4 for p <br/>
2141          * - i=5 for λ <br/>
2142          * </p>
2143          */
2144         private final ShortPeriodicsInterpolatedCoefficient[][] sijm;
2145 
2146         /** Simple constructor.
2147          *  @param mMax maximum value for m index
2148          *  @param jMax maximum value for j index
2149          *  @param interpolationPoints number of points used in the interpolation process
2150          */
2151         Slot(final int mMax, final int jMax, final int interpolationPoints) {
2152 
2153             final int rows    = mMax + 1;
2154             final int columns = 2 * jMax + 1;
2155             cijm = new ShortPeriodicsInterpolatedCoefficient[rows][columns];
2156             sijm = new ShortPeriodicsInterpolatedCoefficient[rows][columns];
2157             for (int m = 1; m <= mMax; m++) {
2158                 for (int j = -jMax; j <= jMax; j++) {
2159                     cijm[m][j + jMax] = new ShortPeriodicsInterpolatedCoefficient(interpolationPoints);
2160                     sijm[m][j + jMax] = new ShortPeriodicsInterpolatedCoefficient(interpolationPoints);
2161                 }
2162             }
2163 
2164         }
2165 
2166         /** Get C<sub>i</sub><sup>j</sup><sup>m</sup>.
2167          *
2168          * @param j j index
2169          * @param m m index
2170          * @param date the date
2171          * @return C<sub>i</sub><sup>j</sup><sup>m</sup>
2172          */
2173         double[] getCijm(final int j, final int m, final AbsoluteDate date) {
2174             final int jMax = (cijm[m].length - 1) / 2;
2175             return cijm[m][j + jMax].value(date);
2176         }
2177 
2178         /** Get S<sub>i</sub><sup>j</sup><sup>m</sup>.
2179          *
2180          * @param j j index
2181          * @param m m index
2182          * @param date the date
2183          * @return S<sub>i</sub><sup>j</sup><sup>m</sup>
2184          */
2185         double[] getSijm(final int j, final int m, final AbsoluteDate date) {
2186             final int jMax = (cijm[m].length - 1) / 2;
2187             return sijm[m][j + jMax].value(date);
2188         }
2189 
2190     }
2191 
2192     /** Coefficients valid for one time slot. */
2193     private static class FieldSlot <T extends CalculusFieldElement<T>> {
2194 
2195         /** The coefficients C<sub>i</sub><sup>j</sup><sup>m</sup>.
2196          * <p>
2197          * The index order is cijm[m][j][i] <br/>
2198          * i corresponds to the equinoctial element, as follows: <br/>
2199          * - i=0 for a <br/>
2200          * - i=1 for k <br/>
2201          * - i=2 for h <br/>
2202          * - i=3 for q <br/>
2203          * - i=4 for p <br/>
2204          * - i=5 for λ <br/>
2205          * </p>
2206          */
2207         private final FieldShortPeriodicsInterpolatedCoefficient<T>[][] cijm;
2208 
2209         /** The coefficients S<sub>i</sub><sup>j</sup><sup>m</sup>.
2210          * <p>
2211          * The index order is sijm[m][j][i] <br/>
2212          * i corresponds to the equinoctial element, as follows: <br/>
2213          * - i=0 for a <br/>
2214          * - i=1 for k <br/>
2215          * - i=2 for h <br/>
2216          * - i=3 for q <br/>
2217          * - i=4 for p <br/>
2218          * - i=5 for λ <br/>
2219          * </p>
2220          */
2221         private final FieldShortPeriodicsInterpolatedCoefficient<T>[][] sijm;
2222 
2223         /** Simple constructor.
2224          *  @param mMax maximum value for m index
2225          *  @param jMax maximum value for j index
2226          *  @param interpolationPoints number of points used in the interpolation process
2227          */
2228         @SuppressWarnings("unchecked")
2229         FieldSlot(final int mMax, final int jMax, final int interpolationPoints) {
2230 
2231             final int rows    = mMax + 1;
2232             final int columns = 2 * jMax + 1;
2233             cijm = (FieldShortPeriodicsInterpolatedCoefficient<T>[][]) Array.newInstance(FieldShortPeriodicsInterpolatedCoefficient.class, rows, columns);
2234             sijm = (FieldShortPeriodicsInterpolatedCoefficient<T>[][]) Array.newInstance(FieldShortPeriodicsInterpolatedCoefficient.class, rows, columns);
2235             for (int m = 1; m <= mMax; m++) {
2236                 for (int j = -jMax; j <= jMax; j++) {
2237                     cijm[m][j + jMax] = new FieldShortPeriodicsInterpolatedCoefficient<>(interpolationPoints);
2238                     sijm[m][j + jMax] = new FieldShortPeriodicsInterpolatedCoefficient<>(interpolationPoints);
2239                 }
2240             }
2241 
2242         }
2243 
2244         /** Get C<sub>i</sub><sup>j</sup><sup>m</sup>.
2245          *
2246          * @param j j index
2247          * @param m m index
2248          * @param date the date
2249          * @return C<sub>i</sub><sup>j</sup><sup>m</sup>
2250          */
2251         T[] getCijm(final int j, final int m, final FieldAbsoluteDate<T> date) {
2252             final int jMax = (cijm[m].length - 1) / 2;
2253             return cijm[m][j + jMax].value(date);
2254         }
2255 
2256         /** Get S<sub>i</sub><sup>j</sup><sup>m</sup>.
2257          *
2258          * @param j j index
2259          * @param m m index
2260          * @param date the date
2261          * @return S<sub>i</sub><sup>j</sup><sup>m</sup>
2262          */
2263         T[] getSijm(final int j, final int m, final FieldAbsoluteDate<T> date) {
2264             final int jMax = (cijm[m].length - 1) / 2;
2265             return sijm[m][j + jMax].value(date);
2266         }
2267 
2268     }
2269 
2270     /** Compute potential and potential derivatives with respect to orbital parameters.
2271      *  <p>The following elements are computed from expression 3.3 - (4).
2272      *  <pre>
2273      *  dU / da
2274      *  dU / dh
2275      *  dU / dk
2276      *  dU / dλ
2277      *  dU / dα
2278      *  dU / dβ
2279      *  dU / dγ
2280      *  </pre>
2281      *  </p>
2282      */
2283     private class UAnddU {
2284 
2285         /** dU / da. */
2286         private  double dUda;
2287 
2288         /** dU / dk. */
2289         private double dUdk;
2290 
2291         /** dU / dh. */
2292         private double dUdh;
2293 
2294         /** dU / dl. */
2295         private double dUdl;
2296 
2297         /** dU / dAlpha. */
2298         private double dUdAl;
2299 
2300         /** dU / dBeta. */
2301         private double dUdBe;
2302 
2303         /** dU / dGamma. */
2304         private double dUdGa;
2305 
2306         /** Simple constuctor.
2307          * @param date current date
2308          * @param context container for attributes
2309          * @param hansen hansen objects
2310          */
2311         UAnddU(final AbsoluteDate date, final DSSTTesseralContext context, final HansenObjects hansen) {
2312 
2313             // Auxiliary elements related to the current orbit
2314             final AuxiliaryElements auxiliaryElements = context.getAuxiliaryElements();
2315 
2316             // Potential derivatives
2317             dUda  = 0.;
2318             dUdh  = 0.;
2319             dUdk  = 0.;
2320             dUdl  = 0.;
2321             dUdAl = 0.;
2322             dUdBe = 0.;
2323             dUdGa = 0.;
2324 
2325             // Compute only if there is at least one resonant tesseral
2326             if (!resOrders.isEmpty()) {
2327                 // Gmsj and Hmsj polynomials
2328                 final GHmsjPolynomials ghMSJ = new GHmsjPolynomials(auxiliaryElements.getK(), auxiliaryElements.getH(), auxiliaryElements.getAlpha(), auxiliaryElements.getBeta(), I);
2329 
2330                 // GAMMAmns function
2331                 final GammaMnsFunction gammaMNS = new GammaMnsFunction(maxDegree, auxiliaryElements.getGamma(), I);
2332 
2333                 // R / a up to power degree
2334                 final double[] roaPow = new double[maxDegree + 1];
2335                 roaPow[0] = 1.;
2336                 for (int i = 1; i <= maxDegree; i++) {
2337                     roaPow[i] = context.getRoa() * roaPow[i - 1];
2338                 }
2339 
2340                 // SUM over resonant terms {j,m}
2341                 for (int m : resOrders) {
2342 
2343                     // Resonant index for the current resonant order
2344                     final int j = FastMath.max(1, (int) FastMath.round(context.getRatio() * m));
2345 
2346                     // Phase angle
2347                     final double jlMmt  = j * auxiliaryElements.getLM() - m * context.getTheta();
2348                     final SinCos scPhi  = FastMath.sinCos(jlMmt);
2349                     final double sinPhi = scPhi.sin();
2350                     final double cosPhi = scPhi.cos();
2351 
2352                     // Potential derivatives components for a given resonant pair {j,m}
2353                     double dUdaCos  = 0.;
2354                     double dUdaSin  = 0.;
2355                     double dUdhCos  = 0.;
2356                     double dUdhSin  = 0.;
2357                     double dUdkCos  = 0.;
2358                     double dUdkSin  = 0.;
2359                     double dUdlCos  = 0.;
2360                     double dUdlSin  = 0.;
2361                     double dUdAlCos = 0.;
2362                     double dUdAlSin = 0.;
2363                     double dUdBeCos = 0.;
2364                     double dUdBeSin = 0.;
2365                     double dUdGaCos = 0.;
2366                     double dUdGaSin = 0.;
2367 
2368                     // s-SUM from -sMin to sMax
2369                     final int sMin = FastMath.min(maxEccPow - j, maxDegree);
2370                     final int sMax = FastMath.min(maxEccPow + j, maxDegree);
2371                     for (int s = 0; s <= sMax; s++) {
2372 
2373                         //Compute the initial values for Hansen coefficients using newComb operators
2374                         hansen.computeHansenObjectsInitValues(context, s + maxDegree, j);
2375 
2376                         // n-SUM for s positive
2377                         final double[][] nSumSpos = computeNSum(date, j, m, s, maxDegree,
2378                                                                 roaPow, ghMSJ, gammaMNS, context, hansen);
2379                         dUdaCos  += nSumSpos[0][0];
2380                         dUdaSin  += nSumSpos[0][1];
2381                         dUdhCos  += nSumSpos[1][0];
2382                         dUdhSin  += nSumSpos[1][1];
2383                         dUdkCos  += nSumSpos[2][0];
2384                         dUdkSin  += nSumSpos[2][1];
2385                         dUdlCos  += nSumSpos[3][0];
2386                         dUdlSin  += nSumSpos[3][1];
2387                         dUdAlCos += nSumSpos[4][0];
2388                         dUdAlSin += nSumSpos[4][1];
2389                         dUdBeCos += nSumSpos[5][0];
2390                         dUdBeSin += nSumSpos[5][1];
2391                         dUdGaCos += nSumSpos[6][0];
2392                         dUdGaSin += nSumSpos[6][1];
2393 
2394                         // n-SUM for s negative
2395                         if (s > 0 && s <= sMin) {
2396                             //Compute the initial values for Hansen coefficients using newComb operators
2397                             hansen.computeHansenObjectsInitValues(context, maxDegree - s, j);
2398 
2399                             final double[][] nSumSneg = computeNSum(date, j, m, -s, maxDegree,
2400                                                                     roaPow, ghMSJ, gammaMNS, context, hansen);
2401                             dUdaCos  += nSumSneg[0][0];
2402                             dUdaSin  += nSumSneg[0][1];
2403                             dUdhCos  += nSumSneg[1][0];
2404                             dUdhSin  += nSumSneg[1][1];
2405                             dUdkCos  += nSumSneg[2][0];
2406                             dUdkSin  += nSumSneg[2][1];
2407                             dUdlCos  += nSumSneg[3][0];
2408                             dUdlSin  += nSumSneg[3][1];
2409                             dUdAlCos += nSumSneg[4][0];
2410                             dUdAlSin += nSumSneg[4][1];
2411                             dUdBeCos += nSumSneg[5][0];
2412                             dUdBeSin += nSumSneg[5][1];
2413                             dUdGaCos += nSumSneg[6][0];
2414                             dUdGaSin += nSumSneg[6][1];
2415                         }
2416                     }
2417 
2418                     // Assembly of potential derivatives componants
2419                     dUda  += cosPhi * dUdaCos  + sinPhi * dUdaSin;
2420                     dUdh  += cosPhi * dUdhCos  + sinPhi * dUdhSin;
2421                     dUdk  += cosPhi * dUdkCos  + sinPhi * dUdkSin;
2422                     dUdl  += cosPhi * dUdlCos  + sinPhi * dUdlSin;
2423                     dUdAl += cosPhi * dUdAlCos + sinPhi * dUdAlSin;
2424                     dUdBe += cosPhi * dUdBeCos + sinPhi * dUdBeSin;
2425                     dUdGa += cosPhi * dUdGaCos + sinPhi * dUdGaSin;
2426                 }
2427 
2428                 this.dUda  = dUda * (-context.getMoa() / auxiliaryElements.getSma());
2429                 this.dUdh  = dUdh * context.getMoa();
2430                 this.dUdk  = dUdk * context.getMoa();
2431                 this.dUdl  = dUdl * context.getMoa();
2432                 this.dUdAl = dUdAl * context.getMoa();
2433                 this.dUdBe = dUdBe * context.getMoa();
2434                 this.dUdGa = dUdGa * context.getMoa();
2435             }
2436 
2437         }
2438 
2439         /** Return value of dU / da.
2440          * @return dUda
2441          */
2442         public double getdUda() {
2443             return dUda;
2444         }
2445 
2446         /** Return value of dU / dk.
2447          * @return dUdk
2448          */
2449         public double getdUdk() {
2450             return dUdk;
2451         }
2452 
2453         /** Return value of dU / dh.
2454          * @return dUdh
2455          */
2456         public double getdUdh() {
2457             return dUdh;
2458         }
2459 
2460         /** Return value of dU / dl.
2461          * @return dUdl
2462          */
2463         public double getdUdl() {
2464             return dUdl;
2465         }
2466 
2467         /** Return value of dU / dAlpha.
2468          * @return dUdAl
2469          */
2470         public double getdUdAl() {
2471             return dUdAl;
2472         }
2473 
2474         /** Return value of dU / dBeta.
2475          * @return dUdBe
2476          */
2477         public double getdUdBe() {
2478             return dUdBe;
2479         }
2480 
2481         /** Return value of dU / dGamma.
2482          * @return dUdGa
2483          */
2484         public double getdUdGa() {
2485             return dUdGa;
2486         }
2487 
2488     }
2489 
2490     /**  Computes the potential U derivatives.
2491      *  <p>The following elements are computed from expression 3.3 - (4).
2492      *  <pre>
2493      *  dU / da
2494      *  dU / dh
2495      *  dU / dk
2496      *  dU / dλ
2497      *  dU / dα
2498      *  dU / dβ
2499      *  dU / dγ
2500      *  </pre>
2501      *  </p>
2502      */
2503     private class FieldUAnddU <T extends CalculusFieldElement<T>> {
2504 
2505         /** dU / da. */
2506         private T dUda;
2507 
2508         /** dU / dk. */
2509         private T dUdk;
2510 
2511         /** dU / dh. */
2512         private T dUdh;
2513 
2514         /** dU / dl. */
2515         private T dUdl;
2516 
2517         /** dU / dAlpha. */
2518         private T dUdAl;
2519 
2520         /** dU / dBeta. */
2521         private T dUdBe;
2522 
2523         /** dU / dGamma. */
2524         private T dUdGa;
2525 
2526         /** Simple constuctor.
2527          * @param date current date
2528          * @param context container for attributes
2529          * @param hansen hansen objects
2530          */
2531         FieldUAnddU(final FieldAbsoluteDate<T> date, final FieldDSSTTesseralContext<T> context,
2532                     final FieldHansenObjects<T> hansen) {
2533 
2534             // Auxiliary elements related to the current orbit
2535             final FieldAuxiliaryElements<T> auxiliaryElements = context.getFieldAuxiliaryElements();
2536 
2537             // Zero for initialization
2538             final Field<T> field = date.getField();
2539             final T zero = field.getZero();
2540 
2541             // Potential derivatives
2542             dUda  = zero;
2543             dUdh  = zero;
2544             dUdk  = zero;
2545             dUdl  = zero;
2546             dUdAl = zero;
2547             dUdBe = zero;
2548             dUdGa = zero;
2549 
2550             // Compute only if there is at least one resonant tesseral
2551             if (!resOrders.isEmpty()) {
2552                 // Gmsj and Hmsj polynomials
2553                 final FieldGHmsjPolynomials<T> ghMSJ = new FieldGHmsjPolynomials<>(auxiliaryElements.getK(), auxiliaryElements.getH(), auxiliaryElements.getAlpha(), auxiliaryElements.getBeta(), I, field);
2554 
2555                 // GAMMAmns function
2556                 final FieldGammaMnsFunction<T> gammaMNS = new FieldGammaMnsFunction<>(maxDegree, auxiliaryElements.getGamma(), I, field);
2557 
2558                 // R / a up to power degree
2559                 final T[] roaPow = MathArrays.buildArray(field, maxDegree + 1);
2560                 roaPow[0] = zero.newInstance(1.);
2561                 for (int i = 1; i <= maxDegree; i++) {
2562                     roaPow[i] = roaPow[i - 1].multiply(context.getRoa());
2563                 }
2564 
2565                 // SUM over resonant terms {j,m}
2566                 for (int m : resOrders) {
2567 
2568                     // Resonant index for the current resonant order
2569                     final int j = FastMath.max(1, (int) FastMath.round(context.getRatio().multiply(m)));
2570 
2571                     // Phase angle
2572                     final T jlMmt              = auxiliaryElements.getLM().multiply(j).subtract(context.getTheta().multiply(m));
2573                     final FieldSinCos<T> scPhi = FastMath.sinCos(jlMmt);
2574                     final T sinPhi             = scPhi.sin();
2575                     final T cosPhi             = scPhi.cos();
2576 
2577                     // Potential derivatives components for a given resonant pair {j,m}
2578                     T dUdaCos  = zero;
2579                     T dUdaSin  = zero;
2580                     T dUdhCos  = zero;
2581                     T dUdhSin  = zero;
2582                     T dUdkCos  = zero;
2583                     T dUdkSin  = zero;
2584                     T dUdlCos  = zero;
2585                     T dUdlSin  = zero;
2586                     T dUdAlCos = zero;
2587                     T dUdAlSin = zero;
2588                     T dUdBeCos = zero;
2589                     T dUdBeSin = zero;
2590                     T dUdGaCos = zero;
2591                     T dUdGaSin = zero;
2592 
2593                     // s-SUM from -sMin to sMax
2594                     final int sMin = FastMath.min(maxEccPow - j, maxDegree);
2595                     final int sMax = FastMath.min(maxEccPow + j, maxDegree);
2596                     for (int s = 0; s <= sMax; s++) {
2597 
2598                         //Compute the initial values for Hansen coefficients using newComb operators
2599                         hansen.computeHansenObjectsInitValues(context, s + maxDegree, j);
2600 
2601                         // n-SUM for s positive
2602                         final T[][] nSumSpos = computeNSum(date, j, m, s, maxDegree,
2603                                                                 roaPow, ghMSJ, gammaMNS, context, hansen);
2604                         dUdaCos  = dUdaCos.add(nSumSpos[0][0]);
2605                         dUdaSin  = dUdaSin.add(nSumSpos[0][1]);
2606                         dUdhCos  = dUdhCos.add(nSumSpos[1][0]);
2607                         dUdhSin  = dUdhSin.add(nSumSpos[1][1]);
2608                         dUdkCos  = dUdkCos.add(nSumSpos[2][0]);
2609                         dUdkSin  = dUdkSin.add(nSumSpos[2][1]);
2610                         dUdlCos  = dUdlCos.add(nSumSpos[3][0]);
2611                         dUdlSin  = dUdlSin.add(nSumSpos[3][1]);
2612                         dUdAlCos = dUdAlCos.add(nSumSpos[4][0]);
2613                         dUdAlSin = dUdAlSin.add(nSumSpos[4][1]);
2614                         dUdBeCos = dUdBeCos.add(nSumSpos[5][0]);
2615                         dUdBeSin = dUdBeSin.add(nSumSpos[5][1]);
2616                         dUdGaCos = dUdGaCos.add(nSumSpos[6][0]);
2617                         dUdGaSin = dUdGaSin.add(nSumSpos[6][1]);
2618 
2619                         // n-SUM for s negative
2620                         if (s > 0 && s <= sMin) {
2621                             //Compute the initial values for Hansen coefficients using newComb operators
2622                             hansen.computeHansenObjectsInitValues(context, maxDegree - s, j);
2623 
2624                             final T[][] nSumSneg = computeNSum(date, j, m, -s, maxDegree,
2625                                                                     roaPow, ghMSJ, gammaMNS, context, hansen);
2626                             dUdaCos  = dUdaCos.add(nSumSneg[0][0]);
2627                             dUdaSin  = dUdaSin.add(nSumSneg[0][1]);
2628                             dUdhCos  = dUdhCos.add(nSumSneg[1][0]);
2629                             dUdhSin  = dUdhSin.add(nSumSneg[1][1]);
2630                             dUdkCos  = dUdkCos.add(nSumSneg[2][0]);
2631                             dUdkSin  = dUdkSin.add(nSumSneg[2][1]);
2632                             dUdlCos  = dUdlCos.add(nSumSneg[3][0]);
2633                             dUdlSin  = dUdlSin.add(nSumSneg[3][1]);
2634                             dUdAlCos = dUdAlCos.add(nSumSneg[4][0]);
2635                             dUdAlSin = dUdAlSin.add(nSumSneg[4][1]);
2636                             dUdBeCos = dUdBeCos.add(nSumSneg[5][0]);
2637                             dUdBeSin = dUdBeSin.add(nSumSneg[5][1]);
2638                             dUdGaCos = dUdGaCos.add(nSumSneg[6][0]);
2639                             dUdGaSin = dUdGaSin.add(nSumSneg[6][1]);
2640                         }
2641                     }
2642 
2643                     // Assembly of potential derivatives componants
2644                     dUda  = dUda.add(dUdaCos.multiply(cosPhi).add(dUdaSin.multiply(sinPhi)));
2645                     dUdh  = dUdh.add(dUdhCos.multiply(cosPhi).add(dUdhSin.multiply(sinPhi)));
2646                     dUdk  = dUdk.add(dUdkCos.multiply(cosPhi).add(dUdkSin.multiply(sinPhi)));
2647                     dUdl  = dUdl.add(dUdlCos.multiply(cosPhi).add(dUdlSin.multiply(sinPhi)));
2648                     dUdAl = dUdAl.add(dUdAlCos.multiply(cosPhi).add(dUdAlSin.multiply(sinPhi)));
2649                     dUdBe = dUdBe.add(dUdBeCos.multiply(cosPhi).add(dUdBeSin.multiply(sinPhi)));
2650                     dUdGa = dUdGa.add(dUdGaCos.multiply(cosPhi).add(dUdGaSin.multiply(sinPhi)));
2651                 }
2652 
2653                 dUda  =  dUda.multiply(context.getMoa().divide(auxiliaryElements.getSma())).negate();
2654                 dUdh  =  dUdh.multiply(context.getMoa());
2655                 dUdk  =  dUdk.multiply(context.getMoa());
2656                 dUdl  =  dUdl.multiply(context.getMoa());
2657                 dUdAl =  dUdAl.multiply(context.getMoa());
2658                 dUdBe =  dUdBe.multiply(context.getMoa());
2659                 dUdGa =  dUdGa.multiply(context.getMoa());
2660 
2661             }
2662 
2663         }
2664 
2665         /** Return value of dU / da.
2666          * @return dUda
2667          */
2668         public T getdUda() {
2669             return dUda;
2670         }
2671 
2672         /** Return value of dU / dk.
2673          * @return dUdk
2674          */
2675         public T getdUdk() {
2676             return dUdk;
2677         }
2678 
2679         /** Return value of dU / dh.
2680          * @return dUdh
2681          */
2682         public T getdUdh() {
2683             return dUdh;
2684         }
2685 
2686         /** Return value of dU / dl.
2687          * @return dUdl
2688          */
2689         public T getdUdl() {
2690             return dUdl;
2691         }
2692 
2693         /** Return value of dU / dAlpha.
2694          * @return dUdAl
2695          */
2696         public T getdUdAl() {
2697             return dUdAl;
2698         }
2699 
2700         /** Return value of dU / dBeta.
2701          * @return dUdBe
2702          */
2703         public T getdUdBe() {
2704             return dUdBe;
2705         }
2706 
2707         /** Return value of dU / dGamma.
2708          * @return dUdGa
2709          */
2710         public T getdUdGa() {
2711             return dUdGa;
2712         }
2713 
2714     }
2715 
2716     /** Computes init values of the Hansen Objects. */
2717     private class HansenObjects {
2718 
2719         /** A two dimensional array that contains the objects needed to build the Hansen coefficients. <br/>
2720          * The indexes are s + maxDegree and j */
2721         private HansenTesseralLinear[][] hansenObjects;
2722 
2723         /** Simple constructor.
2724          * @param ratio Ratio of satellite period to central body rotation period
2725          * @param type type of the elements used during the propagation
2726          */
2727         HansenObjects(final double ratio,
2728                       final PropagationType type) {
2729 
2730             //Allocate the two dimensional array
2731             final int rows     = 2 * maxDegree + 1;
2732             final int columns  = maxFrequencyShortPeriodics + 1;
2733             this.hansenObjects = new HansenTesseralLinear[rows][columns];
2734 
2735             switch (type) {
2736                 case MEAN:
2737                     // loop through the resonant orders
2738                     for (int m : resOrders) {
2739                         //Compute the corresponding j term
2740                         final int j = FastMath.max(1, (int) FastMath.round(ratio * m));
2741 
2742                         //Compute the sMin and sMax values
2743                         final int sMin = FastMath.min(maxEccPow - j, maxDegree);
2744                         final int sMax = FastMath.min(maxEccPow + j, maxDegree);
2745 
2746                         //loop through the s values
2747                         for (int s = 0; s <= sMax; s++) {
2748                             //Compute the n0 value
2749                             final int n0 = FastMath.max(FastMath.max(2, m), s);
2750 
2751                             //Create the object for the pair j, s
2752                             this.hansenObjects[s + maxDegree][j] = new HansenTesseralLinear(maxDegree, s, j, n0, maxHansen);
2753 
2754                             if (s > 0 && s <= sMin) {
2755                                 //Also create the object for the pair j, -s
2756                                 this.hansenObjects[maxDegree - s][j] =  new HansenTesseralLinear(maxDegree, -s, j, n0, maxHansen);
2757                             }
2758                         }
2759                     }
2760                     break;
2761 
2762                 case OSCULATING:
2763                     // create all objects
2764                     for (int j = 0; j <= maxFrequencyShortPeriodics; j++) {
2765                         for (int s = -maxDegree; s <= maxDegree; s++) {
2766                             //Compute the n0 value
2767                             final int n0 = FastMath.max(2, FastMath.abs(s));
2768                             this.hansenObjects[s + maxDegree][j] = new HansenTesseralLinear(maxDegree, s, j, n0, maxHansen);
2769                         }
2770                     }
2771                     break;
2772 
2773                 default:
2774                     throw new OrekitInternalError(null);
2775             }
2776 
2777         }
2778 
2779         /** Compute init values for hansen objects.
2780          * @param context container for attributes
2781          * @param rows number of rows of the hansen matrix
2782          * @param columns columns number of columns of the hansen matrix
2783          */
2784         public void computeHansenObjectsInitValues(final DSSTTesseralContext context, final int rows, final int columns) {
2785             hansenObjects[rows][columns].computeInitValues(context.getE2(), context.getChi(), context.getChi2());
2786         }
2787 
2788         /** Get hansen object.
2789          * @return hansenObjects
2790          */
2791         public HansenTesseralLinear[][] getHansenObjects() {
2792             return hansenObjects;
2793         }
2794 
2795     }
2796 
2797     /** Computes init values of the Hansen Objects. */
2798     private class FieldHansenObjects<T extends CalculusFieldElement<T>> {
2799 
2800         /** A two dimensional array that contains the objects needed to build the Hansen coefficients. <br/>
2801          * The indexes are s + maxDegree and j */
2802         private FieldHansenTesseralLinear<T>[][] hansenObjects;
2803 
2804         /** Simple constructor.
2805          * @param ratio Ratio of satellite period to central body rotation period
2806          * @param type type of the elements used during the propagation
2807          */
2808         @SuppressWarnings("unchecked")
2809         FieldHansenObjects(final T ratio,
2810                            final PropagationType type) {
2811 
2812             // Set the maximum power of the eccentricity to use in Hansen coefficient Kernel expansion.
2813             maxHansen = maxEccPow / 2;
2814 
2815             //Allocate the two dimensional array
2816             final int rows     = 2 * maxDegree + 1;
2817             final int columns  = maxFrequencyShortPeriodics + 1;
2818             this.hansenObjects = (FieldHansenTesseralLinear<T>[][]) Array.newInstance(FieldHansenTesseralLinear.class, rows, columns);
2819 
2820             switch (type) {
2821                 case MEAN:
2822                  // loop through the resonant orders
2823                     for (int m : resOrders) {
2824                         //Compute the corresponding j term
2825                         final int j = FastMath.max(1, (int) FastMath.round(ratio.multiply(m)));
2826 
2827                         //Compute the sMin and sMax values
2828                         final int sMin = FastMath.min(maxEccPow - j, maxDegree);
2829                         final int sMax = FastMath.min(maxEccPow + j, maxDegree);
2830 
2831                         //loop through the s values
2832                         for (int s = 0; s <= sMax; s++) {
2833                             //Compute the n0 value
2834                             final int n0 = FastMath.max(FastMath.max(2, m), s);
2835 
2836                             //Create the object for the pair j, s
2837                             this.hansenObjects[s + maxDegree][j] = new FieldHansenTesseralLinear<>(maxDegree, s, j, n0, maxHansen, ratio.getField());
2838 
2839                             if (s > 0 && s <= sMin) {
2840                                 //Also create the object for the pair j, -s
2841                                 this.hansenObjects[maxDegree - s][j] =  new FieldHansenTesseralLinear<>(maxDegree, -s, j, n0, maxHansen, ratio.getField());
2842                             }
2843                         }
2844                     }
2845                     break;
2846 
2847                 case OSCULATING:
2848                     // create all objects
2849                     for (int j = 0; j <= maxFrequencyShortPeriodics; j++) {
2850                         for (int s = -maxDegree; s <= maxDegree; s++) {
2851                             //Compute the n0 value
2852                             final int n0 = FastMath.max(2, FastMath.abs(s));
2853                             this.hansenObjects[s + maxDegree][j] = new FieldHansenTesseralLinear<>(maxDegree, s, j, n0, maxHansen, ratio.getField());
2854                         }
2855                     }
2856                     break;
2857 
2858                 default:
2859                     throw new OrekitInternalError(null);
2860             }
2861 
2862         }
2863 
2864         /** Compute init values for hansen objects.
2865          * @param context container for attributes
2866          * @param rows number of rows of the hansen matrix
2867          * @param columns columns number of columns of the hansen matrix
2868          */
2869         public void computeHansenObjectsInitValues(final FieldDSSTTesseralContext<T> context,
2870                                                    final int rows, final int columns) {
2871             hansenObjects[rows][columns].computeInitValues(context.getE2(), context.getChi(), context.getChi2());
2872         }
2873 
2874         /** Get hansen object.
2875          * @return hansenObjects
2876          */
2877         public FieldHansenTesseralLinear<T>[][] getHansenObjects() {
2878             return hansenObjects;
2879         }
2880 
2881     }
2882 
2883 }