1   /* Copyright 2002-2021 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.numerical;
18  
19  import org.hipparchus.linear.Array2DRowRealMatrix;
20  import org.hipparchus.linear.DecompositionSolver;
21  import org.hipparchus.linear.QRDecomposition;
22  import org.hipparchus.linear.RealMatrix;
23  import org.orekit.orbits.Orbit;
24  import org.orekit.orbits.OrbitType;
25  import org.orekit.orbits.PositionAngle;
26  import org.orekit.propagation.SpacecraftState;
27  import org.orekit.propagation.integration.AbstractJacobiansMapper;
28  import org.orekit.utils.ParameterDriversList;
29  
30  /** Mapper between two-dimensional Jacobian matrices and one-dimensional {@link
31   * SpacecraftState#getAdditionalState(String) additional state arrays}.
32   * <p>
33   * This class does not hold the states by itself. Instances of this class are guaranteed
34   * to be immutable.
35   * </p>
36   * @author Luc Maisonobe
37   * @see org.orekit.propagation.numerical.PartialDerivativesEquations
38   * @see org.orekit.propagation.numerical.NumericalPropagator
39   * @see SpacecraftState#getAdditionalState(String)
40   * @see org.orekit.propagation.AbstractPropagator
41   */
42  public class JacobiansMapper extends AbstractJacobiansMapper {
43  
44      /** State dimension, fixed to 6.
45       * @since 9.0
46       */
47      public static final int STATE_DIMENSION = 6;
48  
49      /** Selected parameters for Jacobian computation. */
50      private final ParameterDriversList parameters;
51  
52      /** Name. */
53      private String name;
54  
55      /** Orbit type. */
56      private final OrbitType orbitType;
57  
58      /** Position angle type. */
59      private final PositionAngle angleType;
60  
61      /** Simple constructor.
62       * @param name name of the Jacobians
63       * @param parameters selected parameters for Jacobian computation
64       * @param orbitType orbit type
65       * @param angleType position angle type
66       */
67      JacobiansMapper(final String name, final ParameterDriversList parameters,
68                      final OrbitType orbitType, final PositionAngle angleType) {
69          super(name, parameters);
70          this.orbitType  = orbitType;
71          this.angleType  = angleType;
72          this.parameters = parameters;
73          this.name = name;
74      }
75  
76      /** Get the conversion Jacobian between state parameters and parameters used for derivatives.
77       * <p>
78       * For DSST and TLE propagators, state parameters and parameters used for derivatives are the same,
79       * so the Jocabian is simply the identity.
80       * </p>
81       * <p>
82       * For Numerical propagator, parameters used for derivatives are cartesian
83       * and they can be different from state parameters because the numerical propagator can accept different type
84       * of orbits.
85       * </p>
86       * @param state spacecraft state
87       * @return conversion Jacobian
88       */
89      protected double[][] getConversionJacobian(final SpacecraftState state) {
90  
91          final double[][] dYdC = new double[STATE_DIMENSION][STATE_DIMENSION];
92  
93          // make sure the state is in the desired orbit type
94          final Orbit orbit = orbitType.convertType(state.getOrbit());
95  
96          // compute the Jacobian, taking the position angle type into account
97          orbit.getJacobianWrtCartesian(angleType, dYdC);
98  
99          return dYdC;
100 
101     }
102 
103     /** {@inheritDoc}
104      * <p>
105      * This method converts the Jacobians to Cartesian parameters and put the converted data
106      * in the one-dimensional {@code p} array.
107      * </p>
108      */
109     public void setInitialJacobians(final SpacecraftState state, final double[][] dY1dY0,
110                              final double[][] dY1dP, final double[] p) {
111 
112         // set up a converter
113         final RealMatrix dY1dC1 = new Array2DRowRealMatrix(getConversionJacobian(state), false);
114         final DecompositionSolver solver = new QRDecomposition(dY1dC1).getSolver();
115 
116         // convert the provided state Jacobian
117         final RealMatrix dC1dY0 = solver.solve(new Array2DRowRealMatrix(dY1dY0, false));
118 
119         // map the converted state Jacobian to one-dimensional array
120         int index = 0;
121         for (int i = 0; i < STATE_DIMENSION; ++i) {
122             for (int j = 0; j < STATE_DIMENSION; ++j) {
123                 p[index++] = dC1dY0.getEntry(i, j);
124             }
125         }
126 
127         if (parameters.getNbParams() != 0) {
128             // convert the provided state Jacobian
129             final RealMatrix dC1dP = solver.solve(new Array2DRowRealMatrix(dY1dP, false));
130 
131             // map the converted parameters Jacobian to one-dimensional array
132             for (int i = 0; i < STATE_DIMENSION; ++i) {
133                 for (int j = 0; j < parameters.getNbParams(); ++j) {
134                     p[index++] = dC1dP.getEntry(i, j);
135                 }
136             }
137         }
138 
139     }
140 
141     /** {@inheritDoc} */
142     public void getStateJacobian(final SpacecraftState state,  final double[][] dYdY0) {
143 
144         // get the conversion Jacobian
145         final double[][] dYdC = getConversionJacobian(state);
146 
147         // extract the additional state
148         final double[] p = state.getAdditionalState(name);
149 
150         // compute dYdY0 = dYdC * dCdY0, without allocating new arrays
151         for (int i = 0; i < STATE_DIMENSION; i++) {
152             final double[] rowC = dYdC[i];
153             final double[] rowD = dYdY0[i];
154             for (int j = 0; j < STATE_DIMENSION; ++j) {
155                 double sum = 0;
156                 int pIndex = j;
157                 for (int k = 0; k < STATE_DIMENSION; ++k) {
158                     sum += rowC[k] * p[pIndex];
159                     pIndex += STATE_DIMENSION;
160                 }
161                 rowD[j] = sum;
162             }
163         }
164 
165     }
166 
167     /** {@inheritDoc} */
168     public void getParametersJacobian(final SpacecraftState state, final double[][] dYdP) {
169 
170         if (parameters.getNbParams() != 0) {
171 
172             // get the conversion Jacobian
173             final double[][] dYdC = getConversionJacobian(state);
174 
175             // extract the additional state
176             final double[] p = state.getAdditionalState(name);
177 
178             // compute dYdP = dYdC * dCdP, without allocating new arrays
179             for (int i = 0; i < STATE_DIMENSION; i++) {
180                 final double[] rowC = dYdC[i];
181                 final double[] rowD = dYdP[i];
182                 for (int j = 0; j < parameters.getNbParams(); ++j) {
183                     double sum = 0;
184                     int pIndex = j + STATE_DIMENSION * STATE_DIMENSION;
185                     for (int k = 0; k < STATE_DIMENSION; ++k) {
186                         sum += rowC[k] * p[pIndex];
187                         pIndex += parameters.getNbParams();
188                     }
189                     rowD[j] = sum;
190                 }
191             }
192 
193         }
194 
195     }
196 
197 }