1   /* Copyright 2002-2013 CS Systèmes d'Information
2    * Licensed to CS Systèmes d'Information (CS) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * CS licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *   http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.orekit.propagation.semianalytical.dsst.forces;
18  
19  import java.util.TreeMap;
20  
21  import org.apache.commons.math3.geometry.euclidean.threed.Vector3D;
22  import org.apache.commons.math3.util.FastMath;
23  import org.orekit.bodies.CelestialBody;
24  import org.orekit.errors.OrekitException;
25  import org.orekit.propagation.SpacecraftState;
26  import org.orekit.propagation.events.EventDetector;
27  import org.orekit.propagation.semianalytical.dsst.utilities.AuxiliaryElements;
28  import org.orekit.propagation.semianalytical.dsst.utilities.CoefficientsFactory;
29  import org.orekit.propagation.semianalytical.dsst.utilities.CoefficientsFactory.NSKey;
30  import org.orekit.propagation.semianalytical.dsst.utilities.UpperBounds;
31  import org.orekit.time.AbsoluteDate;
32  
33  /** Third body attraction perturbation to the
34   *  {@link org.orekit.propagation.semianalytical.dsst.DSSTPropagator DSSTPropagator}.
35   *
36   *  @author Romain Di Costanzo
37   *  @author Pascal Parraud
38   */
39  public class DSSTThirdBody  implements DSSTForceModel {
40  
41      /** Max power for summation. */
42      private static final int       MAX_POWER = 22;
43  
44      /** Truncation tolerance for big, eccentric  orbits. */
45      private static final double    BIG_TRUNCATION_TOLERANCE = 1.e-1;
46  
47      /** Truncation tolerance for small orbits. */
48      private static final double    SMALL_TRUNCATION_TOLERANCE = 1.e-4;
49  
50      /** The 3rd body to consider. */
51      private final CelestialBody    body;
52  
53      /** Standard gravitational parameter &mu; for the body in m<sup>3</sup>/s<sup>2</sup>. */
54      private final double           gm;
55  
56      /** Factorial. */
57      private final double[]         fact;
58  
59      /** V<sub>ns</sub> coefficients. */
60      private TreeMap<NSKey, Double> Vns;
61  
62      /** Distance from center of mass of the central body to the 3rd body. */
63      private double R3;
64  
65      // Equinoctial elements (according to DSST notation)
66      /** a. */
67      private double a;
68      /** e<sub>x</sub>. */
69      private double k;
70      /** e<sub>y</sub>. */
71      private double h;
72      /** h<sub>x</sub>. */
73      private double q;
74      /** h<sub>y</sub>. */
75      private double p;
76  
77      /** Eccentricity. */
78      private double ecc;
79  
80      // Direction cosines of the symmetry axis
81      /** &alpha;. */
82      private double alpha;
83      /** &beta;. */
84      private double beta;
85      /** &gamma;. */
86      private double gamma;
87  
88      // Common factors for potential computation
89      /** &Chi; = 1 / sqrt(1 - e<sup>2</sup>) = 1 / B. */
90      private double X;
91      /** &Chi;<sup>2</sup>. */
92      private double XX;
93      /** &Chi;<sup>3</sup>. */
94      private double XXX;
95      /** -2 * a / A. */
96      private double m2aoA;
97      /** B / A. */
98      private double BoA;
99      /** 1 / (A * B). */
100     private double ooAB;
101     /** -C / (2 * A * B). */
102     private double mCo2AB;
103     /** B / A(1 + B). */
104     private double BoABpo;
105 
106     /** Retrograde factor. */
107     private int    I;
108 
109     /** Maw power for a/R3 in the serie expansion. */
110     private int    maxAR3Pow;
111 
112     /** Maw power for e in the serie expansion. */
113     private int    maxEccPow;
114 
115     /** Complete constructor.
116      *  @param body the 3rd body to consider
117      *  @see org.orekit.bodies.CelestialBodyFactory
118      */
119     public DSSTThirdBody(final CelestialBody body) {
120         this.body = body;
121         this.gm   = body.getGM();
122 
123         this.maxAR3Pow = Integer.MIN_VALUE;
124         this.maxEccPow = Integer.MIN_VALUE;
125 
126         this.Vns = CoefficientsFactory.computeVns(MAX_POWER);
127 
128         // Factorials computation
129         final int dim = 2 * MAX_POWER;
130         this.fact = new double[dim];
131         fact[0] = 1.;
132         for (int i = 1; i < dim; i++) {
133             fact[i] = i * fact[i - 1];
134         }
135     }
136 
137     /** Get third body.
138      *  @return third body
139      */
140     public CelestialBody getBody() {
141         return body;
142     }
143 
144     /** Computes the highest power of the eccentricity and the highest power
145      *  of a/R3 to appear in the truncated analytical power series expansion.
146      *  <p>
147      *  This method computes the upper value for the 3rd body potential and
148      *  determines the maximal powers for the eccentricity and a/R3 producing
149      *  potential terms bigger than a defined tolerance.
150      *  </p>
151      *  @param aux auxiliary elements related to the current orbit
152      *  @throws OrekitException if some specific error occurs
153      */
154     public void initialize(final AuxiliaryElements aux)
155         throws OrekitException {
156 
157         // Initializes specific parameters.
158         initializeStep(aux);
159 
160         // Truncation tolerance.
161         final double aor = a / R3;
162         final double tol = ( aor > .3 || (aor > .15  && ecc > .25) ) ? BIG_TRUNCATION_TOLERANCE : SMALL_TRUNCATION_TOLERANCE;
163 
164         // Utilities for truncation
165         // Set a lower bound for eccentricity
166         final double eo2  = FastMath.max(0.0025, ecc / 2.);
167         final double x2o2 = XX / 2.;
168         final double[] eccPwr = new double[MAX_POWER];
169         final double[] chiPwr = new double[MAX_POWER];
170         eccPwr[0] = 1.;
171         chiPwr[0] = X;
172         for (int i = 1; i < MAX_POWER; i++) {
173             eccPwr[i] = eccPwr[i - 1] * eo2;
174             chiPwr[i] = chiPwr[i - 1] * x2o2;
175         }
176 
177         // Auxiliary quantities.
178         final double ao2rxx = aor / (2. * XX);
179         double xmuarn       = ao2rxx * ao2rxx * gm / (X * R3);
180         double term         = 0.;
181 
182         // Compute max power for a/R3 and e.
183         maxAR3Pow = 2;
184         maxEccPow = 0;
185         int n     = 2;
186         int m     = 2;
187         int nsmd2 = 0;
188 
189         do {
190             // Upper bound for Tnm.
191             term =  xmuarn *
192                    (fact[n + m] / (fact[nsmd2] * fact[nsmd2 + m])) *
193                    (fact[n + m + 1] / (fact[m] * fact[n + 1])) *
194                    (fact[n - m + 1] / fact[n + 1]) *
195                    eccPwr[m] * UpperBounds.getDnl(XX, chiPwr[m], n + 2, m);
196 
197             if (term < tol) {
198                 if (m == 0) {
199                     break;
200                 } else if (m < 2) {
201                     xmuarn *= ao2rxx;
202                     m = 0;
203                     n++;
204                     nsmd2++;
205                 } else {
206                     m -= 2;
207                     nsmd2++;
208                 }
209             } else {
210                 maxAR3Pow = n;
211                 maxEccPow = FastMath.max(m, maxEccPow);
212                 xmuarn *= ao2rxx;
213                 m++;
214                 n++;
215             }
216         } while (n < MAX_POWER);
217 
218         maxEccPow = FastMath.min(maxAR3Pow, maxEccPow);
219 
220     }
221 
222     /** {@inheritDoc} */
223     public void initializeStep(final AuxiliaryElements aux) throws OrekitException {
224 
225         // Equinoctial elements
226         a = aux.getSma();
227         k = aux.getK();
228         h = aux.getH();
229         q = aux.getQ();
230         p = aux.getP();
231 
232         // Retrograde factor
233         I = aux.getRetrogradeFactor();
234 
235         // Eccentricity
236         ecc = aux.getEcc();
237 
238         // Distance from center of mass of the central body to the 3rd body
239         final Vector3D bodyPos = body.getPVCoordinates(aux.getDate(), aux.getFrame()).getPosition();
240         R3 = bodyPos.getNorm();
241 
242         // Direction cosines
243         final Vector3D bodyDir = bodyPos.normalize();
244         alpha = bodyDir.dotProduct(aux.getVectorF());
245         beta  = bodyDir.dotProduct(aux.getVectorG());
246         gamma = bodyDir.dotProduct(aux.getVectorW());
247 
248         // Equinoctial coefficients
249         final double A = aux.getA();
250         final double B = aux.getB();
251         final double C = aux.getC();
252 
253         // &Chi;
254         X = 1. / B;
255         XX = X * X;
256         XXX = X * XX;
257         // -2 * a / A
258         m2aoA = -2. * a / A;
259         // B / A
260         BoA = B / A;
261         // 1 / AB
262         ooAB = 1. / (A * B);
263         // -C / 2AB
264         mCo2AB = -C * ooAB / 2.;
265         // B / A(1 + B)
266         BoABpo = BoA / (1. + B);
267 
268     }
269 
270     /** {@inheritDoc} */
271     public double[] getMeanElementRate(final SpacecraftState currentState) throws OrekitException {
272 
273         // Compute potential U derivatives
274         final double[] dU  = computeUDerivatives();
275         final double dUda  = dU[0];
276         final double dUdk  = dU[1];
277         final double dUdh  = dU[2];
278         final double dUdAl = dU[3];
279         final double dUdBe = dU[4];
280         final double dUdGa = dU[5];
281 
282         // Compute cross derivatives [Eq. 2.2-(8)]
283         // U(alpha,gamma) = alpha * dU/dgamma - gamma * dU/dalpha
284         final double UAlphaGamma   = alpha * dUdGa - gamma * dUdAl;
285         // U(beta,gamma) = beta * dU/dgamma - gamma * dU/dbeta
286         final double UBetaGamma    =  beta * dUdGa - gamma * dUdBe;
287         // Common factor
288         final double pUAGmIqUBGoAB = (p * UAlphaGamma - I * q * UBetaGamma) * ooAB;
289 
290         // Compute mean elements rates [Eq. 3.1-(1)]
291         final double da =  0.;
292         final double dh =  BoA * dUdk + k * pUAGmIqUBGoAB;
293         final double dk = -BoA * dUdh - h * pUAGmIqUBGoAB;
294         final double dp =  mCo2AB * UBetaGamma;
295         final double dq =  mCo2AB * UAlphaGamma * I;
296         final double dM =  m2aoA * dUda + BoABpo * (h * dUdh + k * dUdk) + pUAGmIqUBGoAB;
297 
298         return new double[] {da, dk, dh, dq, dp, dM};
299 
300     }
301 
302     /** {@inheritDoc} */
303     public double[] getShortPeriodicVariations(final AbsoluteDate date,
304                                                final double[] meanElements) throws OrekitException {
305         // TODO: not implemented yet, Short Periodic Variations are set to null
306         return new double[] {0., 0., 0., 0., 0., 0.};
307     }
308 
309     /** {@inheritDoc} */
310     public EventDetector[] getEventsDetectors() {
311         return null;
312     }
313 
314     /** Compute potential derivatives.
315      *  @return derivatives of the potential with respect to orbital parameters
316      *  @throws OrekitException if Hansen coefficients cannot be computed
317      */
318     private double[] computeUDerivatives() throws OrekitException {
319 
320         // Hansen coefficients
321         final HansenThirdBody hansen = new HansenThirdBody();
322         // Gs and Hs coefficients
323         final double[][] GsHs = CoefficientsFactory.computeGsHs(k, h, alpha, beta, maxEccPow);
324         // Qns coefficients
325         final double[][] Qns = CoefficientsFactory.computeQns(gamma, maxAR3Pow, maxEccPow);
326         // a / R3 up to power maxAR3Pow
327         final double aoR3 = a / R3;
328         final double[] aoR3Pow = new double[maxAR3Pow + 1];
329         aoR3Pow[0] = 1.;
330         for (int i = 1; i <= maxAR3Pow; i++) {
331             aoR3Pow[i] = aoR3 * aoR3Pow[i - 1];
332         }
333 
334         // Potential derivatives
335         double dUda  = 0.;
336         double dUdk  = 0.;
337         double dUdh  = 0.;
338         double dUdAl = 0.;
339         double dUdBe = 0.;
340         double dUdGa = 0.;
341 
342         for (int s = 0; s <= maxEccPow; s++) {
343             // Get the current Gs coefficient
344             final double gs = GsHs[0][s];
345 
346             // Compute Gs partial derivatives from 3.1-(9)
347             double dGsdh  = 0.;
348             double dGsdk  = 0.;
349             double dGsdAl = 0.;
350             double dGsdBe = 0.;
351             if (s > 0) {
352                 // First get the G(s-1) and the H(s-1) coefficients
353                 final double sxGsm1 = s * GsHs[0][s - 1];
354                 final double sxHsm1 = s * GsHs[1][s - 1];
355                 // Then compute derivatives
356                 dGsdh  = beta  * sxGsm1 - alpha * sxHsm1;
357                 dGsdk  = alpha * sxGsm1 + beta  * sxHsm1;
358                 dGsdAl = k * sxGsm1 - h * sxHsm1;
359                 dGsdBe = h * sxGsm1 + k * sxHsm1;
360             }
361 
362             // Kronecker symbol (2 - delta(0,s))
363             final double delta0s = (s == 0) ? 1. : 2.;
364 
365             for (int n = FastMath.max(2, s); n <= maxAR3Pow; n++) {
366                 // (n - s) must be even
367                 if ((n - s) % 2 == 0) {
368                     // Extract data from previous computation :
369                     final double kns   = hansen.getValue(n, s);
370                     final double dkns  = hansen.getDerivative(n, s);
371                     final double vns   = Vns.get(new NSKey(n, s));
372                     final double coef0 = delta0s * aoR3Pow[n] * vns;
373                     final double coef1 = coef0 * Qns[n][s];
374                     final double coef2 = coef1 * kns;
375                     // dQns/dGamma = Q(n, s + 1) from Equation 3.1-(8)
376                     // for n = s, Q(n, n + 1) = 0. (Cefola & Broucke, 1975)
377                     final double dqns = (n == s) ? 0. : Qns[n][s + 1];
378 
379                     // Compute dU / da :
380                     dUda += coef2 * n * gs;
381                     // Compute dU / dh
382                     dUdh += coef1 * (kns * dGsdh + h * XXX * gs * dkns);
383                     // Compute dU / dk
384                     dUdk += coef1 * (kns * dGsdk + k * XXX * gs * dkns);
385                     // Compute dU / dAlpha
386                     dUdAl += coef2 * dGsdAl;
387                     // Compute dU / dBeta
388                     dUdBe += coef2 * dGsdBe;
389                     // Compute dU / dGamma
390                     dUdGa += coef0 * kns * dqns * gs;
391                 }
392             }
393         }
394 
395         // mu3 / R3
396         final double muoR3 = gm / R3;
397 
398         return new double[] {
399             dUda  * muoR3 / a,
400             dUdk  * muoR3,
401             dUdh  * muoR3,
402             dUdAl * muoR3,
403             dUdBe * muoR3,
404             dUdGa * muoR3
405         };
406 
407     }
408 
409     /** Hansen coefficients for 3rd body force model.
410      *  <p>
411      *  Hansen coefficients are functions of the eccentricity.
412      *  For a given eccentricity, the computed elements are stored in a map.
413      *  </p>
414      */
415     private class HansenThirdBody {
416 
417         /** Map to store every Hansen coefficient computed. */
418         private TreeMap<NSKey, Double> coefficients;
419 
420         /** Map to store every Hansen coefficient derivative computed. */
421         private TreeMap<NSKey, Double> derivatives;
422 
423         /** Simple constructor. */
424         public HansenThirdBody() {
425             coefficients = new TreeMap<CoefficientsFactory.NSKey, Double>();
426             derivatives  = new TreeMap<CoefficientsFactory.NSKey, Double>();
427             initialize();
428         }
429 
430         /** Get the K<sub>0</sub><sup>n,s</sup> coefficient value.
431          *  @param n n value
432          *  @param s s value
433          *  @return K<sub>0</sub><sup>n,s</sup>
434          */
435         public double getValue(final int n, final int s) {
436             if (coefficients.containsKey(new NSKey(n, s))) {
437                 return coefficients.get(new NSKey(n, s));
438             } else {
439                 // Compute the K0(n,s) coefficients
440                 return computeValue(n, s);
441             }
442         }
443 
444         /** Get the dK<sub>0</sub><sup>n,s</sup> / d&x; coefficient derivative.
445          *  @param n n value
446          *  @param s s value
447          *  @return dK<sub>j</sub><sup>n,s</sup> / d&x;
448          */
449         public double getDerivative(final int n, final int s) {
450             if (derivatives.containsKey(new NSKey(n, s))) {
451                 return derivatives.get(new NSKey(n, s));
452             } else {
453                 // Compute the dK0(n,s) / dX derivative
454                 return computeDerivative(n, s);
455             }
456         }
457 
458         /** Initialization. */
459         private void initialize() {
460             final double ec2 = ecc * ecc;
461             final double oX3 = 1. / XXX;
462             coefficients.put(new NSKey(0, 0),  1.);
463             coefficients.put(new NSKey(0, 1), -1.);
464             coefficients.put(new NSKey(1, 0),  1. + 0.5 * ec2);
465             coefficients.put(new NSKey(1, 1), -1.5);
466             coefficients.put(new NSKey(2, 0),  1. + 1.5 * ec2);
467             coefficients.put(new NSKey(2, 1), -2. - 0.5 * ec2);
468             derivatives.put(new NSKey(0, 0),  0.);
469             derivatives.put(new NSKey(1, 0),  oX3);
470             derivatives.put(new NSKey(2, 0),  3. * oX3);
471             derivatives.put(new NSKey(2, 1), -oX3);
472         }
473 
474         /** Compute K<sub>0</sub><sup>n,s</sup> from Equation 2.7.3-(7)(8).
475          *  @param n n value
476          *  @param s s value
477          *  @return K<sub>0</sub><sup>n,s</sup>
478          */
479         private double computeValue(final int n, final int s) {
480             // Initialize return value
481             double kns = 0.;
482 
483             if (n == (s - 1)) {
484 
485                 final NSKey key = new NSKey(s - 2, s - 1);
486                 if (coefficients.containsKey(key)) {
487                     kns = coefficients.get(key);
488                 } else {
489                     kns = computeValue(s - 2, s - 1);
490                 }
491 
492                 kns *= -(2. * s - 1.) / s;
493 
494             } else if (n == s) {
495 
496                 final NSKey key = new NSKey(s - 1, s);
497                 if (coefficients.containsKey(key)) {
498                     kns = coefficients.get(key);
499                 } else {
500                     kns = computeValue(s - 1, s);
501                 }
502 
503                 kns *= (2. * s + 1.) / (s + 1.);
504 
505             } else if (n > s) {
506 
507                 final NSKey key1 = new NSKey(n - 1, s);
508                 double knM1 = 0.;
509                 if (coefficients.containsKey(key1)) {
510                     knM1 = coefficients.get(key1);
511                 } else {
512                     knM1 = computeValue(n - 1, s);
513                 }
514 
515                 final NSKey key2 = new NSKey(n - 2, s);
516                 double knM2 = 0.;
517                 if (coefficients.containsKey(key2)) {
518                     knM2 = coefficients.get(key2);
519                 } else {
520                     knM2 = computeValue(n - 2, s);
521                 }
522 
523                 final double val1 = (2. * n + 1.) / (n + 1.);
524                 final double val2 = (n + s) * (n - s) / (n * (n + 1.) * XX);
525 
526                 kns = val1 * knM1 - val2 * knM2;
527             }
528 
529             coefficients.put(new NSKey(n, s), kns);
530             return kns;
531         }
532 
533         /** Compute dK<sub>0</sub><sup>n,s</sup> / d&x; from Equation 3.2-(3).
534          *  @param n n value
535          *  @param s s value
536          *  @return dK<sub>0</sub><sup>n,s</sup> / d&x;
537          */
538         private double computeDerivative(final int n, final int s) {
539             // Initialize return value
540             double dknsdx = 0.;
541 
542             if (n > s) {
543 
544                 final NSKey keyNm1 = new NSKey(n - 1, s);
545                 double dKnM1 = 0.;
546                 if (derivatives.containsKey(keyNm1)) {
547                     dKnM1 = derivatives.get(keyNm1);
548                 } else {
549                     dKnM1 = computeDerivative(n - 1, s);
550                 }
551 
552                 final NSKey keyNm2 = new NSKey(n - 2, s);
553                 double dKnM2 = 0.;
554                 if (derivatives.containsKey(keyNm2)) {
555                     dKnM2 = derivatives.get(keyNm2);
556                 } else {
557                     dKnM2 = computeDerivative(n - 2, s);
558                 }
559 
560                 final double knM2 = getValue(n - 2, s);
561 
562                 final double val1 = (2. * n + 1.) / (n + 1.);
563                 final double coef = (n + s) * (n - s) / (n * (n + 1.));
564                 final double val2 = coef / XX;
565                 final double val3 = 2. * coef / XXX;
566 
567                 dknsdx = val1 * dKnM1 - val2 * dKnM2 + val3 * knM2;
568             }
569 
570             derivatives.put(new NSKey(n, s), dknsdx);
571             return dknsdx;
572         }
573 
574     }
575 
576 }