1   /* Copyright 2002-2024 CS GROUP
2    * Licensed to CS GROUP (CS) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * CS licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *   http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.orekit.forces.gravity;
18  
19  import org.hipparchus.analysis.differentiation.DerivativeStructure;
20  import org.hipparchus.dfp.Dfp;
21  import org.hipparchus.dfp.DfpField;
22  
23  import java.util.ArrayList;
24  import java.util.HashMap;
25  import java.util.List;
26  import java.util.Map;
27  
28  /** Implementation of associated Legendre functions from defining formulas.
29   * <p>
30   * This implementation is for test purposes only! It is limited to low degrees
31   * and order and is slow.
32   * </p>
33   * @author Luc Maisonobe
34   */
35  class AssociatedLegendreFunction {
36  
37      static final Map<Integer, List<Dfp[]>> LEGENDRE_POLYNOMIALS = new HashMap<Integer, List<Dfp[]>>();
38      final int m;
39      final Dfp[] polynomial;
40      final Dfp normalization;
41  
42      private Dfp[] getLegendrePolynomial(int n, DfpField dfpField) {
43  
44          // get (or create) the list of polynomials for the specified field
45          List<Dfp[]> list = LEGENDRE_POLYNOMIALS.get(dfpField.getRadixDigits());
46          if (list == null) {
47              list = new ArrayList<Dfp[]>();
48              list.add(new Dfp[] {
49                  dfpField.getOne()                     // P0(X) = 1
50              });
51              list.add(new Dfp[] {
52                  dfpField.getZero(), dfpField.getOne() // P1(X) = 0 + 1 * X
53              });
54          }
55  
56          while (list.size() <= n) {
57  
58              // build polynomial Pk+1 using recursion formula
59              // (k+1) P<sub>k+1</sub>(X) = (2k+1) X P<sub>k</sub>(X) - k P<sub>k-1</sub>(X)
60              int   k       = list.size() - 1;
61              Dfp   kDfp    = dfpField.newDfp(k);
62              Dfp   kP1Dfp  = kDfp.add(dfpField.getOne());
63              Dfp   ckDfp   = kP1Dfp.add(kDfp).divide(kP1Dfp);
64              Dfp[] pk      = list.get(k);
65              Dfp   ckM1Dfp = kDfp.divide(kP1Dfp).negate();
66              Dfp[] pkM1    = list.get(k - 1);
67  
68              Dfp[] pkP1    = new Dfp[k + 2];
69              pkP1[0] = ckM1Dfp.multiply(pkM1[0]);
70              for (int i = 0; i < k; ++i) {
71                  if ((k - i) % 2 == 1) {
72                      pkP1[i + 1] = dfpField.getZero();
73                  } else {
74                      pkP1[i + 1] = ckDfp.multiply(pk[i]).add(ckM1Dfp.multiply(pkM1[i + 1]));
75                  }
76              }
77              pkP1[k + 1] = ckDfp.multiply(pk[k]);
78  
79              list.add(pkP1);
80  
81          }
82  
83          // retrieve degree n polynomial
84          return list.get(n);
85  
86      }
87  
88      private Dfp[] differentiateLegendrePolynomial(Dfp[] p, int m, DfpField dfpField) {
89          Dfp[] dp = new Dfp[p.length - m];
90          for (int i = 0; i < dp.length; ++i) {
91              dp[i] = p[i + m];
92              for (int j = m; j > 0; --j) {
93                  dp[i] = dp[i].multiply(dfpField.newDfp(i + j));
94              }
95          }
96          return dp;
97      }
98  
99      public AssociatedLegendreFunction(boolean normalized, int n, int m, DfpField dfpField) {
100 
101         this.m = m;
102 
103         // store mth derivative of the degree n Legendre polynomial
104         polynomial = differentiateLegendrePolynomial(getLegendrePolynomial(n, dfpField), m, dfpField);
105 
106         if (normalized) {
107             // compute normalization coefficient
108             Dfp c = dfpField.newDfp(((m == 0) ? 1.0 : 2.0) * (2 * n + 1));
109             for (int k = 0; k < m; ++k) {
110                 c = c.divide(dfpField.newDfp((n + 1 + k) * (n - k)));
111             }
112             this.normalization = c.sqrt();
113         } else {
114             this.normalization = dfpField.getOne();
115         }
116 
117     }
118 
119     public DerivativeStructure value(DerivativeStructure t) {
120         DerivativeStructure y1 = t.getField().getOne().multiply(polynomial[polynomial.length - 1].toDouble());
121         for (int j = polynomial.length - 2; j >= 0; j--) {
122             y1 = y1.multiply(t).add(polynomial[j].toDouble());
123         }
124         DerivativeStructure oneMinusT2 = t.getField().getOne().subtract(t.multiply(t));
125         DerivativeStructure y2 = oneMinusT2.pow(m).sqrt();
126         return y1.multiply(y2).multiply(normalization.toDouble());
127     }
128 
129     public Dfp value(Dfp t) {
130         Dfp y1 = polynomial[polynomial.length - 1];
131         for (int j = polynomial.length - 2; j >= 0; j--) {
132             y1 = y1.multiply(t).add(polynomial[j]);
133         }
134         Dfp oneMinusT2 = t.getField().getOne().subtract(t.multiply(t));
135         Dfp y2 = t.getField().getOne();
136         for (int j = 0; j < m; ++j) {
137             y2 = y2.multiply(oneMinusT2);
138         }
139         y2 = y2.sqrt();
140         return y1.multiply(y2).multiply(normalization);
141     }
142 
143 }