1   /* Copyright 2002-2025 CS GROUP
2    * Licensed to CS GROUP (CS) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * CS licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *   http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.orekit.propagation.semianalytical.dsst.utilities;
18  
19  import java.util.ArrayList;
20  import java.util.HashMap;
21  import java.util.List;
22  import java.util.Map;
23  
24  import org.hipparchus.CalculusFieldElement;
25  import org.hipparchus.analysis.differentiation.FieldGradient;
26  import org.hipparchus.analysis.differentiation.Gradient;
27  import org.hipparchus.analysis.polynomials.JacobiKey;
28  import org.hipparchus.analysis.polynomials.PolynomialFunction;
29  import org.hipparchus.analysis.polynomials.PolynomialsUtils;
30  import org.orekit.propagation.semianalytical.dsst.forces.DSSTThirdBody;
31  
32  /** Provider of the Jacobi polynomials P<sub>l</sub><sup>v,w</sup>.
33   * <p>
34   * This class is used for {@link
35   * org.orekit.propagation.semianalytical.dsst.forces.DSSTTesseral
36   * tesseral contribution} computation and {@link DSSTThirdBody}.
37   * </p>
38   *
39   * @author Nicolas Bernard
40   * @since 6.1
41   */
42  public class JacobiPolynomials {
43  
44      /** Storage map. */
45      private static final Map<JacobiKey, List<PolynomialFunction>> MAP = new HashMap<>();
46  
47      /** Private constructor as class is a utility. */
48      private JacobiPolynomials() {
49      }
50  
51      /** Returns the value and derivatives of the Jacobi polynomial P<sub>l</sub><sup>v,w</sup> evaluated at γ.
52       * <p>This method is guaranteed to be thread-safe
53       * <p>It was added to improve performances of DSST propagation with tesseral gravity field or third-body perturbations.
54       * <p>See issue <a href="https://gitlab.orekit.org/orekit/orekit/-/issues/1098">1098</a>.
55       * <p>It appeared the "Gradient" version was degrading performances. This last was however kept for validation purposes.
56       * @param l degree of the polynomial
57       * @param v v value
58       * @param w w value
59       * @param x x value
60       * @return value and derivatives of the Jacobi polynomial P<sub>l</sub><sup>v,w</sup>(γ)
61       * @since 11.3.3
62       */
63      public static double[] getValueAndDerivative(final int l, final int v, final int w, final double x) {
64          // compute value and derivative
65          return getValueAndDerivative(computePolynomial(l, v, w), x);
66      }
67  
68      /** Get value and 1st-order of a mono-variate polynomial.
69       *
70       * <p> This method was added to improve performances of DSST propagation with tesseral gravity field or third-body perturbations.
71       * <p> See issue <a href="https://gitlab.orekit.org/orekit/orekit/-/issues/1098">1098</a>.
72       * @param polynomial polynomial to evaluate
73       * @param x value to evaluate on
74       * @return value and 1s-order derivative as a double array
75       * @since 11.3.3
76       */
77      private static double[] getValueAndDerivative(final PolynomialFunction polynomial, final double x) {
78  
79          // Polynomial coefficients
80          final double[] coefficients = polynomial.getCoefficients();
81  
82          // Degree of the polynomial
83          final int degree = polynomial.degree();
84  
85          // Initialize value and 1st-order derivative
86          double value      = coefficients[degree];
87          double derivative = value * degree;
88          for (int j = degree - 1; j >= 1; j--) {
89  
90              // Increment both value and derivative
91              final double coef = coefficients[j];
92              value        = value      * x +  coef;
93              derivative   = derivative * x +  coef * j;
94          }
95          // If degree > 0, perform last operation
96          if (degree > 0) {
97              value = value * x + coefficients[0];
98          }
99  
100         // Return value and 1st-order derivative as double array
101         return new double[] {value, derivative};
102     }
103 
104     /** Returns the value and derivatives of the Jacobi polynomial P<sub>l</sub><sup>v,w</sup> evaluated at γ.
105      *
106      * <p>This method is guaranteed to be thread-safe
107      * <p>It's not used in the code anymore, see {@link #getValueAndDerivative(int, int, int, double)}, but was kept for validation purpose.
108      * @param l degree of the polynomial
109      * @param v v value
110      * @param w w value
111      * @param gamma γ value
112      * @return value and derivatives of the Jacobi polynomial P<sub>l</sub><sup>v,w</sup>(γ)
113      * @since 10.2
114      */
115     public static Gradient getValue(final int l, final int v, final int w, final Gradient gamma) {
116         // compute value and derivative
117         return computePolynomial(l, v, w).value(gamma);
118     }
119 
120     /** Returns the value and derivatives of the Jacobi polynomial P<sub>l</sub><sup>v,w</sup> evaluated at γ.
121      * <p>
122      * This method is guaranteed to be thread-safe
123      * </p>
124      * @param <T> the type of the field elements
125      * @param l degree of the polynomial
126      * @param v v value
127      * @param w w value
128      * @param gamma γ value
129      * @return value and derivatives of the Jacobi polynomial P<sub>l</sub><sup>v,w</sup>(γ)
130      * @since 10.2
131      */
132     public static <T extends CalculusFieldElement<T>> FieldGradient<T> getValue(final int l, final int v, final int w,
133                                                                                 final FieldGradient<T> gamma) {
134         // compute value and derivative
135         return computePolynomial(l, v, w).value(gamma);
136 
137     }
138 
139     /** Initializes the polynomial to evalutate.
140      * @param l degree of the polynomial
141      * @param v v value
142      * @param w w value
143      * @return the polynomial to evaluate
144      */
145     private static PolynomialFunction computePolynomial(final int l, final int v, final int w) {
146         final List<PolynomialFunction> polyList;
147         synchronized (MAP) {
148 
149             final JacobiKey key = new JacobiKey(v, w);
150 
151             // Check the existence of the corresponding key in the map.
152             if (!MAP.containsKey(key)) {
153                 MAP.put(key, new ArrayList<>());
154             }
155 
156             polyList = MAP.get(key);
157 
158         }
159 
160         final PolynomialFunction polynomial;
161         synchronized (polyList) {
162             // If the l-th degree polynomial has not been computed yet, the polynomials
163             // up to this degree are computed.
164             for (int degree = polyList.size(); degree <= l; degree++) {
165                 polyList.add(degree, PolynomialsUtils.createJacobiPolynomial(degree, v, w));
166             }
167             polynomial = polyList.get(l);
168         }
169 
170         return polynomial;
171     }
172 }