1   /* Copyright 2002-2025 CS GROUP
2    * Licensed to CS GROUP (CS) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * CS licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *   http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.orekit.propagation.semianalytical.dsst.forces;
18  
19  import java.lang.reflect.Array;
20  import java.util.ArrayList;
21  import java.util.Arrays;
22  import java.util.Collections;
23  import java.util.HashMap;
24  import java.util.List;
25  import java.util.Map;
26  import java.util.Set;
27  import java.util.SortedMap;
28  import java.util.TreeMap;
29  
30  import org.hipparchus.CalculusFieldElement;
31  import org.hipparchus.Field;
32  import org.hipparchus.analysis.differentiation.FieldGradient;
33  import org.hipparchus.exception.LocalizedCoreFormats;
34  import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
35  import org.hipparchus.geometry.euclidean.threed.Vector3D;
36  import org.hipparchus.util.FastMath;
37  import org.hipparchus.util.FieldSinCos;
38  import org.hipparchus.util.MathArrays;
39  import org.hipparchus.util.MathUtils;
40  import org.hipparchus.util.SinCos;
41  import org.orekit.attitudes.AttitudeProvider;
42  import org.orekit.errors.OrekitException;
43  import org.orekit.errors.OrekitInternalError;
44  import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider;
45  import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider.UnnormalizedSphericalHarmonics;
46  import org.orekit.frames.FieldStaticTransform;
47  import org.orekit.frames.Frame;
48  import org.orekit.frames.StaticTransform;
49  import org.orekit.orbits.FieldOrbit;
50  import org.orekit.orbits.Orbit;
51  import org.orekit.propagation.FieldSpacecraftState;
52  import org.orekit.propagation.PropagationType;
53  import org.orekit.propagation.SpacecraftState;
54  import org.orekit.propagation.semianalytical.dsst.utilities.AuxiliaryElements;
55  import org.orekit.propagation.semianalytical.dsst.utilities.CoefficientsFactory;
56  import org.orekit.propagation.semianalytical.dsst.utilities.FieldAuxiliaryElements;
57  import org.orekit.propagation.semianalytical.dsst.utilities.FieldGHmsjPolynomials;
58  import org.orekit.propagation.semianalytical.dsst.utilities.FieldGammaMnsFunction;
59  import org.orekit.propagation.semianalytical.dsst.utilities.FieldShortPeriodicsInterpolatedCoefficient;
60  import org.orekit.propagation.semianalytical.dsst.utilities.GHmsjPolynomials;
61  import org.orekit.propagation.semianalytical.dsst.utilities.GammaMnsFunction;
62  import org.orekit.propagation.semianalytical.dsst.utilities.JacobiPolynomials;
63  import org.orekit.propagation.semianalytical.dsst.utilities.ShortPeriodicsInterpolatedCoefficient;
64  import org.orekit.propagation.semianalytical.dsst.utilities.hansen.FieldHansenTesseralLinear;
65  import org.orekit.propagation.semianalytical.dsst.utilities.hansen.HansenTesseralLinear;
66  import org.orekit.time.AbsoluteDate;
67  import org.orekit.time.FieldAbsoluteDate;
68  import org.orekit.utils.FieldTimeSpanMap;
69  import org.orekit.utils.ParameterDriver;
70  import org.orekit.utils.TimeSpanMap;
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   = context.getAlpha() * udu.getdUdGa() - context.getGamma() * udu.getdUdAl();
479         final double UAlphaBeta    = context.getAlpha() * udu.getdUdBe() - context.getBeta()  * udu.getdUdAl();
480         final double UBetaGamma    = context.getBeta() * udu.getdUdGa() - context.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(context.getAlpha()).subtract(udu.getdUdAl().multiply(context.getGamma()));
515         final T UAlphaBeta    = udu.getdUdBe().multiply(context.getAlpha()).subtract(udu.getdUdAl().multiply(context.getBeta()));
516         final T UBetaGamma    = udu.getdUdGa().multiply(context.getBeta()).subtract(udu.getdUdBe().multiply(context.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, context.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, context.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(), context.getAlpha(), context.getBeta(), I);
1199 
1200                 // GAMMAmns function
1201                 gammaMNS = new GammaMnsFunction(maxDegree, context.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             final double muOnA = context.getMuoa();
1303             dRdaCos  *= -muOnA / auxiliaryElements.getSma();
1304             dRdaSin  *= -muOnA / auxiliaryElements.getSma();
1305             dRdhCos  *=  muOnA;
1306             dRdhSin  *=  muOnA;
1307             dRdkCos  *=  muOnA;
1308             dRdkSin  *=  muOnA;
1309             dRdlCos  *=  muOnA;
1310             dRdlSin  *=  muOnA;
1311             dRdAlCos *=  muOnA;
1312             dRdAlSin *=  muOnA;
1313             dRdBeCos *=  muOnA;
1314             dRdBeSin *=  muOnA;
1315             dRdGaCos *=  muOnA;
1316             dRdGaSin *=  muOnA;
1317 
1318             // Compute the cross derivative operator :
1319             final double RAlphaGammaCos   = context.getAlpha() * dRdGaCos - context.getGamma() * dRdAlCos;
1320             final double RAlphaGammaSin   = context.getAlpha() * dRdGaSin - context.getGamma() * dRdAlSin;
1321             final double RAlphaBetaCos    = context.getAlpha() * dRdBeCos - context.getBeta()  * dRdAlCos;
1322             final double RAlphaBetaSin    = context.getAlpha() * dRdBeSin - context.getBeta()  * dRdAlSin;
1323             final double RBetaGammaCos    =  context.getBeta() * dRdGaCos - context.getGamma() * dRdBeCos;
1324             final double RBetaGammaSin    =  context.getBeta() * dRdGaSin - context.getGamma() * dRdBeSin;
1325             final double RhkCos           =     auxiliaryElements.getH() * dRdkCos  -     auxiliaryElements.getK() * dRdhCos;
1326             final double RhkSin           =     auxiliaryElements.getH() * dRdkSin  -     auxiliaryElements.getK() * dRdhSin;
1327             final double pRagmIqRbgoABCos = (auxiliaryElements.getP() * RAlphaGammaCos - I * auxiliaryElements.getQ() * RBetaGammaCos) * context.getOoAB();
1328             final double pRagmIqRbgoABSin = (auxiliaryElements.getP() * RAlphaGammaSin - I * auxiliaryElements.getQ() * RBetaGammaSin) * context.getOoAB();
1329             final double RhkmRabmdRdlCos  =  RhkCos - RAlphaBetaCos - dRdlCos;
1330             final double RhkmRabmdRdlSin  =  RhkSin - RAlphaBetaSin - dRdlSin;
1331 
1332             // da/dt
1333             cCoef[m][j + jMax][0] = context.getAx2oA() * dRdlCos;
1334             sCoef[m][j + jMax][0] = context.getAx2oA() * dRdlSin;
1335 
1336             // dk/dt
1337             cCoef[m][j + jMax][1] = -(context.getBoA() * dRdhCos + auxiliaryElements.getH() * pRagmIqRbgoABCos + auxiliaryElements.getK() * context.getBoABpo() * dRdlCos);
1338             sCoef[m][j + jMax][1] = -(context.getBoA() * dRdhSin + auxiliaryElements.getH() * pRagmIqRbgoABSin + auxiliaryElements.getK() * context.getBoABpo() * dRdlSin);
1339 
1340             // dh/dt
1341             cCoef[m][j + jMax][2] = context.getBoA() * dRdkCos + auxiliaryElements.getK() * pRagmIqRbgoABCos - auxiliaryElements.getH() * context.getBoABpo() * dRdlCos;
1342             sCoef[m][j + jMax][2] = context.getBoA() * dRdkSin + auxiliaryElements.getK() * pRagmIqRbgoABSin - auxiliaryElements.getH() * context.getBoABpo() * dRdlSin;
1343 
1344             // dq/dt
1345             cCoef[m][j + jMax][3] = context.getCo2AB() * (auxiliaryElements.getQ() * RhkmRabmdRdlCos - I * RAlphaGammaCos);
1346             sCoef[m][j + jMax][3] = context.getCo2AB() * (auxiliaryElements.getQ() * RhkmRabmdRdlSin - I * RAlphaGammaSin);
1347 
1348             // dp/dt
1349             cCoef[m][j + jMax][4] = context.getCo2AB() * (auxiliaryElements.getP() * RhkmRabmdRdlCos - RBetaGammaCos);
1350             sCoef[m][j + jMax][4] = context.getCo2AB() * (auxiliaryElements.getP() * RhkmRabmdRdlSin - RBetaGammaSin);
1351 
1352             // dλ/dt
1353             cCoef[m][j + jMax][5] = -context.getAx2oA() * dRdaCos + context.getBoABpo() * (auxiliaryElements.getH() * dRdhCos + auxiliaryElements.getK() * dRdkCos) + pRagmIqRbgoABCos;
1354             sCoef[m][j + jMax][5] = -context.getAx2oA() * dRdaSin + context.getBoABpo() * (auxiliaryElements.getH() * dRdhSin + auxiliaryElements.getK() * dRdkSin) + pRagmIqRbgoABSin;
1355         }
1356 
1357         /** Get the coefficient C<sub>i</sub><sup>jm</sup>.
1358          * @param i i index - corresponds to the required variation
1359          * @param j j index
1360          * @param m m index
1361          * @return the coefficient C<sub>i</sub><sup>jm</sup>
1362          */
1363         public double getCijm(final int i, final int j, final int m) {
1364             return cCoef[m][j + jMax][i];
1365         }
1366 
1367         /** Get the coefficient S<sub>i</sub><sup>jm</sup>.
1368          * @param i i index - corresponds to the required variation
1369          * @param j j index
1370          * @param m m index
1371          * @return the coefficient S<sub>i</sub><sup>jm</sup>
1372          */
1373         public double getSijm(final int i, final int j, final int m) {
1374             return sCoef[m][j + jMax][i];
1375         }
1376     }
1377 
1378     /** Compute the C<sup>j</sup> and the S<sup>j</sup> coefficients.
1379      *  <p>
1380      *  Those coefficients are given in Danielson paper by substituting the
1381      *  disturbing function (2.7.1-16) with m != 0 into (2.2-10)
1382      *  </p>
1383      */
1384     private class FieldFourierCjSjCoefficients <T extends CalculusFieldElement<T>> {
1385 
1386         /** Absolute limit for j ( -jMax <= j <= jMax ).  */
1387         private final int jMax;
1388 
1389         /** The C<sub>i</sub><sup>jm</sup> coefficients.
1390          * <p>
1391          * The index order is [m][j][i] <br/>
1392          * The i index corresponds to the C<sub>i</sub><sup>jm</sup> coefficients used to
1393          * compute the following: <br/>
1394          * - da/dt <br/>
1395          * - dk/dt <br/>
1396          * - dh/dt / dk <br/>
1397          * - dq/dt <br/>
1398          * - dp/dt / dα <br/>
1399          * - dλ/dt / dβ <br/>
1400          * </p>
1401          */
1402         private final T[][][] cCoef;
1403 
1404         /** The S<sub>i</sub><sup>jm</sup> coefficients.
1405          * <p>
1406          * The index order is [m][j][i] <br/>
1407          * The i index corresponds to the C<sub>i</sub><sup>jm</sup> coefficients used to
1408          * compute the following: <br/>
1409          * - da/dt <br/>
1410          * - dk/dt <br/>
1411          * - dh/dt / dk <br/>
1412          * - dq/dt <br/>
1413          * - dp/dt / dα <br/>
1414          * - dλ/dt / dβ <br/>
1415          * </p>
1416          */
1417         private final T[][][] sCoef;
1418 
1419         /** G<sub>ms</sub><sup>j</sup> and H<sub>ms</sub><sup>j</sup> polynomials. */
1420         private FieldGHmsjPolynomials<T> ghMSJ;
1421 
1422         /** &Gamma;<sub>ns</sub><sup>m</sup> function. */
1423         private FieldGammaMnsFunction<T> gammaMNS;
1424 
1425         /** R / a up to power degree. */
1426         private final T[] roaPow;
1427 
1428         /** Create a set of C<sub>i</sub><sup>jm</sup> and S<sub>i</sub><sup>jm</sup> coefficients.
1429          *  @param jMax absolute limit for j ( -jMax <= j <= jMax )
1430          *  @param mMax maximum value for m
1431          *  @param field field used by default
1432          */
1433         FieldFourierCjSjCoefficients(final int jMax, final int mMax, final Field<T> field) {
1434             // initialise fields
1435             final T zero = field.getZero();
1436             final int rows    = mMax + 1;
1437             final int columns = 2 * jMax + 1;
1438             this.jMax         = jMax;
1439             this.cCoef        = MathArrays.buildArray(field, rows, columns, 6);
1440             this.sCoef        = MathArrays.buildArray(field, rows, columns, 6);
1441             this.roaPow       = MathArrays.buildArray(field, maxDegree + 1);
1442             roaPow[0] = zero.newInstance(1.);
1443         }
1444 
1445         /**
1446          * Generate the coefficients.
1447          * @param date the current date
1448          * @param context container for attributes
1449          * @param hansenObjects initialization of hansen objects
1450          * @param field field used by default
1451          */
1452         public void generateCoefficients(final FieldAbsoluteDate<T> date,
1453                                          final FieldDSSTTesseralContext<T> context,
1454                                          final FieldHansenObjects<T> hansenObjects,
1455                                          final Field<T> field) {
1456 
1457             final FieldAuxiliaryElements<T> auxiliaryElements = context.getFieldAuxiliaryElements();
1458             // Compute only if there is at least one non-resonant tesseral
1459             if (!nonResOrders.isEmpty() || maxDegreeTesseralSP < 0) {
1460                 // Gmsj and Hmsj polynomials
1461                 ghMSJ = new FieldGHmsjPolynomials<>(auxiliaryElements.getK(), auxiliaryElements.getH(), context.getAlpha(), context.getBeta(), I, field);
1462 
1463                 // GAMMAmns function
1464                 gammaMNS = new FieldGammaMnsFunction<>(maxDegree, context.getGamma(), I, field);
1465 
1466                 final int maxRoaPower = FastMath.max(maxDegreeTesseralSP, maxDegreeMdailyTesseralSP);
1467 
1468                 // R / a up to power degree
1469                 for (int i = 1; i <= maxRoaPower; i++) {
1470                     roaPow[i] = context.getRoa().multiply(roaPow[i - 1]);
1471                 }
1472 
1473                 //generate the m-daily coefficients
1474                 for (int m = 1; m <= maxOrderMdailyTesseralSP; m++) {
1475                     buildFourierCoefficients(date, m, 0, maxDegreeMdailyTesseralSP, context, hansenObjects, field);
1476                 }
1477 
1478                 // generate the other coefficients only if required
1479                 if (maxDegreeTesseralSP >= 0) {
1480                     for (int m: nonResOrders.keySet()) {
1481                         final List<Integer> listJ = nonResOrders.get(m);
1482 
1483                         for (int j: listJ) {
1484                             buildFourierCoefficients(date, m, j, maxDegreeTesseralSP, context, hansenObjects, field);
1485                         }
1486                     }
1487                 }
1488             }
1489         }
1490 
1491         /** Build a set of fourier coefficients for a given m and j.
1492          *
1493          * @param date the date of the coefficients
1494          * @param m m index
1495          * @param j j index
1496          * @param maxN  maximum value for n index
1497          * @param context container for attributes
1498          * @param hansenObjects initialization of hansen objects
1499          * @param field field used by default
1500          */
1501         private void buildFourierCoefficients(final FieldAbsoluteDate<T> date,
1502                                               final int m, final int j, final int maxN,
1503                                               final FieldDSSTTesseralContext<T> context,
1504                                               final FieldHansenObjects<T> hansenObjects,
1505                                               final Field<T> field) {
1506 
1507             // Zero
1508             final T zero = field.getZero();
1509             // Common parameters
1510             final FieldAuxiliaryElements<T> auxiliaryElements = context.getFieldAuxiliaryElements();
1511 
1512             // Potential derivatives components for a given non-resonant pair {j,m}
1513             T dRdaCos  = zero;
1514             T dRdaSin  = zero;
1515             T dRdhCos  = zero;
1516             T dRdhSin  = zero;
1517             T dRdkCos  = zero;
1518             T dRdkSin  = zero;
1519             T dRdlCos  = zero;
1520             T dRdlSin  = zero;
1521             T dRdAlCos = zero;
1522             T dRdAlSin = zero;
1523             T dRdBeCos = zero;
1524             T dRdBeSin = zero;
1525             T dRdGaCos = zero;
1526             T dRdGaSin = zero;
1527 
1528             // s-SUM from -sMin to sMax
1529             final int sMin = j == 0 ? maxEccPowMdailyTesseralSP : maxEccPowTesseralSP;
1530             final int sMax = j == 0 ? maxEccPowMdailyTesseralSP : maxEccPowTesseralSP;
1531             for (int s = 0; s <= sMax; s++) {
1532 
1533                 // n-SUM for s positive
1534                 final T[][] nSumSpos = computeNSum(date, j, m, s, maxN,
1535                                                         roaPow, ghMSJ, gammaMNS, context, hansenObjects);
1536                 dRdaCos  =  dRdaCos.add(nSumSpos[0][0]);
1537                 dRdaSin  =  dRdaSin.add(nSumSpos[0][1]);
1538                 dRdhCos  =  dRdhCos.add(nSumSpos[1][0]);
1539                 dRdhSin  =  dRdhSin.add(nSumSpos[1][1]);
1540                 dRdkCos  =  dRdkCos.add(nSumSpos[2][0]);
1541                 dRdkSin  =  dRdkSin.add(nSumSpos[2][1]);
1542                 dRdlCos  =  dRdlCos.add(nSumSpos[3][0]);
1543                 dRdlSin  =  dRdlSin.add(nSumSpos[3][1]);
1544                 dRdAlCos = dRdAlCos.add(nSumSpos[4][0]);
1545                 dRdAlSin = dRdAlSin.add(nSumSpos[4][1]);
1546                 dRdBeCos = dRdBeCos.add(nSumSpos[5][0]);
1547                 dRdBeSin = dRdBeSin.add(nSumSpos[5][1]);
1548                 dRdGaCos = dRdGaCos.add(nSumSpos[6][0]);
1549                 dRdGaSin = dRdGaSin.add(nSumSpos[6][1]);
1550 
1551                 // n-SUM for s negative
1552                 if (s > 0 && s <= sMin) {
1553                     final T[][] nSumSneg = computeNSum(date, j, m, -s, maxN,
1554                                                             roaPow, ghMSJ, gammaMNS, context, hansenObjects);
1555                     dRdaCos  =  dRdaCos.add(nSumSneg[0][0]);
1556                     dRdaSin  =  dRdaSin.add(nSumSneg[0][1]);
1557                     dRdhCos  =  dRdhCos.add(nSumSneg[1][0]);
1558                     dRdhSin  =  dRdhSin.add(nSumSneg[1][1]);
1559                     dRdkCos  =  dRdkCos.add(nSumSneg[2][0]);
1560                     dRdkSin  =  dRdkSin.add(nSumSneg[2][1]);
1561                     dRdlCos  =  dRdlCos.add(nSumSneg[3][0]);
1562                     dRdlSin  =  dRdlSin.add(nSumSneg[3][1]);
1563                     dRdAlCos = dRdAlCos.add(nSumSneg[4][0]);
1564                     dRdAlSin = dRdAlSin.add(nSumSneg[4][1]);
1565                     dRdBeCos = dRdBeCos.add(nSumSneg[5][0]);
1566                     dRdBeSin = dRdBeSin.add(nSumSneg[5][1]);
1567                     dRdGaCos = dRdGaCos.add(nSumSneg[6][0]);
1568                     dRdGaSin = dRdGaSin.add(nSumSneg[6][1]);
1569                 }
1570             }
1571             final T muOnA = context.getMuoa();
1572             dRdaCos  =  dRdaCos.multiply(muOnA.negate().divide(auxiliaryElements.getSma()));
1573             dRdaSin  =  dRdaSin.multiply(muOnA.negate().divide(auxiliaryElements.getSma()));
1574             dRdhCos  =  dRdhCos.multiply(muOnA);
1575             dRdhSin  =  dRdhSin.multiply(muOnA);
1576             dRdkCos  =  dRdkCos.multiply(muOnA);
1577             dRdkSin  =  dRdkSin.multiply(muOnA);
1578             dRdlCos  =  dRdlCos.multiply(muOnA);
1579             dRdlSin  =  dRdlSin.multiply(muOnA);
1580             dRdAlCos = dRdAlCos.multiply(muOnA);
1581             dRdAlSin = dRdAlSin.multiply(muOnA);
1582             dRdBeCos = dRdBeCos.multiply(muOnA);
1583             dRdBeSin = dRdBeSin.multiply(muOnA);
1584             dRdGaCos = dRdGaCos.multiply(muOnA);
1585             dRdGaSin = dRdGaSin.multiply(muOnA);
1586 
1587             // Compute the cross derivative operator :
1588             final T RAlphaGammaCos   = context.getAlpha().multiply(dRdGaCos).subtract(context.getGamma().multiply(dRdAlCos));
1589             final T RAlphaGammaSin   = context.getAlpha().multiply(dRdGaSin).subtract(context.getGamma().multiply(dRdAlSin));
1590             final T RAlphaBetaCos    = context.getAlpha().multiply(dRdBeCos).subtract(context.getBeta().multiply(dRdAlCos));
1591             final T RAlphaBetaSin    = context.getAlpha().multiply(dRdBeSin).subtract(context.getBeta().multiply(dRdAlSin));
1592             final T RBetaGammaCos    =  context.getBeta().multiply(dRdGaCos).subtract(context.getGamma().multiply(dRdBeCos));
1593             final T RBetaGammaSin    =  context.getBeta().multiply(dRdGaSin).subtract(context.getGamma().multiply(dRdBeSin));
1594             final T RhkCos           =     auxiliaryElements.getH().multiply(dRdkCos).subtract(auxiliaryElements.getK().multiply(dRdhCos));
1595             final T RhkSin           =     auxiliaryElements.getH().multiply(dRdkSin).subtract(auxiliaryElements.getK().multiply(dRdhSin));
1596             final T pRagmIqRbgoABCos = (auxiliaryElements.getP().multiply(RAlphaGammaCos).subtract(auxiliaryElements.getQ().multiply(RBetaGammaCos).multiply(I))).multiply(context.getOoAB());
1597             final T pRagmIqRbgoABSin = (auxiliaryElements.getP().multiply(RAlphaGammaSin).subtract(auxiliaryElements.getQ().multiply(RBetaGammaSin).multiply(I))).multiply(context.getOoAB());
1598             final T RhkmRabmdRdlCos  =  RhkCos.subtract(RAlphaBetaCos).subtract(dRdlCos);
1599             final T RhkmRabmdRdlSin  =  RhkSin.subtract(RAlphaBetaSin).subtract(dRdlSin);
1600 
1601             // da/dt
1602             cCoef[m][j + jMax][0] = context.getAx2oA().multiply(dRdlCos);
1603             sCoef[m][j + jMax][0] = context.getAx2oA().multiply(dRdlSin);
1604 
1605             // dk/dt
1606             cCoef[m][j + jMax][1] = (context.getBoA().multiply(dRdhCos).add(auxiliaryElements.getH().multiply(pRagmIqRbgoABCos)).add(auxiliaryElements.getK().multiply(context.getBoABpo()).multiply(dRdlCos))).negate();
1607             sCoef[m][j + jMax][1] = (context.getBoA().multiply(dRdhSin).add(auxiliaryElements.getH().multiply(pRagmIqRbgoABSin)).add(auxiliaryElements.getK().multiply(context.getBoABpo()).multiply(dRdlSin))).negate();
1608 
1609             // dh/dt
1610             cCoef[m][j + jMax][2] = context.getBoA().multiply(dRdkCos).add(auxiliaryElements.getK().multiply(pRagmIqRbgoABCos)).subtract(auxiliaryElements.getH().multiply(context.getBoABpo()).multiply(dRdlCos));
1611             sCoef[m][j + jMax][2] = context.getBoA().multiply(dRdkSin).add(auxiliaryElements.getK().multiply(pRagmIqRbgoABSin)).subtract(auxiliaryElements.getH().multiply(context.getBoABpo()).multiply(dRdlSin));
1612 
1613             // dq/dt
1614             cCoef[m][j + jMax][3] = context.getCo2AB().multiply(auxiliaryElements.getQ().multiply(RhkmRabmdRdlCos).subtract(RAlphaGammaCos.multiply(I)));
1615             sCoef[m][j + jMax][3] = context.getCo2AB().multiply(auxiliaryElements.getQ().multiply(RhkmRabmdRdlSin).subtract(RAlphaGammaSin.multiply(I)));
1616 
1617             // dp/dt
1618             cCoef[m][j + jMax][4] = context.getCo2AB().multiply(auxiliaryElements.getP().multiply(RhkmRabmdRdlCos).subtract(RBetaGammaCos));
1619             sCoef[m][j + jMax][4] = context.getCo2AB().multiply(auxiliaryElements.getP().multiply(RhkmRabmdRdlSin).subtract(RBetaGammaSin));
1620 
1621             // dλ/dt
1622             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);
1623             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);
1624         }
1625 
1626         /** Get the coefficient C<sub>i</sub><sup>jm</sup>.
1627          * @param i i index - corresponds to the required variation
1628          * @param j j index
1629          * @param m m index
1630          * @return the coefficient C<sub>i</sub><sup>jm</sup>
1631          */
1632         public T getCijm(final int i, final int j, final int m) {
1633             return cCoef[m][j + jMax][i];
1634         }
1635 
1636         /** Get the coefficient S<sub>i</sub><sup>jm</sup>.
1637          * @param i i index - corresponds to the required variation
1638          * @param j j index
1639          * @param m m index
1640          * @return the coefficient S<sub>i</sub><sup>jm</sup>
1641          */
1642         public T getSijm(final int i, final int j, final int m) {
1643             return sCoef[m][j + jMax][i];
1644         }
1645     }
1646 
1647     /** 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
1648      * the short-periodic zonal contribution.
1649      *   <p>
1650      *  Those coefficients are given by expression 2.5.4-5 from the Danielson paper.
1651      *   </p>
1652      *
1653      * @author Sorin Scortan
1654      *
1655      */
1656     private static class TesseralShortPeriodicCoefficients implements ShortPeriodTerms {
1657 
1658         /** Retrograde factor I.
1659          *  <p>
1660          *  DSST model needs equinoctial orbit as internal representation.
1661          *  Classical equinoctial elements have discontinuities when inclination
1662          *  is close to zero. In this representation, I = +1. <br>
1663          *  To avoid this discontinuity, another representation exists and equinoctial
1664          *  elements can be expressed in a different way, called "retrograde" orbit.
1665          *  This implies I = -1. <br>
1666          *  As Orekit doesn't implement the retrograde orbit, I is always set to +1.
1667          *  But for the sake of consistency with the theory, the retrograde factor
1668          *  has been kept in the formulas.
1669          *  </p>
1670          */
1671         private static final int I = 1;
1672 
1673         /** Central body rotating frame. */
1674         private final Frame bodyFrame;
1675 
1676         /** Maximal order to consider for short periodics m-daily tesseral harmonics potential. */
1677         private final int maxOrderMdailyTesseralSP;
1678 
1679         /** Flag to take into account only M-dailies harmonic tesserals for short periodic perturbations.  */
1680         private final boolean mDailiesOnly;
1681 
1682         /** List of non resonant orders with j != 0. */
1683         private final SortedMap<Integer, List<Integer> > nonResOrders;
1684 
1685         /** Maximum value for m index. */
1686         private final int mMax;
1687 
1688         /** Maximum value for j index. */
1689         private final int jMax;
1690 
1691         /** Number of points used in the interpolation process. */
1692         private final int interpolationPoints;
1693 
1694         /** All coefficients slots. */
1695         private final transient TimeSpanMap<Slot> slots;
1696 
1697         /** Constructor.
1698          * @param bodyFrame central body rotating frame
1699          * @param maxOrderMdailyTesseralSP maximal order to consider for short periodics m-daily tesseral harmonics potential
1700          * @param mDailiesOnly flag to take into account only M-dailies harmonic tesserals for short periodic perturbations
1701          * @param nonResOrders lst of non resonant orders with j != 0
1702          * @param mMax maximum value for m index
1703          * @param jMax maximum value for j index
1704          * @param interpolationPoints number of points used in the interpolation process
1705          * @param slots all coefficients slots
1706          */
1707         TesseralShortPeriodicCoefficients(final Frame bodyFrame, final int maxOrderMdailyTesseralSP,
1708                                           final boolean mDailiesOnly, final SortedMap<Integer, List<Integer> > nonResOrders,
1709                                           final int mMax, final int jMax, final int interpolationPoints,
1710                                           final TimeSpanMap<Slot> slots) {
1711             this.bodyFrame                = bodyFrame;
1712             this.maxOrderMdailyTesseralSP = maxOrderMdailyTesseralSP;
1713             this.mDailiesOnly             = mDailiesOnly;
1714             this.nonResOrders             = nonResOrders;
1715             this.mMax                     = mMax;
1716             this.jMax                     = jMax;
1717             this.interpolationPoints      = interpolationPoints;
1718             this.slots                    = slots;
1719         }
1720 
1721         /** Get the slot valid for some date.
1722          * @param meanStates mean states defining the slot
1723          * @return slot valid at the specified date
1724          */
1725         public Slot createSlot(final SpacecraftState... meanStates) {
1726             final Slot         slot  = new Slot(mMax, jMax, interpolationPoints);
1727             final AbsoluteDate first = meanStates[0].getDate();
1728             final AbsoluteDate last  = meanStates[meanStates.length - 1].getDate();
1729             final int compare = first.compareTo(last);
1730             if (compare < 0) {
1731                 slots.addValidAfter(slot, first, false);
1732             } else if (compare > 0) {
1733                 slots.addValidBefore(slot, first, false);
1734             } else {
1735                 // single date, valid for all time
1736                 slots.addValidAfter(slot, AbsoluteDate.PAST_INFINITY, false);
1737             }
1738             return slot;
1739         }
1740 
1741         /** {@inheritDoc} */
1742         @Override
1743         public double[] value(final Orbit meanOrbit) {
1744 
1745             // select the coefficients slot
1746             final Slot slot = slots.get(meanOrbit.getDate());
1747 
1748             // Initialise the short periodic variations
1749             final double[] shortPeriodicVariation = new double[6];
1750 
1751             // Compute only if there is at least one non-resonant tesseral or
1752             // only the m-daily tesseral should be taken into account
1753             if (!nonResOrders.isEmpty() || mDailiesOnly) {
1754 
1755                 //Build an auxiliary object
1756                 final AuxiliaryElements auxiliaryElements = new AuxiliaryElements(meanOrbit, I);
1757 
1758                 // Central body rotation angle from equation 2.7.1-(3)(4).
1759                 final StaticTransform t = bodyFrame.getStaticTransformTo(
1760                         auxiliaryElements.getFrame(),
1761                         auxiliaryElements.getDate());
1762                 final Vector3D xB = t.transformVector(Vector3D.PLUS_I);
1763                 final Vector3D yB = t.transformVector(Vector3D.PLUS_J);
1764                 final Vector3D  f = auxiliaryElements.getVectorF();
1765                 final Vector3D  g = auxiliaryElements.getVectorG();
1766                 final double currentTheta = FastMath.atan2(-f.dotProduct(yB) + I * g.dotProduct(xB),
1767                                                             f.dotProduct(xB) + I * g.dotProduct(yB));
1768 
1769                 //Add the m-daily contribution
1770                 for (int m = 1; m <= maxOrderMdailyTesseralSP; m++) {
1771                     // Phase angle
1772                     final double jlMmt  = -m * currentTheta;
1773                     final SinCos scPhi  = FastMath.sinCos(jlMmt);
1774                     final double sinPhi = scPhi.sin();
1775                     final double cosPhi = scPhi.cos();
1776 
1777                     // compute contribution for each element
1778                     final double[] c = slot.getCijm(0, m, meanOrbit.getDate());
1779                     final double[] s = slot.getSijm(0, m, meanOrbit.getDate());
1780                     for (int i = 0; i < 6; i++) {
1781                         shortPeriodicVariation[i] += c[i] * cosPhi + s[i] * sinPhi;
1782                     }
1783                 }
1784 
1785                 // loop through all non-resonant (j,m) pairs
1786                 for (final Map.Entry<Integer, List<Integer>> entry : nonResOrders.entrySet()) {
1787                     final int           m     = entry.getKey();
1788                     final List<Integer> listJ = entry.getValue();
1789 
1790                     for (int j : listJ) {
1791                         // Phase angle
1792                         final double jlMmt  = j * meanOrbit.getLM() - m * currentTheta;
1793                         final SinCos scPhi  = FastMath.sinCos(jlMmt);
1794                         final double sinPhi = scPhi.sin();
1795                         final double cosPhi = scPhi.cos();
1796 
1797                         // compute contribution for each element
1798                         final double[] c = slot.getCijm(j, m, meanOrbit.getDate());
1799                         final double[] s = slot.getSijm(j, m, meanOrbit.getDate());
1800                         for (int i = 0; i < 6; i++) {
1801                             shortPeriodicVariation[i] += c[i] * cosPhi + s[i] * sinPhi;
1802                         }
1803 
1804                     }
1805                 }
1806             }
1807 
1808             return shortPeriodicVariation;
1809 
1810         }
1811 
1812         /** {@inheritDoc} */
1813         @Override
1814         public String getCoefficientsKeyPrefix() {
1815             return DSSTTesseral.SHORT_PERIOD_PREFIX;
1816         }
1817 
1818         /** {@inheritDoc}
1819          * <p>
1820          * For tesseral terms contributions,there are maxOrderMdailyTesseralSP
1821          * m-daily cMm coefficients, maxOrderMdailyTesseralSP m-daily sMm
1822          * coefficients, nbNonResonant cjm coefficients and nbNonResonant
1823          * sjm coefficients, where maxOrderMdailyTesseralSP and nbNonResonant both
1824          * depend on the orbit. The j index is the integer multiplier for the true
1825          * longitude argument and the m index is the integer multiplier for m-dailies.
1826          * </p>
1827          */
1828         @Override
1829         public Map<String, double[]> getCoefficients(final AbsoluteDate date, final Set<String> selected) {
1830 
1831             // select the coefficients slot
1832             final Slot slot = slots.get(date);
1833 
1834             if (!nonResOrders.isEmpty() || mDailiesOnly) {
1835                 final Map<String, double[]> coefficients = new HashMap<>(12 * maxOrderMdailyTesseralSP + 12 * nonResOrders.size());
1836 
1837                 for (int m = 1; m <= maxOrderMdailyTesseralSP; m++) {
1838                     storeIfSelected(coefficients, selected, slot.getCijm(0, m, date), DSSTTesseral.CM_COEFFICIENTS, m);
1839                     storeIfSelected(coefficients, selected, slot.getSijm(0, m, date), DSSTTesseral.SM_COEFFICIENTS, m);
1840                 }
1841 
1842                 for (final Map.Entry<Integer, List<Integer>> entry : nonResOrders.entrySet()) {
1843                     final int           m     = entry.getKey();
1844                     final List<Integer> listJ = entry.getValue();
1845 
1846                     for (int j : listJ) {
1847                         for (int i = 0; i < 6; ++i) {
1848                             storeIfSelected(coefficients, selected, slot.getCijm(j, m, date), "c", j, m);
1849                             storeIfSelected(coefficients, selected, slot.getSijm(j, m, date), "s", j, m);
1850                         }
1851                     }
1852                 }
1853 
1854                 return coefficients;
1855 
1856             } else {
1857                 return Collections.emptyMap();
1858             }
1859 
1860         }
1861 
1862         /** Put a coefficient in a map if selected.
1863          * @param map map to populate
1864          * @param selected set of coefficients that should be put in the map
1865          * (empty set means all coefficients are selected)
1866          * @param value coefficient value
1867          * @param id coefficient identifier
1868          * @param indices list of coefficient indices
1869          */
1870         private void storeIfSelected(final Map<String, double[]> map, final Set<String> selected,
1871                                      final double[] value, final String id, final int... indices) {
1872             final StringBuilder keyBuilder = new StringBuilder(getCoefficientsKeyPrefix());
1873             keyBuilder.append(id);
1874             for (int index : indices) {
1875                 keyBuilder.append('[').append(index).append(']');
1876             }
1877             final String key = keyBuilder.toString();
1878             if (selected.isEmpty() || selected.contains(key)) {
1879                 map.put(key, value);
1880             }
1881         }
1882 
1883     }
1884 
1885     /** 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
1886      * the short-periodic zonal contribution.
1887      *   <p>
1888      *  Those coefficients are given by expression 2.5.4-5 from the Danielson paper.
1889      *   </p>
1890      *
1891      * @author Sorin Scortan
1892      *
1893      */
1894     private static class FieldTesseralShortPeriodicCoefficients <T extends CalculusFieldElement<T>> implements FieldShortPeriodTerms<T> {
1895 
1896         /** Retrograde factor I.
1897          *  <p>
1898          *  DSST model needs equinoctial orbit as internal representation.
1899          *  Classical equinoctial elements have discontinuities when inclination
1900          *  is close to zero. In this representation, I = +1. <br>
1901          *  To avoid this discontinuity, another representation exists and equinoctial
1902          *  elements can be expressed in a different way, called "retrograde" orbit.
1903          *  This implies I = -1. <br>
1904          *  As Orekit doesn't implement the retrograde orbit, I is always set to +1.
1905          *  But for the sake of consistency with the theory, the retrograde factor
1906          *  has been kept in the formulas.
1907          *  </p>
1908          */
1909         private static final int I = 1;
1910 
1911         /** Central body rotating frame. */
1912         private final Frame bodyFrame;
1913 
1914         /** Maximal order to consider for short periodics m-daily tesseral harmonics potential. */
1915         private final int maxOrderMdailyTesseralSP;
1916 
1917         /** Flag to take into account only M-dailies harmonic tesserals for short periodic perturbations.  */
1918         private final boolean mDailiesOnly;
1919 
1920         /** List of non resonant orders with j != 0. */
1921         private final SortedMap<Integer, List<Integer> > nonResOrders;
1922 
1923         /** Maximum value for m index. */
1924         private final int mMax;
1925 
1926         /** Maximum value for j index. */
1927         private final int jMax;
1928 
1929         /** Number of points used in the interpolation process. */
1930         private final int interpolationPoints;
1931 
1932         /** All coefficients slots. */
1933         private final transient FieldTimeSpanMap<FieldSlot<T>, T> slots;
1934 
1935         /** Constructor.
1936          * @param bodyFrame central body rotating frame
1937          * @param maxOrderMdailyTesseralSP maximal order to consider for short periodics m-daily tesseral harmonics potential
1938          * @param mDailiesOnly flag to take into account only M-dailies harmonic tesserals for short periodic perturbations
1939          * @param nonResOrders lst of non resonant orders with j != 0
1940          * @param mMax maximum value for m index
1941          * @param jMax maximum value for j index
1942          * @param interpolationPoints number of points used in the interpolation process
1943          * @param slots all coefficients slots
1944          */
1945         FieldTesseralShortPeriodicCoefficients(final Frame bodyFrame, final int maxOrderMdailyTesseralSP,
1946                                                final boolean mDailiesOnly, final SortedMap<Integer, List<Integer> > nonResOrders,
1947                                                final int mMax, final int jMax, final int interpolationPoints,
1948                                                final FieldTimeSpanMap<FieldSlot<T>, T> slots) {
1949             this.bodyFrame                = bodyFrame;
1950             this.maxOrderMdailyTesseralSP = maxOrderMdailyTesseralSP;
1951             this.mDailiesOnly             = mDailiesOnly;
1952             this.nonResOrders             = nonResOrders;
1953             this.mMax                     = mMax;
1954             this.jMax                     = jMax;
1955             this.interpolationPoints      = interpolationPoints;
1956             this.slots                    = slots;
1957         }
1958 
1959         /** Get the slot valid for some date.
1960          * @param meanStates mean states defining the slot
1961          * @return slot valid at the specified date
1962          */
1963         @SuppressWarnings("unchecked")
1964         public FieldSlot<T> createSlot(final FieldSpacecraftState<T>... meanStates) {
1965             final FieldSlot<T>         slot  = new FieldSlot<>(mMax, jMax, interpolationPoints);
1966             final FieldAbsoluteDate<T> first = meanStates[0].getDate();
1967             final FieldAbsoluteDate<T> last  = meanStates[meanStates.length - 1].getDate();
1968             if (first.compareTo(last) <= 0) {
1969                 slots.addValidAfter(slot, first);
1970             } else {
1971                 slots.addValidBefore(slot, first);
1972             }
1973             return slot;
1974         }
1975 
1976         /** {@inheritDoc} */
1977         @Override
1978         public T[] value(final FieldOrbit<T> meanOrbit) {
1979 
1980             // select the coefficients slot
1981             final FieldSlot<T> slot = slots.get(meanOrbit.getDate());
1982 
1983             // Initialise the short periodic variations
1984             final T[] shortPeriodicVariation = MathArrays.buildArray(meanOrbit.getDate().getField(), 6);
1985 
1986             // Compute only if there is at least one non-resonant tesseral or
1987             // only the m-daily tesseral should be taken into account
1988             if (!nonResOrders.isEmpty() || mDailiesOnly) {
1989 
1990                 //Build an auxiliary object
1991                 final FieldAuxiliaryElements<T> auxiliaryElements = new FieldAuxiliaryElements<>(meanOrbit, I);
1992 
1993                 // Central body rotation angle from equation 2.7.1-(3)(4).
1994                 final FieldStaticTransform<T> t = bodyFrame.getStaticTransformTo(auxiliaryElements.getFrame(), auxiliaryElements.getDate());
1995                 final FieldVector3D<T> xB = t.transformVector(Vector3D.PLUS_I);
1996                 final FieldVector3D<T> yB = t.transformVector(Vector3D.PLUS_J);
1997                 final FieldVector3D<T>  f = auxiliaryElements.getVectorF();
1998                 final FieldVector3D<T>  g = auxiliaryElements.getVectorG();
1999                 final T currentTheta = FastMath.atan2(f.dotProduct(yB).negate().add(g.dotProduct(xB).multiply(I)),
2000                                                       f.dotProduct(xB).add(g.dotProduct(yB).multiply(I)));
2001 
2002                 //Add the m-daily contribution
2003                 for (int m = 1; m <= maxOrderMdailyTesseralSP; m++) {
2004                     // Phase angle
2005                     final T jlMmt              = currentTheta.multiply(-m);
2006                     final FieldSinCos<T> scPhi = FastMath.sinCos(jlMmt);
2007                     final T sinPhi             = scPhi.sin();
2008                     final T cosPhi             = scPhi.cos();
2009 
2010                     // compute contribution for each element
2011                     final T[] c = slot.getCijm(0, m, meanOrbit.getDate());
2012                     final T[] s = slot.getSijm(0, m, meanOrbit.getDate());
2013                     for (int i = 0; i < 6; i++) {
2014                         shortPeriodicVariation[i] = shortPeriodicVariation[i].add(c[i].multiply(cosPhi).add(s[i].multiply(sinPhi)));
2015                     }
2016                 }
2017 
2018                 // loop through all non-resonant (j,m) pairs
2019                 for (final Map.Entry<Integer, List<Integer>> entry : nonResOrders.entrySet()) {
2020                     final int           m     = entry.getKey();
2021                     final List<Integer> listJ = entry.getValue();
2022 
2023                     for (int j : listJ) {
2024                         // Phase angle
2025                         final T jlMmt              = meanOrbit.getLM().multiply(j).subtract(currentTheta.multiply(m));
2026                         final FieldSinCos<T> scPhi = FastMath.sinCos(jlMmt);
2027                         final T sinPhi             = scPhi.sin();
2028                         final T cosPhi             = scPhi.cos();
2029 
2030                         // compute contribution for each element
2031                         final T[] c = slot.getCijm(j, m, meanOrbit.getDate());
2032                         final T[] s = slot.getSijm(j, m, meanOrbit.getDate());
2033                         for (int i = 0; i < 6; i++) {
2034                             shortPeriodicVariation[i] = shortPeriodicVariation[i].add(c[i].multiply(cosPhi).add(s[i].multiply(sinPhi)));
2035                         }
2036 
2037                     }
2038                 }
2039             }
2040 
2041             return shortPeriodicVariation;
2042 
2043         }
2044 
2045         /** {@inheritDoc} */
2046         @Override
2047         public String getCoefficientsKeyPrefix() {
2048             return DSSTTesseral.SHORT_PERIOD_PREFIX;
2049         }
2050 
2051         /** {@inheritDoc}
2052          * <p>
2053          * For tesseral terms contributions,there are maxOrderMdailyTesseralSP
2054          * m-daily cMm coefficients, maxOrderMdailyTesseralSP m-daily sMm
2055          * coefficients, nbNonResonant cjm coefficients and nbNonResonant
2056          * sjm coefficients, where maxOrderMdailyTesseralSP and nbNonResonant both
2057          * depend on the orbit. The j index is the integer multiplier for the true
2058          * longitude argument and the m index is the integer multiplier for m-dailies.
2059          * </p>
2060          */
2061         @Override
2062         public Map<String, T[]> getCoefficients(final FieldAbsoluteDate<T> date, final Set<String> selected) {
2063 
2064             // select the coefficients slot
2065             final FieldSlot<T> slot = slots.get(date);
2066 
2067             if (!nonResOrders.isEmpty() || mDailiesOnly) {
2068                 final Map<String, T[]> coefficients = new HashMap<>(12 * maxOrderMdailyTesseralSP + 12 * nonResOrders.size());
2069 
2070                 for (int m = 1; m <= maxOrderMdailyTesseralSP; m++) {
2071                     storeIfSelected(coefficients, selected, slot.getCijm(0, m, date), DSSTTesseral.CM_COEFFICIENTS, m);
2072                     storeIfSelected(coefficients, selected, slot.getSijm(0, m, date), DSSTTesseral.SM_COEFFICIENTS, m);
2073                 }
2074 
2075                 for (final Map.Entry<Integer, List<Integer>> entry : nonResOrders.entrySet()) {
2076                     final int           m     = entry.getKey();
2077                     final List<Integer> listJ = entry.getValue();
2078 
2079                     for (int j : listJ) {
2080                         for (int i = 0; i < 6; ++i) {
2081                             storeIfSelected(coefficients, selected, slot.getCijm(j, m, date), "c", j, m);
2082                             storeIfSelected(coefficients, selected, slot.getSijm(j, m, date), "s", j, m);
2083                         }
2084                     }
2085                 }
2086 
2087                 return coefficients;
2088 
2089             } else {
2090                 return Collections.emptyMap();
2091             }
2092 
2093         }
2094 
2095         /** Put a coefficient in a map if selected.
2096          * @param map map to populate
2097          * @param selected set of coefficients that should be put in the map
2098          * (empty set means all coefficients are selected)
2099          * @param value coefficient value
2100          * @param id coefficient identifier
2101          * @param indices list of coefficient indices
2102          */
2103         private void storeIfSelected(final Map<String, T[]> map, final Set<String> selected,
2104                                      final T[] value, final String id, final int... indices) {
2105             final StringBuilder keyBuilder = new StringBuilder(getCoefficientsKeyPrefix());
2106             keyBuilder.append(id);
2107             for (int index : indices) {
2108                 keyBuilder.append('[').append(index).append(']');
2109             }
2110             final String key = keyBuilder.toString();
2111             if (selected.isEmpty() || selected.contains(key)) {
2112                 map.put(key, value);
2113             }
2114         }
2115     }
2116 
2117     /** Coefficients valid for one time slot. */
2118     private static class Slot {
2119 
2120         /** The coefficients C<sub>i</sub><sup>j</sup><sup>m</sup>.
2121          * <p>
2122          * The index order is cijm[m][j][i] <br/>
2123          * i corresponds to the equinoctial element, as follows: <br/>
2124          * - i=0 for a <br/>
2125          * - i=1 for k <br/>
2126          * - i=2 for h <br/>
2127          * - i=3 for q <br/>
2128          * - i=4 for p <br/>
2129          * - i=5 for λ <br/>
2130          * </p>
2131          */
2132         private final ShortPeriodicsInterpolatedCoefficient[][] cijm;
2133 
2134         /** The coefficients S<sub>i</sub><sup>j</sup><sup>m</sup>.
2135          * <p>
2136          * The index order is sijm[m][j][i] <br/>
2137          * i corresponds to the equinoctial element, as follows: <br/>
2138          * - i=0 for a <br/>
2139          * - i=1 for k <br/>
2140          * - i=2 for h <br/>
2141          * - i=3 for q <br/>
2142          * - i=4 for p <br/>
2143          * - i=5 for λ <br/>
2144          * </p>
2145          */
2146         private final ShortPeriodicsInterpolatedCoefficient[][] sijm;
2147 
2148         /** Simple constructor.
2149          *  @param mMax maximum value for m index
2150          *  @param jMax maximum value for j index
2151          *  @param interpolationPoints number of points used in the interpolation process
2152          */
2153         Slot(final int mMax, final int jMax, final int interpolationPoints) {
2154 
2155             final int rows    = mMax + 1;
2156             final int columns = 2 * jMax + 1;
2157             cijm = new ShortPeriodicsInterpolatedCoefficient[rows][columns];
2158             sijm = new ShortPeriodicsInterpolatedCoefficient[rows][columns];
2159             for (int m = 1; m <= mMax; m++) {
2160                 for (int j = -jMax; j <= jMax; j++) {
2161                     cijm[m][j + jMax] = new ShortPeriodicsInterpolatedCoefficient(interpolationPoints);
2162                     sijm[m][j + jMax] = new ShortPeriodicsInterpolatedCoefficient(interpolationPoints);
2163                 }
2164             }
2165 
2166         }
2167 
2168         /** Get C<sub>i</sub><sup>j</sup><sup>m</sup>.
2169          *
2170          * @param j j index
2171          * @param m m index
2172          * @param date the date
2173          * @return C<sub>i</sub><sup>j</sup><sup>m</sup>
2174          */
2175         double[] getCijm(final int j, final int m, final AbsoluteDate date) {
2176             final int jMax = (cijm[m].length - 1) / 2;
2177             return cijm[m][j + jMax].value(date);
2178         }
2179 
2180         /** Get S<sub>i</sub><sup>j</sup><sup>m</sup>.
2181          *
2182          * @param j j index
2183          * @param m m index
2184          * @param date the date
2185          * @return S<sub>i</sub><sup>j</sup><sup>m</sup>
2186          */
2187         double[] getSijm(final int j, final int m, final AbsoluteDate date) {
2188             final int jMax = (cijm[m].length - 1) / 2;
2189             return sijm[m][j + jMax].value(date);
2190         }
2191 
2192     }
2193 
2194     /** Coefficients valid for one time slot. */
2195     private static class FieldSlot <T extends CalculusFieldElement<T>> {
2196 
2197         /** The coefficients C<sub>i</sub><sup>j</sup><sup>m</sup>.
2198          * <p>
2199          * The index order is cijm[m][j][i] <br/>
2200          * i corresponds to the equinoctial element, as follows: <br/>
2201          * - i=0 for a <br/>
2202          * - i=1 for k <br/>
2203          * - i=2 for h <br/>
2204          * - i=3 for q <br/>
2205          * - i=4 for p <br/>
2206          * - i=5 for λ <br/>
2207          * </p>
2208          */
2209         private final FieldShortPeriodicsInterpolatedCoefficient<T>[][] cijm;
2210 
2211         /** The coefficients S<sub>i</sub><sup>j</sup><sup>m</sup>.
2212          * <p>
2213          * The index order is sijm[m][j][i] <br/>
2214          * i corresponds to the equinoctial element, as follows: <br/>
2215          * - i=0 for a <br/>
2216          * - i=1 for k <br/>
2217          * - i=2 for h <br/>
2218          * - i=3 for q <br/>
2219          * - i=4 for p <br/>
2220          * - i=5 for λ <br/>
2221          * </p>
2222          */
2223         private final FieldShortPeriodicsInterpolatedCoefficient<T>[][] sijm;
2224 
2225         /** Simple constructor.
2226          *  @param mMax maximum value for m index
2227          *  @param jMax maximum value for j index
2228          *  @param interpolationPoints number of points used in the interpolation process
2229          */
2230         @SuppressWarnings("unchecked")
2231         FieldSlot(final int mMax, final int jMax, final int interpolationPoints) {
2232 
2233             final int rows    = mMax + 1;
2234             final int columns = 2 * jMax + 1;
2235             cijm = (FieldShortPeriodicsInterpolatedCoefficient<T>[][]) Array.newInstance(FieldShortPeriodicsInterpolatedCoefficient.class, rows, columns);
2236             sijm = (FieldShortPeriodicsInterpolatedCoefficient<T>[][]) Array.newInstance(FieldShortPeriodicsInterpolatedCoefficient.class, rows, columns);
2237             for (int m = 1; m <= mMax; m++) {
2238                 for (int j = -jMax; j <= jMax; j++) {
2239                     cijm[m][j + jMax] = new FieldShortPeriodicsInterpolatedCoefficient<>(interpolationPoints);
2240                     sijm[m][j + jMax] = new FieldShortPeriodicsInterpolatedCoefficient<>(interpolationPoints);
2241                 }
2242             }
2243 
2244         }
2245 
2246         /** Get C<sub>i</sub><sup>j</sup><sup>m</sup>.
2247          *
2248          * @param j j index
2249          * @param m m index
2250          * @param date the date
2251          * @return C<sub>i</sub><sup>j</sup><sup>m</sup>
2252          */
2253         T[] getCijm(final int j, final int m, final FieldAbsoluteDate<T> date) {
2254             final int jMax = (cijm[m].length - 1) / 2;
2255             return cijm[m][j + jMax].value(date);
2256         }
2257 
2258         /** Get S<sub>i</sub><sup>j</sup><sup>m</sup>.
2259          *
2260          * @param j j index
2261          * @param m m index
2262          * @param date the date
2263          * @return S<sub>i</sub><sup>j</sup><sup>m</sup>
2264          */
2265         T[] getSijm(final int j, final int m, final FieldAbsoluteDate<T> date) {
2266             final int jMax = (cijm[m].length - 1) / 2;
2267             return sijm[m][j + jMax].value(date);
2268         }
2269 
2270     }
2271 
2272     /** Compute potential and potential derivatives with respect to orbital parameters.
2273      *  <p>The following elements are computed from expression 3.3 - (4).
2274      *  <pre>
2275      *  dU / da
2276      *  dU / dh
2277      *  dU / dk
2278      *  dU / dλ
2279      *  dU / dα
2280      *  dU / dβ
2281      *  dU / dγ
2282      *  </pre>
2283      *  </p>
2284      */
2285     private class UAnddU {
2286 
2287         /** dU / da. */
2288         private  double dUda;
2289 
2290         /** dU / dk. */
2291         private double dUdk;
2292 
2293         /** dU / dh. */
2294         private double dUdh;
2295 
2296         /** dU / dl. */
2297         private double dUdl;
2298 
2299         /** dU / dAlpha. */
2300         private double dUdAl;
2301 
2302         /** dU / dBeta. */
2303         private double dUdBe;
2304 
2305         /** dU / dGamma. */
2306         private double dUdGa;
2307 
2308         /** Simple constuctor.
2309          * @param date current date
2310          * @param context container for attributes
2311          * @param hansen hansen objects
2312          */
2313         UAnddU(final AbsoluteDate date, final DSSTTesseralContext context, final HansenObjects hansen) {
2314 
2315             // Auxiliary elements related to the current orbit
2316             final AuxiliaryElements auxiliaryElements = context.getAuxiliaryElements();
2317 
2318             // Potential derivatives
2319             dUda  = 0.;
2320             dUdh  = 0.;
2321             dUdk  = 0.;
2322             dUdl  = 0.;
2323             dUdAl = 0.;
2324             dUdBe = 0.;
2325             dUdGa = 0.;
2326 
2327             // Compute only if there is at least one resonant tesseral
2328             if (!resOrders.isEmpty()) {
2329                 // Gmsj and Hmsj polynomials
2330                 final GHmsjPolynomials ghMSJ = new GHmsjPolynomials(auxiliaryElements.getK(), auxiliaryElements.getH(), context.getAlpha(), context.getBeta(), I);
2331 
2332                 // GAMMAmns function
2333                 final GammaMnsFunction gammaMNS = new GammaMnsFunction(maxDegree, context.getGamma(), I);
2334 
2335                 // R / a up to power degree
2336                 final double[] roaPow = new double[maxDegree + 1];
2337                 roaPow[0] = 1.;
2338                 for (int i = 1; i <= maxDegree; i++) {
2339                     roaPow[i] = context.getRoa() * roaPow[i - 1];
2340                 }
2341 
2342                 // SUM over resonant terms {j,m}
2343                 for (int m : resOrders) {
2344 
2345                     // Resonant index for the current resonant order
2346                     final int j = FastMath.max(1, (int) FastMath.round(context.getRatio() * m));
2347 
2348                     // Phase angle
2349                     final double jlMmt  = j * auxiliaryElements.getLM() - m * context.getTheta();
2350                     final SinCos scPhi  = FastMath.sinCos(jlMmt);
2351                     final double sinPhi = scPhi.sin();
2352                     final double cosPhi = scPhi.cos();
2353 
2354                     // Potential derivatives components for a given resonant pair {j,m}
2355                     double dUdaCos  = 0.;
2356                     double dUdaSin  = 0.;
2357                     double dUdhCos  = 0.;
2358                     double dUdhSin  = 0.;
2359                     double dUdkCos  = 0.;
2360                     double dUdkSin  = 0.;
2361                     double dUdlCos  = 0.;
2362                     double dUdlSin  = 0.;
2363                     double dUdAlCos = 0.;
2364                     double dUdAlSin = 0.;
2365                     double dUdBeCos = 0.;
2366                     double dUdBeSin = 0.;
2367                     double dUdGaCos = 0.;
2368                     double dUdGaSin = 0.;
2369 
2370                     // s-SUM from -sMin to sMax
2371                     final int sMin = FastMath.min(maxEccPow - j, maxDegree);
2372                     final int sMax = FastMath.min(maxEccPow + j, maxDegree);
2373                     for (int s = 0; s <= sMax; s++) {
2374 
2375                         //Compute the initial values for Hansen coefficients using newComb operators
2376                         hansen.computeHansenObjectsInitValues(context, s + maxDegree, j);
2377 
2378                         // n-SUM for s positive
2379                         final double[][] nSumSpos = computeNSum(date, j, m, s, maxDegree,
2380                                                                 roaPow, ghMSJ, gammaMNS, context, hansen);
2381                         dUdaCos  += nSumSpos[0][0];
2382                         dUdaSin  += nSumSpos[0][1];
2383                         dUdhCos  += nSumSpos[1][0];
2384                         dUdhSin  += nSumSpos[1][1];
2385                         dUdkCos  += nSumSpos[2][0];
2386                         dUdkSin  += nSumSpos[2][1];
2387                         dUdlCos  += nSumSpos[3][0];
2388                         dUdlSin  += nSumSpos[3][1];
2389                         dUdAlCos += nSumSpos[4][0];
2390                         dUdAlSin += nSumSpos[4][1];
2391                         dUdBeCos += nSumSpos[5][0];
2392                         dUdBeSin += nSumSpos[5][1];
2393                         dUdGaCos += nSumSpos[6][0];
2394                         dUdGaSin += nSumSpos[6][1];
2395 
2396                         // n-SUM for s negative
2397                         if (s > 0 && s <= sMin) {
2398                             //Compute the initial values for Hansen coefficients using newComb operators
2399                             hansen.computeHansenObjectsInitValues(context, maxDegree - s, j);
2400 
2401                             final double[][] nSumSneg = computeNSum(date, j, m, -s, maxDegree,
2402                                                                     roaPow, ghMSJ, gammaMNS, context, hansen);
2403                             dUdaCos  += nSumSneg[0][0];
2404                             dUdaSin  += nSumSneg[0][1];
2405                             dUdhCos  += nSumSneg[1][0];
2406                             dUdhSin  += nSumSneg[1][1];
2407                             dUdkCos  += nSumSneg[2][0];
2408                             dUdkSin  += nSumSneg[2][1];
2409                             dUdlCos  += nSumSneg[3][0];
2410                             dUdlSin  += nSumSneg[3][1];
2411                             dUdAlCos += nSumSneg[4][0];
2412                             dUdAlSin += nSumSneg[4][1];
2413                             dUdBeCos += nSumSneg[5][0];
2414                             dUdBeSin += nSumSneg[5][1];
2415                             dUdGaCos += nSumSneg[6][0];
2416                             dUdGaSin += nSumSneg[6][1];
2417                         }
2418                     }
2419 
2420                     // Assembly of potential derivatives componants
2421                     dUda  += cosPhi * dUdaCos  + sinPhi * dUdaSin;
2422                     dUdh  += cosPhi * dUdhCos  + sinPhi * dUdhSin;
2423                     dUdk  += cosPhi * dUdkCos  + sinPhi * dUdkSin;
2424                     dUdl  += cosPhi * dUdlCos  + sinPhi * dUdlSin;
2425                     dUdAl += cosPhi * dUdAlCos + sinPhi * dUdAlSin;
2426                     dUdBe += cosPhi * dUdBeCos + sinPhi * dUdBeSin;
2427                     dUdGa += cosPhi * dUdGaCos + sinPhi * dUdGaSin;
2428                 }
2429 
2430                 final double muOnA = context.getMuoa();
2431                 this.dUda  = dUda * (-muOnA / auxiliaryElements.getSma());
2432                 this.dUdh  = dUdh * muOnA;
2433                 this.dUdk  = dUdk * muOnA;
2434                 this.dUdl  = dUdl * muOnA;
2435                 this.dUdAl = dUdAl * muOnA;
2436                 this.dUdBe = dUdBe * muOnA;
2437                 this.dUdGa = dUdGa * muOnA;
2438             }
2439 
2440         }
2441 
2442         /** Return value of dU / da.
2443          * @return dUda
2444          */
2445         public double getdUda() {
2446             return dUda;
2447         }
2448 
2449         /** Return value of dU / dk.
2450          * @return dUdk
2451          */
2452         public double getdUdk() {
2453             return dUdk;
2454         }
2455 
2456         /** Return value of dU / dh.
2457          * @return dUdh
2458          */
2459         public double getdUdh() {
2460             return dUdh;
2461         }
2462 
2463         /** Return value of dU / dl.
2464          * @return dUdl
2465          */
2466         public double getdUdl() {
2467             return dUdl;
2468         }
2469 
2470         /** Return value of dU / dAlpha.
2471          * @return dUdAl
2472          */
2473         public double getdUdAl() {
2474             return dUdAl;
2475         }
2476 
2477         /** Return value of dU / dBeta.
2478          * @return dUdBe
2479          */
2480         public double getdUdBe() {
2481             return dUdBe;
2482         }
2483 
2484         /** Return value of dU / dGamma.
2485          * @return dUdGa
2486          */
2487         public double getdUdGa() {
2488             return dUdGa;
2489         }
2490 
2491     }
2492 
2493     /**  Computes the potential U derivatives.
2494      *  <p>The following elements are computed from expression 3.3 - (4).
2495      *  <pre>
2496      *  dU / da
2497      *  dU / dh
2498      *  dU / dk
2499      *  dU / dλ
2500      *  dU / dα
2501      *  dU / dβ
2502      *  dU / dγ
2503      *  </pre>
2504      *  </p>
2505      */
2506     private class FieldUAnddU <T extends CalculusFieldElement<T>> {
2507 
2508         /** dU / da. */
2509         private T dUda;
2510 
2511         /** dU / dk. */
2512         private T dUdk;
2513 
2514         /** dU / dh. */
2515         private T dUdh;
2516 
2517         /** dU / dl. */
2518         private T dUdl;
2519 
2520         /** dU / dAlpha. */
2521         private T dUdAl;
2522 
2523         /** dU / dBeta. */
2524         private T dUdBe;
2525 
2526         /** dU / dGamma. */
2527         private T dUdGa;
2528 
2529         /** Simple constuctor.
2530          * @param date current date
2531          * @param context container for attributes
2532          * @param hansen hansen objects
2533          */
2534         FieldUAnddU(final FieldAbsoluteDate<T> date, final FieldDSSTTesseralContext<T> context,
2535                     final FieldHansenObjects<T> hansen) {
2536 
2537             // Auxiliary elements related to the current orbit
2538             final FieldAuxiliaryElements<T> auxiliaryElements = context.getFieldAuxiliaryElements();
2539 
2540             // Zero for initialization
2541             final Field<T> field = date.getField();
2542             final T zero = field.getZero();
2543 
2544             // Potential derivatives
2545             dUda  = zero;
2546             dUdh  = zero;
2547             dUdk  = zero;
2548             dUdl  = zero;
2549             dUdAl = zero;
2550             dUdBe = zero;
2551             dUdGa = zero;
2552 
2553             // Compute only if there is at least one resonant tesseral
2554             if (!resOrders.isEmpty()) {
2555                 // Gmsj and Hmsj polynomials
2556                 final FieldGHmsjPolynomials<T> ghMSJ = new FieldGHmsjPolynomials<>(auxiliaryElements.getK(), auxiliaryElements.getH(), context.getAlpha(), context.getBeta(), I, field);
2557 
2558                 // GAMMAmns function
2559                 final FieldGammaMnsFunction<T> gammaMNS = new FieldGammaMnsFunction<>(maxDegree, context.getGamma(), I, field);
2560 
2561                 // R / a up to power degree
2562                 final T[] roaPow = MathArrays.buildArray(field, maxDegree + 1);
2563                 roaPow[0] = zero.newInstance(1.);
2564                 for (int i = 1; i <= maxDegree; i++) {
2565                     roaPow[i] = roaPow[i - 1].multiply(context.getRoa());
2566                 }
2567 
2568                 // SUM over resonant terms {j,m}
2569                 for (int m : resOrders) {
2570 
2571                     // Resonant index for the current resonant order
2572                     final int j = FastMath.max(1, (int) FastMath.round(context.getRatio().multiply(m)));
2573 
2574                     // Phase angle
2575                     final T jlMmt              = auxiliaryElements.getLM().multiply(j).subtract(context.getTheta().multiply(m));
2576                     final FieldSinCos<T> scPhi = FastMath.sinCos(jlMmt);
2577                     final T sinPhi             = scPhi.sin();
2578                     final T cosPhi             = scPhi.cos();
2579 
2580                     // Potential derivatives components for a given resonant pair {j,m}
2581                     T dUdaCos  = zero;
2582                     T dUdaSin  = zero;
2583                     T dUdhCos  = zero;
2584                     T dUdhSin  = zero;
2585                     T dUdkCos  = zero;
2586                     T dUdkSin  = zero;
2587                     T dUdlCos  = zero;
2588                     T dUdlSin  = zero;
2589                     T dUdAlCos = zero;
2590                     T dUdAlSin = zero;
2591                     T dUdBeCos = zero;
2592                     T dUdBeSin = zero;
2593                     T dUdGaCos = zero;
2594                     T dUdGaSin = zero;
2595 
2596                     // s-SUM from -sMin to sMax
2597                     final int sMin = FastMath.min(maxEccPow - j, maxDegree);
2598                     final int sMax = FastMath.min(maxEccPow + j, maxDegree);
2599                     for (int s = 0; s <= sMax; s++) {
2600 
2601                         //Compute the initial values for Hansen coefficients using newComb operators
2602                         hansen.computeHansenObjectsInitValues(context, s + maxDegree, j);
2603 
2604                         // n-SUM for s positive
2605                         final T[][] nSumSpos = computeNSum(date, j, m, s, maxDegree,
2606                                                                 roaPow, ghMSJ, gammaMNS, context, hansen);
2607                         dUdaCos  = dUdaCos.add(nSumSpos[0][0]);
2608                         dUdaSin  = dUdaSin.add(nSumSpos[0][1]);
2609                         dUdhCos  = dUdhCos.add(nSumSpos[1][0]);
2610                         dUdhSin  = dUdhSin.add(nSumSpos[1][1]);
2611                         dUdkCos  = dUdkCos.add(nSumSpos[2][0]);
2612                         dUdkSin  = dUdkSin.add(nSumSpos[2][1]);
2613                         dUdlCos  = dUdlCos.add(nSumSpos[3][0]);
2614                         dUdlSin  = dUdlSin.add(nSumSpos[3][1]);
2615                         dUdAlCos = dUdAlCos.add(nSumSpos[4][0]);
2616                         dUdAlSin = dUdAlSin.add(nSumSpos[4][1]);
2617                         dUdBeCos = dUdBeCos.add(nSumSpos[5][0]);
2618                         dUdBeSin = dUdBeSin.add(nSumSpos[5][1]);
2619                         dUdGaCos = dUdGaCos.add(nSumSpos[6][0]);
2620                         dUdGaSin = dUdGaSin.add(nSumSpos[6][1]);
2621 
2622                         // n-SUM for s negative
2623                         if (s > 0 && s <= sMin) {
2624                             //Compute the initial values for Hansen coefficients using newComb operators
2625                             hansen.computeHansenObjectsInitValues(context, maxDegree - s, j);
2626 
2627                             final T[][] nSumSneg = computeNSum(date, j, m, -s, maxDegree,
2628                                                                     roaPow, ghMSJ, gammaMNS, context, hansen);
2629                             dUdaCos  = dUdaCos.add(nSumSneg[0][0]);
2630                             dUdaSin  = dUdaSin.add(nSumSneg[0][1]);
2631                             dUdhCos  = dUdhCos.add(nSumSneg[1][0]);
2632                             dUdhSin  = dUdhSin.add(nSumSneg[1][1]);
2633                             dUdkCos  = dUdkCos.add(nSumSneg[2][0]);
2634                             dUdkSin  = dUdkSin.add(nSumSneg[2][1]);
2635                             dUdlCos  = dUdlCos.add(nSumSneg[3][0]);
2636                             dUdlSin  = dUdlSin.add(nSumSneg[3][1]);
2637                             dUdAlCos = dUdAlCos.add(nSumSneg[4][0]);
2638                             dUdAlSin = dUdAlSin.add(nSumSneg[4][1]);
2639                             dUdBeCos = dUdBeCos.add(nSumSneg[5][0]);
2640                             dUdBeSin = dUdBeSin.add(nSumSneg[5][1]);
2641                             dUdGaCos = dUdGaCos.add(nSumSneg[6][0]);
2642                             dUdGaSin = dUdGaSin.add(nSumSneg[6][1]);
2643                         }
2644                     }
2645 
2646                     // Assembly of potential derivatives componants
2647                     dUda  = dUda.add(dUdaCos.multiply(cosPhi).add(dUdaSin.multiply(sinPhi)));
2648                     dUdh  = dUdh.add(dUdhCos.multiply(cosPhi).add(dUdhSin.multiply(sinPhi)));
2649                     dUdk  = dUdk.add(dUdkCos.multiply(cosPhi).add(dUdkSin.multiply(sinPhi)));
2650                     dUdl  = dUdl.add(dUdlCos.multiply(cosPhi).add(dUdlSin.multiply(sinPhi)));
2651                     dUdAl = dUdAl.add(dUdAlCos.multiply(cosPhi).add(dUdAlSin.multiply(sinPhi)));
2652                     dUdBe = dUdBe.add(dUdBeCos.multiply(cosPhi).add(dUdBeSin.multiply(sinPhi)));
2653                     dUdGa = dUdGa.add(dUdGaCos.multiply(cosPhi).add(dUdGaSin.multiply(sinPhi)));
2654                 }
2655 
2656                 final T muOnA = context.getMuoa();
2657                 dUda  =  dUda.multiply(muOnA.divide(auxiliaryElements.getSma())).negate();
2658                 dUdh  =  dUdh.multiply(muOnA);
2659                 dUdk  =  dUdk.multiply(muOnA);
2660                 dUdl  =  dUdl.multiply(muOnA);
2661                 dUdAl =  dUdAl.multiply(muOnA);
2662                 dUdBe =  dUdBe.multiply(muOnA);
2663                 dUdGa =  dUdGa.multiply(muOnA);
2664             }
2665         }
2666 
2667         /** Return value of dU / da.
2668          * @return dUda
2669          */
2670         public T getdUda() {
2671             return dUda;
2672         }
2673 
2674         /** Return value of dU / dk.
2675          * @return dUdk
2676          */
2677         public T getdUdk() {
2678             return dUdk;
2679         }
2680 
2681         /** Return value of dU / dh.
2682          * @return dUdh
2683          */
2684         public T getdUdh() {
2685             return dUdh;
2686         }
2687 
2688         /** Return value of dU / dl.
2689          * @return dUdl
2690          */
2691         public T getdUdl() {
2692             return dUdl;
2693         }
2694 
2695         /** Return value of dU / dAlpha.
2696          * @return dUdAl
2697          */
2698         public T getdUdAl() {
2699             return dUdAl;
2700         }
2701 
2702         /** Return value of dU / dBeta.
2703          * @return dUdBe
2704          */
2705         public T getdUdBe() {
2706             return dUdBe;
2707         }
2708 
2709         /** Return value of dU / dGamma.
2710          * @return dUdGa
2711          */
2712         public T getdUdGa() {
2713             return dUdGa;
2714         }
2715 
2716     }
2717 
2718     /** Computes init values of the Hansen Objects. */
2719     private class HansenObjects {
2720 
2721         /** A two dimensional array that contains the objects needed to build the Hansen coefficients. <br/>
2722          * The indexes are s + maxDegree and j */
2723         private HansenTesseralLinear[][] hansenObjects;
2724 
2725         /** Simple constructor.
2726          * @param ratio Ratio of satellite period to central body rotation period
2727          * @param type type of the elements used during the propagation
2728          */
2729         HansenObjects(final double ratio,
2730                       final PropagationType type) {
2731 
2732             //Allocate the two dimensional array
2733             final int rows     = 2 * maxDegree + 1;
2734             final int columns  = maxFrequencyShortPeriodics + 1;
2735             this.hansenObjects = new HansenTesseralLinear[rows][columns];
2736 
2737             switch (type) {
2738                 case MEAN:
2739                     // loop through the resonant orders
2740                     for (int m : resOrders) {
2741                         //Compute the corresponding j term
2742                         final int j = FastMath.max(1, (int) FastMath.round(ratio * m));
2743 
2744                         //Compute the sMin and sMax values
2745                         final int sMin = FastMath.min(maxEccPow - j, maxDegree);
2746                         final int sMax = FastMath.min(maxEccPow + j, maxDegree);
2747 
2748                         //loop through the s values
2749                         for (int s = 0; s <= sMax; s++) {
2750                             //Compute the n0 value
2751                             final int n0 = FastMath.max(FastMath.max(2, m), s);
2752 
2753                             //Create the object for the pair j, s
2754                             this.hansenObjects[s + maxDegree][j] = new HansenTesseralLinear(maxDegree, s, j, n0, maxHansen);
2755 
2756                             if (s > 0 && s <= sMin) {
2757                                 //Also create the object for the pair j, -s
2758                                 this.hansenObjects[maxDegree - s][j] =  new HansenTesseralLinear(maxDegree, -s, j, n0, maxHansen);
2759                             }
2760                         }
2761                     }
2762                     break;
2763 
2764                 case OSCULATING:
2765                     // create all objects
2766                     for (int j = 0; j <= maxFrequencyShortPeriodics; j++) {
2767                         for (int s = -maxDegree; s <= maxDegree; s++) {
2768                             //Compute the n0 value
2769                             final int n0 = FastMath.max(2, FastMath.abs(s));
2770                             this.hansenObjects[s + maxDegree][j] = new HansenTesseralLinear(maxDegree, s, j, n0, maxHansen);
2771                         }
2772                     }
2773                     break;
2774 
2775                 default:
2776                     throw new OrekitInternalError(null);
2777             }
2778 
2779         }
2780 
2781         /** Compute init values for hansen objects.
2782          * @param context container for attributes
2783          * @param rows number of rows of the hansen matrix
2784          * @param columns columns number of columns of the hansen matrix
2785          */
2786         public void computeHansenObjectsInitValues(final DSSTTesseralContext context, final int rows, final int columns) {
2787             hansenObjects[rows][columns].computeInitValues(context.getE2(), context.getChi(), context.getChi2());
2788         }
2789 
2790         /** Get hansen object.
2791          * @return hansenObjects
2792          */
2793         public HansenTesseralLinear[][] getHansenObjects() {
2794             return hansenObjects;
2795         }
2796 
2797     }
2798 
2799     /** Computes init values of the Hansen Objects. */
2800     private class FieldHansenObjects<T extends CalculusFieldElement<T>> {
2801 
2802         /** A two dimensional array that contains the objects needed to build the Hansen coefficients. <br/>
2803          * The indexes are s + maxDegree and j */
2804         private FieldHansenTesseralLinear<T>[][] hansenObjects;
2805 
2806         /** Simple constructor.
2807          * @param ratio Ratio of satellite period to central body rotation period
2808          * @param type type of the elements used during the propagation
2809          */
2810         @SuppressWarnings("unchecked")
2811         FieldHansenObjects(final T ratio,
2812                            final PropagationType type) {
2813 
2814             // Set the maximum power of the eccentricity to use in Hansen coefficient Kernel expansion.
2815             maxHansen = maxEccPow / 2;
2816 
2817             //Allocate the two dimensional array
2818             final int rows     = 2 * maxDegree + 1;
2819             final int columns  = maxFrequencyShortPeriodics + 1;
2820             this.hansenObjects = (FieldHansenTesseralLinear<T>[][]) Array.newInstance(FieldHansenTesseralLinear.class, rows, columns);
2821 
2822             switch (type) {
2823                 case MEAN:
2824                  // loop through the resonant orders
2825                     for (int m : resOrders) {
2826                         //Compute the corresponding j term
2827                         final int j = FastMath.max(1, (int) FastMath.round(ratio.multiply(m)));
2828 
2829                         //Compute the sMin and sMax values
2830                         final int sMin = FastMath.min(maxEccPow - j, maxDegree);
2831                         final int sMax = FastMath.min(maxEccPow + j, maxDegree);
2832 
2833                         //loop through the s values
2834                         for (int s = 0; s <= sMax; s++) {
2835                             //Compute the n0 value
2836                             final int n0 = FastMath.max(FastMath.max(2, m), s);
2837 
2838                             //Create the object for the pair j, s
2839                             this.hansenObjects[s + maxDegree][j] = new FieldHansenTesseralLinear<>(maxDegree, s, j, n0, maxHansen, ratio.getField());
2840 
2841                             if (s > 0 && s <= sMin) {
2842                                 //Also create the object for the pair j, -s
2843                                 this.hansenObjects[maxDegree - s][j] =  new FieldHansenTesseralLinear<>(maxDegree, -s, j, n0, maxHansen, ratio.getField());
2844                             }
2845                         }
2846                     }
2847                     break;
2848 
2849                 case OSCULATING:
2850                     // create all objects
2851                     for (int j = 0; j <= maxFrequencyShortPeriodics; j++) {
2852                         for (int s = -maxDegree; s <= maxDegree; s++) {
2853                             //Compute the n0 value
2854                             final int n0 = FastMath.max(2, FastMath.abs(s));
2855                             this.hansenObjects[s + maxDegree][j] = new FieldHansenTesseralLinear<>(maxDegree, s, j, n0, maxHansen, ratio.getField());
2856                         }
2857                     }
2858                     break;
2859 
2860                 default:
2861                     throw new OrekitInternalError(null);
2862             }
2863 
2864         }
2865 
2866         /** Compute init values for hansen objects.
2867          * @param context container for attributes
2868          * @param rows number of rows of the hansen matrix
2869          * @param columns columns number of columns of the hansen matrix
2870          */
2871         public void computeHansenObjectsInitValues(final FieldDSSTTesseralContext<T> context,
2872                                                    final int rows, final int columns) {
2873             hansenObjects[rows][columns].computeInitValues(context.getE2(), context.getChi(), context.getChi2());
2874         }
2875 
2876         /** Get hansen object.
2877          * @return hansenObjects
2878          */
2879         public FieldHansenTesseralLinear<T>[][] getHansenObjects() {
2880             return hansenObjects;
2881         }
2882 
2883     }
2884 
2885 }