1   /* Copyright 2002-2022 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.TreeMap;
28  
29  import org.hipparchus.Field;
30  import org.hipparchus.CalculusFieldElement;
31  import org.hipparchus.exception.LocalizedCoreFormats;
32  import org.hipparchus.util.CombinatoricsUtils;
33  import org.hipparchus.util.FastMath;
34  import org.hipparchus.util.FieldSinCos;
35  import org.hipparchus.util.MathArrays;
36  import org.hipparchus.util.SinCos;
37  import org.orekit.attitudes.AttitudeProvider;
38  import org.orekit.errors.OrekitException;
39  import org.orekit.errors.OrekitInternalError;
40  import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider;
41  import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider.UnnormalizedSphericalHarmonics;
42  import org.orekit.orbits.FieldOrbit;
43  import org.orekit.orbits.Orbit;
44  import org.orekit.propagation.FieldSpacecraftState;
45  import org.orekit.propagation.PropagationType;
46  import org.orekit.propagation.SpacecraftState;
47  import org.orekit.propagation.events.EventDetector;
48  import org.orekit.propagation.events.FieldEventDetector;
49  import org.orekit.propagation.semianalytical.dsst.utilities.AuxiliaryElements;
50  import org.orekit.propagation.semianalytical.dsst.utilities.CjSjCoefficient;
51  import org.orekit.propagation.semianalytical.dsst.utilities.CoefficientsFactory;
52  import org.orekit.propagation.semianalytical.dsst.utilities.CoefficientsFactory.NSKey;
53  import org.orekit.propagation.semianalytical.dsst.utilities.FieldAuxiliaryElements;
54  import org.orekit.propagation.semianalytical.dsst.utilities.FieldCjSjCoefficient;
55  import org.orekit.propagation.semianalytical.dsst.utilities.FieldGHIJjsPolynomials;
56  import org.orekit.propagation.semianalytical.dsst.utilities.FieldLnsCoefficients;
57  import org.orekit.propagation.semianalytical.dsst.utilities.FieldShortPeriodicsInterpolatedCoefficient;
58  import org.orekit.propagation.semianalytical.dsst.utilities.GHIJjsPolynomials;
59  import org.orekit.propagation.semianalytical.dsst.utilities.LnsCoefficients;
60  import org.orekit.propagation.semianalytical.dsst.utilities.ShortPeriodicsInterpolatedCoefficient;
61  import org.orekit.propagation.semianalytical.dsst.utilities.UpperBounds;
62  import org.orekit.propagation.semianalytical.dsst.utilities.hansen.FieldHansenZonalLinear;
63  import org.orekit.propagation.semianalytical.dsst.utilities.hansen.HansenZonalLinear;
64  import org.orekit.time.AbsoluteDate;
65  import org.orekit.time.FieldAbsoluteDate;
66  import org.orekit.utils.FieldTimeSpanMap;
67  import org.orekit.utils.ParameterDriver;
68  import org.orekit.utils.TimeSpanMap;
69  
70  /** Zonal contribution to the central body gravitational perturbation.
71   *
72   *   @author Romain Di Costanzo
73   *   @author Pascal Parraud
74   *   @author Bryan Cazabonne (field translation)
75   */
76  public class DSSTZonal implements DSSTForceModel {
77  
78      /**  Name of the prefix for short period coefficients keys. */
79      public static final String SHORT_PERIOD_PREFIX = "DSST-central-body-zonal-";
80  
81      /** Retrograde factor I.
82       *  <p>
83       *  DSST model needs equinoctial orbit as internal representation.
84       *  Classical equinoctial elements have discontinuities when inclination
85       *  is close to zero. In this representation, I = +1. <br>
86       *  To avoid this discontinuity, another representation exists and equinoctial
87       *  elements can be expressed in a different way, called "retrograde" orbit.
88       *  This implies I = -1. <br>
89       *  As Orekit doesn't implement the retrograde orbit, I is always set to +1.
90       *  But for the sake of consistency with the theory, the retrograde factor
91       *  has been kept in the formulas.
92       *  </p>
93       */
94      private static final int I = 1;
95  
96      /** Number of points for interpolation. */
97      private static final int INTERPOLATION_POINTS = 3;
98  
99      /** Truncation tolerance. */
100     private static final double TRUNCATION_TOLERANCE = 1e-4;
101 
102     /** Central attraction scaling factor.
103      * <p>
104      * We use a power of 2 to avoid numeric noise introduction
105      * in the multiplications/divisions sequences.
106      * </p>
107      */
108     private static final double MU_SCALE = FastMath.scalb(1.0, 32);
109 
110     /** Coefficient used to define the mean disturbing function V<sub>ns</sub> coefficient. */
111     private final TreeMap<NSKey, Double> Vns;
112 
113     /** Provider for spherical harmonics. */
114     private final UnnormalizedSphericalHarmonicsProvider provider;
115 
116     /** Maximal degree to consider for harmonics potential. */
117     private final int maxDegree;
118 
119     /** Maximal degree to consider for harmonics potential. */
120     private final int maxDegreeShortPeriodics;
121 
122     /** Highest power of the eccentricity to be used in short periodic computations. */
123     private final int maxEccPowShortPeriodics;
124 
125     /** Maximum frequency in true longitude for short periodic computations. */
126     private final int maxFrequencyShortPeriodics;
127 
128     /** Highest power of the eccentricity to be used in mean elements computations. */
129     private int maxEccPowMeanElements;
130 
131     /** Highest power of the eccentricity. */
132     private int maxEccPow;
133 
134     /** Short period terms. */
135     private ZonalShortPeriodicCoefficients zonalSPCoefs;
136 
137     /** Short period terms. */
138     private Map<Field<?>, FieldZonalShortPeriodicCoefficients<?>> zonalFieldSPCoefs;
139 
140     /** Driver for gravitational parameter. */
141     private final ParameterDriver gmParameterDriver;
142 
143     /** Hansen objects. */
144     private HansenObjects hansen;
145 
146     /** Hansen objects for field elements. */
147     private Map<Field<?>, FieldHansenObjects<?>> fieldHansen;
148 
149     /** Constructor with default reference values.
150      * <p>
151      * When this constructor is used, maximum allowed values are used
152      * for the short periodic coefficients:
153      * </p>
154      * <ul>
155      *    <li> {@link #maxDegreeShortPeriodics} is set to {@code provider.getMaxDegree()} </li>
156      *    <li> {@link #maxEccPowShortPeriodics} is set to {@code min(provider.getMaxDegree() - 1, 4)}.
157      *         This parameter should not exceed 4 as higher values will exceed computer capacity </li>
158      *    <li> {@link #maxFrequencyShortPeriodics} is set to {@code 2 * provider.getMaxDegree() + 1} </li>
159      * </ul>
160      * @param provider provider for spherical harmonics
161      * @since 10.1
162      */
163     public DSSTZonal(final UnnormalizedSphericalHarmonicsProvider provider) {
164         this(provider, provider.getMaxDegree(), FastMath.min(4, provider.getMaxDegree() - 1), 2 * provider.getMaxDegree() + 1);
165     }
166 
167     /** Simple constructor.
168      * @param provider provider for spherical harmonics
169      * @param maxDegreeShortPeriodics maximum degree to consider for short periodics zonal harmonics potential
170      * (must be between 2 and {@code provider.getMaxDegree()})
171      * @param maxEccPowShortPeriodics maximum power of the eccentricity to be used in short periodic computations
172      * (must be between 0 and {@code maxDegreeShortPeriodics - 1}, but should typically not exceed 4 as higher
173      * values will exceed computer capacity)
174      * @param maxFrequencyShortPeriodics maximum frequency in true longitude for short periodic computations
175      * (must be between 1 and {@code 2 * maxDegreeShortPeriodics + 1})
176      * @since 7.2
177      */
178     public DSSTZonal(final UnnormalizedSphericalHarmonicsProvider provider,
179                      final int maxDegreeShortPeriodics,
180                      final int maxEccPowShortPeriodics,
181                      final int maxFrequencyShortPeriodics) {
182 
183         gmParameterDriver = new ParameterDriver(DSSTNewtonianAttraction.CENTRAL_ATTRACTION_COEFFICIENT,
184                                                 provider.getMu(), MU_SCALE,
185                                                 0.0, Double.POSITIVE_INFINITY);
186 
187         // Vns coefficients
188         this.Vns = CoefficientsFactory.computeVns(provider.getMaxDegree() + 1);
189 
190         this.provider  = provider;
191         this.maxDegree = provider.getMaxDegree();
192 
193         checkIndexRange(maxDegreeShortPeriodics, 2, provider.getMaxDegree());
194         this.maxDegreeShortPeriodics = maxDegreeShortPeriodics;
195 
196         checkIndexRange(maxEccPowShortPeriodics, 0, maxDegreeShortPeriodics - 1);
197         this.maxEccPowShortPeriodics = maxEccPowShortPeriodics;
198 
199         checkIndexRange(maxFrequencyShortPeriodics, 1, 2 * maxDegreeShortPeriodics + 1);
200         this.maxFrequencyShortPeriodics = maxFrequencyShortPeriodics;
201 
202         // Initialize default values
203         this.maxEccPowMeanElements = (maxDegree == 2) ? 0 : Integer.MIN_VALUE;
204 
205         zonalFieldSPCoefs = new HashMap<>();
206         fieldHansen       = new HashMap<>();
207 
208     }
209 
210     /** Check an index range.
211      * @param index index value
212      * @param min minimum value for index
213      * @param max maximum value for index
214      */
215     private void checkIndexRange(final int index, final int min, final int max) {
216         if (index < min || index > max) {
217             throw new OrekitException(LocalizedCoreFormats.OUT_OF_RANGE_SIMPLE, index, min, max);
218         }
219     }
220 
221     /** Get the spherical harmonics provider.
222      *  @return the spherical harmonics provider
223      */
224     public UnnormalizedSphericalHarmonicsProvider getProvider() {
225         return provider;
226     }
227 
228     /** {@inheritDoc}
229      *  <p>
230      *  Computes the highest power of the eccentricity to appear in the truncated
231      *  analytical power series expansion.
232      *  </p>
233      *  <p>
234      *  This method computes the upper value for the central body potential and
235      *  determines the maximal power for the eccentricity producing potential
236      *  terms bigger than a defined tolerance.
237      *  </p>
238      */
239     @Override
240     public List<ShortPeriodTerms> initializeShortPeriodTerms(final AuxiliaryElements auxiliaryElements,
241                                              final PropagationType type,
242                                              final double[] parameters) {
243 
244         computeMeanElementsTruncations(auxiliaryElements, parameters);
245 
246         switch (type) {
247             case MEAN:
248                 maxEccPow = maxEccPowMeanElements;
249                 break;
250             case OSCULATING:
251                 maxEccPow = FastMath.max(maxEccPowMeanElements, maxEccPowShortPeriodics);
252                 break;
253             default:
254                 throw new OrekitInternalError(null);
255         }
256 
257         hansen = new HansenObjects();
258 
259         final List<ShortPeriodTerms> list = new ArrayList<ShortPeriodTerms>();
260         zonalSPCoefs = new ZonalShortPeriodicCoefficients(maxFrequencyShortPeriodics,
261                                                           INTERPOLATION_POINTS,
262                                                           new TimeSpanMap<Slot>(new Slot(maxFrequencyShortPeriodics,
263                                                                                          INTERPOLATION_POINTS)));
264         list.add(zonalSPCoefs);
265         return list;
266 
267     }
268 
269     /** {@inheritDoc}
270      *  <p>
271      *  Computes the highest power of the eccentricity to appear in the truncated
272      *  analytical power series expansion.
273      *  </p>
274      *  <p>
275      *  This method computes the upper value for the central body potential and
276      *  determines the maximal power for the eccentricity producing potential
277      *  terms bigger than a defined tolerance.
278      *  </p>
279      */
280     @Override
281     public <T extends CalculusFieldElement<T>> List<FieldShortPeriodTerms<T>> initializeShortPeriodTerms(final FieldAuxiliaryElements<T> auxiliaryElements,
282                                                                                      final PropagationType type,
283                                                                                      final T[] parameters) {
284 
285         // Field used by default
286         final Field<T> field = auxiliaryElements.getDate().getField();
287 
288         computeMeanElementsTruncations(auxiliaryElements, parameters, field);
289 
290         switch (type) {
291             case MEAN:
292                 maxEccPow = maxEccPowMeanElements;
293                 break;
294             case OSCULATING:
295                 maxEccPow = FastMath.max(maxEccPowMeanElements, maxEccPowShortPeriodics);
296                 break;
297             default:
298                 throw new OrekitInternalError(null);
299         }
300 
301         fieldHansen.put(field, new FieldHansenObjects<>(field));
302 
303         final FieldZonalShortPeriodicCoefficients<T> fzspc =
304                         new FieldZonalShortPeriodicCoefficients<>(maxFrequencyShortPeriodics,
305                                                                   INTERPOLATION_POINTS,
306                                                                   new FieldTimeSpanMap<>(new FieldSlot<>(maxFrequencyShortPeriodics,
307                                                                                                          INTERPOLATION_POINTS),
308                                                                                          field));
309         zonalFieldSPCoefs.put(field, fzspc);
310         return Collections.singletonList(fzspc);
311 
312     }
313 
314     /** Compute indices truncations for mean elements computations.
315      * @param auxiliaryElements auxiliary elements
316      * @param parameters values of the force model parameters
317      */
318     private void computeMeanElementsTruncations(final AuxiliaryElements auxiliaryElements, final double[] parameters) {
319 
320         final DSSTZonalContext context = new DSSTZonalContext(auxiliaryElements, provider, parameters);
321         //Compute the max eccentricity power for the mean element rate expansion
322         if (maxDegree == 2) {
323             maxEccPowMeanElements = 0;
324         } else {
325             // Initializes specific parameters.
326             final UnnormalizedSphericalHarmonics harmonics = provider.onDate(auxiliaryElements.getDate());
327 
328             // Utilities for truncation
329             final double ax2or = 2. * auxiliaryElements.getSma() / provider.getAe();
330             double xmuran = parameters[0] / auxiliaryElements.getSma();
331             // Set a lower bound for eccentricity
332             final double eo2  = FastMath.max(0.0025, auxiliaryElements.getEcc() / 2.);
333             final double x2o2 = context.getXX() / 2.;
334             final double[] eccPwr = new double[maxDegree + 1];
335             final double[] chiPwr = new double[maxDegree + 1];
336             final double[] hafPwr = new double[maxDegree + 1];
337             eccPwr[0] = 1.;
338             chiPwr[0] = context.getX();
339             hafPwr[0] = 1.;
340             for (int i = 1; i <= maxDegree; i++) {
341                 eccPwr[i] = eccPwr[i - 1] * eo2;
342                 chiPwr[i] = chiPwr[i - 1] * x2o2;
343                 hafPwr[i] = hafPwr[i - 1] * 0.5;
344                 xmuran  /= ax2or;
345             }
346 
347             // Set highest power of e and degree of current spherical harmonic.
348             maxEccPowMeanElements = 0;
349             int maxDeg = maxDegree;
350             // Loop over n
351             do {
352                 // Set order of current spherical harmonic.
353                 int m = 0;
354                 // Loop over m
355                 do {
356                     // Compute magnitude of current spherical harmonic coefficient.
357                     final double cnm = harmonics.getUnnormalizedCnm(maxDeg, m);
358                     final double snm = harmonics.getUnnormalizedSnm(maxDeg, m);
359                     final double csnm = FastMath.hypot(cnm, snm);
360                     if (csnm == 0.) {
361                         break;
362                     }
363                     // Set magnitude of last spherical harmonic term.
364                     double lastTerm = 0.;
365                     // Set current power of e and related indices.
366                     int nsld2 = (maxDeg - maxEccPowMeanElements - 1) / 2;
367                     int l = maxDeg - 2 * nsld2;
368                     // Loop over l
369                     double term = 0.;
370                     do {
371                         // Compute magnitude of current spherical harmonic term.
372                         if (m < l) {
373                             term =  csnm * xmuran *
374                                     (CombinatoricsUtils.factorialDouble(maxDeg - l) / (CombinatoricsUtils.factorialDouble(maxDeg - m))) *
375                                     (CombinatoricsUtils.factorialDouble(maxDeg + l) / (CombinatoricsUtils.factorialDouble(nsld2) * CombinatoricsUtils.factorialDouble(nsld2 + l))) *
376                                     eccPwr[l] * UpperBounds.getDnl(context.getXX(), chiPwr[l], maxDeg, l) *
377                                     (UpperBounds.getRnml(auxiliaryElements.getGamma(), maxDeg, l, m, 1, I) + UpperBounds.getRnml(auxiliaryElements.getGamma(), maxDeg, l, m, -1, I));
378                         } else {
379                             term =  csnm * xmuran *
380                                     (CombinatoricsUtils.factorialDouble(maxDeg + m) / (CombinatoricsUtils.factorialDouble(nsld2) * CombinatoricsUtils.factorialDouble(nsld2 + l))) *
381                                     eccPwr[l] * hafPwr[m - l] * UpperBounds.getDnl(context.getXX(), chiPwr[l], maxDeg, l) *
382                                     (UpperBounds.getRnml(auxiliaryElements.getGamma(), maxDeg, m, l, 1, I) + UpperBounds.getRnml(auxiliaryElements.getGamma(), maxDeg, m, l, -1, I));
383                         }
384                         // Is the current spherical harmonic term bigger than the truncation tolerance ?
385                         if (term >= TRUNCATION_TOLERANCE) {
386                             maxEccPowMeanElements = l;
387                         } else {
388                             // Is the current term smaller than the last term ?
389                             if (term < lastTerm) {
390                                 break;
391                             }
392                         }
393                         // Proceed to next power of e.
394                         lastTerm = term;
395                         l += 2;
396                         nsld2--;
397                     } while (l < maxDeg);
398                     // Is the current spherical harmonic term bigger than the truncation tolerance ?
399                     if (term >= TRUNCATION_TOLERANCE) {
400                         maxEccPowMeanElements = FastMath.min(maxDegree - 2, maxEccPowMeanElements);
401                         return;
402                     }
403                     // Proceed to next order.
404                     m++;
405                 } while (m <= FastMath.min(maxDeg, provider.getMaxOrder()));
406                 // Proceed to next degree.
407                 xmuran *= ax2or;
408                 maxDeg--;
409             } while (maxDeg > maxEccPowMeanElements + 2);
410 
411             maxEccPowMeanElements = FastMath.min(maxDegree - 2, maxEccPowMeanElements);
412         }
413     }
414 
415     /** Compute indices truncations for mean elements computations.
416      * @param <T> type of the elements
417      * @param auxiliaryElements auxiliary elements
418      * @param parameters values of the force model parameters
419      * @param field field used by default
420      */
421     private <T extends CalculusFieldElement<T>> void computeMeanElementsTruncations(final FieldAuxiliaryElements<T> auxiliaryElements,
422                                                                                 final T[] parameters,
423                                                                                 final Field<T> field) {
424 
425         final T zero = field.getZero();
426         final FieldDSSTZonalContext<T> context = new FieldDSSTZonalContext<>(auxiliaryElements, provider, parameters);
427         //Compute the max eccentricity power for the mean element rate expansion
428         if (maxDegree == 2) {
429             maxEccPowMeanElements = 0;
430         } else {
431             // Initializes specific parameters.
432             final UnnormalizedSphericalHarmonics harmonics = provider.onDate(auxiliaryElements.getDate().toAbsoluteDate());
433 
434             // Utilities for truncation
435             final T ax2or = auxiliaryElements.getSma().multiply(2.).divide(provider.getAe());
436             T xmuran = parameters[0].divide(auxiliaryElements.getSma());
437             // Set a lower bound for eccentricity
438             final T eo2  = FastMath.max(zero.add(0.0025), auxiliaryElements.getEcc().divide(2.));
439             final T x2o2 = context.getXX().divide(2.);
440             final T[] eccPwr = MathArrays.buildArray(field, maxDegree + 1);
441             final T[] chiPwr = MathArrays.buildArray(field, maxDegree + 1);
442             final T[] hafPwr = MathArrays.buildArray(field, maxDegree + 1);
443             eccPwr[0] = zero.add(1.);
444             chiPwr[0] = context.getX();
445             hafPwr[0] = zero.add(1.);
446             for (int i = 1; i <= maxDegree; i++) {
447                 eccPwr[i] = eccPwr[i - 1].multiply(eo2);
448                 chiPwr[i] = chiPwr[i - 1].multiply(x2o2);
449                 hafPwr[i] = hafPwr[i - 1].multiply(0.5);
450                 xmuran  = xmuran.divide(ax2or);
451             }
452 
453             // Set highest power of e and degree of current spherical harmonic.
454             maxEccPowMeanElements = 0;
455             int maxDeg = maxDegree;
456             // Loop over n
457             do {
458                 // Set order of current spherical harmonic.
459                 int m = 0;
460                 // Loop over m
461                 do {
462                     // Compute magnitude of current spherical harmonic coefficient.
463                     final T cnm = zero.add(harmonics.getUnnormalizedCnm(maxDeg, m));
464                     final T snm = zero.add(harmonics.getUnnormalizedSnm(maxDeg, m));
465                     final T csnm = FastMath.hypot(cnm, snm);
466                     if (csnm.getReal() == 0.) {
467                         break;
468                     }
469                     // Set magnitude of last spherical harmonic term.
470                     T lastTerm = zero;
471                     // Set current power of e and related indices.
472                     int nsld2 = (maxDeg - maxEccPowMeanElements - 1) / 2;
473                     int l = maxDeg - 2 * nsld2;
474                     // Loop over l
475                     T term = zero;
476                     do {
477                         // Compute magnitude of current spherical harmonic term.
478                         if (m < l) {
479                             term = csnm.multiply(xmuran).
480                                    multiply((CombinatoricsUtils.factorialDouble(maxDeg - l) / (CombinatoricsUtils.factorialDouble(maxDeg - m))) *
481                                    (CombinatoricsUtils.factorialDouble(maxDeg + l) / (CombinatoricsUtils.factorialDouble(nsld2) * CombinatoricsUtils.factorialDouble(nsld2 + l)))).
482                                    multiply(eccPwr[l]).multiply(UpperBounds.getDnl(context.getXX(), chiPwr[l], maxDeg, l)).
483                                    multiply(UpperBounds.getRnml(auxiliaryElements.getGamma(), maxDeg, l, m, 1, I).add(UpperBounds.getRnml(auxiliaryElements.getGamma(), maxDeg, l, m, -1, I)));
484                         } else {
485                             term = csnm.multiply(xmuran).
486                                    multiply(CombinatoricsUtils.factorialDouble(maxDeg + m) / (CombinatoricsUtils.factorialDouble(nsld2) * CombinatoricsUtils.factorialDouble(nsld2 + l))).
487                                    multiply(eccPwr[l]).multiply(hafPwr[m - l]).multiply(UpperBounds.getDnl(context.getXX(), chiPwr[l], maxDeg, l)).
488                                    multiply(UpperBounds.getRnml(auxiliaryElements.getGamma(), maxDeg, m, l, 1, I).add(UpperBounds.getRnml(auxiliaryElements.getGamma(), maxDeg, m, l, -1, I)));
489                         }
490                         // Is the current spherical harmonic term bigger than the truncation tolerance ?
491                         if (term.getReal() >= TRUNCATION_TOLERANCE) {
492                             maxEccPowMeanElements = l;
493                         } else {
494                             // Is the current term smaller than the last term ?
495                             if (term.getReal() < lastTerm.getReal()) {
496                                 break;
497                             }
498                         }
499                         // Proceed to next power of e.
500                         lastTerm = term;
501                         l += 2;
502                         nsld2--;
503                     } while (l < maxDeg);
504                     // Is the current spherical harmonic term bigger than the truncation tolerance ?
505                     if (term.getReal() >= TRUNCATION_TOLERANCE) {
506                         maxEccPowMeanElements = FastMath.min(maxDegree - 2, maxEccPowMeanElements);
507                         return;
508                     }
509                     // Proceed to next order.
510                     m++;
511                 } while (m <= FastMath.min(maxDeg, provider.getMaxOrder()));
512                 // Proceed to next degree.
513                 xmuran = xmuran.multiply(ax2or);
514                 maxDeg--;
515             } while (maxDeg > maxEccPowMeanElements + 2);
516 
517             maxEccPowMeanElements = FastMath.min(maxDegree - 2, maxEccPowMeanElements);
518         }
519     }
520 
521     /** Performs initialization at each integration step for the current force model.
522      *  <p>
523      *  This method aims at being called before mean elements rates computation.
524      *  </p>
525      *  @param auxiliaryElements auxiliary elements related to the current orbit
526      *  @param parameters values of the force model parameters
527      *  @return new force model context
528      */
529     private DSSTZonalContext initializeStep(final AuxiliaryElements auxiliaryElements, final double[] parameters) {
530         return new DSSTZonalContext(auxiliaryElements, provider, parameters);
531     }
532 
533     /** Performs initialization at each integration step for the current force model.
534      *  <p>
535      *  This method aims at being called before mean elements rates computation.
536      *  </p>
537      *  @param <T> type of the elements
538      *  @param auxiliaryElements auxiliary elements related to the current orbit
539      *  @param parameters values of the force model parameters
540      *  @return new force model context
541      */
542     private <T extends CalculusFieldElement<T>> FieldDSSTZonalContext<T> initializeStep(final FieldAuxiliaryElements<T> auxiliaryElements,
543                                                                                     final T[] parameters) {
544         return new FieldDSSTZonalContext<>(auxiliaryElements, provider, parameters);
545     }
546 
547     /** {@inheritDoc} */
548     @Override
549     public double[] getMeanElementRate(final SpacecraftState spacecraftState,
550                                        final AuxiliaryElements auxiliaryElements, final double[] parameters) {
551 
552         // Container of attributes
553         final DSSTZonalContext context = initializeStep(auxiliaryElements, parameters);
554         // Access to potential U derivatives
555         final UAnddU udu = new UAnddU(spacecraftState.getDate(), context, auxiliaryElements, hansen);
556 
557         return computeMeanElementRates(spacecraftState.getDate(), context, udu);
558 
559     }
560 
561     /** {@inheritDoc} */
562     @Override
563     public <T extends CalculusFieldElement<T>> T[] getMeanElementRate(final FieldSpacecraftState<T> spacecraftState,
564                                                                   final FieldAuxiliaryElements<T> auxiliaryElements,
565                                                                   final T[] parameters) {
566 
567         // Field used by default
568         final Field<T> field = auxiliaryElements.getDate().getField();
569         // Container of attributes
570         final FieldDSSTZonalContext<T> context = initializeStep(auxiliaryElements, parameters);
571 
572         @SuppressWarnings("unchecked")
573         final FieldHansenObjects<T> fho = (FieldHansenObjects<T>) fieldHansen.get(field);
574 
575         // Access to potential U derivatives
576         final FieldUAnddU<T> udu = new FieldUAnddU<>(spacecraftState.getDate(), context, auxiliaryElements, fho);
577 
578         return computeMeanElementRates(spacecraftState.getDate(), context, udu);
579 
580     }
581 
582     /** {@inheritDoc} */
583     @Override
584     public EventDetector[] getEventsDetectors() {
585         return null;
586     }
587 
588     /** {@inheritDoc} */
589     @Override
590     public <T extends CalculusFieldElement<T>> FieldEventDetector<T>[] getFieldEventsDetectors(final Field<T> field) {
591         return null;
592     }
593 
594     /** Compute the mean element rates.
595      * @param date current date
596      * @param context container for attributes
597      * @param udu derivatives of the gravitational potential U
598      * @return the mean element rates
599      */
600     private double[] computeMeanElementRates(final AbsoluteDate date,
601                                              final DSSTZonalContext context,
602                                              final UAnddU udu) {
603 
604         // Auxiliary elements related to the current orbit
605         final AuxiliaryElements auxiliaryElements = context.getAuxiliaryElements();
606 
607         // Compute cross derivatives [Eq. 2.2-(8)]
608         // U(alpha,gamma) = alpha * dU/dgamma - gamma * dU/dalpha
609         final double UAlphaGamma   = auxiliaryElements.getAlpha() * udu.getdUdGa() - auxiliaryElements.getGamma() * udu.getdUdAl();
610         // U(beta,gamma) = beta * dU/dgamma - gamma * dU/dbeta
611         final double UBetaGamma    =  auxiliaryElements.getBeta() * udu.getdUdGa() - auxiliaryElements.getGamma() * udu.getdUdBe();
612         // Common factor
613         final double pUAGmIqUBGoAB = (auxiliaryElements.getP() * UAlphaGamma - I * auxiliaryElements.getQ() * UBetaGamma) * context.getOoAB();
614 
615         // Compute mean elements rates [Eq. 3.1-(1)]
616         final double da =  0.;
617         final double dh =  context.getBoA() * udu.getdUdk() + auxiliaryElements.getK() * pUAGmIqUBGoAB;
618         final double dk = -context.getBoA() * udu.getdUdh() - auxiliaryElements.getH() * pUAGmIqUBGoAB;
619         final double dp =  context.getMCo2AB() * UBetaGamma;
620         final double dq =  context.getMCo2AB() * UAlphaGamma * I;
621         final double dM =  context.getM2aoA() * udu.getdUda() + context.getBoABpo() * (auxiliaryElements.getH() * udu.getdUdh() + auxiliaryElements.getK() * udu.getdUdk()) + pUAGmIqUBGoAB;
622 
623         return new double[] {da, dk, dh, dq, dp, dM};
624     }
625 
626     /** Compute the mean element rates.
627      * @param <T> type of the elements
628      * @param date current date
629      * @param context container for attributes
630      * @param udu derivatives of the gravitational potential U
631      * @return the mean element rates
632      */
633     private <T extends CalculusFieldElement<T>> T[] computeMeanElementRates(final FieldAbsoluteDate<T> date,
634                                                                         final FieldDSSTZonalContext<T> context,
635                                                                         final FieldUAnddU<T> udu) {
636 
637         // Auxiliary elements related to the current orbit
638         final FieldAuxiliaryElements<T> auxiliaryElements = context.getFieldAuxiliaryElements();
639 
640         // Parameter for array building
641         final Field<T> field = date.getField();
642 
643         // Compute cross derivatives [Eq. 2.2-(8)]
644         // U(alpha,gamma) = alpha * dU/dgamma - gamma * dU/dalpha
645         final T UAlphaGamma   = udu.getdUdGa().multiply(auxiliaryElements.getAlpha()).subtract(udu.getdUdAl().multiply(auxiliaryElements.getGamma()));
646         // U(beta,gamma) = beta * dU/dgamma - gamma * dU/dbeta
647         final T UBetaGamma    =  udu.getdUdGa().multiply(auxiliaryElements.getBeta()).subtract(udu.getdUdBe().multiply(auxiliaryElements.getGamma()));
648         // Common factor
649         final T pUAGmIqUBGoAB = (UAlphaGamma.multiply(auxiliaryElements.getP()).subtract(UBetaGamma.multiply(I).multiply(auxiliaryElements.getQ()))).multiply(context.getOoAB());
650 
651         // Compute mean elements rates [Eq. 3.1-(1)]
652         final T da =  field.getZero();
653         final T dh =  udu.getdUdk().multiply(context.getBoA()).add(pUAGmIqUBGoAB.multiply(auxiliaryElements.getK()));
654         final T dk =  (udu.getdUdh().multiply(context.getBoA()).negate()).subtract(pUAGmIqUBGoAB.multiply(auxiliaryElements.getH()));
655         final T dp =  UBetaGamma.multiply(context.getMCo2AB());
656         final T dq =  UAlphaGamma.multiply(context.getMCo2AB()).multiply(I);
657         final T dM =  pUAGmIqUBGoAB.add(udu.getdUda().multiply(context.getM2aoA())).add((udu.getdUdh().multiply(auxiliaryElements.getH()).add(udu.getdUdk().multiply(auxiliaryElements.getK()))).multiply(context.getBoABpo()));
658 
659         final T[] elements =  MathArrays.buildArray(field, 6);
660         elements[0] = da;
661         elements[1] = dk;
662         elements[2] = dh;
663         elements[3] = dq;
664         elements[4] = dp;
665         elements[5] = dM;
666 
667         return elements;
668     }
669 
670     /** {@inheritDoc} */
671     @Override
672     public void registerAttitudeProvider(final AttitudeProvider attitudeProvider) {
673         //nothing is done since this contribution is not sensitive to attitude
674     }
675 
676     /** Check if an index is within the accepted interval.
677      *
678      * @param index the index to check
679      * @param lowerBound the lower bound of the interval
680      * @param upperBound the upper bound of the interval
681      * @return true if the index is between the lower and upper bounds, false otherwise
682      */
683     private boolean isBetween(final int index, final int lowerBound, final int upperBound) {
684         return index >= lowerBound && index <= upperBound;
685     }
686 
687     /** {@inheritDoc} */
688     @Override
689     public void updateShortPeriodTerms(final double[] parameters, final SpacecraftState... meanStates) {
690 
691         final Slot slot = zonalSPCoefs.createSlot(meanStates);
692         for (final SpacecraftState meanState : meanStates) {
693 
694             // Auxiliary elements related to the current orbit
695             final AuxiliaryElements auxiliaryElements = new AuxiliaryElements(meanState.getOrbit(), I);
696 
697             // Container of attributes
698             final DSSTZonalContext context = initializeStep(auxiliaryElements, parameters);
699 
700             // Access to potential U derivatives
701             final UAnddU udu = new UAnddU(meanState.getDate(), context, auxiliaryElements, hansen);
702 
703             // Compute rhoj and sigmaj
704             final double[][] rhoSigma = computeRhoSigmaCoefficients(meanState.getDate(), slot, auxiliaryElements);
705 
706             // Compute Di
707             computeDiCoefficients(meanState.getDate(), slot, context, udu);
708 
709             // generate the Cij and Sij coefficients
710             final FourierCjSjCoefficients cjsj = new FourierCjSjCoefficients(meanState.getDate(),
711                                                                              maxDegreeShortPeriodics,
712                                                                              maxEccPowShortPeriodics,
713                                                                              maxFrequencyShortPeriodics,
714                                                                              context,
715                                                                              hansen);
716 
717             computeCijSijCoefficients(meanState.getDate(), slot, cjsj, rhoSigma, context, auxiliaryElements, udu);
718         }
719 
720     }
721 
722     /** {@inheritDoc} */
723     @Override
724     @SuppressWarnings("unchecked")
725     public <T extends CalculusFieldElement<T>> void updateShortPeriodTerms(final T[] parameters,
726                                                                        final FieldSpacecraftState<T>... meanStates) {
727 
728         // Field used by default
729         final Field<T> field = meanStates[0].getDate().getField();
730 
731         final FieldZonalShortPeriodicCoefficients<T> fzspc = (FieldZonalShortPeriodicCoefficients<T>) zonalFieldSPCoefs.get(field);
732         final FieldSlot<T> slot = fzspc.createSlot(meanStates);
733         for (final FieldSpacecraftState<T> meanState : meanStates) {
734 
735             // Auxiliary elements related to the current orbit
736             final FieldAuxiliaryElements<T> auxiliaryElements = new FieldAuxiliaryElements<>(meanState.getOrbit(), I);
737 
738             // Container of attributes
739             final FieldDSSTZonalContext<T> context = initializeStep(auxiliaryElements, parameters);
740 
741             final FieldHansenObjects<T> fho = (FieldHansenObjects<T>) fieldHansen.get(field);
742 
743             // Access to potential U derivatives
744             final FieldUAnddU<T> udu = new FieldUAnddU<>(meanState.getDate(), context, auxiliaryElements, fho);
745 
746             // Compute rhoj and sigmaj
747             final T[][] rhoSigma = computeRhoSigmaCoefficients(meanState.getDate(), slot, auxiliaryElements, field);
748 
749             // Compute Di
750             computeDiCoefficients(meanState.getDate(), slot, context, field, udu);
751 
752             // generate the Cij and Sij coefficients
753             final FieldFourierCjSjCoefficients<T> cjsj = new FieldFourierCjSjCoefficients<>(meanState.getDate(),
754                                                                                             maxDegreeShortPeriodics,
755                                                                                             maxEccPowShortPeriodics,
756                                                                                             maxFrequencyShortPeriodics,
757                                                                                             context,
758                                                                                             fho);
759 
760 
761             computeCijSijCoefficients(meanState.getDate(), slot, cjsj, rhoSigma, context, auxiliaryElements, field, udu);
762         }
763     }
764 
765     /** {@inheritDoc} */
766     public List<ParameterDriver> getParametersDrivers() {
767         return Collections.singletonList(gmParameterDriver);
768     }
769 
770     /** Generate the values for the D<sub>i</sub> coefficients.
771      * @param date target date
772      * @param slot slot to which the coefficients belong
773      * @param context container for attributes
774      * @param udu derivatives of the gravitational potential U
775      */
776     private void computeDiCoefficients(final AbsoluteDate date,
777                                        final Slot slot,
778                                        final DSSTZonalContext context,
779                                        final UAnddU udu) {
780 
781         final double[] meanElementRates = computeMeanElementRates(date, context, udu);
782 
783         final double[] currentDi = new double[6];
784 
785         // Add the coefficients to the interpolation grid
786         for (int i = 0; i < 6; i++) {
787             currentDi[i] = meanElementRates[i] / context.getMeanMotion();
788 
789             if (i == 5) {
790                 currentDi[i] += -1.5 * 2 * udu.getU() * context.getOON2A2();
791             }
792 
793         }
794 
795         slot.di.addGridPoint(date, currentDi);
796 
797     }
798 
799     /** Generate the values for the D<sub>i</sub> coefficients.
800      * @param <T> type of the elements
801      * @param date target date
802      * @param slot slot to which the coefficients belong
803      * @param context container for attributes
804      * @param field field used by default
805      * @param udu derivatives of the gravitational potential U
806      */
807     private <T extends CalculusFieldElement<T>> void computeDiCoefficients(final FieldAbsoluteDate<T> date,
808                                                                        final FieldSlot<T> slot,
809                                                                        final FieldDSSTZonalContext<T> context,
810                                                                        final Field<T> field,
811                                                                        final FieldUAnddU<T> udu) {
812 
813         final T[] meanElementRates = computeMeanElementRates(date, context, udu);
814 
815         final T[] currentDi = MathArrays.buildArray(field, 6);
816 
817         // Add the coefficients to the interpolation grid
818         for (int i = 0; i < 6; i++) {
819             currentDi[i] = meanElementRates[i].divide(context.getMeanMotion());
820 
821             if (i == 5) {
822                 currentDi[i] = currentDi[i].add(context.getOON2A2().multiply(udu.getU()).multiply(2.).multiply(-1.5));
823             }
824 
825         }
826 
827         slot.di.addGridPoint(date, currentDi);
828 
829     }
830 
831     /**
832      * Generate the values for the C<sub>i</sub><sup>j</sup> and the S<sub>i</sub><sup>j</sup> coefficients.
833      * @param date date of computation
834      * @param slot slot to which the coefficients belong
835      * @param cjsj Fourier coefficients
836      * @param rhoSigma ρ<sub>j</sub> and σ<sub>j</sub>
837      * @param context container for attributes
838      * @param auxiliaryElements auxiliary elements related to the current orbit
839      * @param udu derivatives of the gravitational potential U
840      */
841     private void computeCijSijCoefficients(final AbsoluteDate date, final Slot slot,
842                                            final FourierCjSjCoefficients cjsj,
843                                            final double[][] rhoSigma, final DSSTZonalContext context,
844                                            final AuxiliaryElements auxiliaryElements,
845                                            final UAnddU udu) {
846 
847         final int nMax = maxDegreeShortPeriodics;
848 
849         // The C<sub>i</sub>⁰ coefficients
850         final double[] currentCi0 = new double[] {0., 0., 0., 0., 0., 0.};
851         for (int j = 1; j < slot.cij.length; j++) {
852 
853             // Create local arrays
854             final double[] currentCij = new double[] {0., 0., 0., 0., 0., 0.};
855             final double[] currentSij = new double[] {0., 0., 0., 0., 0., 0.};
856 
857             // j == 1
858             if (j == 1) {
859                 final double coef1 = 4 * auxiliaryElements.getK() * udu.getU() - context.getHK() * cjsj.getCj(1) + context.getK2MH2O2() * cjsj.getSj(1);
860                 final double coef2 = 4 * auxiliaryElements.getH() * udu.getU() + context.getK2MH2O2() * cjsj.getCj(1) + context.getHK() * cjsj.getSj(1);
861                 final double coef3 = (auxiliaryElements.getK() * cjsj.getCj(1) + auxiliaryElements.getH() * cjsj.getSj(1)) / 4.;
862                 final double coef4 = (8 * udu.getU() - auxiliaryElements.getH() * cjsj.getCj(1) + auxiliaryElements.getK() * cjsj.getSj(1)) / 4.;
863 
864                 //Coefficients for a
865                 currentCij[0] += coef1;
866                 currentSij[0] += coef2;
867 
868                 //Coefficients for k
869                 currentCij[1] += coef4;
870                 currentSij[1] += coef3;
871 
872                 //Coefficients for h
873                 currentCij[2] -= coef3;
874                 currentSij[2] += coef4;
875 
876                 //Coefficients for λ
877                 currentCij[5] -= coef2 / 2;
878                 currentSij[5] += coef1 / 2;
879             }
880 
881             // j == 2
882             if (j == 2) {
883                 final double coef1 = context.getK2MH2() * udu.getU();
884                 final double coef2 = 2 * context.getHK() * udu.getU();
885                 final double coef3 = auxiliaryElements.getH() * udu.getU() / 2;
886                 final double coef4 = auxiliaryElements.getK() * udu.getU() / 2;
887 
888                 //Coefficients for a
889                 currentCij[0] += coef1;
890                 currentSij[0] += coef2;
891 
892                 //Coefficients for k
893                 currentCij[1] += coef4;
894                 currentSij[1] += coef3;
895 
896                 //Coefficients for h
897                 currentCij[2] -= coef3;
898                 currentSij[2] += coef4;
899 
900                 //Coefficients for λ
901                 currentCij[5] -= coef2 / 2;
902                 currentSij[5] += coef1 / 2;
903             }
904 
905             // j between 1 and 2N-3
906             if (isBetween(j, 1, 2 * nMax - 3) && j + 2 < cjsj.jMax) {
907                 final double coef1 = ( j + 2 ) * (-context.getHK() * cjsj.getCj(j + 2) + context.getK2MH2O2() * cjsj.getSj(j + 2));
908                 final double coef2 = ( j + 2 ) * (context.getK2MH2O2() * cjsj.getCj(j + 2) + context.getHK() * cjsj.getSj(j + 2));
909                 final double coef3 = ( j + 2 ) * (auxiliaryElements.getK() * cjsj.getCj(j + 2) + auxiliaryElements.getH() * cjsj.getSj(j + 2)) / 4;
910                 final double coef4 = ( j + 2 ) * (auxiliaryElements.getH() * cjsj.getCj(j + 2) - auxiliaryElements.getK() * cjsj.getSj(j + 2)) / 4;
911 
912                 //Coefficients for a
913                 currentCij[0] += coef1;
914                 currentSij[0] -= coef2;
915 
916                 //Coefficients for k
917                 currentCij[1] += -coef4;
918                 currentSij[1] -= coef3;
919 
920                 //Coefficients for h
921                 currentCij[2] -= coef3;
922                 currentSij[2] += coef4;
923 
924                 //Coefficients for λ
925                 currentCij[5] -= coef2 / 2;
926                 currentSij[5] += -coef1 / 2;
927             }
928 
929             // j between 1 and 2N-2
930             if (isBetween(j, 1, 2 * nMax - 2) && j + 1 < cjsj.jMax) {
931                 final double coef1 = 2 * ( j + 1 ) * (-auxiliaryElements.getH() * cjsj.getCj(j + 1) + auxiliaryElements.getK() * cjsj.getSj(j + 1));
932                 final double coef2 = 2 * ( j + 1 ) * (auxiliaryElements.getK() * cjsj.getCj(j + 1) + auxiliaryElements.getH() * cjsj.getSj(j + 1));
933                 final double coef3 = ( j + 1 ) * cjsj.getCj(j + 1);
934                 final double coef4 = ( j + 1 ) * cjsj.getSj(j + 1);
935 
936                 //Coefficients for a
937                 currentCij[0] += coef1;
938                 currentSij[0] -= coef2;
939 
940                 //Coefficients for k
941                 currentCij[1] += coef4;
942                 currentSij[1] -= coef3;
943 
944                 //Coefficients for h
945                 currentCij[2] -= coef3;
946                 currentSij[2] -= coef4;
947 
948                 //Coefficients for λ
949                 currentCij[5] -= coef2 / 2;
950                 currentSij[5] += -coef1 / 2;
951             }
952 
953             // j between 2 and 2N
954             if (isBetween(j, 2, 2 * nMax) && j - 1 < cjsj.jMax) {
955                 final double coef1 = 2 * ( j - 1 ) * (auxiliaryElements.getH() * cjsj.getCj(j - 1) + auxiliaryElements.getK() * cjsj.getSj(j - 1));
956                 final double coef2 = 2 * ( j - 1 ) * (auxiliaryElements.getK() * cjsj.getCj(j - 1) - auxiliaryElements.getH() * cjsj.getSj(j - 1));
957                 final double coef3 = ( j - 1 ) * cjsj.getCj(j - 1);
958                 final double coef4 = ( j - 1 ) * cjsj.getSj(j - 1);
959 
960                 //Coefficients for a
961                 currentCij[0] += coef1;
962                 currentSij[0] -= coef2;
963 
964                 //Coefficients for k
965                 currentCij[1] += coef4;
966                 currentSij[1] -= coef3;
967 
968                 //Coefficients for h
969                 currentCij[2] += coef3;
970                 currentSij[2] += coef4;
971 
972                 //Coefficients for λ
973                 currentCij[5] += coef2 / 2;
974                 currentSij[5] += coef1 / 2;
975             }
976 
977             // j between 3 and 2N + 1
978             if (isBetween(j, 3, 2 * nMax + 1) && j - 2 < cjsj.jMax) {
979                 final double coef1 = ( j - 2 ) * (context.getHK() * cjsj.getCj(j - 2) + context.getK2MH2O2() * cjsj.getSj(j - 2));
980                 final double coef2 = ( j - 2 ) * (-context.getK2MH2O2() * cjsj.getCj(j - 2) + context.getHK() * cjsj.getSj(j - 2));
981                 final double coef3 = ( j - 2 ) * (auxiliaryElements.getK() * cjsj.getCj(j - 2) - auxiliaryElements.getH() * cjsj.getSj(j - 2)) / 4;
982                 final double coef4 = ( j - 2 ) * (auxiliaryElements.getH() * cjsj.getCj(j - 2) + auxiliaryElements.getK() * cjsj.getSj(j - 2)) / 4;
983                 final double coef5 = ( j - 2 ) * (context.getK2MH2O2() * cjsj.getCj(j - 2) - context.getHK() * cjsj.getSj(j - 2));
984 
985                 //Coefficients for a
986                 currentCij[0] += coef1;
987                 currentSij[0] += coef2;
988 
989                 //Coefficients for k
990                 currentCij[1] += coef4;
991                 currentSij[1] += -coef3;
992 
993                 //Coefficients for h
994                 currentCij[2] += coef3;
995                 currentSij[2] += coef4;
996 
997                 //Coefficients for λ
998                 currentCij[5] += coef5 / 2;
999                 currentSij[5] += coef1 / 2;
1000             }
1001 
1002             //multiply by the common factor
1003             //for a (i == 0) -> χ³ / (n² * a)
1004             currentCij[0] *= context.getX3ON2A();
1005             currentSij[0] *= context.getX3ON2A();
1006             //for k (i == 1) -> χ / (n² * a²)
1007             currentCij[1] *= context.getXON2A2();
1008             currentSij[1] *= context.getXON2A2();
1009             //for h (i == 2) -> χ / (n² * a²)
1010             currentCij[2] *= context.getXON2A2();
1011             currentSij[2] *= context.getXON2A2();
1012             //for λ (i == 5) -> (χ²) / (n² * a² * (χ + 1 ) )
1013             currentCij[5] *= context.getX2ON2A2XP1();
1014             currentSij[5] *= context.getX2ON2A2XP1();
1015 
1016             // j is between 1 and 2 * N - 1
1017             if (isBetween(j, 1, 2 * nMax - 1) && j < cjsj.jMax) {
1018                 // Compute cross derivatives
1019                 // Cj(alpha,gamma) = alpha * dC/dgamma - gamma * dC/dalpha
1020                 final double CjAlphaGamma   = auxiliaryElements.getAlpha() * cjsj.getdCjdGamma(j) - auxiliaryElements.getGamma() * cjsj.getdCjdAlpha(j);
1021                 // Cj(alpha,beta) = alpha * dC/dbeta - beta * dC/dalpha
1022                 final double CjAlphaBeta   = auxiliaryElements.getAlpha() * cjsj.getdCjdBeta(j) - auxiliaryElements.getBeta() * cjsj.getdCjdAlpha(j);
1023                 // Cj(beta,gamma) = beta * dC/dgamma - gamma * dC/dbeta
1024                 final double CjBetaGamma    =  auxiliaryElements.getBeta() * cjsj.getdCjdGamma(j) - auxiliaryElements.getGamma() * cjsj.getdCjdBeta(j);
1025                 // Cj(h,k) = h * dC/dk - k * dC/dh
1026                 final double CjHK   = auxiliaryElements.getH() * cjsj.getdCjdK(j) - auxiliaryElements.getK() * cjsj.getdCjdH(j);
1027                 // Sj(alpha,gamma) = alpha * dS/dgamma - gamma * dS/dalpha
1028                 final double SjAlphaGamma   = auxiliaryElements.getAlpha() * cjsj.getdSjdGamma(j) - auxiliaryElements.getGamma() * cjsj.getdSjdAlpha(j);
1029                 // Sj(alpha,beta) = alpha * dS/dbeta - beta * dS/dalpha
1030                 final double SjAlphaBeta   = auxiliaryElements.getAlpha() * cjsj.getdSjdBeta(j) - auxiliaryElements.getBeta() * cjsj.getdSjdAlpha(j);
1031                 // Sj(beta,gamma) = beta * dS/dgamma - gamma * dS/dbeta
1032                 final double SjBetaGamma    =  auxiliaryElements.getBeta() * cjsj.getdSjdGamma(j) - auxiliaryElements.getGamma() * cjsj.getdSjdBeta(j);
1033                 // Sj(h,k) = h * dS/dk - k * dS/dh
1034                 final double SjHK   = auxiliaryElements.getH() * cjsj.getdSjdK(j) - auxiliaryElements.getK() * cjsj.getdSjdH(j);
1035 
1036                 //Coefficients for a
1037                 final double coef1 = context.getX3ON2A() * (3 - context.getBB()) * j;
1038                 currentCij[0] += coef1 * cjsj.getSj(j);
1039                 currentSij[0] -= coef1 * cjsj.getCj(j);
1040 
1041                 //Coefficients for k and h
1042                 final double coef2 = auxiliaryElements.getP() * CjAlphaGamma - I * auxiliaryElements.getQ() * CjBetaGamma;
1043                 final double coef3 = auxiliaryElements.getP() * SjAlphaGamma - I * auxiliaryElements.getQ() * SjBetaGamma;
1044                 currentCij[1] -= context.getXON2A2() * (auxiliaryElements.getH() * coef2 + context.getBB() * cjsj.getdCjdH(j) - 1.5 * auxiliaryElements.getK() * j * cjsj.getSj(j));
1045                 currentSij[1] -= context.getXON2A2() * (auxiliaryElements.getH() * coef3 + context.getBB() * cjsj.getdSjdH(j) + 1.5 * auxiliaryElements.getK() * j * cjsj.getCj(j));
1046                 currentCij[2] += context.getXON2A2() * (auxiliaryElements.getK() * coef2 + context.getBB() * cjsj.getdCjdK(j) + 1.5 * auxiliaryElements.getH() * j * cjsj.getSj(j));
1047                 currentSij[2] += context.getXON2A2() * (auxiliaryElements.getK() * coef3 + context.getBB() * cjsj.getdSjdK(j) - 1.5 * auxiliaryElements.getH() * j * cjsj.getCj(j));
1048 
1049                 //Coefficients for q and p
1050                 final double coef4 = CjHK - CjAlphaBeta - j * cjsj.getSj(j);
1051                 final double coef5 = SjHK - SjAlphaBeta + j * cjsj.getCj(j);
1052                 currentCij[3] = context.getCXO2N2A2() * (-I * CjAlphaGamma + auxiliaryElements.getQ() * coef4);
1053                 currentSij[3] = context.getCXO2N2A2() * (-I * SjAlphaGamma + auxiliaryElements.getQ() * coef5);
1054                 currentCij[4] = context.getCXO2N2A2() * (-CjBetaGamma + auxiliaryElements.getP() * coef4);
1055                 currentSij[4] = context.getCXO2N2A2() * (-SjBetaGamma + auxiliaryElements.getP() * coef5);
1056 
1057                 //Coefficients for λ
1058                 final double coef6 = auxiliaryElements.getH() * cjsj.getdCjdH(j) + auxiliaryElements.getK() * cjsj.getdCjdK(j);
1059                 final double coef7 = auxiliaryElements.getH() * cjsj.getdSjdH(j) + auxiliaryElements.getK() * cjsj.getdSjdK(j);
1060                 currentCij[5] += context.getOON2A2() * (-2 * auxiliaryElements.getSma() * cjsj.getdCjdA(j) + coef6 / (context.getX() + 1) + context.getX() * coef2 - 3 * cjsj.getCj(j));
1061                 currentSij[5] += context.getOON2A2() * (-2 * auxiliaryElements.getSma() * cjsj.getdSjdA(j) + coef7 / (context.getX() + 1) + context.getX() * coef3 - 3 * cjsj.getSj(j));
1062             }
1063 
1064             for (int i = 0; i < 6; i++) {
1065                 //Add the current coefficients contribution to C<sub>i</sub>⁰
1066                 currentCi0[i] -= currentCij[i] * rhoSigma[j][0] + currentSij[i] * rhoSigma[j][1];
1067             }
1068 
1069             // Add the coefficients to the interpolation grid
1070             slot.cij[j].addGridPoint(date, currentCij);
1071             slot.sij[j].addGridPoint(date, currentSij);
1072 
1073         }
1074 
1075         //Add C<sub>i</sub>⁰ to the interpolation grid
1076         slot.cij[0].addGridPoint(date, currentCi0);
1077 
1078     }
1079 
1080     /**
1081      * Generate the values for the C<sub>i</sub><sup>j</sup> and the S<sub>i</sub><sup>j</sup> coefficients.
1082      * @param <T> type of the elements
1083      * @param date date of computation
1084      * @param slot slot to which the coefficients belong
1085      * @param cjsj Fourier coefficients
1086      * @param rhoSigma ρ<sub>j</sub> and σ<sub>j</sub>
1087      * @param context container for attributes
1088      * @param auxiliaryElements auxiliary elements related to the current orbit
1089      * @param field field used by default
1090      * @param udu derivatives of the gravitational potential U
1091      */
1092     private <T extends CalculusFieldElement<T>> void computeCijSijCoefficients(final FieldAbsoluteDate<T> date,
1093                                                                            final FieldSlot<T> slot,
1094                                                                            final FieldFourierCjSjCoefficients<T> cjsj,
1095                                                                            final T[][] rhoSigma,
1096                                                                            final FieldDSSTZonalContext<T> context,
1097                                                                            final FieldAuxiliaryElements<T> auxiliaryElements,
1098                                                                            final Field<T> field,
1099                                                                            final FieldUAnddU<T> udu) {
1100 
1101         // Zero
1102         final T zero = field.getZero();
1103 
1104         final int nMax = maxDegreeShortPeriodics;
1105 
1106         // The C<sub>i</sub>⁰ coefficients
1107         final T[] currentCi0 = MathArrays.buildArray(field, 6);
1108         Arrays.fill(currentCi0, zero);
1109 
1110         for (int j = 1; j < slot.cij.length; j++) {
1111 
1112             // Create local arrays
1113             final T[] currentCij = MathArrays.buildArray(field, 6);
1114             final T[] currentSij = MathArrays.buildArray(field, 6);
1115 
1116             Arrays.fill(currentCij, zero);
1117             Arrays.fill(currentSij, zero);
1118 
1119             // j == 1
1120             if (j == 1) {
1121                 final T coef1 = auxiliaryElements.getK().multiply(udu.getU()).multiply(4.).subtract(context.getHK().multiply(cjsj.getCj(1))).add(context.getK2MH2O2().multiply(cjsj.getSj(1)));
1122                 final T coef2 = auxiliaryElements.getH().multiply(udu.getU()).multiply(4.).add(context.getK2MH2O2().multiply(cjsj.getCj(1))).add(context.getHK().multiply(cjsj.getSj(1)));
1123                 final T coef3 = auxiliaryElements.getK().multiply(cjsj.getCj(1)).add(auxiliaryElements.getH().multiply(cjsj.getSj(1))).divide(4.);
1124                 final T coef4 = udu.getU().multiply(8.).subtract(auxiliaryElements.getH().multiply(cjsj.getCj(1))).add(auxiliaryElements.getK().multiply(cjsj.getSj(1))).divide(4.);
1125 
1126                 //Coefficients for a
1127                 currentCij[0] = currentCij[0].add(coef1);
1128                 currentSij[0] = currentSij[0].add(coef2);
1129 
1130                 //Coefficients for k
1131                 currentCij[1] = currentCij[1].add(coef4);
1132                 currentSij[1] = currentSij[1].add(coef3);
1133 
1134                 //Coefficients for h
1135                 currentCij[2] = currentCij[2].subtract(coef3);
1136                 currentSij[2] = currentSij[2].add(coef4);
1137 
1138                 //Coefficients for λ
1139                 currentCij[5] = currentCij[5].subtract(coef2.divide(2.));
1140                 currentSij[5] = currentSij[5].add(coef1.divide(2.));
1141             }
1142 
1143             // j == 2
1144             if (j == 2) {
1145                 final T coef1 = context.getK2MH2().multiply(udu.getU());
1146                 final T coef2 = context.getHK().multiply(udu.getU()).multiply(2.);
1147                 final T coef3 = auxiliaryElements.getH().multiply(udu.getU()).divide(2.);
1148                 final T coef4 = auxiliaryElements.getK().multiply(udu.getU()).divide(2.);
1149 
1150                 //Coefficients for a
1151                 currentCij[0] = currentCij[0].add(coef1);
1152                 currentSij[0] = currentSij[0].add(coef2);
1153 
1154                 //Coefficients for k
1155                 currentCij[1] = currentCij[1].add(coef4);
1156                 currentSij[1] = currentSij[1].add(coef3);
1157 
1158                 //Coefficients for h
1159                 currentCij[2] = currentCij[2].subtract(coef3);
1160                 currentSij[2] = currentSij[2].add(coef4);
1161 
1162                 //Coefficients for λ
1163                 currentCij[5] = currentCij[5].subtract(coef2.divide(2.));
1164                 currentSij[5] = currentSij[5].add(coef1.divide(2.));
1165             }
1166 
1167             // j between 1 and 2N-3
1168             if (isBetween(j, 1, 2 * nMax - 3) && j + 2 < cjsj.jMax) {
1169                 final T coef1 = context.getHK().negate().multiply(cjsj.getCj(j + 2)).add(context.getK2MH2O2().multiply(cjsj.getSj(j + 2))).multiply(j + 2);
1170                 final T coef2 = context.getK2MH2O2().multiply(cjsj.getCj(j + 2)).add(context.getHK().multiply(cjsj.getSj(j + 2))).multiply(j + 2);
1171                 final T coef3 = auxiliaryElements.getK().multiply(cjsj.getCj(j + 2)).add(auxiliaryElements.getH().multiply(cjsj.getSj(j + 2))).multiply(j + 2).divide(4.);
1172                 final T coef4 = auxiliaryElements.getH().multiply(cjsj.getCj(j + 2)).subtract(auxiliaryElements.getK().multiply(cjsj.getSj(j + 2))).multiply(j + 2).divide(4.);
1173 
1174                 //Coefficients for a
1175                 currentCij[0] = currentCij[0].add(coef1);
1176                 currentSij[0] = currentSij[0].subtract(coef2);
1177 
1178                 //Coefficients for k
1179                 currentCij[1] = currentCij[1].add(coef4.negate());
1180                 currentSij[1] = currentSij[1].subtract(coef3);
1181 
1182                 //Coefficients for h
1183                 currentCij[2] = currentCij[2].subtract(coef3);
1184                 currentSij[2] = currentSij[2].add(coef4);
1185 
1186                 //Coefficients for λ
1187                 currentCij[5] = currentCij[5].subtract(coef2.divide(2.));
1188                 currentSij[5] = currentSij[5].add(coef1.negate().divide(2.));
1189             }
1190 
1191             // j between 1 and 2N-2
1192             if (isBetween(j, 1, 2 * nMax - 2) && j + 1 < cjsj.jMax) {
1193                 final T coef1 = auxiliaryElements.getH().negate().multiply(cjsj.getCj(j + 1)).add(auxiliaryElements.getK().multiply(cjsj.getSj(j + 1))).multiply(2. * (j + 1));
1194                 final T coef2 = auxiliaryElements.getK().multiply(cjsj.getCj(j + 1)).add(auxiliaryElements.getH().multiply(cjsj.getSj(j + 1))).multiply(2. * (j + 1));
1195                 final T coef3 = cjsj.getCj(j + 1).multiply(j + 1);
1196                 final T coef4 = cjsj.getSj(j + 1).multiply(j + 1);
1197 
1198                 //Coefficients for a
1199                 currentCij[0] = currentCij[0].add(coef1);
1200                 currentSij[0] = currentSij[0].subtract(coef2);
1201 
1202                 //Coefficients for k
1203                 currentCij[1] = currentCij[1].add(coef4);
1204                 currentSij[1] = currentSij[1].subtract(coef3);
1205 
1206                 //Coefficients for h
1207                 currentCij[2] = currentCij[2].subtract(coef3);
1208                 currentSij[2] = currentSij[2].subtract(coef4);
1209 
1210                 //Coefficients for λ
1211                 currentCij[5] = currentCij[5].subtract(coef2.divide(2.));
1212                 currentSij[5] = currentSij[5].add(coef1.negate().divide(2.));
1213             }
1214 
1215             // j between 2 and 2N
1216             if (isBetween(j, 2, 2 * nMax) && j - 1 < cjsj.jMax) {
1217                 final T coef1 = auxiliaryElements.getH().multiply(cjsj.getCj(j - 1)).add(auxiliaryElements.getK().multiply(cjsj.getSj(j - 1))).multiply(2 * ( j - 1 ));
1218                 final T coef2 = auxiliaryElements.getK().multiply(cjsj.getCj(j - 1)).subtract(auxiliaryElements.getH().multiply(cjsj.getSj(j - 1))).multiply(2 * ( j - 1 ));
1219                 final T coef3 = cjsj.getCj(j - 1).multiply(j - 1);
1220                 final T coef4 = cjsj.getSj(j - 1).multiply(j - 1);
1221 
1222                 //Coefficients for a
1223                 currentCij[0] = currentCij[0].add(coef1);
1224                 currentSij[0] = currentSij[0].subtract(coef2);
1225 
1226                 //Coefficients for k
1227                 currentCij[1] = currentCij[1].add(coef4);
1228                 currentSij[1] = currentSij[1].subtract(coef3);
1229 
1230                 //Coefficients for h
1231                 currentCij[2] = currentCij[2].add(coef3);
1232                 currentSij[2] = currentSij[2].add(coef4);
1233 
1234                 //Coefficients for λ
1235                 currentCij[5] = currentCij[5].add(coef2.divide(2.));
1236                 currentSij[5] = currentSij[5].add(coef1.divide(2.));
1237             }
1238 
1239             // j between 3 and 2N + 1
1240             if (isBetween(j, 3, 2 * nMax + 1) && j - 2 < cjsj.jMax) {
1241                 final T coef1 = context.getHK().multiply(cjsj.getCj(j - 2)).add(context.getK2MH2O2().multiply(cjsj.getSj(j - 2))).multiply(j - 2);
1242                 final T coef2 = context.getK2MH2O2().negate().multiply(cjsj.getCj(j - 2)).add(context.getHK().multiply(cjsj.getSj(j - 2))).multiply(j - 2);
1243                 final T coef3 = auxiliaryElements.getK().multiply(cjsj.getCj(j - 2)).subtract(auxiliaryElements.getH().multiply(cjsj.getSj(j - 2))).multiply(j - 2).divide(4.);
1244                 final T coef4 = auxiliaryElements.getH().multiply(cjsj.getCj(j - 2)).add(auxiliaryElements.getK().multiply(cjsj.getSj(j - 2))).multiply(j - 2).divide(4.);
1245                 final T coef5 = context.getK2MH2O2().multiply(cjsj.getCj(j - 2)).subtract(context.getHK().multiply(cjsj.getSj(j - 2))).multiply(j - 2);
1246 
1247                 //Coefficients for a
1248                 currentCij[0] = currentCij[0].add(coef1);
1249                 currentSij[0] = currentSij[0].add(coef2);
1250 
1251                 //Coefficients for k
1252                 currentCij[1] = currentCij[1].add(coef4);
1253                 currentSij[1] = currentSij[1].add(coef3.negate());
1254 
1255                 //Coefficients for h
1256                 currentCij[2] = currentCij[2].add(coef3);
1257                 currentSij[2] = currentSij[2].add(coef4);
1258 
1259                 //Coefficients for λ
1260                 currentCij[5] = currentCij[5].add(coef5.divide(2.));
1261                 currentSij[5] = currentSij[5].add(coef1.divide(2.));
1262             }
1263 
1264             //multiply by the common factor
1265             //for a (i == 0) -> χ³ / (n² * a)
1266             currentCij[0] = currentCij[0].multiply(context.getX3ON2A());
1267             currentSij[0] = currentSij[0].multiply(context.getX3ON2A());
1268             //for k (i == 1) -> χ / (n² * a²)
1269             currentCij[1] = currentCij[1].multiply(context.getXON2A2());
1270             currentSij[1] = currentSij[1].multiply(context.getXON2A2());
1271             //for h (i == 2) -> χ / (n² * a²)
1272             currentCij[2] = currentCij[2].multiply(context.getXON2A2());
1273             currentSij[2] = currentSij[2].multiply(context.getXON2A2());
1274             //for λ (i == 5) -> (χ²) / (n² * a² * (χ + 1 ) )
1275             currentCij[5] = currentCij[5].multiply(context.getX2ON2A2XP1());
1276             currentSij[5] = currentSij[5].multiply(context.getX2ON2A2XP1());
1277 
1278             // j is between 1 and 2 * N - 1
1279             if (isBetween(j, 1, 2 * nMax - 1) && j < cjsj.jMax) {
1280                 // Compute cross derivatives
1281                 // Cj(alpha,gamma) = alpha * dC/dgamma - gamma * dC/dalpha
1282                 final T CjAlphaGamma = auxiliaryElements.getAlpha().multiply(cjsj.getdCjdGamma(j)).subtract(auxiliaryElements.getGamma().multiply(cjsj.getdCjdAlpha(j)));
1283                 // Cj(alpha,beta) = alpha * dC/dbeta - beta * dC/dalpha
1284                 final T CjAlphaBeta  = auxiliaryElements.getAlpha().multiply(cjsj.getdCjdBeta(j)).subtract(auxiliaryElements.getBeta().multiply(cjsj.getdCjdAlpha(j)));
1285                 // Cj(beta,gamma) = beta * dC/dgamma - gamma * dC/dbeta
1286                 final T CjBetaGamma  = auxiliaryElements.getBeta().multiply(cjsj.getdCjdGamma(j)).subtract(auxiliaryElements.getGamma().multiply(cjsj.getdCjdBeta(j)));
1287                 // Cj(h,k) = h * dC/dk - k * dC/dh
1288                 final T CjHK         = auxiliaryElements.getH().multiply(cjsj.getdCjdK(j)).subtract(auxiliaryElements.getK().multiply(cjsj.getdCjdH(j)));
1289                 // Sj(alpha,gamma) = alpha * dS/dgamma - gamma * dS/dalpha
1290                 final T SjAlphaGamma = auxiliaryElements.getAlpha().multiply(cjsj.getdSjdGamma(j)).subtract(auxiliaryElements.getGamma().multiply(cjsj.getdSjdAlpha(j)));
1291                 // Sj(alpha,beta) = alpha * dS/dbeta - beta * dS/dalpha
1292                 final T SjAlphaBeta  = auxiliaryElements.getAlpha().multiply(cjsj.getdSjdBeta(j)).subtract(auxiliaryElements.getBeta().multiply(cjsj.getdSjdAlpha(j)));
1293                 // Sj(beta,gamma) = beta * dS/dgamma - gamma * dS/dbeta
1294                 final T SjBetaGamma  = auxiliaryElements.getBeta().multiply(cjsj.getdSjdGamma(j)).subtract(auxiliaryElements.getGamma().multiply(cjsj.getdSjdBeta(j)));
1295                 // Sj(h,k) = h * dS/dk - k * dS/dh
1296                 final T SjHK         = auxiliaryElements.getH().multiply(cjsj.getdSjdK(j)).subtract(auxiliaryElements.getK().multiply(cjsj.getdSjdH(j)));
1297 
1298                 //Coefficients for a
1299                 final T coef1 = context.getX3ON2A().multiply(context.getBB().negate().add(3.)).multiply(j);
1300                 currentCij[0] = currentCij[0].add(coef1.multiply(cjsj.getSj(j)));
1301                 currentSij[0] = currentSij[0].subtract(coef1.multiply(cjsj.getCj(j)));
1302 
1303                 //Coefficients for k and h
1304                 final T coef2 = auxiliaryElements.getP().multiply(CjAlphaGamma).subtract(auxiliaryElements.getQ().multiply(CjBetaGamma).multiply(I));
1305                 final T coef3 = auxiliaryElements.getP().multiply(SjAlphaGamma).subtract(auxiliaryElements.getQ().multiply(SjBetaGamma).multiply(I));
1306                 currentCij[1] = currentCij[1].subtract(context.getXON2A2().multiply(auxiliaryElements.getH().multiply(coef2).add(context.getBB().multiply(cjsj.getdCjdH(j))).subtract(auxiliaryElements.getK().multiply(1.5).multiply(j).multiply(cjsj.getSj(j)))));
1307                 currentSij[1] = currentSij[1].subtract(context.getXON2A2().multiply(auxiliaryElements.getH().multiply(coef3).add(context.getBB().multiply(cjsj.getdSjdH(j))).add(auxiliaryElements.getK().multiply(1.5).multiply(j).multiply(cjsj.getCj(j)))));
1308                 currentCij[2] = currentCij[2].add(context.getXON2A2().multiply(auxiliaryElements.getK().multiply(coef2).add(context.getBB().multiply(cjsj.getdCjdK(j))).add(auxiliaryElements.getH().multiply(1.5).multiply(j).multiply(cjsj.getSj(j)))));
1309                 currentSij[2] = currentSij[2].add(context.getXON2A2().multiply(auxiliaryElements.getK().multiply(coef3).add(context.getBB().multiply(cjsj.getdSjdK(j))).subtract(auxiliaryElements.getH().multiply(1.5).multiply(j).multiply(cjsj.getCj(j)))));
1310 
1311                 //Coefficients for q and p
1312                 final T coef4 = CjHK.subtract(CjAlphaBeta).subtract(cjsj.getSj(j).multiply(j));
1313                 final T coef5 = SjHK.subtract(SjAlphaBeta).add(cjsj.getCj(j).multiply(j));
1314                 currentCij[3] = context.getCXO2N2A2().multiply(CjAlphaGamma.multiply(-I).add(auxiliaryElements.getQ().multiply(coef4)));
1315                 currentSij[3] = context.getCXO2N2A2().multiply(SjAlphaGamma.multiply(-I).add(auxiliaryElements.getQ().multiply(coef5)));
1316                 currentCij[4] = context.getCXO2N2A2().multiply(CjBetaGamma.negate().add(auxiliaryElements.getP().multiply(coef4)));
1317                 currentSij[4] = context.getCXO2N2A2().multiply(SjBetaGamma.negate().add(auxiliaryElements.getP().multiply(coef5)));
1318 
1319                 //Coefficients for λ
1320                 final T coef6 = auxiliaryElements.getH().multiply(cjsj.getdCjdH(j)).add(auxiliaryElements.getK().multiply(cjsj.getdCjdK(j)));
1321                 final T coef7 = auxiliaryElements.getH().multiply(cjsj.getdSjdH(j)).add(auxiliaryElements.getK().multiply(cjsj.getdSjdK(j)));
1322                 currentCij[5] = currentCij[5].add(context.getOON2A2().multiply(auxiliaryElements.getSma().multiply(-2.).multiply(cjsj.getdCjdA(j)).add(coef6.divide(context.getX().add(1.))).add(context.getX().multiply(coef2)).subtract(cjsj.getCj(j).multiply(3.))));
1323                 currentSij[5] = currentSij[5].add(context.getOON2A2().multiply(auxiliaryElements.getSma().multiply(-2.).multiply(cjsj.getdSjdA(j)).add(coef7.divide(context.getX().add(1.))).add(context.getX().multiply(coef3)).subtract(cjsj.getSj(j).multiply(3.))));
1324             }
1325 
1326             for (int i = 0; i < 6; i++) {
1327                 //Add the current coefficients contribution to C<sub>i</sub>⁰
1328                 currentCi0[i] = currentCi0[i].subtract(currentCij[i].multiply(rhoSigma[j][0]).add(currentSij[i].multiply(rhoSigma[j][1])));
1329             }
1330 
1331             // Add the coefficients to the interpolation grid
1332             slot.cij[j].addGridPoint(date, currentCij);
1333             slot.sij[j].addGridPoint(date, currentSij);
1334 
1335         }
1336 
1337         //Add C<sub>i</sub>⁰ to the interpolation grid
1338         slot.cij[0].addGridPoint(date, currentCi0);
1339 
1340     }
1341 
1342     /**
1343      * Compute the auxiliary quantities ρ<sub>j</sub> and σ<sub>j</sub>.
1344      * <p>
1345      * The expressions used are equations 2.5.3-(4) from the Danielson paper. <br/>
1346      *  ρ<sub>j</sub> = (1+jB)(-b)<sup>j</sup>C<sub>j</sub>(k, h) <br/>
1347      *  σ<sub>j</sub> = (1+jB)(-b)<sup>j</sup>S<sub>j</sub>(k, h) <br/>
1348      * </p>
1349      * @param date target date
1350      * @param slot slot to which the coefficients belong
1351      * @param auxiliaryElements auxiliary elements related to the current orbit
1352      * @return array containing ρ<sub>j</sub> and σ<sub>j</sub>
1353      */
1354     private double[][] computeRhoSigmaCoefficients(final AbsoluteDate date,
1355                                                    final Slot slot,
1356                                                    final AuxiliaryElements auxiliaryElements) {
1357 
1358         final CjSjCoefficient cjsjKH = new CjSjCoefficient(auxiliaryElements.getK(), auxiliaryElements.getH());
1359         final double b = 1. / (1 + auxiliaryElements.getB());
1360 
1361         // (-b)<sup>j</sup>
1362         double mbtj = 1;
1363 
1364         final double[][] rhoSigma = new double[slot.cij.length][2];
1365         for (int j = 1; j < rhoSigma.length; j++) {
1366 
1367             //Compute current rho and sigma;
1368             mbtj *= -b;
1369             final double coef  = (1 + j * auxiliaryElements.getB()) * mbtj;
1370             final double rho   = coef * cjsjKH.getCj(j);
1371             final double sigma = coef * cjsjKH.getSj(j);
1372 
1373             // Add the coefficients to the interpolation grid
1374             rhoSigma[j][0] = rho;
1375             rhoSigma[j][1] = sigma;
1376         }
1377 
1378         return rhoSigma;
1379 
1380     }
1381 
1382     /**
1383      * Compute the auxiliary quantities ρ<sub>j</sub> and σ<sub>j</sub>.
1384      * <p>
1385      * The expressions used are equations 2.5.3-(4) from the Danielson paper. <br/>
1386      *  ρ<sub>j</sub> = (1+jB)(-b)<sup>j</sup>C<sub>j</sub>(k, h) <br/>
1387      *  σ<sub>j</sub> = (1+jB)(-b)<sup>j</sup>S<sub>j</sub>(k, h) <br/>
1388      * </p>
1389      * @param <T> type of the elements
1390      * @param date target date
1391      * @param slot slot to which the coefficients belong
1392      * @param auxiliaryElements auxiliary elements related to the current orbit
1393      * @param field field used by default
1394      * @return array containing ρ<sub>j</sub> and σ<sub>j</sub>
1395      */
1396     private <T extends CalculusFieldElement<T>> T[][] computeRhoSigmaCoefficients(final FieldAbsoluteDate<T> date,
1397                                                                               final FieldSlot<T> slot,
1398                                                                               final FieldAuxiliaryElements<T> auxiliaryElements,
1399                                                                               final Field<T> field) {
1400         final T zero = field.getZero();
1401 
1402         final FieldCjSjCoefficient<T> cjsjKH = new FieldCjSjCoefficient<>(auxiliaryElements.getK(), auxiliaryElements.getH(), field);
1403         final T b = auxiliaryElements.getB().add(1.).reciprocal();
1404 
1405         // (-b)<sup>j</sup>
1406         T mbtj = zero.add(1.);
1407 
1408         final T[][] rhoSigma = MathArrays.buildArray(field, slot.cij.length, 2);
1409         for (int j = 1; j < rhoSigma.length; j++) {
1410 
1411             //Compute current rho and sigma;
1412             mbtj = mbtj.multiply(b.negate());
1413             final T coef  = mbtj.multiply(auxiliaryElements.getB().multiply(j).add(1.));
1414             final T rho   = coef.multiply(cjsjKH.getCj(j));
1415             final T sigma = coef.multiply(cjsjKH.getSj(j));
1416 
1417             // Add the coefficients to the interpolation grid
1418             rhoSigma[j][0] = rho;
1419             rhoSigma[j][1] = sigma;
1420         }
1421 
1422         return rhoSigma;
1423 
1424     }
1425 
1426     /** The coefficients used to compute the short-periodic zonal contribution.
1427      *
1428      * <p>
1429      * Those coefficients are given in Danielson paper by expressions 4.1-(20) to 4.1.-(25)
1430      * </p>
1431      * <p>
1432      * The coefficients are: <br>
1433      * - C<sub>i</sub><sup>j</sup> and S<sub>i</sub><sup>j</sup> <br>
1434      * - ρ<sub>j</sub> and σ<sub>j</sub> <br>
1435      * - C<sub>i</sub>⁰
1436      * </p>
1437      *
1438      * @author Lucian Barbulescu
1439      */
1440     private static class ZonalShortPeriodicCoefficients implements ShortPeriodTerms {
1441 
1442         /** Maximum value for j index. */
1443         private final int maxFrequencyShortPeriodics;
1444 
1445         /** Number of points used in the interpolation process. */
1446         private final int interpolationPoints;
1447 
1448         /** All coefficients slots. */
1449         private final transient TimeSpanMap<Slot> slots;
1450 
1451         /** Constructor.
1452          * @param maxFrequencyShortPeriodics maximum value for j index
1453          * @param interpolationPoints number of points used in the interpolation process
1454          * @param slots all coefficients slots
1455          */
1456         ZonalShortPeriodicCoefficients(final int maxFrequencyShortPeriodics, final int interpolationPoints,
1457                                        final TimeSpanMap<Slot> slots) {
1458 
1459             // Save parameters
1460             this.maxFrequencyShortPeriodics = maxFrequencyShortPeriodics;
1461             this.interpolationPoints        = interpolationPoints;
1462             this.slots                      = slots;
1463 
1464         }
1465 
1466         /** Get the slot valid for some date.
1467          * @param meanStates mean states defining the slot
1468          * @return slot valid at the specified date
1469          */
1470         public Slot createSlot(final SpacecraftState... meanStates) {
1471             final Slot         slot  = new Slot(maxFrequencyShortPeriodics, interpolationPoints);
1472             final AbsoluteDate first = meanStates[0].getDate();
1473             final AbsoluteDate last  = meanStates[meanStates.length - 1].getDate();
1474             final int compare = first.compareTo(last);
1475             if (compare < 0) {
1476                 slots.addValidAfter(slot, first, false);
1477             } else if (compare > 0) {
1478                 slots.addValidBefore(slot, first, false);
1479             } else {
1480                 // single date, valid for all time
1481                 slots.addValidAfter(slot, AbsoluteDate.PAST_INFINITY, false);
1482             }
1483             return slot;
1484         }
1485 
1486         /** {@inheritDoc} */
1487         @Override
1488         public double[] value(final Orbit meanOrbit) {
1489 
1490             // select the coefficients slot
1491             final Slot slot = slots.get(meanOrbit.getDate());
1492 
1493             // Get the True longitude L
1494             final double L = meanOrbit.getLv();
1495 
1496             // Compute the center
1497             final double center = L - meanOrbit.getLM();
1498 
1499             // Initialize short periodic variations
1500             final double[] shortPeriodicVariation = slot.cij[0].value(meanOrbit.getDate());
1501             final double[] d = slot.di.value(meanOrbit.getDate());
1502             for (int i = 0; i < 6; i++) {
1503                 shortPeriodicVariation[i] +=  center * d[i];
1504             }
1505 
1506             for (int j = 1; j <= maxFrequencyShortPeriodics; j++) {
1507                 final double[] c = slot.cij[j].value(meanOrbit.getDate());
1508                 final double[] s = slot.sij[j].value(meanOrbit.getDate());
1509                 final SinCos sc  = FastMath.sinCos(j * L);
1510                 for (int i = 0; i < 6; i++) {
1511                     // add corresponding term to the short periodic variation
1512                     shortPeriodicVariation[i] += c[i] * sc.cos();
1513                     shortPeriodicVariation[i] += s[i] * sc.sin();
1514                 }
1515             }
1516 
1517             return shortPeriodicVariation;
1518         }
1519 
1520         /** {@inheritDoc} */
1521         @Override
1522         public String getCoefficientsKeyPrefix() {
1523             return DSSTZonal.SHORT_PERIOD_PREFIX;
1524         }
1525 
1526         /** {@inheritDoc}
1527          * <p>
1528          * For zonal terms contributions,there are maxJ cj coefficients,
1529          * maxJ sj coefficients and 2 dj coefficients, where maxJ depends
1530          * on the orbit. The j index is the integer multiplier for the true
1531          * longitude argument in the cj and sj coefficients and the degree
1532          * in the polynomial dj coefficients.
1533          * </p>
1534          */
1535         @Override
1536         public Map<String, double[]> getCoefficients(final AbsoluteDate date, final Set<String> selected) {
1537 
1538             // select the coefficients slot
1539             final Slot slot = slots.get(date);
1540 
1541             final Map<String, double[]> coefficients = new HashMap<String, double[]>(2 * maxFrequencyShortPeriodics + 2);
1542             storeIfSelected(coefficients, selected, slot.cij[0].value(date), "d", 0);
1543             storeIfSelected(coefficients, selected, slot.di.value(date), "d", 1);
1544             for (int j = 1; j <= maxFrequencyShortPeriodics; j++) {
1545                 storeIfSelected(coefficients, selected, slot.cij[j].value(date), "c", j);
1546                 storeIfSelected(coefficients, selected, slot.sij[j].value(date), "s", j);
1547             }
1548             return coefficients;
1549 
1550         }
1551 
1552         /** Put a coefficient in a map if selected.
1553          * @param map map to populate
1554          * @param selected set of coefficients that should be put in the map
1555          * (empty set means all coefficients are selected)
1556          * @param value coefficient value
1557          * @param id coefficient identifier
1558          * @param indices list of coefficient indices
1559          */
1560         private void storeIfSelected(final Map<String, double[]> map, final Set<String> selected,
1561                                      final double[] value, final String id, final int... indices) {
1562             final StringBuilder keyBuilder = new StringBuilder(getCoefficientsKeyPrefix());
1563             keyBuilder.append(id);
1564             for (int index : indices) {
1565                 keyBuilder.append('[').append(index).append(']');
1566             }
1567             final String key = keyBuilder.toString();
1568             if (selected.isEmpty() || selected.contains(key)) {
1569                 map.put(key, value);
1570             }
1571         }
1572 
1573     }
1574 
1575     /** The coefficients used to compute the short-periodic zonal contribution.
1576     *
1577     * <p>
1578     * Those coefficients are given in Danielson paper by expressions 4.1-(20) to 4.1.-(25)
1579     * </p>
1580     * <p>
1581     * The coefficients are: <br>
1582     * - C<sub>i</sub><sup>j</sup> and S<sub>i</sub><sup>j</sup> <br>
1583     * - ρ<sub>j</sub> and σ<sub>j</sub> <br>
1584     * - C<sub>i</sub>⁰
1585     * </p>
1586     *
1587     * @author Lucian Barbulescu
1588     */
1589     private static class FieldZonalShortPeriodicCoefficients <T extends CalculusFieldElement<T>> implements FieldShortPeriodTerms<T> {
1590 
1591         /** Maximum value for j index. */
1592         private final int maxFrequencyShortPeriodics;
1593 
1594         /** Number of points used in the interpolation process. */
1595         private final int interpolationPoints;
1596 
1597         /** All coefficients slots. */
1598         private final transient FieldTimeSpanMap<FieldSlot<T>, T> slots;
1599 
1600        /** Constructor.
1601         * @param maxFrequencyShortPeriodics maximum value for j index
1602         * @param interpolationPoints number of points used in the interpolation process
1603         * @param slots all coefficients slots
1604         */
1605         FieldZonalShortPeriodicCoefficients(final int maxFrequencyShortPeriodics, final int interpolationPoints,
1606                                             final FieldTimeSpanMap<FieldSlot<T>, T> slots) {
1607 
1608             // Save parameters
1609             this.maxFrequencyShortPeriodics = maxFrequencyShortPeriodics;
1610             this.interpolationPoints        = interpolationPoints;
1611             this.slots                      = slots;
1612 
1613         }
1614 
1615        /** Get the slot valid for some date.
1616         * @param meanStates mean states defining the slot
1617         * @return slot valid at the specified date
1618         */
1619         @SuppressWarnings("unchecked")
1620         public FieldSlot<T> createSlot(final FieldSpacecraftState<T>... meanStates) {
1621             final FieldSlot<T>         slot  = new FieldSlot<>(maxFrequencyShortPeriodics, interpolationPoints);
1622             final FieldAbsoluteDate<T> first = meanStates[0].getDate();
1623             final FieldAbsoluteDate<T> last  = meanStates[meanStates.length - 1].getDate();
1624             if (first.compareTo(last) <= 0) {
1625                 slots.addValidAfter(slot, first);
1626             } else {
1627                 slots.addValidBefore(slot, first);
1628             }
1629             return slot;
1630         }
1631 
1632         /** {@inheritDoc} */
1633         @Override
1634         public T[] value(final FieldOrbit<T> meanOrbit) {
1635 
1636             // select the coefficients slot
1637             final FieldSlot<T> slot = slots.get(meanOrbit.getDate());
1638 
1639             // Get the True longitude L
1640             final T L = meanOrbit.getLv();
1641 
1642             // Compute the center
1643             final T center = L.subtract(meanOrbit.getLM());
1644 
1645             // Initialize short periodic variations
1646             final T[] shortPeriodicVariation = slot.cij[0].value(meanOrbit.getDate());
1647             final T[] d = slot.di.value(meanOrbit.getDate());
1648             for (int i = 0; i < 6; i++) {
1649                 shortPeriodicVariation[i] = shortPeriodicVariation[i].add(center.multiply(d[i]));
1650             }
1651 
1652             for (int j = 1; j <= maxFrequencyShortPeriodics; j++) {
1653                 final T[]            c   = slot.cij[j].value(meanOrbit.getDate());
1654                 final T[]            s   = slot.sij[j].value(meanOrbit.getDate());
1655                 final FieldSinCos<T> sc  = FastMath.sinCos(L.multiply(j));
1656                 for (int i = 0; i < 6; i++) {
1657                     // add corresponding term to the short periodic variation
1658                     shortPeriodicVariation[i] = shortPeriodicVariation[i].add(c[i].multiply(sc.cos()));
1659                     shortPeriodicVariation[i] = shortPeriodicVariation[i].add(s[i].multiply(sc.sin()));
1660                 }
1661             }
1662 
1663             return shortPeriodicVariation;
1664         }
1665 
1666         /** {@inheritDoc} */
1667         @Override
1668         public String getCoefficientsKeyPrefix() {
1669             return DSSTZonal.SHORT_PERIOD_PREFIX;
1670         }
1671 
1672        /** {@inheritDoc}
1673         * <p>
1674         * For zonal terms contributions,there are maxJ cj coefficients,
1675         * maxJ sj coefficients and 2 dj coefficients, where maxJ depends
1676         * on the orbit. The j index is the integer multiplier for the true
1677         * longitude argument in the cj and sj coefficients and the degree
1678         * in the polynomial dj coefficients.
1679         * </p>
1680         */
1681         @Override
1682         public Map<String, T[]> getCoefficients(final FieldAbsoluteDate<T> date, final Set<String> selected) {
1683 
1684             // select the coefficients slot
1685             final FieldSlot<T> slot = slots.get(date);
1686 
1687             final Map<String, T[]> coefficients = new HashMap<String, T[]>(2 * maxFrequencyShortPeriodics + 2);
1688             storeIfSelected(coefficients, selected, slot.cij[0].value(date), "d", 0);
1689             storeIfSelected(coefficients, selected, slot.di.value(date), "d", 1);
1690             for (int j = 1; j <= maxFrequencyShortPeriodics; j++) {
1691                 storeIfSelected(coefficients, selected, slot.cij[j].value(date), "c", j);
1692                 storeIfSelected(coefficients, selected, slot.sij[j].value(date), "s", j);
1693             }
1694             return coefficients;
1695 
1696         }
1697 
1698        /** Put a coefficient in a map if selected.
1699         * @param map map to populate
1700         * @param selected set of coefficients that should be put in the map
1701         * (empty set means all coefficients are selected)
1702         * @param value coefficient value
1703         * @param id coefficient identifier
1704         * @param indices list of coefficient indices
1705         */
1706         private void storeIfSelected(final Map<String, T[]> map, final Set<String> selected,
1707                                      final T[] value, final String id, final int... indices) {
1708             final StringBuilder keyBuilder = new StringBuilder(getCoefficientsKeyPrefix());
1709             keyBuilder.append(id);
1710             for (int index : indices) {
1711                 keyBuilder.append('[').append(index).append(']');
1712             }
1713             final String key = keyBuilder.toString();
1714             if (selected.isEmpty() || selected.contains(key)) {
1715                 map.put(key, value);
1716             }
1717         }
1718 
1719     }
1720 
1721     /** Compute the C<sup>j</sup> and the S<sup>j</sup> coefficients.
1722      *  <p>
1723      *  Those coefficients are given in Danielson paper by expressions 4.1-(13) to 4.1.-(16b)
1724      *  </p>
1725      */
1726     private class FourierCjSjCoefficients {
1727 
1728         /** The G<sub>js</sub>, H<sub>js</sub>, I<sub>js</sub> and J<sub>js</sub> polynomials. */
1729         private final GHIJjsPolynomials ghijCoef;
1730 
1731         /** L<sub>n</sub><sup>s</sup>(γ). */
1732         private final LnsCoefficients lnsCoef;
1733 
1734         /** Maximum possible value for n. */
1735         private final int nMax;
1736 
1737         /** Maximum possible value for s. */
1738         private final int sMax;
1739 
1740         /** Maximum possible value for j. */
1741         private final int jMax;
1742 
1743         /** The C<sup>j</sup> coefficients and their derivatives.
1744          * <p>
1745          * Each column of the matrix contains the following values: <br/>
1746          * - C<sup>j</sup> <br/>
1747          * - dC<sup>j</sup> / da <br/>
1748          * - dC<sup>j</sup> / dh <br/>
1749          * - dC<sup>j</sup> / dk <br/>
1750          * - dC<sup>j</sup> / dα <br/>
1751          * - dC<sup>j</sup> / dβ <br/>
1752          * - dC<sup>j</sup> / dγ <br/>
1753          * </p>
1754          */
1755         private final double[][] cCoef;
1756 
1757         /** The S<sup>j</sup> coefficients and their derivatives.
1758          * <p>
1759          * Each column of the matrix contains the following values: <br/>
1760          * - S<sup>j</sup> <br/>
1761          * - dS<sup>j</sup> / da <br/>
1762          * - dS<sup>j</sup> / dh <br/>
1763          * - dS<sup>j</sup> / dk <br/>
1764          * - dS<sup>j</sup> / dα <br/>
1765          * - dS<sup>j</sup> / dβ <br/>
1766          * - dS<sup>j</sup> / dγ <br/>
1767          * </p>
1768          */
1769         private final double[][] sCoef;
1770 
1771         /** h * &Chi;³. */
1772         private final double hXXX;
1773         /** k * &Chi;³. */
1774         private final double kXXX;
1775 
1776         /** Create a set of C<sup>j</sup> and the S<sup>j</sup> coefficients.
1777          *  @param date the current date
1778          *  @param nMax maximum possible value for n
1779          *  @param sMax maximum possible value for s
1780          *  @param jMax maximum possible value for j
1781          *  @param context container for attributes
1782          *  @param hansenObjects initialization of hansen objects
1783          */
1784         FourierCjSjCoefficients(final AbsoluteDate date,
1785                                 final int nMax, final int sMax, final int jMax, final DSSTZonalContext context,
1786                                 final HansenObjects hansenObjects) {
1787 
1788             final AuxiliaryElements auxiliaryElements = context.getAuxiliaryElements();
1789 
1790             this.ghijCoef = new GHIJjsPolynomials(auxiliaryElements.getK(), auxiliaryElements.getH(), auxiliaryElements.getAlpha(), auxiliaryElements.getBeta());
1791             // Qns coefficients
1792             final double[][] Qns  = CoefficientsFactory.computeQns(auxiliaryElements.getGamma(), nMax, nMax);
1793 
1794             this.lnsCoef = new LnsCoefficients(nMax, nMax, Qns, Vns, context.getRoa());
1795             this.nMax = nMax;
1796             this.sMax = sMax;
1797             this.jMax = jMax;
1798 
1799             // compute the common factors that depends on the mean elements
1800             this.hXXX = auxiliaryElements.getH() * context.getXXX();
1801             this.kXXX = auxiliaryElements.getK() * context.getXXX();
1802 
1803             this.cCoef = new double[7][jMax + 1];
1804             this.sCoef = new double[7][jMax + 1];
1805 
1806             for (int s = 0; s <= sMax; s++) {
1807                 //Initialise the Hansen roots
1808                 hansenObjects.computeHansenObjectsInitValues(context, s);
1809             }
1810             generateCoefficients(date, context, auxiliaryElements, hansenObjects);
1811         }
1812 
1813         /** Generate all coefficients.
1814          * @param date the current date
1815          * @param context container for attributes
1816          * @param auxiliaryElements auxiliary elements related to the current orbit
1817          * @param hansenObjects initialization of hansen objects
1818          */
1819         private void generateCoefficients(final AbsoluteDate date,
1820                                           final DSSTZonalContext context,
1821                                           final AuxiliaryElements auxiliaryElements,
1822                                           final HansenObjects hansenObjects) {
1823 
1824             final UnnormalizedSphericalHarmonics harmonics = provider.onDate(date);
1825             for (int j = 1; j <= jMax; j++) {
1826 
1827                 //init arrays
1828                 for (int i = 0; i <= 6; i++) {
1829                     cCoef[i][j] = 0.;
1830                     sCoef[i][j] = 0.;
1831                 }
1832 
1833                 if (isBetween(j, 1, nMax - 1)) {
1834 
1835                     //compute first double sum where s: j -> N-1 and n: s+1 -> N
1836                     for (int s = j; s <= FastMath.min(nMax - 1, sMax); s++) {
1837                         // j - s
1838                         final int jms = j - s;
1839                         // Kronecker symbols (2 - delta(0,s-j)) and (2 - delta(0,j-s))
1840                         final int d0smj = (s == j) ? 1 : 2;
1841 
1842                         for (int n = s + 1; n <= nMax; n++) {
1843                             // if n + (j-s) is odd, then the term is equal to zero due to the factor Vn,s-j
1844                             if ((n + jms) % 2 == 0) {
1845                                 // (2 - delta(0,s-j)) * J<sub>n</sub> * K₀<sup>-n-1,s</sup> * L<sub>n</sub><sup>j-s</sup>
1846                                 final double lns = lnsCoef.getLns(n, -jms);
1847                                 final double dlns = lnsCoef.getdLnsdGamma(n, -jms);
1848 
1849                                 final double hjs = ghijCoef.getHjs(s, -jms);
1850                                 final double dHjsdh = ghijCoef.getdHjsdh(s, -jms);
1851                                 final double dHjsdk = ghijCoef.getdHjsdk(s, -jms);
1852                                 final double dHjsdAlpha = ghijCoef.getdHjsdAlpha(s, -jms);
1853                                 final double dHjsdBeta = ghijCoef.getdHjsdBeta(s, -jms);
1854 
1855                                 final double gjs = ghijCoef.getGjs(s, -jms);
1856                                 final double dGjsdh = ghijCoef.getdGjsdh(s, -jms);
1857                                 final double dGjsdk = ghijCoef.getdGjsdk(s, -jms);
1858                                 final double dGjsdAlpha = ghijCoef.getdGjsdAlpha(s, -jms);
1859                                 final double dGjsdBeta = ghijCoef.getdGjsdBeta(s, -jms);
1860 
1861                                 // J<sub>n</sub>
1862                                 final double jn = -harmonics.getUnnormalizedCnm(n, 0);
1863 
1864                                 // K₀<sup>-n-1,s</sup>
1865                                 final double kns   = hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getX());
1866                                 final double dkns  = hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getX());
1867 
1868                                 final double coef0 = d0smj * jn;
1869                                 final double coef1 = coef0 * lns;
1870                                 final double coef2 = coef1 * kns;
1871                                 final double coef3 = coef2 * hjs;
1872                                 final double coef4 = coef2 * gjs;
1873 
1874                                 // Add the term to the coefficients
1875                                 cCoef[0][j] += coef3;
1876                                 cCoef[1][j] += coef3 * (n + 1);
1877                                 cCoef[2][j] += coef1 * (kns * dHjsdh + hjs * hXXX * dkns);
1878                                 cCoef[3][j] += coef1 * (kns * dHjsdk + hjs * kXXX * dkns);
1879                                 cCoef[4][j] += coef2 * dHjsdAlpha;
1880                                 cCoef[5][j] += coef2 * dHjsdBeta;
1881                                 cCoef[6][j] += coef0 * dlns * kns * hjs;
1882 
1883                                 sCoef[0][j] += coef4;
1884                                 sCoef[1][j] += coef4 * (n + 1);
1885                                 sCoef[2][j] += coef1 * (kns * dGjsdh + gjs * hXXX * dkns);
1886                                 sCoef[3][j] += coef1 * (kns * dGjsdk + gjs * kXXX * dkns);
1887                                 sCoef[4][j] += coef2 * dGjsdAlpha;
1888                                 sCoef[5][j] += coef2 * dGjsdBeta;
1889                                 sCoef[6][j] += coef0 * dlns * kns * gjs;
1890                             }
1891                         }
1892                     }
1893 
1894                     //compute second double sum where s: 0 -> N-j and n: max(j+s, j+1) -> N
1895                     for (int s = 0; s <= FastMath.min(nMax - j, sMax); s++) {
1896                         // j + s
1897                         final int jps = j + s;
1898                         // Kronecker symbols (2 - delta(0,j+s))
1899                         final double d0spj = (s == -j) ? 1 : 2;
1900 
1901                         for (int n = FastMath.max(j + s, j + 1); n <= nMax; n++) {
1902                             // if n + (j+s) is odd, then the term is equal to zero due to the factor Vn,s+j
1903                             if ((n + jps) % 2 == 0) {
1904                                 // (2 - delta(0,s+j)) * J<sub>n</sub> * K₀<sup>-n-1,s</sup> * L<sub>n</sub><sup>j+s</sup>
1905                                 final double lns = lnsCoef.getLns(n, jps);
1906                                 final double dlns = lnsCoef.getdLnsdGamma(n, jps);
1907 
1908                                 final double hjs = ghijCoef.getHjs(s, jps);
1909                                 final double dHjsdh = ghijCoef.getdHjsdh(s, jps);
1910                                 final double dHjsdk = ghijCoef.getdHjsdk(s, jps);
1911                                 final double dHjsdAlpha = ghijCoef.getdHjsdAlpha(s, jps);
1912                                 final double dHjsdBeta = ghijCoef.getdHjsdBeta(s, jps);
1913 
1914                                 final double gjs = ghijCoef.getGjs(s, jps);
1915                                 final double dGjsdh = ghijCoef.getdGjsdh(s, jps);
1916                                 final double dGjsdk = ghijCoef.getdGjsdk(s, jps);
1917                                 final double dGjsdAlpha = ghijCoef.getdGjsdAlpha(s, jps);
1918                                 final double dGjsdBeta = ghijCoef.getdGjsdBeta(s, jps);
1919 
1920                                 // J<sub>n</sub>
1921                                 final double jn = -harmonics.getUnnormalizedCnm(n, 0);
1922 
1923                                 // K₀<sup>-n-1,s</sup>
1924                                 final double kns   = hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getX());
1925                                 final double dkns  = hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getX());
1926 
1927                                 final double coef0 = d0spj * jn;
1928                                 final double coef1 = coef0 * lns;
1929                                 final double coef2 = coef1 * kns;
1930 
1931                                 final double coef3 = coef2 * hjs;
1932                                 final double coef4 = coef2 * gjs;
1933 
1934                                 // Add the term to the coefficients
1935                                 cCoef[0][j] -= coef3;
1936                                 cCoef[1][j] -= coef3 * (n + 1);
1937                                 cCoef[2][j] -= coef1 * (kns * dHjsdh + hjs * hXXX * dkns);
1938                                 cCoef[3][j] -= coef1 * (kns * dHjsdk + hjs * kXXX * dkns);
1939                                 cCoef[4][j] -= coef2 * dHjsdAlpha;
1940                                 cCoef[5][j] -= coef2 * dHjsdBeta;
1941                                 cCoef[6][j] -= coef0 * dlns * kns * hjs;
1942 
1943                                 sCoef[0][j] += coef4;
1944                                 sCoef[1][j] += coef4 * (n + 1);
1945                                 sCoef[2][j] += coef1 * (kns * dGjsdh + gjs * hXXX * dkns);
1946                                 sCoef[3][j] += coef1 * (kns * dGjsdk + gjs * kXXX * dkns);
1947                                 sCoef[4][j] += coef2 * dGjsdAlpha;
1948                                 sCoef[5][j] += coef2 * dGjsdBeta;
1949                                 sCoef[6][j] += coef0 * dlns * kns * gjs;
1950                             }
1951                         }
1952                     }
1953 
1954                     //compute third double sum where s: 1 -> j and  n: j+1 -> N
1955                     for (int s = 1; s <= FastMath.min(j, sMax); s++) {
1956                         // j - s
1957                         final int jms = j - s;
1958                         // Kronecker symbols (2 - delta(0,s-j)) and (2 - delta(0,j-s))
1959                         final int d0smj = (s == j) ? 1 : 2;
1960 
1961                         for (int n = j + 1; n <= nMax; n++) {
1962                             // if n + (j-s) is odd, then the term is equal to zero due to the factor Vn,s-j
1963                             if ((n + jms) % 2 == 0) {
1964                                 // (2 - delta(0,j-s)) * J<sub>n</sub> * K₀<sup>-n-1,s</sup> * L<sub>n</sub><sup>j-s</sup>
1965                                 final double lns = lnsCoef.getLns(n, jms);
1966                                 final double dlns = lnsCoef.getdLnsdGamma(n, jms);
1967 
1968                                 final double ijs = ghijCoef.getIjs(s, jms);
1969                                 final double dIjsdh = ghijCoef.getdIjsdh(s, jms);
1970                                 final double dIjsdk = ghijCoef.getdIjsdk(s, jms);
1971                                 final double dIjsdAlpha = ghijCoef.getdIjsdAlpha(s, jms);
1972                                 final double dIjsdBeta = ghijCoef.getdIjsdBeta(s, jms);
1973 
1974                                 final double jjs = ghijCoef.getJjs(s, jms);
1975                                 final double dJjsdh = ghijCoef.getdJjsdh(s, jms);
1976                                 final double dJjsdk = ghijCoef.getdJjsdk(s, jms);
1977                                 final double dJjsdAlpha = ghijCoef.getdJjsdAlpha(s, jms);
1978                                 final double dJjsdBeta = ghijCoef.getdJjsdBeta(s, jms);
1979 
1980                                 // J<sub>n</sub>
1981                                 final double jn = -harmonics.getUnnormalizedCnm(n, 0);
1982 
1983                                 // K₀<sup>-n-1,s</sup>
1984                                 final double kns   = hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getX());
1985                                 final double dkns  = hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getX());
1986 
1987                                 final double coef0 = d0smj * jn;
1988                                 final double coef1 = coef0 * lns;
1989                                 final double coef2 = coef1 * kns;
1990 
1991                                 final double coef3 = coef2 * ijs;
1992                                 final double coef4 = coef2 * jjs;
1993 
1994                                 // Add the term to the coefficients
1995                                 cCoef[0][j] -= coef3;
1996                                 cCoef[1][j] -= coef3 * (n + 1);
1997                                 cCoef[2][j] -= coef1 * (kns * dIjsdh + ijs * hXXX * dkns);
1998                                 cCoef[3][j] -= coef1 * (kns * dIjsdk + ijs * kXXX * dkns);
1999                                 cCoef[4][j] -= coef2 * dIjsdAlpha;
2000                                 cCoef[5][j] -= coef2 * dIjsdBeta;
2001                                 cCoef[6][j] -= coef0 * dlns * kns * ijs;
2002 
2003                                 sCoef[0][j] += coef4;
2004                                 sCoef[1][j] += coef4 * (n + 1);
2005                                 sCoef[2][j] += coef1 * (kns * dJjsdh + jjs * hXXX * dkns);
2006                                 sCoef[3][j] += coef1 * (kns * dJjsdk + jjs * kXXX * dkns);
2007                                 sCoef[4][j] += coef2 * dJjsdAlpha;
2008                                 sCoef[5][j] += coef2 * dJjsdBeta;
2009                                 sCoef[6][j] += coef0 * dlns * kns * jjs;
2010                             }
2011                         }
2012                     }
2013                 }
2014 
2015                 if (isBetween(j, 2, nMax)) {
2016                     //add first term
2017                     // J<sub>j</sub>
2018                     final double jj = -harmonics.getUnnormalizedCnm(j, 0);
2019                     double kns = hansenObjects.getHansenObjects()[0].getValue(-j - 1, context.getX());
2020                     double dkns = hansenObjects.getHansenObjects()[0].getDerivative(-j - 1, context.getX());
2021 
2022                     double lns = lnsCoef.getLns(j, j);
2023                     //dlns is 0 because n == s == j
2024 
2025                     final double hjs = ghijCoef.getHjs(0, j);
2026                     final double dHjsdh = ghijCoef.getdHjsdh(0, j);
2027                     final double dHjsdk = ghijCoef.getdHjsdk(0, j);
2028                     final double dHjsdAlpha = ghijCoef.getdHjsdAlpha(0, j);
2029                     final double dHjsdBeta = ghijCoef.getdHjsdBeta(0, j);
2030 
2031                     final double gjs = ghijCoef.getGjs(0, j);
2032                     final double dGjsdh = ghijCoef.getdGjsdh(0, j);
2033                     final double dGjsdk = ghijCoef.getdGjsdk(0, j);
2034                     final double dGjsdAlpha = ghijCoef.getdGjsdAlpha(0, j);
2035                     final double dGjsdBeta = ghijCoef.getdGjsdBeta(0, j);
2036 
2037                     // 2 * J<sub>j</sub> * K₀<sup>-j-1,0</sup> * L<sub>j</sub><sup>j</sup>
2038                     double coef0 = 2 * jj;
2039                     double coef1 = coef0 * lns;
2040                     double coef2 = coef1 * kns;
2041 
2042                     double coef3 = coef2 * hjs;
2043                     double coef4 = coef2 * gjs;
2044 
2045                     // Add the term to the coefficients
2046                     cCoef[0][j] -= coef3;
2047                     cCoef[1][j] -= coef3 * (j + 1);
2048                     cCoef[2][j] -= coef1 * (kns * dHjsdh + hjs * hXXX * dkns);
2049                     cCoef[3][j] -= coef1 * (kns * dHjsdk + hjs * kXXX * dkns);
2050                     cCoef[4][j] -= coef2 * dHjsdAlpha;
2051                     cCoef[5][j] -= coef2 * dHjsdBeta;
2052                     //no contribution to cCoef[6][j] because dlns is 0
2053 
2054                     sCoef[0][j] += coef4;
2055                     sCoef[1][j] += coef4 * (j + 1);
2056                     sCoef[2][j] += coef1 * (kns * dGjsdh + gjs * hXXX * dkns);
2057                     sCoef[3][j] += coef1 * (kns * dGjsdk + gjs * kXXX * dkns);
2058                     sCoef[4][j] += coef2 * dGjsdAlpha;
2059                     sCoef[5][j] += coef2 * dGjsdBeta;
2060                     //no contribution to sCoef[6][j] because dlns is 0
2061 
2062                     //compute simple sum where s: 1 -> j-1
2063                     for (int s = 1; s <= FastMath.min(j - 1, sMax); s++) {
2064                         // j - s
2065                         final int jms = j - s;
2066                         // Kronecker symbols (2 - delta(0,s-j)) and (2 - delta(0,j-s))
2067                         final int d0smj = (s == j) ? 1 : 2;
2068 
2069                         // if s is odd, then the term is equal to zero due to the factor Vj,s-j
2070                         if (s % 2 == 0) {
2071                             // (2 - delta(0,j-s)) * J<sub>j</sub> * K₀<sup>-j-1,s</sup> * L<sub>j</sub><sup>j-s</sup>
2072                             kns   = hansenObjects.getHansenObjects()[s].getValue(-j - 1, context.getX());
2073                             dkns  = hansenObjects.getHansenObjects()[s].getDerivative(-j - 1, context.getX());
2074 
2075                             lns = lnsCoef.getLns(j, jms);
2076                             final double dlns = lnsCoef.getdLnsdGamma(j, jms);
2077 
2078                             final double ijs = ghijCoef.getIjs(s, jms);
2079                             final double dIjsdh = ghijCoef.getdIjsdh(s, jms);
2080                             final double dIjsdk = ghijCoef.getdIjsdk(s, jms);
2081                             final double dIjsdAlpha = ghijCoef.getdIjsdAlpha(s, jms);
2082                             final double dIjsdBeta = ghijCoef.getdIjsdBeta(s, jms);
2083 
2084                             final double jjs = ghijCoef.getJjs(s, jms);
2085                             final double dJjsdh = ghijCoef.getdJjsdh(s, jms);
2086                             final double dJjsdk = ghijCoef.getdJjsdk(s, jms);
2087                             final double dJjsdAlpha = ghijCoef.getdJjsdAlpha(s, jms);
2088                             final double dJjsdBeta = ghijCoef.getdJjsdBeta(s, jms);
2089 
2090                             coef0 = d0smj * jj;
2091                             coef1 = coef0 * lns;
2092                             coef2 = coef1 * kns;
2093 
2094                             coef3 = coef2 * ijs;
2095                             coef4 = coef2 * jjs;
2096 
2097                             // Add the term to the coefficients
2098                             cCoef[0][j] -= coef3;
2099                             cCoef[1][j] -= coef3 * (j + 1);
2100                             cCoef[2][j] -= coef1 * (kns * dIjsdh + ijs * hXXX * dkns);
2101                             cCoef[3][j] -= coef1 * (kns * dIjsdk + ijs * kXXX * dkns);
2102                             cCoef[4][j] -= coef2 * dIjsdAlpha;
2103                             cCoef[5][j] -= coef2 * dIjsdBeta;
2104                             cCoef[6][j] -= coef0 * dlns * kns * ijs;
2105 
2106                             sCoef[0][j] += coef4;
2107                             sCoef[1][j] += coef4 * (j + 1);
2108                             sCoef[2][j] += coef1 * (kns * dJjsdh + jjs * hXXX * dkns);
2109                             sCoef[3][j] += coef1 * (kns * dJjsdk + jjs * kXXX * dkns);
2110                             sCoef[4][j] += coef2 * dJjsdAlpha;
2111                             sCoef[5][j] += coef2 * dJjsdBeta;
2112                             sCoef[6][j] += coef0 * dlns * kns * jjs;
2113                         }
2114                     }
2115                 }
2116 
2117                 if (isBetween(j, 3, 2 * nMax - 1)) {
2118                     //compute uppercase sigma expressions
2119 
2120                     //min(j-1,N)
2121                     final int minjm1on = FastMath.min(j - 1, nMax);
2122 
2123                     //if j is even
2124                     if (j % 2 == 0) {
2125                         //compute first double sum where s: j-min(j-1,N) -> j/2-1 and n: j-s -> min(j-1,N)
2126                         for (int s = j - minjm1on; s <= FastMath.min(j / 2 - 1, sMax); s++) {
2127                             // j - s
2128                             final int jms = j - s;
2129                             // Kronecker symbols (2 - delta(0,s-j)) and (2 - delta(0,j-s))
2130                             final int d0smj = (s == j) ? 1 : 2;
2131 
2132                             for (int n = j - s; n <= minjm1on; n++) {
2133                                 // if n + (j-s) is odd, then the term is equal to zero due to the factor Vn,s-j
2134                                 if ((n + jms) % 2 == 0) {
2135                                     // (2 - delta(0,j-s)) * J<sub>n</sub> * K₀<sup>-n-1,s</sup> * L<sub>n</sub><sup>j-s</sup>
2136                                     final double lns = lnsCoef.getLns(n, jms);
2137                                     final double dlns = lnsCoef.getdLnsdGamma(n, jms);
2138 
2139                                     final double ijs = ghijCoef.getIjs(s, jms);
2140                                     final double dIjsdh = ghijCoef.getdIjsdh(s, jms);
2141                                     final double dIjsdk = ghijCoef.getdIjsdk(s, jms);
2142                                     final double dIjsdAlpha = ghijCoef.getdIjsdAlpha(s, jms);
2143                                     final double dIjsdBeta = ghijCoef.getdIjsdBeta(s, jms);
2144 
2145                                     final double jjs = ghijCoef.getJjs(s, jms);
2146                                     final double dJjsdh = ghijCoef.getdJjsdh(s, jms);
2147                                     final double dJjsdk = ghijCoef.getdJjsdk(s, jms);
2148                                     final double dJjsdAlpha = ghijCoef.getdJjsdAlpha(s, jms);
2149                                     final double dJjsdBeta = ghijCoef.getdJjsdBeta(s, jms);
2150 
2151                                     // J<sub>n</sub>
2152                                     final double jn = -harmonics.getUnnormalizedCnm(n, 0);
2153 
2154                                     // K₀<sup>-n-1,s</sup>
2155                                     final double kns   = hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getX());
2156                                     final double dkns  = hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getX());
2157 
2158                                     final double coef0 = d0smj * jn;
2159                                     final double coef1 = coef0 * lns;
2160                                     final double coef2 = coef1 * kns;
2161 
2162                                     final double coef3 = coef2 * ijs;
2163                                     final double coef4 = coef2 * jjs;
2164 
2165                                     // Add the term to the coefficients
2166                                     cCoef[0][j] -= coef3;
2167                                     cCoef[1][j] -= coef3 * (n + 1);
2168                                     cCoef[2][j] -= coef1 * (kns * dIjsdh + ijs * hXXX * dkns);
2169                                     cCoef[3][j] -= coef1 * (kns * dIjsdk + ijs * kXXX * dkns);
2170                                     cCoef[4][j] -= coef2 * dIjsdAlpha;
2171                                     cCoef[5][j] -= coef2 * dIjsdBeta;
2172                                     cCoef[6][j] -= coef0 * dlns * kns * ijs;
2173 
2174                                     sCoef[0][j] += coef4;
2175                                     sCoef[1][j] += coef4 * (n + 1);
2176                                     sCoef[2][j] += coef1 * (kns * dJjsdh + jjs * hXXX * dkns);
2177                                     sCoef[3][j] += coef1 * (kns * dJjsdk + jjs * kXXX * dkns);
2178                                     sCoef[4][j] += coef2 * dJjsdAlpha;
2179                                     sCoef[5][j] += coef2 * dJjsdBeta;
2180                                     sCoef[6][j] += coef0 * dlns * kns * jjs;
2181                                 }
2182                             }
2183                         }
2184 
2185                         //compute second double sum where s: j/2 -> min(j-1,N)-1 and n: s+1 -> min(j-1,N)
2186                         for (int s = j / 2; s <=  FastMath.min(minjm1on - 1, sMax); s++) {
2187                             // j - s
2188                             final int jms = j - s;
2189                             // Kronecker symbols (2 - delta(0,s-j)) and (2 - delta(0,j-s))
2190                             final int d0smj = (s == j) ? 1 : 2;
2191 
2192                             for (int n = s + 1; n <= minjm1on; n++) {
2193                                 // if n + (j-s) is odd, then the term is equal to zero due to the factor Vn,s-j
2194                                 if ((n + jms) % 2 == 0) {
2195                                     // (2 - delta(0,j-s)) * J<sub>n</sub> * K₀<sup>-n-1,s</sup> * L<sub>n</sub><sup>j-s</sup>
2196                                     final double lns = lnsCoef.getLns(n, jms);
2197                                     final double dlns = lnsCoef.getdLnsdGamma(n, jms);
2198 
2199                                     final double ijs = ghijCoef.getIjs(s, jms);
2200                                     final double dIjsdh = ghijCoef.getdIjsdh(s, jms);
2201                                     final double dIjsdk = ghijCoef.getdIjsdk(s, jms);
2202                                     final double dIjsdAlpha = ghijCoef.getdIjsdAlpha(s, jms);
2203                                     final double dIjsdBeta = ghijCoef.getdIjsdBeta(s, jms);
2204 
2205                                     final double jjs = ghijCoef.getJjs(s, jms);
2206                                     final double dJjsdh = ghijCoef.getdJjsdh(s, jms);
2207                                     final double dJjsdk = ghijCoef.getdJjsdk(s, jms);
2208                                     final double dJjsdAlpha = ghijCoef.getdJjsdAlpha(s, jms);
2209                                     final double dJjsdBeta = ghijCoef.getdJjsdBeta(s, jms);
2210 
2211                                     // J<sub>n</sub>
2212                                     final double jn = -harmonics.getUnnormalizedCnm(n, 0);
2213 
2214                                     // K₀<sup>-n-1,s</sup>
2215                                     final double kns   = hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getX());
2216                                     final double dkns  = hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getX());
2217 
2218                                     final double coef0 = d0smj * jn;
2219                                     final double coef1 = coef0 * lns;
2220                                     final double coef2 = coef1 * kns;
2221 
2222                                     final double coef3 = coef2 * ijs;
2223                                     final double coef4 = coef2 * jjs;
2224 
2225                                     // Add the term to the coefficients
2226                                     cCoef[0][j] -= coef3;
2227                                     cCoef[1][j] -= coef3 * (n + 1);
2228                                     cCoef[2][j] -= coef1 * (kns * dIjsdh + ijs * hXXX * dkns);
2229                                     cCoef[3][j] -= coef1 * (kns * dIjsdk + ijs * kXXX * dkns);
2230                                     cCoef[4][j] -= coef2 * dIjsdAlpha;
2231                                     cCoef[5][j] -= coef2 * dIjsdBeta;
2232                                     cCoef[6][j] -= coef0 * dlns * kns * ijs;
2233 
2234                                     sCoef[0][j] += coef4;
2235                                     sCoef[1][j] += coef4 * (n + 1);
2236                                     sCoef[2][j] += coef1 * (kns * dJjsdh + jjs * hXXX * dkns);
2237                                     sCoef[3][j] += coef1 * (kns * dJjsdk + jjs * kXXX * dkns);
2238                                     sCoef[4][j] += coef2 * dJjsdAlpha;
2239                                     sCoef[5][j] += coef2 * dJjsdBeta;
2240                                     sCoef[6][j] += coef0 * dlns * kns * jjs;
2241                                 }
2242                             }
2243                         }
2244                     }
2245 
2246                     //if j is odd
2247                     else {
2248                         //compute first double sum where s: (j-1)/2 -> min(j-1,N)-1 and n: s+1 -> min(j-1,N)
2249                         for (int s = (j - 1) / 2; s <= FastMath.min(minjm1on - 1, sMax); s++) {
2250                             // j - s
2251                             final int jms = j - s;
2252                             // Kronecker symbols (2 - delta(0,s-j)) and (2 - delta(0,j-s))
2253                             final int d0smj = (s == j) ? 1 : 2;
2254 
2255                             for (int n = s + 1; n <= minjm1on; n++) {
2256                                 // if n + (j-s) is odd, then the term is equal to zero due to the factor Vn,s-j
2257                                 if ((n + jms) % 2 == 0) {
2258                                     // (2 - delta(0,j-s)) * J<sub>n</sub> * K₀<sup>-n-1,s</sup> * L<sub>n</sub><sup>j-s</sup>
2259                                     final double lns = lnsCoef.getLns(n, jms);
2260                                     final double dlns = lnsCoef.getdLnsdGamma(n, jms);
2261 
2262                                     final double ijs = ghijCoef.getIjs(s, jms);
2263                                     final double dIjsdh = ghijCoef.getdIjsdh(s, jms);
2264                                     final double dIjsdk = ghijCoef.getdIjsdk(s, jms);
2265                                     final double dIjsdAlpha = ghijCoef.getdIjsdAlpha(s, jms);
2266                                     final double dIjsdBeta = ghijCoef.getdIjsdBeta(s, jms);
2267 
2268                                     final double jjs = ghijCoef.getJjs(s, jms);
2269                                     final double dJjsdh = ghijCoef.getdJjsdh(s, jms);
2270                                     final double dJjsdk = ghijCoef.getdJjsdk(s, jms);
2271                                     final double dJjsdAlpha = ghijCoef.getdJjsdAlpha(s, jms);
2272                                     final double dJjsdBeta = ghijCoef.getdJjsdBeta(s, jms);
2273 
2274                                     // J<sub>n</sub>
2275                                     final double jn = -harmonics.getUnnormalizedCnm(n, 0);
2276 
2277                                     // K₀<sup>-n-1,s</sup>
2278 
2279                                     final double kns = hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getX());
2280                                     final double dkns  = hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getX());
2281 
2282                                     final double coef0 = d0smj * jn;
2283                                     final double coef1 = coef0 * lns;
2284                                     final double coef2 = coef1 * kns;
2285 
2286                                     final double coef3 = coef2 * ijs;
2287                                     final double coef4 = coef2 * jjs;
2288 
2289                                     // Add the term to the coefficients
2290                                     cCoef[0][j] -= coef3;
2291                                     cCoef[1][j] -= coef3 * (n + 1);
2292                                     cCoef[2][j] -= coef1 * (kns * dIjsdh + ijs * hXXX * dkns);
2293                                     cCoef[3][j] -= coef1 * (kns * dIjsdk + ijs * kXXX * dkns);
2294                                     cCoef[4][j] -= coef2 * dIjsdAlpha;
2295                                     cCoef[5][j] -= coef2 * dIjsdBeta;
2296                                     cCoef[6][j] -= coef0 * dlns * kns * ijs;
2297 
2298                                     sCoef[0][j] += coef4;
2299                                     sCoef[1][j] += coef4 * (n + 1);
2300                                     sCoef[2][j] += coef1 * (kns * dJjsdh + jjs * hXXX * dkns);
2301                                     sCoef[3][j] += coef1 * (kns * dJjsdk + jjs * kXXX * dkns);
2302                                     sCoef[4][j] += coef2 * dJjsdAlpha;
2303                                     sCoef[5][j] += coef2 * dJjsdBeta;
2304                                     sCoef[6][j] += coef0 * dlns * kns * jjs;
2305                                 }
2306                             }
2307                         }
2308 
2309                         //the second double sum is added only if N >= 4 and j between 5 and 2*N-3
2310                         if (nMax >= 4 && isBetween(j, 5, 2 * nMax - 3)) {
2311                             //compute second double sum where s: j-min(j-1,N) -> (j-3)/2 and n: j-s -> min(j-1,N)
2312                             for (int s = j - minjm1on; s <= FastMath.min((j - 3) / 2, sMax); s++) {
2313                                 // j - s
2314                                 final int jms = j - s;
2315                                 // Kronecker symbols (2 - delta(0,s-j)) and (2 - delta(0,j-s))
2316                                 final int d0smj = (s == j) ? 1 : 2;
2317 
2318                                 for (int n = j - s; n <= minjm1on; n++) {
2319                                     // if n + (j-s) is odd, then the term is equal to zero due to the factor Vn,s-j
2320                                     if ((n + jms) % 2 == 0) {
2321                                         // (2 - delta(0,j-s)) * J<sub>n</sub> * K₀<sup>-n-1,s</sup> * L<sub>n</sub><sup>j-s</sup>
2322                                         final double lns = lnsCoef.getLns(n, jms);
2323                                         final double dlns = lnsCoef.getdLnsdGamma(n, jms);
2324 
2325                                         final double ijs = ghijCoef.getIjs(s, jms);
2326                                         final double dIjsdh = ghijCoef.getdIjsdh(s, jms);
2327                                         final double dIjsdk = ghijCoef.getdIjsdk(s, jms);
2328                                         final double dIjsdAlpha = ghijCoef.getdIjsdAlpha(s, jms);
2329                                         final double dIjsdBeta = ghijCoef.getdIjsdBeta(s, jms);
2330 
2331                                         final double jjs = ghijCoef.getJjs(s, jms);
2332                                         final double dJjsdh = ghijCoef.getdJjsdh(s, jms);
2333                                         final double dJjsdk = ghijCoef.getdJjsdk(s, jms);
2334                                         final double dJjsdAlpha = ghijCoef.getdJjsdAlpha(s, jms);
2335                                         final double dJjsdBeta = ghijCoef.getdJjsdBeta(s, jms);
2336 
2337                                         // J<sub>n</sub>
2338                                         final double jn = -harmonics.getUnnormalizedCnm(n, 0);
2339 
2340                                         // K₀<sup>-n-1,s</sup>
2341                                         final double kns   = hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getX());
2342                                         final double dkns  = hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getX());
2343 
2344                                         final double coef0 = d0smj * jn;
2345                                         final double coef1 = coef0 * lns;
2346                                         final double coef2 = coef1 * kns;
2347 
2348                                         final double coef3 = coef2 * ijs;
2349                                         final double coef4 = coef2 * jjs;
2350 
2351                                         // Add the term to the coefficients
2352                                         cCoef[0][j] -= coef3;
2353                                         cCoef[1][j] -= coef3 * (n + 1);
2354                                         cCoef[2][j] -= coef1 * (kns * dIjsdh + ijs * hXXX * dkns);
2355                                         cCoef[3][j] -= coef1 * (kns * dIjsdk + ijs * kXXX * dkns);
2356                                         cCoef[4][j] -= coef2 * dIjsdAlpha;
2357                                         cCoef[5][j] -= coef2 * dIjsdBeta;
2358                                         cCoef[6][j] -= coef0 * dlns * kns * ijs;
2359 
2360                                         sCoef[0][j] += coef4;
2361                                         sCoef[1][j] += coef4 * (n + 1);
2362                                         sCoef[2][j] += coef1 * (kns * dJjsdh + jjs * hXXX * dkns);
2363                                         sCoef[3][j] += coef1 * (kns * dJjsdk + jjs * kXXX * dkns);
2364                                         sCoef[4][j] += coef2 * dJjsdAlpha;
2365                                         sCoef[5][j] += coef2 * dJjsdBeta;
2366                                         sCoef[6][j] += coef0 * dlns * kns * jjs;
2367                                     }
2368                                 }
2369                             }
2370                         }
2371                     }
2372                 }
2373 
2374                 cCoef[0][j] *= -context.getMuoa() / j;
2375                 cCoef[1][j] *=  context.getMuoa() / ( j * auxiliaryElements.getSma() );
2376                 cCoef[2][j] *= -context.getMuoa() / j;
2377                 cCoef[3][j] *= -context.getMuoa() / j;
2378                 cCoef[4][j] *= -context.getMuoa() / j;
2379                 cCoef[5][j] *= -context.getMuoa() / j;
2380                 cCoef[6][j] *= -context.getMuoa() / j;
2381 
2382                 sCoef[0][j] *= -context.getMuoa() / j;
2383                 sCoef[1][j] *=  context.getMuoa() / ( j * auxiliaryElements.getSma() );
2384                 sCoef[2][j] *= -context.getMuoa() / j;
2385                 sCoef[3][j] *= -context.getMuoa() / j;
2386                 sCoef[4][j] *= -context.getMuoa() / j;
2387                 sCoef[5][j] *= -context.getMuoa() / j;
2388                 sCoef[6][j] *= -context.getMuoa() / j;
2389 
2390             }
2391         }
2392 
2393         /** Check if an index is within the accepted interval.
2394          *
2395          * @param index the index to check
2396          * @param lowerBound the lower bound of the interval
2397          * @param upperBound the upper bound of the interval
2398          * @return true if the index is between the lower and upper bounds, false otherwise
2399          */
2400         private boolean isBetween(final int index, final int lowerBound, final int upperBound) {
2401             return index >= lowerBound && index <= upperBound;
2402         }
2403 
2404         /**Get the value of C<sup>j</sup>.
2405          *
2406          * @param j j index
2407          * @return C<sup>j</sup>
2408          */
2409         public double getCj(final int j) {
2410             return cCoef[0][j];
2411         }
2412 
2413         /**Get the value of dC<sup>j</sup> / da.
2414          *
2415          * @param j j index
2416          * @return dC<sup>j</sup> / da
2417          */
2418         public double getdCjdA(final int j) {
2419             return cCoef[1][j];
2420         }
2421 
2422         /**Get the value of dC<sup>j</sup> / dh.
2423          *
2424          * @param j j index
2425          * @return dC<sup>j</sup> / dh
2426          */
2427         public double getdCjdH(final int j) {
2428             return cCoef[2][j];
2429         }
2430 
2431         /**Get the value of dC<sup>j</sup> / dk.
2432          *
2433          * @param j j index
2434          * @return dC<sup>j</sup> / dk
2435          */
2436         public double getdCjdK(final int j) {
2437             return cCoef[3][j];
2438         }
2439 
2440         /**Get the value of dC<sup>j</sup> / dα.
2441          *
2442          * @param j j index
2443          * @return dC<sup>j</sup> / dα
2444          */
2445         public double getdCjdAlpha(final int j) {
2446             return cCoef[4][j];
2447         }
2448 
2449         /**Get the value of dC<sup>j</sup> / dβ.
2450          *
2451          * @param j j index
2452          * @return dC<sup>j</sup> / dβ
2453          */
2454         public double getdCjdBeta(final int j) {
2455             return cCoef[5][j];
2456         }
2457 
2458         /**Get the value of dC<sup>j</sup> / dγ.
2459          *
2460          * @param j j index
2461          * @return dC<sup>j</sup> / dγ
2462          */
2463         public double getdCjdGamma(final int j) {
2464             return cCoef[6][j];
2465         }
2466 
2467         /**Get the value of S<sup>j</sup>.
2468          *
2469          * @param j j index
2470          * @return S<sup>j</sup>
2471          */
2472         public double getSj(final int j) {
2473             return sCoef[0][j];
2474         }
2475 
2476         /**Get the value of dS<sup>j</sup> / da.
2477          *
2478          * @param j j index
2479          * @return dS<sup>j</sup> / da
2480          */
2481         public double getdSjdA(final int j) {
2482             return sCoef[1][j];
2483         }
2484 
2485         /**Get the value of dS<sup>j</sup> / dh.
2486          *
2487          * @param j j index
2488          * @return dS<sup>j</sup> / dh
2489          */
2490         public double getdSjdH(final int j) {
2491             return sCoef[2][j];
2492         }
2493 
2494         /**Get the value of dS<sup>j</sup> / dk.
2495          *
2496          * @param j j index
2497          * @return dS<sup>j</sup> / dk
2498          */
2499         public double getdSjdK(final int j) {
2500             return sCoef[3][j];
2501         }
2502 
2503         /**Get the value of dS<sup>j</sup> / dα.
2504          *
2505          * @param j j index
2506          * @return dS<sup>j</sup> / dα
2507          */
2508         public double getdSjdAlpha(final int j) {
2509             return sCoef[4][j];
2510         }
2511 
2512         /**Get the value of dS<sup>j</sup> / dβ.
2513          *
2514          * @param j j index
2515          * @return dS<sup>j</sup> / dβ
2516          */
2517         public double getdSjdBeta(final int j) {
2518             return sCoef[5][j];
2519         }
2520 
2521         /**Get the value of dS<sup>j</sup> /  dγ.
2522          *
2523          * @param j j index
2524          * @return dS<sup>j</sup> /  dγ
2525          */
2526         public double getdSjdGamma(final int j) {
2527             return sCoef[6][j];
2528         }
2529     }
2530 
2531     /** Compute the C<sup>j</sup> and the S<sup>j</sup> coefficients.
2532      *  <p>
2533      *  Those coefficients are given in Danielson paper by expressions 4.1-(13) to 4.1.-(16b)
2534      *  </p>
2535      */
2536     private class FieldFourierCjSjCoefficients <T extends CalculusFieldElement<T>> {
2537 
2538         /** The G<sub>js</sub>, H<sub>js</sub>, I<sub>js</sub> and J<sub>js</sub> polynomials. */
2539         private final FieldGHIJjsPolynomials<T> ghijCoef;
2540 
2541         /** L<sub>n</sub><sup>s</sup>(γ). */
2542         private final FieldLnsCoefficients<T> lnsCoef;
2543 
2544         /** Maximum possible value for n. */
2545         private final int nMax;
2546 
2547         /** Maximum possible value for s. */
2548         private final int sMax;
2549 
2550         /** Maximum possible value for j. */
2551         private final int jMax;
2552 
2553         /** The C<sup>j</sup> coefficients and their derivatives.
2554          * <p>
2555          * Each column of the matrix contains the following values: <br/>
2556          * - C<sup>j</sup> <br/>
2557          * - dC<sup>j</sup> / da <br/>
2558          * - dC<sup>j</sup> / dh <br/>
2559          * - dC<sup>j</sup> / dk <br/>
2560          * - dC<sup>j</sup> / dα <br/>
2561          * - dC<sup>j</sup> / dβ <br/>
2562          * - dC<sup>j</sup> / dγ <br/>
2563          * </p>
2564          */
2565         private final T[][] cCoef;
2566 
2567         /** The S<sup>j</sup> coefficients and their derivatives.
2568          * <p>
2569          * Each column of the matrix contains the following values: <br/>
2570          * - S<sup>j</sup> <br/>
2571          * - dS<sup>j</sup> / da <br/>
2572          * - dS<sup>j</sup> / dh <br/>
2573          * - dS<sup>j</sup> / dk <br/>
2574          * - dS<sup>j</sup> / dα <br/>
2575          * - dS<sup>j</sup> / dβ <br/>
2576          * - dS<sup>j</sup> / dγ <br/>
2577          * </p>
2578          */
2579         private final T[][] sCoef;
2580 
2581         /** h * &Chi;³. */
2582         private final T hXXX;
2583         /** k * &Chi;³. */
2584         private final T kXXX;
2585 
2586         /** Create a set of C<sup>j</sup> and the S<sup>j</sup> coefficients.
2587          *  @param date the current date
2588          *  @param nMax maximum possible value for n
2589          *  @param sMax maximum possible value for s
2590          *  @param jMax maximum possible value for j
2591          *  @param context container for attributes
2592          *  @param hansenObjects initialization of hansen objects
2593          */
2594         FieldFourierCjSjCoefficients(final FieldAbsoluteDate<T> date,
2595                                      final int nMax, final int sMax, final int jMax,
2596                                      final FieldDSSTZonalContext<T> context,
2597                                      final FieldHansenObjects<T> hansenObjects) {
2598 
2599             //Field used by default
2600             final Field<T> field = date.getField();
2601 
2602             final FieldAuxiliaryElements<T> auxiliaryElements = context.getFieldAuxiliaryElements();
2603 
2604             this.ghijCoef = new FieldGHIJjsPolynomials<>(auxiliaryElements.getK(), auxiliaryElements.getH(), auxiliaryElements.getAlpha(), auxiliaryElements.getBeta());
2605             // Qns coefficients
2606             final T[][] Qns = CoefficientsFactory.computeQns(auxiliaryElements.getGamma(), nMax, nMax);
2607 
2608             this.lnsCoef = new FieldLnsCoefficients<>(nMax, nMax, Qns, Vns, context.getRoa(), field);
2609             this.nMax = nMax;
2610             this.sMax = sMax;
2611             this.jMax = jMax;
2612 
2613             // compute the common factors that depends on the mean elements
2614             this.hXXX = auxiliaryElements.getH().multiply(context.getXXX());
2615             this.kXXX = auxiliaryElements.getK().multiply(context.getXXX());
2616 
2617             this.cCoef = MathArrays.buildArray(field, 7, jMax + 1);
2618             this.sCoef = MathArrays.buildArray(field, 7, jMax + 1);
2619 
2620             for (int s = 0; s <= sMax; s++) {
2621                 //Initialise the Hansen roots
2622                 hansenObjects.computeHansenObjectsInitValues(context, s);
2623             }
2624             generateCoefficients(date, context, auxiliaryElements, hansenObjects, field);
2625         }
2626 
2627         /** Generate all coefficients.
2628          * @param date the current date
2629          * @param context container for attributes
2630          * @param hansenObjects initialization of hansen objects
2631          * @param auxiliaryElements auxiliary elements related to the current orbit
2632          * @param field field used by default
2633          */
2634         private void generateCoefficients(final FieldAbsoluteDate<T> date,
2635                                           final FieldDSSTZonalContext<T> context,
2636                                           final FieldAuxiliaryElements<T> auxiliaryElements,
2637                                           final FieldHansenObjects<T> hansenObjects,
2638                                           final Field<T> field) {
2639 
2640             //Zero
2641             final T zero = field.getZero();
2642 
2643             final UnnormalizedSphericalHarmonics harmonics = provider.onDate(date.toAbsoluteDate());
2644             for (int j = 1; j <= jMax; j++) {
2645 
2646                 //init arrays
2647                 for (int i = 0; i <= 6; i++) {
2648                     cCoef[i][j] = zero;
2649                     sCoef[i][j] = zero;
2650                 }
2651 
2652                 if (isBetween(j, 1, nMax - 1)) {
2653 
2654                     //compute first double sum where s: j -> N-1 and n: s+1 -> N
2655                     for (int s = j; s <= FastMath.min(nMax - 1, sMax); s++) {
2656                         // j - s
2657                         final int jms = j - s;
2658                         // Kronecker symbols (2 - delta(0,s-j)) and (2 - delta(0,j-s))
2659                         final int d0smj = (s == j) ? 1 : 2;
2660 
2661                         for (int n = s + 1; n <= nMax; n++) {
2662                             // if n + (j-s) is odd, then the term is equal to zero due to the factor Vn,s-j
2663                             if ((n + jms) % 2 == 0) {
2664                                 // (2 - delta(0,s-j)) * J<sub>n</sub> * K₀<sup>-n-1,s</sup> * L<sub>n</sub><sup>j-s</sup>
2665                                 final T lns  = lnsCoef.getLns(n, -jms);
2666                                 final T dlns = lnsCoef.getdLnsdGamma(n, -jms);
2667 
2668                                 final T hjs        = ghijCoef.getHjs(s, -jms);
2669                                 final T dHjsdh     = ghijCoef.getdHjsdh(s, -jms);
2670                                 final T dHjsdk     = ghijCoef.getdHjsdk(s, -jms);
2671                                 final T dHjsdAlpha = ghijCoef.getdHjsdAlpha(s, -jms);
2672                                 final T dHjsdBeta  = ghijCoef.getdHjsdBeta(s, -jms);
2673 
2674                                 final T gjs        = ghijCoef.getGjs(s, -jms);
2675                                 final T dGjsdh     = ghijCoef.getdGjsdh(s, -jms);
2676                                 final T dGjsdk     = ghijCoef.getdGjsdk(s, -jms);
2677                                 final T dGjsdAlpha = ghijCoef.getdGjsdAlpha(s, -jms);
2678                                 final T dGjsdBeta  = ghijCoef.getdGjsdBeta(s, -jms);
2679 
2680                                 // J<sub>n</sub>
2681                                 final T jn = zero.subtract(harmonics.getUnnormalizedCnm(n, 0));
2682 
2683                                 // K₀<sup>-n-1,s</sup>
2684                                 final T kns   = (T) hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getX());
2685                                 final T dkns  = (T) hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getX());
2686 
2687                                 final T coef0 = jn.multiply(d0smj);
2688                                 final T coef1 = coef0.multiply(lns);
2689                                 final T coef2 = coef1.multiply(kns);
2690                                 final T coef3 = coef2.multiply(hjs);
2691                                 final T coef4 = coef2.multiply(gjs);
2692 
2693                                 // Add the term to the coefficients
2694                                 cCoef[0][j] = cCoef[0][j].add(coef3);
2695                                 cCoef[1][j] = cCoef[1][j].add(coef3.multiply(n + 1));
2696                                 cCoef[2][j] = cCoef[2][j].add(coef1.multiply(kns.multiply(dHjsdh).add(hjs.multiply(hXXX).multiply(dkns))));
2697                                 cCoef[3][j] = cCoef[3][j].add(coef1.multiply(kns.multiply(dHjsdk).add(hjs.multiply(kXXX).multiply(dkns))));
2698                                 cCoef[4][j] = cCoef[4][j].add(coef2.multiply(dHjsdAlpha));
2699                                 cCoef[5][j] = cCoef[5][j].add(coef2.multiply(dHjsdBeta));
2700                                 cCoef[6][j] = cCoef[6][j].add(coef0.multiply(dlns).multiply(kns).multiply(hjs));
2701 
2702                                 sCoef[0][j] = sCoef[0][j].add(coef4);
2703                                 sCoef[1][j] = sCoef[1][j].add(coef4.multiply(n + 1));
2704                                 sCoef[2][j] = sCoef[2][j].add(coef1.multiply(kns.multiply(dGjsdh).add(gjs.multiply(hXXX).multiply(dkns))));
2705                                 sCoef[3][j] = sCoef[3][j].add(coef1.multiply(kns.multiply(dGjsdk).add(gjs.multiply(kXXX).multiply(dkns))));
2706                                 sCoef[4][j] = sCoef[4][j].add(coef2.multiply(dGjsdAlpha));
2707                                 sCoef[5][j] = sCoef[5][j].add(coef2.multiply(dGjsdBeta));
2708                                 sCoef[6][j] = sCoef[6][j].add(coef0.multiply(dlns).multiply(kns).multiply(gjs));
2709                             }
2710                         }
2711                     }
2712 
2713                     //compute second double sum where s: 0 -> N-j and n: max(j+s, j+1) -> N
2714                     for (int s = 0; s <= FastMath.min(nMax - j, sMax); s++) {
2715                         // j + s
2716                         final int jps = j + s;
2717                         // Kronecker symbols (2 - delta(0,j+s))
2718                         final double d0spj = (s == -j) ? 1 : 2;
2719 
2720                         for (int n = FastMath.max(j + s, j + 1); n <= nMax; n++) {
2721                             // if n + (j+s) is odd, then the term is equal to zero due to the factor Vn,s+j
2722                             if ((n + jps) % 2 == 0) {
2723                                 // (2 - delta(0,s+j)) * J<sub>n</sub> * K₀<sup>-n-1,s</sup> * L<sub>n</sub><sup>j+s</sup>
2724                                 final T lns  = lnsCoef.getLns(n, jps);
2725                                 final T dlns = lnsCoef.getdLnsdGamma(n, jps);
2726 
2727                                 final T hjs        = ghijCoef.getHjs(s, jps);
2728                                 final T dHjsdh     = ghijCoef.getdHjsdh(s, jps);
2729                                 final T dHjsdk     = ghijCoef.getdHjsdk(s, jps);
2730                                 final T dHjsdAlpha = ghijCoef.getdHjsdAlpha(s, jps);
2731                                 final T dHjsdBeta  = ghijCoef.getdHjsdBeta(s, jps);
2732 
2733                                 final T gjs        = ghijCoef.getGjs(s, jps);
2734                                 final T dGjsdh     = ghijCoef.getdGjsdh(s, jps);
2735                                 final T dGjsdk     = ghijCoef.getdGjsdk(s, jps);
2736                                 final T dGjsdAlpha = ghijCoef.getdGjsdAlpha(s, jps);
2737                                 final T dGjsdBeta  = ghijCoef.getdGjsdBeta(s, jps);
2738 
2739                                 // J<sub>n</sub>
2740                                 final T jn = zero.subtract(harmonics.getUnnormalizedCnm(n, 0));
2741 
2742                                 // K₀<sup>-n-1,s</sup>
2743                                 final T kns   = (T) hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getX());
2744                                 final T dkns  = (T) hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getX());
2745 
2746                                 final T coef0 = jn.multiply(d0spj);
2747                                 final T coef1 = coef0.multiply(lns);
2748                                 final T coef2 = coef1.multiply(kns);
2749 
2750                                 final T coef3 = coef2.multiply(hjs);
2751                                 final T coef4 = coef2.multiply(gjs);
2752 
2753                                 // Add the term to the coefficients
2754                                 cCoef[0][j] = cCoef[0][j].subtract(coef3);
2755                                 cCoef[1][j] = cCoef[1][j].subtract(coef3.multiply(n + 1));
2756                                 cCoef[2][j] = cCoef[2][j].subtract(coef1.multiply(kns.multiply(dHjsdh).add(hjs.multiply(hXXX).multiply(dkns))));
2757                                 cCoef[3][j] = cCoef[3][j].subtract(coef1.multiply(kns.multiply(dHjsdk).add(hjs.multiply(kXXX).multiply(dkns))));
2758                                 cCoef[4][j] = cCoef[4][j].subtract(coef2.multiply(dHjsdAlpha));
2759                                 cCoef[5][j] = cCoef[5][j].subtract(coef2.multiply(dHjsdBeta));
2760                                 cCoef[6][j] = cCoef[6][j].subtract(coef0.multiply(dlns).multiply(kns).multiply(hjs));
2761 
2762                                 sCoef[0][j] = sCoef[0][j].add(coef4);
2763                                 sCoef[1][j] = sCoef[1][j].add(coef4.multiply(n + 1));
2764                                 sCoef[2][j] = sCoef[2][j].add(coef1.multiply(kns.multiply(dGjsdh).add(gjs.multiply(hXXX).multiply(dkns))));
2765                                 sCoef[3][j] = sCoef[3][j].add(coef1.multiply(kns.multiply(dGjsdk).add(gjs.multiply(kXXX).multiply(dkns))));
2766                                 sCoef[4][j] = sCoef[4][j].add(coef2.multiply(dGjsdAlpha));
2767                                 sCoef[5][j] = sCoef[5][j].add(coef2.multiply(dGjsdBeta));
2768                                 sCoef[6][j] = sCoef[6][j].add(coef0.multiply(dlns).multiply(kns).multiply(gjs));
2769                             }
2770                         }
2771                     }
2772 
2773                     //compute third double sum where s: 1 -> j and  n: j+1 -> N
2774                     for (int s = 1; s <= FastMath.min(j, sMax); s++) {
2775                         // j - s
2776                         final int jms = j - s;
2777                         // Kronecker symbols (2 - delta(0,s-j)) and (2 - delta(0,j-s))
2778                         final int d0smj = (s == j) ? 1 : 2;
2779 
2780                         for (int n = j + 1; n <= nMax; n++) {
2781                             // if n + (j-s) is odd, then the term is equal to zero due to the factor Vn,s-j
2782                             if ((n + jms) % 2 == 0) {
2783                                 // (2 - delta(0,j-s)) * J<sub>n</sub> * K₀<sup>-n-1,s</sup> * L<sub>n</sub><sup>j-s</sup>
2784                                 final T lns  = lnsCoef.getLns(n, jms);
2785                                 final T dlns = lnsCoef.getdLnsdGamma(n, jms);
2786 
2787                                 final T ijs        = ghijCoef.getIjs(s, jms);
2788                                 final T dIjsdh     = ghijCoef.getdIjsdh(s, jms);
2789                                 final T dIjsdk     = ghijCoef.getdIjsdk(s, jms);
2790                                 final T dIjsdAlpha = ghijCoef.getdIjsdAlpha(s, jms);
2791                                 final T dIjsdBeta  = ghijCoef.getdIjsdBeta(s, jms);
2792 
2793                                 final T jjs        = ghijCoef.getJjs(s, jms);
2794                                 final T dJjsdh     = ghijCoef.getdJjsdh(s, jms);
2795                                 final T dJjsdk     = ghijCoef.getdJjsdk(s, jms);
2796                                 final T dJjsdAlpha = ghijCoef.getdJjsdAlpha(s, jms);
2797                                 final T dJjsdBeta  = ghijCoef.getdJjsdBeta(s, jms);
2798 
2799                                 // J<sub>n</sub>
2800                                 final T jn = zero.subtract(harmonics.getUnnormalizedCnm(n, 0));
2801 
2802                                 // K₀<sup>-n-1,s</sup>
2803                                 final T kns   = (T) hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getX());
2804                                 final T dkns  = (T) hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getX());
2805 
2806                                 final T coef0 = jn.multiply(d0smj);
2807                                 final T coef1 = coef0.multiply(lns);
2808                                 final T coef2 = coef1.multiply(kns);
2809 
2810                                 final T coef3 = coef2.multiply(ijs);
2811                                 final T coef4 = coef2.multiply(jjs);
2812 
2813                                 // Add the term to the coefficients
2814                                 cCoef[0][j] = cCoef[0][j].subtract(coef3);
2815                                 cCoef[1][j] = cCoef[1][j].subtract(coef3.multiply(n + 1));
2816                                 cCoef[2][j] = cCoef[2][j].subtract(coef1.multiply(kns.multiply(dIjsdh).add(ijs.multiply(hXXX).multiply(dkns))));
2817                                 cCoef[3][j] = cCoef[3][j].subtract(coef1.multiply(kns.multiply(dIjsdk).add(ijs.multiply(kXXX).multiply(dkns))));
2818                                 cCoef[4][j] = cCoef[4][j].subtract(coef2.multiply(dIjsdAlpha));
2819                                 cCoef[5][j] = cCoef[5][j].subtract(coef2.multiply(dIjsdBeta));
2820                                 cCoef[6][j] = cCoef[6][j].subtract(coef0.multiply(dlns).multiply(kns).multiply(ijs));
2821 
2822                                 sCoef[0][j] = sCoef[0][j].add(coef4);
2823                                 sCoef[1][j] = sCoef[1][j].add(coef4.multiply(n + 1));
2824                                 sCoef[2][j] = sCoef[2][j].add(coef1.multiply(kns.multiply(dJjsdh).add(jjs.multiply(hXXX).multiply(dkns))));
2825                                 sCoef[3][j] = sCoef[3][j].add(coef1.multiply(kns.multiply(dJjsdk).add(jjs.multiply(kXXX).multiply(dkns))));
2826                                 sCoef[4][j] = sCoef[4][j].add(coef2.multiply(dJjsdAlpha));
2827                                 sCoef[5][j] = sCoef[5][j].add(coef2.multiply(dJjsdBeta));
2828                                 sCoef[6][j] = sCoef[6][j].add(coef0.multiply(dlns).multiply(kns).multiply(jjs));
2829                             }
2830                         }
2831                     }
2832                 }
2833 
2834                 if (isBetween(j, 2, nMax)) {
2835                     //add first term
2836                     // J<sub>j</sub>
2837                     final T jj = zero.subtract(harmonics.getUnnormalizedCnm(j, 0));
2838                     T kns  = (T) hansenObjects.getHansenObjects()[0].getValue(-j - 1, context.getX());
2839                     T dkns = (T) hansenObjects.getHansenObjects()[0].getDerivative(-j - 1, context.getX());
2840 
2841                     T lns = lnsCoef.getLns(j, j);
2842                     //dlns is 0 because n == s == j
2843 
2844                     final T hjs        = ghijCoef.getHjs(0, j);
2845                     final T dHjsdh     = ghijCoef.getdHjsdh(0, j);
2846                     final T dHjsdk     = ghijCoef.getdHjsdk(0, j);
2847                     final T dHjsdAlpha = ghijCoef.getdHjsdAlpha(0, j);
2848                     final T dHjsdBeta  = ghijCoef.getdHjsdBeta(0, j);
2849 
2850                     final T gjs        = ghijCoef.getGjs(0, j);
2851                     final T dGjsdh     = ghijCoef.getdGjsdh(0, j);
2852                     final T dGjsdk     = ghijCoef.getdGjsdk(0, j);
2853                     final T dGjsdAlpha = ghijCoef.getdGjsdAlpha(0, j);
2854                     final T dGjsdBeta  = ghijCoef.getdGjsdBeta(0, j);
2855 
2856                     // 2 * J<sub>j</sub> * K₀<sup>-j-1,0</sup> * L<sub>j</sub><sup>j</sup>
2857                     T coef0 = jj.multiply(2.);
2858                     T coef1 = coef0.multiply(lns);
2859                     T coef2 = coef1.multiply(kns);
2860 
2861                     T coef3 = coef2.multiply(hjs);
2862                     T coef4 = coef2.multiply(gjs);
2863 
2864                     // Add the term to the coefficients
2865                     cCoef[0][j] = cCoef[0][j].subtract(coef3);
2866                     cCoef[1][j] = cCoef[1][j].subtract(coef3.multiply(j + 1));
2867                     cCoef[2][j] = cCoef[2][j].subtract(coef1.multiply(kns.multiply(dHjsdh).add(hjs.multiply(hXXX).multiply(dkns))));
2868                     cCoef[3][j] = cCoef[3][j].subtract(coef1.multiply(kns.multiply(dHjsdk).add(hjs.multiply(kXXX).multiply(dkns))));
2869                     cCoef[4][j] = cCoef[4][j].subtract(coef2.multiply(dHjsdAlpha));
2870                     cCoef[5][j] = cCoef[5][j].subtract(coef2.multiply(dHjsdBeta));
2871                     //no contribution to cCoef[6][j] because dlns is 0
2872 
2873                     sCoef[0][j] = sCoef[0][j].add(coef4);
2874                     sCoef[1][j] = sCoef[1][j].add(coef4.multiply(j + 1));
2875                     sCoef[2][j] = sCoef[2][j].add(coef1.multiply(kns.multiply(dGjsdh).add(gjs.multiply(hXXX).multiply(dkns))));
2876                     sCoef[3][j] = sCoef[3][j].add(coef1.multiply(kns.multiply(dGjsdk).add(gjs.multiply(kXXX).multiply(dkns))));
2877                     sCoef[4][j] = sCoef[4][j].add(coef2.multiply(dGjsdAlpha));
2878                     sCoef[5][j] = sCoef[5][j].add(coef2.multiply(dGjsdBeta));
2879                     //no contribution to sCoef[6][j] because dlns is 0
2880 
2881                     //compute simple sum where s: 1 -> j-1
2882                     for (int s = 1; s <= FastMath.min(j - 1, sMax); s++) {
2883                         // j - s
2884                         final int jms = j - s;
2885                         // Kronecker symbols (2 - delta(0,s-j)) and (2 - delta(0,j-s))
2886                         final int d0smj = (s == j) ? 1 : 2;
2887 
2888                         // if s is odd, then the term is equal to zero due to the factor Vj,s-j
2889                         if (s % 2 == 0) {
2890                             // (2 - delta(0,j-s)) * J<sub>j</sub> * K₀<sup>-j-1,s</sup> * L<sub>j</sub><sup>j-s</sup>
2891                             kns   = (T) hansenObjects.getHansenObjects()[s].getValue(-j - 1, context.getX());
2892                             dkns  = (T) hansenObjects.getHansenObjects()[s].getDerivative(-j - 1, context.getX());
2893 
2894                             lns = lnsCoef.getLns(j, jms);
2895                             final T dlns = lnsCoef.getdLnsdGamma(j, jms);
2896 
2897                             final T ijs        = ghijCoef.getIjs(s, jms);
2898                             final T dIjsdh     = ghijCoef.getdIjsdh(s, jms);
2899                             final T dIjsdk     = ghijCoef.getdIjsdk(s, jms);
2900                             final T dIjsdAlpha = ghijCoef.getdIjsdAlpha(s, jms);
2901                             final T dIjsdBeta  = ghijCoef.getdIjsdBeta(s, jms);
2902 
2903                             final T jjs        = ghijCoef.getJjs(s, jms);
2904                             final T dJjsdh     = ghijCoef.getdJjsdh(s, jms);
2905                             final T dJjsdk     = ghijCoef.getdJjsdk(s, jms);
2906                             final T dJjsdAlpha = ghijCoef.getdJjsdAlpha(s, jms);
2907                             final T dJjsdBeta  = ghijCoef.getdJjsdBeta(s, jms);
2908 
2909                             coef0 = jj.multiply(d0smj);
2910                             coef1 = coef0.multiply(lns);
2911                             coef2 = coef1.multiply(kns);
2912 
2913                             coef3 = coef2.multiply(ijs);
2914                             coef4 = coef2.multiply(jjs);
2915 
2916                             // Add the term to the coefficients
2917                             cCoef[0][j] = cCoef[0][j].subtract(coef3);
2918                             cCoef[1][j] = cCoef[1][j].subtract(coef3.multiply(j + 1));
2919                             cCoef[2][j] = cCoef[2][j].subtract(coef1.multiply(kns.multiply(dIjsdh).add(ijs.multiply(hXXX).multiply(dkns))));
2920                             cCoef[3][j] = cCoef[3][j].subtract(coef1.multiply(kns.multiply(dIjsdk).add(ijs.multiply(kXXX).multiply(dkns))));
2921                             cCoef[4][j] = cCoef[4][j].subtract(coef2.multiply(dIjsdAlpha));
2922                             cCoef[5][j] = cCoef[5][j].subtract(coef2.multiply(dIjsdBeta));
2923                             cCoef[6][j] = cCoef[6][j].subtract(coef0.multiply(dlns).multiply(kns).multiply(ijs));
2924 
2925                             sCoef[0][j] = sCoef[0][j].add(coef4);
2926                             sCoef[1][j] = sCoef[1][j].add(coef4.multiply(j + 1));
2927                             sCoef[2][j] = sCoef[2][j].add(coef1.multiply(kns.multiply(dJjsdh).add(jjs.multiply(hXXX).multiply(dkns))));
2928                             sCoef[3][j] = sCoef[3][j].add(coef1.multiply(kns.multiply(dJjsdk).add(jjs.multiply(kXXX).multiply(dkns))));
2929                             sCoef[4][j] = sCoef[4][j].add(coef2.multiply(dJjsdAlpha));
2930                             sCoef[5][j] = sCoef[5][j].add(coef2.multiply(dJjsdBeta));
2931                             sCoef[6][j] = sCoef[6][j].add(coef0.multiply(dlns).multiply(kns).multiply(jjs));
2932                         }
2933                     }
2934                 }
2935 
2936                 if (isBetween(j, 3, 2 * nMax - 1)) {
2937                     //compute uppercase sigma expressions
2938 
2939                     //min(j-1,N)
2940                     final int minjm1on = FastMath.min(j - 1, nMax);
2941 
2942                     //if j is even
2943                     if (j % 2 == 0) {
2944                         //compute first double sum where s: j-min(j-1,N) -> j/2-1 and n: j-s -> min(j-1,N)
2945                         for (int s = j - minjm1on; s <= FastMath.min(j / 2 - 1, sMax); s++) {
2946                             // j - s
2947                             final int jms = j - s;
2948                             // Kronecker symbols (2 - delta(0,s-j)) and (2 - delta(0,j-s))
2949                             final int d0smj = (s == j) ? 1 : 2;
2950 
2951                             for (int n = j - s; n <= minjm1on; n++) {
2952                                 // if n + (j-s) is odd, then the term is equal to zero due to the factor Vn,s-j
2953                                 if ((n + jms) % 2 == 0) {
2954                                     // (2 - delta(0,j-s)) * J<sub>n</sub> * K₀<sup>-n-1,s</sup> * L<sub>n</sub><sup>j-s</sup>
2955                                     final T lns  = lnsCoef.getLns(n, jms);
2956                                     final T dlns = lnsCoef.getdLnsdGamma(n, jms);
2957 
2958                                     final T ijs        = ghijCoef.getIjs(s, jms);
2959                                     final T dIjsdh     = ghijCoef.getdIjsdh(s, jms);
2960                                     final T dIjsdk     = ghijCoef.getdIjsdk(s, jms);
2961                                     final T dIjsdAlpha = ghijCoef.getdIjsdAlpha(s, jms);
2962                                     final T dIjsdBeta  = ghijCoef.getdIjsdBeta(s, jms);
2963 
2964                                     final T jjs        = ghijCoef.getJjs(s, jms);
2965                                     final T dJjsdh     = ghijCoef.getdJjsdh(s, jms);
2966                                     final T dJjsdk     = ghijCoef.getdJjsdk(s, jms);
2967                                     final T dJjsdAlpha = ghijCoef.getdJjsdAlpha(s, jms);
2968                                     final T dJjsdBeta  = ghijCoef.getdJjsdBeta(s, jms);
2969 
2970                                     // J<sub>n</sub>
2971                                     final T jn = zero.subtract(harmonics.getUnnormalizedCnm(n, 0));
2972 
2973                                     // K₀<sup>-n-1,s</sup>
2974                                     final T kns   = (T) hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getX());
2975                                     final T dkns  = (T) hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getX());
2976 
2977                                     final T coef0 = jn.multiply(d0smj);
2978                                     final T coef1 = coef0.multiply(lns);
2979                                     final T coef2 = coef1.multiply(kns);
2980 
2981                                     final T coef3 = coef2.multiply(ijs);
2982                                     final T coef4 = coef2.multiply(jjs);
2983 
2984                                     // Add the term to the coefficients
2985                                     cCoef[0][j] = cCoef[0][j].subtract(coef3);
2986                                     cCoef[1][j] = cCoef[1][j].subtract(coef3.multiply(n + 1));
2987                                     cCoef[2][j] = cCoef[2][j].subtract(coef1.multiply(kns.multiply(dIjsdh).add(ijs.multiply(hXXX).multiply(dkns))));
2988                                     cCoef[3][j] = cCoef[3][j].subtract(coef1.multiply(kns.multiply(dIjsdk).add(ijs.multiply(kXXX).multiply(dkns))));
2989                                     cCoef[4][j] = cCoef[4][j].subtract(coef2.multiply(dIjsdAlpha));
2990                                     cCoef[5][j] = cCoef[5][j].subtract(coef2.multiply(dIjsdBeta));
2991                                     cCoef[6][j] = cCoef[6][j].subtract(coef0.multiply(dlns).multiply(kns).multiply(ijs));
2992 
2993                                     sCoef[0][j] = sCoef[0][j].add(coef4);
2994                                     sCoef[1][j] = sCoef[1][j].add(coef4.multiply(n + 1));
2995                                     sCoef[2][j] = sCoef[2][j].add(coef1.multiply(kns.multiply(dJjsdh).add(jjs.multiply(hXXX).multiply(dkns))));
2996                                     sCoef[3][j] = sCoef[3][j].add(coef1.multiply(kns.multiply(dJjsdk).add(jjs.multiply(kXXX).multiply(dkns))));
2997                                     sCoef[4][j] = sCoef[4][j].add(coef2.multiply(dJjsdAlpha));
2998                                     sCoef[5][j] = sCoef[5][j].add(coef2.multiply(dJjsdBeta));
2999                                     sCoef[6][j] = sCoef[6][j].add(coef0.multiply(dlns).multiply(kns).multiply(jjs));
3000                                 }
3001                             }
3002                         }
3003 
3004                         //compute second double sum where s: j/2 -> min(j-1,N)-1 and n: s+1 -> min(j-1,N)
3005                         for (int s = j / 2; s <=  FastMath.min(minjm1on - 1, sMax); s++) {
3006                             // j - s
3007                             final int jms = j - s;
3008                             // Kronecker symbols (2 - delta(0,s-j)) and (2 - delta(0,j-s))
3009                             final int d0smj = (s == j) ? 1 : 2;
3010 
3011                             for (int n = s + 1; n <= minjm1on; n++) {
3012                                 // if n + (j-s) is odd, then the term is equal to zero due to the factor Vn,s-j
3013                                 if ((n + jms) % 2 == 0) {
3014                                     // (2 - delta(0,j-s)) * J<sub>n</sub> * K₀<sup>-n-1,s</sup> * L<sub>n</sub><sup>j-s</sup>
3015                                     final T lns  = lnsCoef.getLns(n, jms);
3016                                     final T dlns = lnsCoef.getdLnsdGamma(n, jms);
3017 
3018                                     final T ijs        = ghijCoef.getIjs(s, jms);
3019                                     final T dIjsdh     = ghijCoef.getdIjsdh(s, jms);
3020                                     final T dIjsdk     = ghijCoef.getdIjsdk(s, jms);
3021                                     final T dIjsdAlpha = ghijCoef.getdIjsdAlpha(s, jms);
3022                                     final T dIjsdBeta  = ghijCoef.getdIjsdBeta(s, jms);
3023 
3024                                     final T jjs        = ghijCoef.getJjs(s, jms);
3025                                     final T dJjsdh     = ghijCoef.getdJjsdh(s, jms);
3026                                     final T dJjsdk     = ghijCoef.getdJjsdk(s, jms);
3027                                     final T dJjsdAlpha = ghijCoef.getdJjsdAlpha(s, jms);
3028                                     final T dJjsdBeta  = ghijCoef.getdJjsdBeta(s, jms);
3029 
3030                                     // J<sub>n</sub>
3031                                     final T jn = zero.subtract(harmonics.getUnnormalizedCnm(n, 0));
3032 
3033                                     // K₀<sup>-n-1,s</sup>
3034                                     final T kns   = (T) hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getX());
3035                                     final T dkns  = (T) hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getX());
3036 
3037                                     final T coef0 = jn.multiply(d0smj);
3038                                     final T coef1 = coef0.multiply(lns);
3039                                     final T coef2 = coef1.multiply(kns);
3040 
3041                                     final T coef3 = coef2.multiply(ijs);
3042                                     final T coef4 = coef2.multiply(jjs);
3043 
3044                                     // Add the term to the coefficients
3045                                     cCoef[0][j] = cCoef[0][j].subtract(coef3);
3046                                     cCoef[1][j] = cCoef[1][j].subtract(coef3.multiply(n + 1));
3047                                     cCoef[2][j] = cCoef[2][j].subtract(coef1.multiply(kns.multiply(dIjsdh).add(ijs.multiply(hXXX).multiply(dkns))));
3048                                     cCoef[3][j] = cCoef[3][j].subtract(coef1.multiply(kns.multiply(dIjsdk).add(ijs.multiply(kXXX).multiply(dkns))));
3049                                     cCoef[4][j] = cCoef[4][j].subtract(coef2.multiply(dIjsdAlpha));
3050                                     cCoef[5][j] = cCoef[5][j].subtract(coef2.multiply(dIjsdBeta));
3051                                     cCoef[6][j] = cCoef[6][j].subtract(coef0.multiply(dlns).multiply(kns).multiply(ijs));
3052 
3053                                     sCoef[0][j] = sCoef[0][j].add(coef4);
3054                                     sCoef[1][j] = sCoef[1][j].add(coef4.multiply(n + 1));
3055                                     sCoef[2][j] = sCoef[2][j].add(coef1.multiply(kns.multiply(dJjsdh).add(jjs.multiply(hXXX).multiply(dkns))));
3056                                     sCoef[3][j] = sCoef[3][j].add(coef1.multiply(kns.multiply(dJjsdk).add(jjs.multiply(kXXX).multiply(dkns))));
3057                                     sCoef[4][j] = sCoef[4][j].add(coef2.multiply(dJjsdAlpha));
3058                                     sCoef[5][j] = sCoef[5][j].add(coef2.multiply(dJjsdBeta));
3059                                     sCoef[6][j] = sCoef[6][j].add(coef0.multiply(dlns).multiply(kns).multiply(jjs));
3060                                 }
3061                             }
3062                         }
3063                     }
3064 
3065                     //if j is odd
3066                     else {
3067                         //compute first double sum where s: (j-1)/2 -> min(j-1,N)-1 and n: s+1 -> min(j-1,N)
3068                         for (int s = (j - 1) / 2; s <= FastMath.min(minjm1on - 1, sMax); s++) {
3069                             // j - s
3070                             final int jms = j - s;
3071                             // Kronecker symbols (2 - delta(0,s-j)) and (2 - delta(0,j-s))
3072                             final int d0smj = (s == j) ? 1 : 2;
3073 
3074                             for (int n = s + 1; n <= minjm1on; n++) {
3075                                 // if n + (j-s) is odd, then the term is equal to zero due to the factor Vn,s-j
3076                                 if ((n + jms) % 2 == 0) {
3077                                     // (2 - delta(0,j-s)) * J<sub>n</sub> * K₀<sup>-n-1,s</sup> * L<sub>n</sub><sup>j-s</sup>
3078                                     final T lns  = lnsCoef.getLns(n, jms);
3079                                     final T dlns = lnsCoef.getdLnsdGamma(n, jms);
3080 
3081                                     final T ijs        = ghijCoef.getIjs(s, jms);
3082                                     final T dIjsdh     = ghijCoef.getdIjsdh(s, jms);
3083                                     final T dIjsdk     = ghijCoef.getdIjsdk(s, jms);
3084                                     final T dIjsdAlpha = ghijCoef.getdIjsdAlpha(s, jms);
3085                                     final T dIjsdBeta  = ghijCoef.getdIjsdBeta(s, jms);
3086 
3087                                     final T jjs        = ghijCoef.getJjs(s, jms);
3088                                     final T dJjsdh     = ghijCoef.getdJjsdh(s, jms);
3089                                     final T dJjsdk     = ghijCoef.getdJjsdk(s, jms);
3090                                     final T dJjsdAlpha = ghijCoef.getdJjsdAlpha(s, jms);
3091                                     final T dJjsdBeta  = ghijCoef.getdJjsdBeta(s, jms);
3092 
3093                                     // J<sub>n</sub>
3094                                     final T jn = zero.subtract(harmonics.getUnnormalizedCnm(n, 0));
3095 
3096                                     // K₀<sup>-n-1,s</sup>
3097 
3098                                     final T kns   = (T) hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getX());
3099                                     final T dkns  = (T) hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getX());
3100 
3101                                     final T coef0 = jn.multiply(d0smj);
3102                                     final T coef1 = coef0.multiply(lns);
3103                                     final T coef2 = coef1.multiply(kns);
3104 
3105                                     final T coef3 = coef2.multiply(ijs);
3106                                     final T coef4 = coef2.multiply(jjs);
3107 
3108                                     // Add the term to the coefficients
3109                                     cCoef[0][j] = cCoef[0][j].subtract(coef3);
3110                                     cCoef[1][j] = cCoef[1][j].subtract(coef3.multiply(n + 1));
3111                                     cCoef[2][j] = cCoef[2][j].subtract(coef1.multiply(kns.multiply(dIjsdh).add(ijs.multiply(hXXX).multiply(dkns))));
3112                                     cCoef[3][j] = cCoef[3][j].subtract(coef1.multiply(kns.multiply(dIjsdk).add(ijs.multiply(kXXX).multiply(dkns))));
3113                                     cCoef[4][j] = cCoef[4][j].subtract(coef2.multiply(dIjsdAlpha));
3114                                     cCoef[5][j] = cCoef[5][j].subtract(coef2.multiply(dIjsdBeta));
3115                                     cCoef[6][j] = cCoef[6][j].subtract(coef0.multiply(dlns).multiply(kns).multiply(ijs));
3116 
3117                                     sCoef[0][j] = sCoef[0][j].add(coef4);
3118                                     sCoef[1][j] = sCoef[1][j].add(coef4.multiply(n + 1));
3119                                     sCoef[2][j] = sCoef[2][j].add(coef1.multiply(kns.multiply(dJjsdh).add(jjs.multiply(hXXX).multiply(dkns))));
3120                                     sCoef[3][j] = sCoef[3][j].add(coef1.multiply(kns.multiply(dJjsdk).add(jjs.multiply(kXXX).multiply(dkns))));
3121                                     sCoef[4][j] = sCoef[4][j].add(coef2.multiply(dJjsdAlpha));
3122                                     sCoef[5][j] = sCoef[5][j].add(coef2.multiply(dJjsdBeta));
3123                                     sCoef[6][j] = sCoef[6][j].add(coef0.multiply(dlns).multiply(kns).multiply(jjs));
3124                                 }
3125                             }
3126                         }
3127 
3128                         //the second double sum is added only if N >= 4 and j between 5 and 2*N-3
3129                         if (nMax >= 4 && isBetween(j, 5, 2 * nMax - 3)) {
3130                             //compute second double sum where s: j-min(j-1,N) -> (j-3)/2 and n: j-s -> min(j-1,N)
3131                             for (int s = j - minjm1on; s <= FastMath.min((j - 3) / 2, sMax); s++) {
3132                                 // j - s
3133                                 final int jms = j - s;
3134                                 // Kronecker symbols (2 - delta(0,s-j)) and (2 - delta(0,j-s))
3135                                 final int d0smj = (s == j) ? 1 : 2;
3136 
3137                                 for (int n = j - s; n <= minjm1on; n++) {
3138                                     // if n + (j-s) is odd, then the term is equal to zero due to the factor Vn,s-j
3139                                     if ((n + jms) % 2 == 0) {
3140                                         // (2 - delta(0,j-s)) * J<sub>n</sub> * K₀<sup>-n-1,s</sup> * L<sub>n</sub><sup>j-s</sup>
3141                                         final T lns  = lnsCoef.getLns(n, jms);
3142                                         final T dlns = lnsCoef.getdLnsdGamma(n, jms);
3143 
3144                                         final T ijs        = ghijCoef.getIjs(s, jms);
3145                                         final T dIjsdh     = ghijCoef.getdIjsdh(s, jms);
3146                                         final T dIjsdk     = ghijCoef.getdIjsdk(s, jms);
3147                                         final T dIjsdAlpha = ghijCoef.getdIjsdAlpha(s, jms);
3148                                         final T dIjsdBeta  = ghijCoef.getdIjsdBeta(s, jms);
3149 
3150                                         final T jjs        = ghijCoef.getJjs(s, jms);
3151                                         final T dJjsdh     = ghijCoef.getdJjsdh(s, jms);
3152                                         final T dJjsdk     = ghijCoef.getdJjsdk(s, jms);
3153                                         final T dJjsdAlpha = ghijCoef.getdJjsdAlpha(s, jms);
3154                                         final T dJjsdBeta  = ghijCoef.getdJjsdBeta(s, jms);
3155 
3156                                         // J<sub>n</sub>
3157                                         final T jn = zero.subtract(harmonics.getUnnormalizedCnm(n, 0));
3158 
3159                                         // K₀<sup>-n-1,s</sup>
3160                                         final T kns   = (T) hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getX());
3161                                         final T dkns  = (T) hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getX());
3162 
3163                                         final T coef0 = jn.multiply(d0smj);
3164                                         final T coef1 = coef0.multiply(lns);
3165                                         final T coef2 = coef1.multiply(kns);
3166 
3167                                         final T coef3 = coef2.multiply(ijs);
3168                                         final T coef4 = coef2.multiply(jjs);
3169 
3170                                         // Add the term to the coefficients
3171                                         cCoef[0][j] = cCoef[0][j].subtract(coef3);
3172                                         cCoef[1][j] = cCoef[1][j].subtract(coef3.multiply(n + 1));
3173                                         cCoef[2][j] = cCoef[2][j].subtract(coef1.multiply(kns.multiply(dIjsdh).add(ijs.multiply(hXXX).multiply(dkns))));
3174                                         cCoef[3][j] = cCoef[3][j].subtract(coef1.multiply(kns.multiply(dIjsdk).add(ijs.multiply(kXXX).multiply(dkns))));
3175                                         cCoef[4][j] = cCoef[4][j].subtract(coef2.multiply(dIjsdAlpha));
3176                                         cCoef[5][j] = cCoef[5][j].subtract(coef2.multiply(dIjsdBeta));
3177                                         cCoef[6][j] = cCoef[6][j].subtract(coef0.multiply(dlns).multiply(kns).multiply(ijs));
3178 
3179                                         sCoef[0][j] = sCoef[0][j].add(coef4);
3180                                         sCoef[1][j] = sCoef[1][j].add(coef4.multiply(n + 1));
3181                                         sCoef[2][j] = sCoef[2][j].add(coef1.multiply(kns.multiply(dJjsdh).add(jjs.multiply(hXXX).multiply(dkns))));
3182                                         sCoef[3][j] = sCoef[3][j].add(coef1.multiply(kns.multiply(dJjsdk).add(jjs.multiply(kXXX).multiply(dkns))));
3183                                         sCoef[4][j] = sCoef[4][j].add(coef2.multiply(dJjsdAlpha));
3184                                         sCoef[5][j] = sCoef[5][j].add(coef2.multiply(dJjsdBeta));
3185                                         sCoef[6][j] = sCoef[6][j].add(coef0.multiply(dlns).multiply(kns).multiply(jjs));
3186                                     }
3187                                 }
3188                             }
3189                         }
3190                     }
3191                 }
3192 
3193                 cCoef[0][j] = cCoef[0][j].multiply(context.getMuoa().divide(j).negate());
3194                 cCoef[1][j] = cCoef[1][j].multiply(context.getMuoa().divide(auxiliaryElements.getSma().multiply(j)));
3195                 cCoef[2][j] = cCoef[2][j].multiply(context.getMuoa().divide(j).negate());
3196                 cCoef[3][j] = cCoef[3][j].multiply(context.getMuoa().divide(j).negate());
3197                 cCoef[4][j] = cCoef[4][j].multiply(context.getMuoa().divide(j).negate());
3198                 cCoef[5][j] = cCoef[5][j].multiply(context.getMuoa().divide(j).negate());
3199                 cCoef[6][j] = cCoef[6][j].multiply(context.getMuoa().divide(j).negate());
3200 
3201                 sCoef[0][j] = sCoef[0][j].multiply(context.getMuoa().divide(j).negate());
3202                 sCoef[1][j] = sCoef[1][j].multiply(context.getMuoa().divide(auxiliaryElements.getSma().multiply(j)));
3203                 sCoef[2][j] = sCoef[2][j].multiply(context.getMuoa().divide(j).negate());
3204                 sCoef[3][j] = sCoef[3][j].multiply(context.getMuoa().divide(j).negate());
3205                 sCoef[4][j] = sCoef[4][j].multiply(context.getMuoa().divide(j).negate());
3206                 sCoef[5][j] = sCoef[5][j].multiply(context.getMuoa().divide(j).negate());
3207                 sCoef[6][j] = sCoef[6][j].multiply(context.getMuoa().divide(j).negate());
3208 
3209             }
3210         }
3211 
3212         /** Check if an index is within the accepted interval.
3213          *
3214          * @param index the index to check
3215          * @param lowerBound the lower bound of the interval
3216          * @param upperBound the upper bound of the interval
3217          * @return true if the index is between the lower and upper bounds, false otherwise
3218          */
3219         private boolean isBetween(final int index, final int lowerBound, final int upperBound) {
3220             return index >= lowerBound && index <= upperBound;
3221         }
3222 
3223         /**Get the value of C<sup>j</sup>.
3224          *
3225          * @param j j index
3226          * @return C<sup>j</sup>
3227          */
3228         public T getCj(final int j) {
3229             return cCoef[0][j];
3230         }
3231 
3232         /**Get the value of dC<sup>j</sup> / da.
3233          *
3234          * @param j j index
3235          * @return dC<sup>j</sup> / da
3236          */
3237         public T getdCjdA(final int j) {
3238             return cCoef[1][j];
3239         }
3240 
3241         /**Get the value of dC<sup>j</sup> / dh.
3242          *
3243          * @param j j index
3244          * @return dC<sup>j</sup> / dh
3245          */
3246         public T getdCjdH(final int j) {
3247             return cCoef[2][j];
3248         }
3249 
3250         /**Get the value of dC<sup>j</sup> / dk.
3251          *
3252          * @param j j index
3253          * @return dC<sup>j</sup> / dk
3254          */
3255         public T getdCjdK(final int j) {
3256             return cCoef[3][j];
3257         }
3258 
3259         /**Get the value of dC<sup>j</sup> / dα.
3260          *
3261          * @param j j index
3262          * @return dC<sup>j</sup> / dα
3263          */
3264         public T getdCjdAlpha(final int j) {
3265             return cCoef[4][j];
3266         }
3267 
3268         /**Get the value of dC<sup>j</sup> / dβ.
3269          *
3270          * @param j j index
3271          * @return dC<sup>j</sup> / dβ
3272          */
3273         public T getdCjdBeta(final int j) {
3274             return cCoef[5][j];
3275         }
3276 
3277         /**Get the value of dC<sup>j</sup> / dγ.
3278          *
3279          * @param j j index
3280          * @return dC<sup>j</sup> / dγ
3281          */
3282         public T getdCjdGamma(final int j) {
3283             return cCoef[6][j];
3284         }
3285 
3286         /**Get the value of S<sup>j</sup>.
3287          *
3288          * @param j j index
3289          * @return S<sup>j</sup>
3290          */
3291         public T getSj(final int j) {
3292             return sCoef[0][j];
3293         }
3294 
3295         /**Get the value of dS<sup>j</sup> / da.
3296          *
3297          * @param j j index
3298          * @return dS<sup>j</sup> / da
3299          */
3300         public T getdSjdA(final int j) {
3301             return sCoef[1][j];
3302         }
3303 
3304         /**Get the value of dS<sup>j</sup> / dh.
3305          *
3306          * @param j j index
3307          * @return dS<sup>j</sup> / dh
3308          */
3309         public T getdSjdH(final int j) {
3310             return sCoef[2][j];
3311         }
3312 
3313         /**Get the value of dS<sup>j</sup> / dk.
3314          *
3315          * @param j j index
3316          * @return dS<sup>j</sup> / dk
3317          */
3318         public T getdSjdK(final int j) {
3319             return sCoef[3][j];
3320         }
3321 
3322         /**Get the value of dS<sup>j</sup> / dα.
3323          *
3324          * @param j j index
3325          * @return dS<sup>j</sup> / dα
3326          */
3327         public T getdSjdAlpha(final int j) {
3328             return sCoef[4][j];
3329         }
3330 
3331         /**Get the value of dS<sup>j</sup> / dβ.
3332          *
3333          * @param j j index
3334          * @return dS<sup>j</sup> / dβ
3335          */
3336         public T getdSjdBeta(final int j) {
3337             return sCoef[5][j];
3338         }
3339 
3340         /**Get the value of dS<sup>j</sup> /  dγ.
3341          *
3342          * @param j j index
3343          * @return dS<sup>j</sup> /  dγ
3344          */
3345         public T getdSjdGamma(final int j) {
3346             return sCoef[6][j];
3347         }
3348     }
3349 
3350     /** Coefficients valid for one time slot. */
3351     private static class Slot {
3352 
3353         /**The coefficients D<sub>i</sub>.
3354          * <p>
3355          * i corresponds to the equinoctial element, as follows:
3356          * - i=0 for a <br/>
3357          * - i=1 for k <br/>
3358          * - i=2 for h <br/>
3359          * - i=3 for q <br/>
3360          * - i=4 for p <br/>
3361          * - i=5 for λ <br/>
3362          * </p>
3363          */
3364         private final ShortPeriodicsInterpolatedCoefficient di;
3365 
3366         /** The coefficients C<sub>i</sub><sup>j</sup>.
3367          * <p>
3368          * The constant term C<sub>i</sub>⁰ is also stored in this variable at index j = 0 <br>
3369          * The index order is cij[j][i] <br/>
3370          * i corresponds to the equinoctial element, as follows: <br/>
3371          * - i=0 for a <br/>
3372          * - i=1 for k <br/>
3373          * - i=2 for h <br/>
3374          * - i=3 for q <br/>
3375          * - i=4 for p <br/>
3376          * - i=5 for λ <br/>
3377          * </p>
3378          */
3379         private final ShortPeriodicsInterpolatedCoefficient[] cij;
3380 
3381         /** The coefficients S<sub>i</sub><sup>j</sup>.
3382          * <p>
3383          * The index order is sij[j][i] <br/>
3384          * i corresponds to the equinoctial element, as follows: <br/>
3385          * - i=0 for a <br/>
3386          * - i=1 for k <br/>
3387          * - i=2 for h <br/>
3388          * - i=3 for q <br/>
3389          * - i=4 for p <br/>
3390          * - i=5 for λ <br/>
3391          * </p>
3392          */
3393         private final ShortPeriodicsInterpolatedCoefficient[] sij;
3394 
3395         /** Simple constructor.
3396          *  @param maxFrequencyShortPeriodics maximum value for j index
3397          *  @param interpolationPoints number of points used in the interpolation process
3398          */
3399         Slot(final int maxFrequencyShortPeriodics, final int interpolationPoints) {
3400 
3401             final int rows = maxFrequencyShortPeriodics + 1;
3402             di  = new ShortPeriodicsInterpolatedCoefficient(interpolationPoints);
3403             cij = new ShortPeriodicsInterpolatedCoefficient[rows];
3404             sij = new ShortPeriodicsInterpolatedCoefficient[rows];
3405 
3406             //Initialize the arrays
3407             for (int j = 0; j <= maxFrequencyShortPeriodics; j++) {
3408                 cij[j] = new ShortPeriodicsInterpolatedCoefficient(interpolationPoints);
3409                 sij[j] = new ShortPeriodicsInterpolatedCoefficient(interpolationPoints);
3410             }
3411 
3412         }
3413 
3414     }
3415 
3416     /** Coefficients valid for one time slot. */
3417     private static class FieldSlot <T extends CalculusFieldElement<T>> {
3418 
3419         /**The coefficients D<sub>i</sub>.
3420          * <p>
3421          * i corresponds to the equinoctial element, as follows:
3422          * - i=0 for a <br/>
3423          * - i=1 for k <br/>
3424          * - i=2 for h <br/>
3425          * - i=3 for q <br/>
3426          * - i=4 for p <br/>
3427          * - i=5 for λ <br/>
3428          * </p>
3429          */
3430         private final FieldShortPeriodicsInterpolatedCoefficient<T> di;
3431 
3432         /** The coefficients C<sub>i</sub><sup>j</sup>.
3433          * <p>
3434          * The constant term C<sub>i</sub>⁰ is also stored in this variable at index j = 0 <br>
3435          * The index order is cij[j][i] <br/>
3436          * i corresponds to the equinoctial element, as follows: <br/>
3437          * - i=0 for a <br/>
3438          * - i=1 for k <br/>
3439          * - i=2 for h <br/>
3440          * - i=3 for q <br/>
3441          * - i=4 for p <br/>
3442          * - i=5 for λ <br/>
3443          * </p>
3444          */
3445         private final FieldShortPeriodicsInterpolatedCoefficient<T>[] cij;
3446 
3447         /** The coefficients S<sub>i</sub><sup>j</sup>.
3448          * <p>
3449          * The index order is sij[j][i] <br/>
3450          * i corresponds to the equinoctial element, as follows: <br/>
3451          * - i=0 for a <br/>
3452          * - i=1 for k <br/>
3453          * - i=2 for h <br/>
3454          * - i=3 for q <br/>
3455          * - i=4 for p <br/>
3456          * - i=5 for λ <br/>
3457          * </p>
3458          */
3459         private final FieldShortPeriodicsInterpolatedCoefficient<T>[] sij;
3460 
3461         /** Simple constructor.
3462          *  @param maxFrequencyShortPeriodics maximum value for j index
3463          *  @param interpolationPoints number of points used in the interpolation process
3464          */
3465         @SuppressWarnings("unchecked")
3466         FieldSlot(final int maxFrequencyShortPeriodics, final int interpolationPoints) {
3467 
3468             final int rows = maxFrequencyShortPeriodics + 1;
3469             di  = new FieldShortPeriodicsInterpolatedCoefficient<>(interpolationPoints);
3470             cij = (FieldShortPeriodicsInterpolatedCoefficient<T>[]) Array.newInstance(FieldShortPeriodicsInterpolatedCoefficient.class, rows);
3471             sij = (FieldShortPeriodicsInterpolatedCoefficient<T>[]) Array.newInstance(FieldShortPeriodicsInterpolatedCoefficient.class, rows);
3472 
3473             //Initialize the arrays
3474             for (int j = 0; j <= maxFrequencyShortPeriodics; j++) {
3475                 cij[j] = new FieldShortPeriodicsInterpolatedCoefficient<>(interpolationPoints);
3476                 sij[j] = new FieldShortPeriodicsInterpolatedCoefficient<>(interpolationPoints);
3477             }
3478 
3479         }
3480 
3481     }
3482 
3483     /** Compute potential and potential derivatives with respect to orbital parameters. */
3484     private class UAnddU {
3485 
3486         /** The current value of the U function. <br/>
3487          * Needed for the short periodic contribution */
3488         private double U;
3489 
3490         /** dU / da. */
3491         private double dUda;
3492 
3493         /** dU / dk. */
3494         private double dUdk;
3495 
3496         /** dU / dh. */
3497         private double dUdh;
3498 
3499         /** dU / dAlpha. */
3500         private double dUdAl;
3501 
3502         /** dU / dBeta. */
3503         private double dUdBe;
3504 
3505         /** dU / dGamma. */
3506         private double dUdGa;
3507 
3508         /** Simple constuctor.
3509          *  @param date current date
3510          *  @param context container for attributes
3511          *  @param auxiliaryElements auxiliary elements related to the current orbit
3512          *  @param hansen initialization of hansen objects
3513          */
3514         UAnddU(final AbsoluteDate date,
3515                final DSSTZonalContext context,
3516                final AuxiliaryElements auxiliaryElements,
3517                final HansenObjects hansen) {
3518 
3519             final UnnormalizedSphericalHarmonics harmonics = provider.onDate(date);
3520 
3521             //Reset U
3522             U = 0.;
3523 
3524             // Gs and Hs coefficients
3525             final double[][] GsHs = CoefficientsFactory.computeGsHs(auxiliaryElements.getK(), auxiliaryElements.getH(), auxiliaryElements.getAlpha(), auxiliaryElements.getBeta(), maxEccPowMeanElements);
3526             // Qns coefficients
3527             final double[][] Qns  = CoefficientsFactory.computeQns(auxiliaryElements.getGamma(), maxDegree, maxEccPowMeanElements);
3528 
3529             final double[] roaPow = new double[maxDegree + 1];
3530             roaPow[0] = 1.;
3531             for (int i = 1; i <= maxDegree; i++) {
3532                 roaPow[i] = context.getRoa() * roaPow[i - 1];
3533             }
3534 
3535             // Potential derivatives
3536             dUda  = 0.;
3537             dUdk  = 0.;
3538             dUdh  = 0.;
3539             dUdAl = 0.;
3540             dUdBe = 0.;
3541             dUdGa = 0.;
3542 
3543             for (int s = 0; s <= maxEccPowMeanElements; s++) {
3544                 //Initialize the Hansen roots
3545                 hansen.computeHansenObjectsInitValues(context, s);
3546 
3547                 // Get the current Gs coefficient
3548                 final double gs = GsHs[0][s];
3549 
3550                 // Compute Gs partial derivatives from 3.1-(9)
3551                 double dGsdh  = 0.;
3552                 double dGsdk  = 0.;
3553                 double dGsdAl = 0.;
3554                 double dGsdBe = 0.;
3555                 if (s > 0) {
3556                     // First get the G(s-1) and the H(s-1) coefficients
3557                     final double sxgsm1 = s * GsHs[0][s - 1];
3558                     final double sxhsm1 = s * GsHs[1][s - 1];
3559                     // Then compute derivatives
3560                     dGsdh  = auxiliaryElements.getBeta()  * sxgsm1 - auxiliaryElements.getAlpha() * sxhsm1;
3561                     dGsdk  = auxiliaryElements.getAlpha() * sxgsm1 + auxiliaryElements.getBeta()  * sxhsm1;
3562                     dGsdAl = auxiliaryElements.getK() * sxgsm1 - auxiliaryElements.getH() * sxhsm1;
3563                     dGsdBe = auxiliaryElements.getH() * sxgsm1 + auxiliaryElements.getK() * sxhsm1;
3564                 }
3565 
3566                 // Kronecker symbol (2 - delta(0,s))
3567                 final double d0s = (s == 0) ? 1 : 2;
3568 
3569                 for (int n = s + 2; n <= maxDegree; n++) {
3570                     // (n - s) must be even
3571                     if ((n - s) % 2 == 0) {
3572 
3573                         //Extract data from previous computation :
3574                         final double kns   = hansen.getHansenObjects()[s].getValue(-n - 1, context.getX());
3575                         final double dkns  = hansen.getHansenObjects()[s].getDerivative(-n - 1, context.getX());
3576 
3577                         final double vns   = Vns.get(new NSKey(n, s));
3578                         final double coef0 = d0s * roaPow[n] * vns * -harmonics.getUnnormalizedCnm(n, 0);
3579                         final double coef1 = coef0 * Qns[n][s];
3580                         final double coef2 = coef1 * kns;
3581                         final double coef3 = coef2 * gs;
3582                         // dQns/dGamma = Q(n, s + 1) from Equation 3.1-(8)
3583                         final double dqns  = Qns[n][s + 1];
3584 
3585                         // Compute U
3586                         U += coef3;
3587                         // Compute dU / da :
3588                         dUda += coef3 * (n + 1);
3589                         // Compute dU / dEx
3590                         dUdk += coef1 * (kns * dGsdk + auxiliaryElements.getK() * context.getXXX() * gs * dkns);
3591                         // Compute dU / dEy
3592                         dUdh += coef1 * (kns * dGsdh + auxiliaryElements.getH() * context.getXXX() * gs * dkns);
3593                         // Compute dU / dAlpha
3594                         dUdAl += coef2 * dGsdAl;
3595                         // Compute dU / dBeta
3596                         dUdBe += coef2 * dGsdBe;
3597                         // Compute dU / dGamma
3598                         dUdGa += coef0 * kns * dqns * gs;
3599 
3600                     }
3601                 }
3602             }
3603 
3604             // Multiply by -(μ / a)
3605             this.U = -context.getMuoa() * U;
3606 
3607             this.dUda = dUda *  context.getMuoa() / auxiliaryElements.getSma();
3608             this.dUdk = dUdk * -context.getMuoa();
3609             this.dUdh = dUdh * -context.getMuoa();
3610             this.dUdAl = dUdAl * -context.getMuoa();
3611             this.dUdBe = dUdBe * -context.getMuoa();
3612             this.dUdGa = dUdGa * -context.getMuoa();
3613 
3614         }
3615 
3616         /** Return value of U.
3617          * @return U
3618          */
3619         public double getU() {
3620             return U;
3621         }
3622 
3623         /** Return value of dU / da.
3624          * @return dUda
3625          */
3626         public double getdUda() {
3627             return dUda;
3628         }
3629 
3630         /** Return value of dU / dk.
3631          * @return dUdk
3632          */
3633         public double getdUdk() {
3634             return dUdk;
3635         }
3636 
3637         /** Return value of dU / dh.
3638          * @return dUdh
3639          */
3640         public double getdUdh() {
3641             return dUdh;
3642         }
3643 
3644         /** Return value of dU / dAlpha.
3645          * @return dUdAl
3646          */
3647         public double getdUdAl() {
3648             return dUdAl;
3649         }
3650 
3651         /** Return value of dU / dBeta.
3652          * @return dUdBe
3653          */
3654         public double getdUdBe() {
3655             return dUdBe;
3656         }
3657 
3658         /** Return value of dU / dGamma.
3659          * @return dUdGa
3660          */
3661         public double getdUdGa() {
3662             return dUdGa;
3663         }
3664 
3665     }
3666 
3667     /** Compute the derivatives of the gravitational potential U [Eq. 3.1-(6)].
3668      *  <p>
3669      *  The result is the array
3670      *  [dU/da, dU/dk, dU/dh, dU/dα, dU/dβ, dU/dγ]
3671      *  </p>
3672      */
3673     private class FieldUAnddU <T extends CalculusFieldElement<T>> {
3674 
3675          /** The current value of the U function. <br/>
3676           * Needed for the short periodic contribution */
3677         private T U;
3678 
3679          /** dU / da. */
3680         private T dUda;
3681 
3682          /** dU / dk. */
3683         private T dUdk;
3684 
3685          /** dU / dh. */
3686         private T dUdh;
3687 
3688          /** dU / dAlpha. */
3689         private T dUdAl;
3690 
3691          /** dU / dBeta. */
3692         private T dUdBe;
3693 
3694          /** dU / dGamma. */
3695         private T dUdGa;
3696 
3697          /** Simple constuctor.
3698           *  @param date current date
3699           *  @param context container for attributes
3700           *  @param auxiliaryElements auxiliary elements related to the current orbit
3701           *  @param hansen initialization of hansen objects
3702           */
3703         FieldUAnddU(final FieldAbsoluteDate<T> date,
3704                     final FieldDSSTZonalContext<T> context,
3705                     final FieldAuxiliaryElements<T> auxiliaryElements,
3706                     final FieldHansenObjects<T> hansen) {
3707 
3708             // Zero for initialization
3709             final Field<T> field = date.getField();
3710             final T zero = field.getZero();
3711 
3712             //spherical harmonics
3713             final UnnormalizedSphericalHarmonics harmonics = provider.onDate(date.toAbsoluteDate());
3714 
3715             //Reset U
3716             U = zero;
3717 
3718             // Gs and Hs coefficients
3719             final T[][] GsHs = CoefficientsFactory.computeGsHs(auxiliaryElements.getK(), auxiliaryElements.getH(), auxiliaryElements.getAlpha(), auxiliaryElements.getBeta(), maxEccPowMeanElements, field);
3720             // Qns coefficients
3721             final T[][] Qns  = CoefficientsFactory.computeQns(auxiliaryElements.getGamma(), maxDegree, maxEccPowMeanElements);
3722 
3723             final T[] roaPow = MathArrays.buildArray(field, maxDegree + 1);
3724             roaPow[0] = zero.add(1.);
3725             for (int i = 1; i <= maxDegree; i++) {
3726                 roaPow[i] = roaPow[i - 1].multiply(context.getRoa());
3727             }
3728 
3729             // Potential derivatives
3730             dUda  = zero;
3731             dUdk  = zero;
3732             dUdh  = zero;
3733             dUdAl = zero;
3734             dUdBe = zero;
3735             dUdGa = zero;
3736 
3737             for (int s = 0; s <= maxEccPowMeanElements; s++) {
3738                 //Initialize the Hansen roots
3739                 hansen.computeHansenObjectsInitValues(context, s);
3740 
3741                 // Get the current Gs coefficient
3742                 final T gs = GsHs[0][s];
3743 
3744                 // Compute Gs partial derivatives from 3.1-(9)
3745                 T dGsdh  = zero;
3746                 T dGsdk  = zero;
3747                 T dGsdAl = zero;
3748                 T dGsdBe = zero;
3749                 if (s > 0) {
3750                     // First get the G(s-1) and the H(s-1) coefficients
3751                     final T sxgsm1 = GsHs[0][s - 1].multiply(s);
3752                     final T sxhsm1 = GsHs[1][s - 1].multiply(s);
3753                     // Then compute derivatives
3754                     dGsdh  = sxgsm1.multiply(auxiliaryElements.getBeta()).subtract(sxhsm1.multiply(auxiliaryElements.getAlpha()));
3755                     dGsdk  = sxgsm1.multiply(auxiliaryElements.getAlpha()).add(sxhsm1.multiply(auxiliaryElements.getBeta()));
3756                     dGsdAl = sxgsm1.multiply(auxiliaryElements.getK()).subtract(sxhsm1.multiply(auxiliaryElements.getH()));
3757                     dGsdBe = sxgsm1.multiply(auxiliaryElements.getH()).add(sxhsm1.multiply(auxiliaryElements.getK()));
3758                 }
3759 
3760                 // Kronecker symbol (2 - delta(0,s))
3761                 final T d0s = zero.add((s == 0) ? 1 : 2);
3762 
3763                 for (int n = s + 2; n <= maxDegree; n++) {
3764                     // (n - s) must be even
3765                     if ((n - s) % 2 == 0) {
3766 
3767                         //Extract data from previous computation :
3768                         final T kns   = (T) hansen.getHansenObjects()[s].getValue(-n - 1, context.getX());
3769                         final T dkns  = (T) hansen.getHansenObjects()[s].getDerivative(-n - 1, context.getX());
3770 
3771                         final double vns   = Vns.get(new NSKey(n, s));
3772                         final T coef0 = d0s.multiply(roaPow[n]).multiply(vns).multiply(-harmonics.getUnnormalizedCnm(n, 0));
3773                         final T coef1 = coef0.multiply(Qns[n][s]);
3774                         final T coef2 = coef1.multiply(kns);
3775                         final T coef3 = coef2.multiply(gs);
3776                         // dQns/dGamma = Q(n, s + 1) from Equation 3.1-(8)
3777                         final T dqns  = Qns[n][s + 1];
3778 
3779                         // Compute U
3780                         U = U.add(coef3);
3781                         // Compute dU / da :
3782                         dUda  = dUda.add(coef3.multiply(n + 1));
3783                         // Compute dU / dEx
3784                         dUdk  = dUdk.add(coef1.multiply(dGsdk.multiply(kns).add(auxiliaryElements.getK().multiply(context.getXXX()).multiply(dkns).multiply(gs))));
3785                         // Compute dU / dEy
3786                         dUdh  = dUdh.add(coef1.multiply(dGsdh.multiply(kns).add(auxiliaryElements.getH().multiply(context.getXXX()).multiply(dkns).multiply(gs))));
3787                         // Compute dU / dAlpha
3788                         dUdAl = dUdAl.add(coef2.multiply(dGsdAl));
3789                         // Compute dU / dBeta
3790                         dUdBe = dUdBe.add(coef2.multiply(dGsdBe));
3791                         // Compute dU / dGamma
3792                         dUdGa = dUdGa.add(coef0.multiply(kns).multiply(dqns).multiply(gs));
3793                     }
3794                 }
3795             }
3796 
3797             // Multiply by -(μ / a)
3798             U = U.multiply(context.getMuoa().negate());
3799 
3800             dUda  = dUda.multiply(context.getMuoa().divide(auxiliaryElements.getSma()));
3801             dUdk  = dUdk.multiply(context.getMuoa()).negate();
3802             dUdh  = dUdh.multiply(context.getMuoa()).negate();
3803             dUdAl = dUdAl.multiply(context.getMuoa()).negate();
3804             dUdBe = dUdBe.multiply(context.getMuoa()).negate();
3805             dUdGa = dUdGa.multiply(context.getMuoa()).negate();
3806 
3807         }
3808 
3809         /** Return value of U.
3810          * @return U
3811          */
3812         public T getU() {
3813             return U;
3814         }
3815 
3816          /** Return value of dU / da.
3817           * @return dUda
3818           */
3819         public T getdUda() {
3820             return dUda;
3821         }
3822 
3823          /** Return value of dU / dk.
3824           * @return dUdk
3825           */
3826         public T getdUdk() {
3827             return dUdk;
3828         }
3829 
3830          /** Return value of dU / dh.
3831           * @return dUdh
3832           */
3833         public T getdUdh() {
3834             return dUdh;
3835         }
3836 
3837          /** Return value of dU / dAlpha.
3838           * @return dUdAl
3839           */
3840         public T getdUdAl() {
3841             return dUdAl;
3842         }
3843 
3844          /** Return value of dU / dBeta.
3845           * @return dUdBe
3846           */
3847         public T getdUdBe() {
3848             return dUdBe;
3849         }
3850 
3851          /** Return value of dU / dGamma.
3852           * @return dUdGa
3853           */
3854         public T getdUdGa() {
3855             return dUdGa;
3856         }
3857 
3858     }
3859 
3860     /** Computes init values of the Hansen Objects. */
3861     private class HansenObjects {
3862 
3863         /** An array that contains the objects needed to build the Hansen coefficients. <br/>
3864          * The index is s*/
3865         private HansenZonalLinear[] hansenObjects;
3866 
3867         /** Simple constructor. */
3868         HansenObjects() {
3869             this.hansenObjects = new HansenZonalLinear[maxEccPow + 1];
3870             for (int s = 0; s <= maxEccPow; s++) {
3871                 this.hansenObjects[s] = new HansenZonalLinear(maxDegree, s);
3872             }
3873         }
3874 
3875         /** Compute init values for hansen objects.
3876          * @param context container for attributes
3877          * @param element element of the array to compute the init values
3878          */
3879         public void computeHansenObjectsInitValues(final DSSTZonalContext context, final int element) {
3880             hansenObjects[element].computeInitValues(context.getX());
3881         }
3882 
3883         /** Get the Hansen Objects.
3884          * @return hansenObjects
3885          */
3886         public HansenZonalLinear[] getHansenObjects() {
3887             return hansenObjects;
3888         }
3889 
3890     }
3891 
3892     /** Computes init values of the Hansen Objects.
3893      * @param <T> type of the elements
3894      */
3895     private class FieldHansenObjects<T extends CalculusFieldElement<T>> {
3896 
3897         /** An array that contains the objects needed to build the Hansen coefficients. <br/>
3898          * The index is s*/
3899         private FieldHansenZonalLinear<T>[] hansenObjects;
3900 
3901         /** Simple constructor.
3902          * @param field field used by default
3903          */
3904         @SuppressWarnings("unchecked")
3905         FieldHansenObjects(final Field<T> field) {
3906             this.hansenObjects = (FieldHansenZonalLinear<T>[]) Array.newInstance(FieldHansenZonalLinear.class, maxEccPow + 1);
3907             for (int s = 0; s <= maxEccPow; s++) {
3908                 this.hansenObjects[s] = new FieldHansenZonalLinear<>(maxDegree, s, field);
3909             }
3910         }
3911 
3912         /** Compute init values for hansen objects.
3913          * @param context container for attributes
3914          * @param element element of the array to compute the init values
3915          */
3916         public void computeHansenObjectsInitValues(final FieldDSSTZonalContext<T> context, final int element) {
3917             hansenObjects[element].computeInitValues(context.getX());
3918         }
3919 
3920         /** Get the Hansen Objects.
3921          * @return hansenObjects
3922          */
3923         public FieldHansenZonalLinear<T>[] getHansenObjects() {
3924             return hansenObjects;
3925         }
3926 
3927     }
3928 
3929 }