1   /* Copyright 2002-2015 CS Systèmes d'Information
2    * Licensed to CS Systèmes d'Information (CS) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * CS licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *   http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.orekit.propagation.semianalytical.dsst.utilities.hansen;
18  
19  import org.apache.commons.math3.analysis.differentiation.DerivativeStructure;
20  import org.apache.commons.math3.analysis.polynomials.PolynomialFunction;
21  import org.apache.commons.math3.util.FastMath;
22  import org.orekit.propagation.semianalytical.dsst.utilities.NewcombOperators;
23  
24  /**
25   * Hansen coefficients K(t,n,s) for t!=0 and n < 0.
26   * <p>
27   * Implements Collins 4-236 or Danielson 2.7.3-(9) for Hansen Coefficients and
28   * Collins 4-240 for derivatives. The recursions are transformed into
29   * composition of linear transformations to obtain the associated polynomials
30   * for coefficients and their derivatives - see Petre's paper
31   *
32   * @author Petre Bazavan
33   * @author Lucian Barbulescu
34   */
35  public class HansenTesseralLinear {
36  
37      /** The number of coefficients that will be computed with a set of roots. */
38      private static final int SLICE = 10;
39  
40      /**
41       * The first vector of polynomials associated to Hansen coefficients and
42       * derivatives.
43       */
44      private PolynomialFunction[][] mpvec;
45  
46      /** The second vector of polynomials associated only to derivatives. */
47      private PolynomialFunction[][] mpvecDeriv;
48  
49      /** The Hansen coefficients used as roots. */
50      private double[][] hansenRoot;
51  
52      /** The derivatives of the Hansen coefficients used as roots. */
53      private double[][] hansenDerivRoot;
54  
55      /** The minimum value for the order. */
56      private int Nmin;
57  
58      /** The index of the initial condition, Petre's paper. */
59      private int N0;
60  
61      /** The s coefficient. */
62      private int s;
63  
64      /** The j coefficient. */
65      private int j;
66  
67      /** The number of slices needed to compute the coefficients. */
68      private int numSlices;
69  
70      /**
71       * The offset used to identify the polynomial that corresponds to a negative.
72       * value of n in the internal array that starts at 0
73       */
74      private int offset;
75  
76      /** The objects used to calculate initial data by means of Newcomb operators. */
77      private HansenCoefficientsBySeries[] hansenInit;
78  
79      /**
80       * Constructor.
81       *
82       * @param nMax the maximum (absolute) value of n parameter
83       * @param s s parameter
84       * @param j j parameter
85       * @param n0 the minimum (absolute) value of n
86       * @param maxHansen maximum power of e2 in Hansen expansion
87       */
88      public HansenTesseralLinear(final int nMax, final int s, final int j, final int n0, final int maxHansen) {
89          //Initialize the fields
90          this.offset = nMax + 1;
91          this.Nmin = -nMax - 1;
92          this.N0 = -n0 - 4;
93          this.s = s;
94          this.j = j;
95  
96          //Ensure that only the needed terms are computed
97          final int maxRoots = FastMath.min(4, N0 - Nmin + 4);
98          this.hansenInit = new HansenCoefficientsBySeries[maxRoots];
99          for (int i = 0; i < maxRoots; i++) {
100             this.hansenInit[i] = new HansenCoefficientsBySeries(N0 - i + 3, s, j, maxHansen);
101         }
102 
103         // The first 4 values are computed with series. No linear combination is needed.
104         final int size = N0 - Nmin;
105         this.numSlices = (int) FastMath.max(FastMath.ceil(((double) size) / SLICE), 1);
106         hansenRoot = new double[numSlices][4];
107         hansenDerivRoot = new double[numSlices][4];
108         if (size > 0) {
109             mpvec = new PolynomialFunction[size][];
110             mpvecDeriv = new PolynomialFunction[size][];
111 
112             // Prepare the database of the associated polynomials
113             generatePolynomials();
114         }
115     }
116 
117     /**
118      * Compute polynomial coefficient a.
119      *
120      *  <p>
121      *  It is used to generate the coefficient for K<sub>j</sub><sup>-n, s</sup> when computing K<sub>j</sub><sup>-n-1, s</sup>
122      *  and the coefficient for dK<sub>j</sub><sup>-n, s</sup> / de² when computing dK<sub>j</sub><sup>-n-1, s</sup> / de²
123      *  </p>
124      *
125      *  <p>
126      *  See Danielson 2.7.3-(9) and Collins 4-236 and 4-240
127      *  </p>
128      *
129      * @param mnm1 -n-1
130      * @return the polynomial
131      */
132     private PolynomialFunction a(final int mnm1) {
133         // Collins 4-236, Danielson 2.7.3-(9)
134         final double r1 = (mnm1 + 2.) * (2. * mnm1 + 5.);
135         final double r2 = (2. + mnm1 + s) * (2. + mnm1 - s);
136         return new PolynomialFunction(new double[] {
137             0.0, 0.0, r1 / r2
138         });
139     }
140 
141     /**
142      * Compute polynomial coefficient b.
143      *
144      *  <p>
145      *  It is used to generate the coefficient for K<sub>j</sub><sup>-n+1, s</sup> when computing K<sub>j</sub><sup>-n-1, s</sup>
146      *  and the coefficient for dK<sub>j</sub><sup>-n+1, s</sup> / de² when computing dK<sub>j</sub><sup>-n-1, s</sup> / de²
147      *  </p>
148      *
149      *  <p>
150      *  See Danielson 2.7.3-(9) and Collins 4-236 and 4-240
151      *  </p>
152      *
153      * @param mnm1 -n-1
154      * @return the polynomial
155      */
156     private PolynomialFunction b(final int mnm1) {
157         // Collins 4-236, Danielson 2.7.3-(9)
158         final double r2 = (2. + mnm1 + s) * (2. + mnm1 - s);
159         final double d1 = (mnm1 + 3.) * 2. * j * s / (r2 * (mnm1 + 4.));
160         final double d2 = (mnm1 + 3.) * (mnm1 + 2.) / r2;
161         return new PolynomialFunction(new double[] {
162             0.0, -d1, -d2
163         });
164     }
165 
166     /**
167      * Compute polynomial coefficient c.
168      *
169      *  <p>
170      *  It is used to generate the coefficient for K<sub>j</sub><sup>-n+3, s</sup> when computing K<sub>j</sub><sup>-n-1, s</sup>
171      *  and the coefficient for dK<sub>j</sub><sup>-n+3, s</sup> / de² when computing dK<sub>j</sub><sup>-n-1, s</sup> / de²
172      *  </p>
173      *
174      *  <p>
175      *  See Danielson 2.7.3-(9) and Collins 4-236 and 4-240
176      *  </p>
177      *
178      * @param mnm1 -n-1
179      * @return the polynomial
180      */
181     private PolynomialFunction c(final int mnm1) {
182         // Collins 4-236, Danielson 2.7.3-(9)
183         final double r1 = j * j * (mnm1 + 2.);
184         final double r2 = (mnm1 + 4.) * (2. + mnm1 + s) * (2. + mnm1 - s);
185 
186         return new PolynomialFunction(new double[] {
187             0.0, 0.0, r1 / r2
188         });
189     }
190 
191     /**
192      * Compute polynomial coefficient d.
193      *
194      *  <p>
195      *  It is used to generate the coefficient for K<sub>j</sub><sup>-n-1, s</sup> / dχ when computing dK<sub>j</sub><sup>-n-1, s</sup> / de²
196      *  </p>
197      *
198      *  <p>
199      *  See Danielson 2.7.3-(9) and Collins 4-236 and 4-240
200      *  </p>
201      *
202      * @param mnm1 -n-1
203      * @return the polynomial
204      */
205     private PolynomialFunction d(final int mnm1) {
206         // Collins 4-236, Danielson 2.7.3-(9)
207         return new PolynomialFunction(new double[] {
208             0.0, 0.0, 1.0
209         });
210     }
211 
212     /**
213      * Compute polynomial coefficient f.
214      *
215      *  <p>
216      *  It is used to generate the coefficient for K<sub>j</sub><sup>-n+1, s</sup> / dχ when computing dK<sub>j</sub><sup>-n-1, s</sup> / de²
217      *  </p>
218      *
219      *  <p>
220      *  See Danielson 2.7.3-(9) and Collins 4-236 and 4-240
221      *  </p>
222      *
223      * @param n index
224      * @return the polynomial
225      */
226     private PolynomialFunction f(final int n) {
227         // Collins 4-236, Danielson 2.7.3-(9)
228         final double r1 = (n + 3.0) * j * s;
229         final double r2 = (n + 4.0) * (2.0 + n + s) * (2.0 + n - s);
230         return new PolynomialFunction(new double[] {
231             0.0, 0.0, 0.0, r1 / r2
232         });
233     }
234 
235     /**
236      * Generate the polynomials needed in the linear transformation.
237      *
238      * <p>
239      * See Petre's paper
240      * </p>
241      */
242     private void generatePolynomials() {
243 
244 
245         // Initialization of the matrices for linear transformations
246         // The final configuration of these matrices are obtained by composition
247         // of linear transformations
248 
249         // The matrix of polynomials associated to Hansen coefficients, Petre's
250         // paper
251         PolynomialFunctionMatrix A = HansenUtilities.buildIdentityMatrix4();
252 
253         // The matrix of polynomials associated to derivatives, Petre's paper
254         final PolynomialFunctionMatrix B = HansenUtilities.buildZeroMatrix4();
255         PolynomialFunctionMatrix D = HansenUtilities.buildZeroMatrix4();
256         final PolynomialFunctionMatrix a = HansenUtilities.buildZeroMatrix4();
257 
258         // The matrix of the current linear transformation
259         a.setMatrixLine(0, new PolynomialFunction[] {
260             HansenUtilities.ZERO, HansenUtilities.ONE, HansenUtilities.ZERO, HansenUtilities.ZERO
261         });
262         a.setMatrixLine(1, new PolynomialFunction[] {
263             HansenUtilities.ZERO, HansenUtilities.ZERO, HansenUtilities.ONE, HansenUtilities.ZERO
264         });
265         a.setMatrixLine(2, new PolynomialFunction[] {
266             HansenUtilities.ZERO, HansenUtilities.ZERO, HansenUtilities.ZERO, HansenUtilities.ONE
267         });
268         // The generation process
269         int index;
270         int sliceCounter = 0;
271         for (int i = N0 - 1; i > Nmin - 1; i--) {
272             index = i + this.offset;
273             // The matrix of the current linear transformation is updated
274             // Petre's paper
275             a.setMatrixLine(3, new PolynomialFunction[] {
276                     c(i), HansenUtilities.ZERO, b(i), a(i)
277             });
278 
279             // composition of the linear transformations to calculate
280             // the polynomials associated to Hansen coefficients
281             // Petre's paper
282             A = A.multiply(a);
283             // store the polynomials for Hansen coefficients
284             mpvec[index] = A.getMatrixLine(3);
285             // composition of the linear transformations to calculate
286             // the polynomials associated to derivatives
287             // Petre's paper
288             D = D.multiply(a);
289 
290             //Update the B matrix
291             B.setMatrixLine(3, new PolynomialFunction[] {
292                 HansenUtilities.ZERO, f(i),
293                 HansenUtilities.ZERO, d(i)
294             });
295             D = D.add(A.multiply(B));
296 
297             // store the polynomials for Hansen coefficients from the
298             // expressions of derivatives
299             mpvecDeriv[index] = D.getMatrixLine(3);
300 
301             if (++sliceCounter % SLICE == 0) {
302                 // Re-Initialisation of matrix for linear transformmations
303                 // The final configuration of these matrix are obtained by composition
304                 // of linear transformations
305                 A = HansenUtilities.buildIdentityMatrix4();
306                 D = HansenUtilities.buildZeroMatrix4();
307             }
308         }
309     }
310 
311     /**
312      * Compute the values for the first four coefficients and their derivatives by means of series.
313      *
314      * @param e2 e²
315      * @param chi &Chi;
316      * @param chi2 &Chi;²
317      */
318     public void computeInitValues(final double e2, final double chi, final double chi2) {
319         // compute the values for n, n+1, n+2 and n+3 by series
320         // See Danielson 2.7.3-(10)
321         //Ensure that only the needed terms are computed
322         final int maxRoots = FastMath.min(4, N0 - Nmin + 4);
323         for (int i = 0; i < maxRoots; i++) {
324             final DerivativeStructure hansenKernel = hansenInit[i].getValue(e2, chi, chi2);
325             this.hansenRoot[0][i] = hansenKernel.getValue();
326             this.hansenDerivRoot[0][i] = hansenKernel.getPartialDerivative(1);
327         }
328 
329         for (int i = 1; i < numSlices; i++) {
330             for (int k = 0; k < 4; k++) {
331                 final PolynomialFunction[] mv = mpvec[N0 - (i * SLICE) - k + 3 + offset];
332                 final PolynomialFunction[] sv = mpvecDeriv[N0 - (i * SLICE) - k + 3 + offset];
333 
334                 hansenDerivRoot[i][k] = mv[3].value(chi) * hansenDerivRoot[i - 1][3] +
335                                         mv[2].value(chi) * hansenDerivRoot[i - 1][2] +
336                                         mv[1].value(chi) * hansenDerivRoot[i - 1][1] +
337                                         mv[0].value(chi) * hansenDerivRoot[i - 1][0] +
338                                         sv[3].value(chi) * hansenRoot[i - 1][3] +
339                                         sv[2].value(chi) * hansenRoot[i - 1][2] +
340                                         sv[1].value(chi) * hansenRoot[i - 1][1] +
341                                         sv[0].value(chi) * hansenRoot[i - 1][0];
342 
343                 hansenRoot[i][k] =  mv[3].value(chi) * hansenRoot[i - 1][3] +
344                                     mv[2].value(chi) * hansenRoot[i - 1][2] +
345                                     mv[1].value(chi) * hansenRoot[i - 1][1] +
346                                     mv[0].value(chi) * hansenRoot[i - 1][0];
347             }
348         }
349     }
350 
351     /**
352      * Compute the value of the Hansen coefficient K<sub>j</sub><sup>-n-1, s</sup>.
353      *
354      * @param mnm1 -n-1
355      * @param chi χ
356      * @return the coefficient K<sub>j</sub><sup>-n-1, s</sup>
357      */
358     public double getValue(final int mnm1, final double chi) {
359         //Compute n
360         final int n = -mnm1 - 1;
361 
362         //Compute the potential slice
363         int sliceNo = (n + N0 + 4) / SLICE;
364         if (sliceNo < numSlices) {
365             //Compute the index within the slice
366             final int indexInSlice = (n + N0 + 4) % SLICE;
367 
368             //Check if a root must be returned
369             if (indexInSlice <= 3) {
370                 return hansenRoot[sliceNo][indexInSlice];
371             }
372         } else {
373             //the value was a potential root for a slice, but that slice was not required
374             //Decrease the slice number
375             sliceNo--;
376         }
377 
378         // Computes the coefficient by linear transformation
379         // Danielson 2.7.3-(9) or Collins 4-236 and Petre's paper
380         final PolynomialFunction[] v = mpvec[mnm1 + offset];
381         return v[3].value(chi) * hansenRoot[sliceNo][3] +
382                v[2].value(chi) * hansenRoot[sliceNo][2] +
383                v[1].value(chi) * hansenRoot[sliceNo][1] +
384                v[0].value(chi) * hansenRoot[sliceNo][0];
385 
386     }
387 
388     /**
389      * Compute the value of the derivative dK<sub>j</sub><sup>-n-1, s</sup> / de².
390      *
391      * @param mnm1 -n-1
392      * @param chi χ
393      * @return the derivative dK<sub>j</sub><sup>-n-1, s</sup> / de²
394      */
395     public double getDerivative(final int mnm1, final double chi) {
396 
397         //Compute n
398         final int n = -mnm1 - 1;
399 
400         //Compute the potential slice
401         int sliceNo = (n + N0 + 4) / SLICE;
402         if (sliceNo < numSlices) {
403             //Compute the index within the slice
404             final int indexInSlice = (n + N0 + 4) % SLICE;
405 
406             //Check if a root must be returned
407             if (indexInSlice <= 3) {
408                 return hansenDerivRoot[sliceNo][indexInSlice];
409             }
410         } else {
411             //the value was a potential root for a slice, but that slice was not required
412             //Decrease the slice number
413             sliceNo--;
414         }
415 
416         // Computes the coefficient by linear transformation
417         // Danielson 2.7.3-(9) or Collins 4-236 and Petre's paper
418         final PolynomialFunction[] v = mpvec[mnm1 + this.offset];
419         final PolynomialFunction[] vv = mpvecDeriv[mnm1 + this.offset];
420 
421         return v[3].value(chi)  * hansenDerivRoot[sliceNo][3] +
422                v[2].value(chi)  * hansenDerivRoot[sliceNo][2] +
423                v[1].value(chi)  * hansenDerivRoot[sliceNo][1] +
424                v[0].value(chi)  * hansenDerivRoot[sliceNo][0] +
425                vv[3].value(chi) * hansenRoot[sliceNo][3] +
426                vv[2].value(chi) * hansenRoot[sliceNo][2] +
427                vv[1].value(chi) * hansenRoot[sliceNo][1] +
428                vv[0].value(chi) * hansenRoot[sliceNo][0];
429 
430     }
431 
432     /**
433      * Compute a Hansen coefficient with series.
434      * <p>
435      * This class implements the computation of the Hansen kernels
436      * through a power series in e² and that is using
437      * modified Newcomb operators. The reference formulae can be found
438      * in Danielson 2.7.3-10 and 3.3-5
439      * </p>
440      */
441     private static class HansenCoefficientsBySeries {
442 
443         /** -n-1 coefficient. */
444         private final int mnm1;
445 
446         /** s coefficient. */
447         private final int s;
448 
449         /** j coefficient. */
450         private final int j;
451 
452         /** Max power in e² for the Newcomb's series expansion. */
453         private final int maxNewcomb;
454 
455         /** Polynomial representing the serie. */
456         private PolynomialFunction polynomial;
457 
458         /**
459          * Class constructor.
460          *
461          * @param mnm1 -n-1 value
462          * @param s s value
463          * @param j j value
464          * @param maxHansen max power of e² in series expansion
465          */
466         public HansenCoefficientsBySeries(final int mnm1, final int s,
467                                           final int j, final int maxHansen) {
468             this.mnm1 = mnm1;
469             this.s = s;
470             this.j = j;
471             this.maxNewcomb = maxHansen;
472             this.polynomial = generatePolynomial();
473         }
474 
475         /** Computes the value of Hansen kernel and its derivative at e².
476          * <p>
477          * The formulae applied are described in Danielson 2.7.3-10 and
478          * 3.3-5
479          * </p>
480          * @param e2 e²
481          * @param chi &Chi;
482          * @param chi2 &Chi;²
483          * @return the value of the Hansen coefficient and its derivative for e²
484          */
485         public DerivativeStructure getValue(final double e2, final double chi, final double chi2) {
486 
487             //Estimation of the serie expansion at e2
488             final DerivativeStructure serie = polynomial.value(
489                     new DerivativeStructure(1, 1, 0, e2));
490 
491             final double value      =  FastMath.pow(chi2, -mnm1 - 1) * serie.getValue() / chi;
492             final double coef       = -(mnm1 + 1.5);
493             final double derivative = coef * chi2 * value +
494                                       FastMath.pow(chi2, -mnm1 - 1) * serie.getPartialDerivative(1) / chi;
495             return new DerivativeStructure(1, 1, value, derivative);
496         }
497 
498         /** Generate the serie expansion in e².
499          * <p>
500          * Generate the series expansion in e² used in the formulation
501          * of the Hansen kernel (see Danielson 2.7.3-10):
502          * &Sigma; Y<sup>ns</sup><sub>α+a,α+b</sub>
503          * *e<sup>2α</sup>
504          * </p>
505          * @return polynomial representing the power serie expansion
506          */
507         private PolynomialFunction generatePolynomial() {
508             // Initialization
509             final int aHT = FastMath.max(j - s, 0);
510             final int bHT = FastMath.max(s - j, 0);
511 
512             final double[] coefficients = new double[maxNewcomb + 1];
513 
514             //Loop for getting the Newcomb operators
515             for (int alphaHT = 0; alphaHT <= maxNewcomb; alphaHT++) {
516                 coefficients[alphaHT] =
517                         NewcombOperators.getValue(alphaHT + aHT, alphaHT + bHT, mnm1, s);
518             }
519 
520             //Creation of the polynomial
521             return new PolynomialFunction(coefficients);
522         }
523     }
524 
525 }