1   /* Copyright 2002-2026 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;
18  
19  import java.util.ArrayList;
20  import java.util.List;
21  
22  import org.hipparchus.analysis.differentiation.Gradient;
23  import org.hipparchus.analysis.differentiation.GradientField;
24  import org.hipparchus.geometry.euclidean.threed.FieldRotation;
25  import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
26  import org.orekit.attitudes.AttitudeProvider;
27  import org.orekit.attitudes.FieldAttitude;
28  import org.orekit.orbits.CartesianOrbit;
29  import org.orekit.orbits.OrbitType;
30  import org.orekit.orbits.FieldOrbit;
31  import org.orekit.orbits.PositionAngleBased;
32  import org.orekit.orbits.PositionAngleType;
33  import org.orekit.time.AbsoluteDate;
34  import org.orekit.time.FieldAbsoluteDate;
35  import org.orekit.utils.DerivativeStateUtils;
36  import org.orekit.utils.FieldPVCoordinates;
37  import org.orekit.utils.FieldAbsolutePVCoordinates;
38  import org.orekit.utils.ParameterDriver;
39  import org.orekit.utils.ParameterDriversProvider;
40  import org.orekit.utils.TimeStampedFieldAngularCoordinates;
41  import org.orekit.utils.TimeSpanMap.Span;
42  
43  /** Converter for states and parameters arrays.
44   *  @author Luc Maisonobe
45   *  @author Bryan Cazabonne
46   *  @since 10.2
47   */
48  public abstract class AbstractGradientConverter {
49  
50      /** Dimension of the state. */
51      private final int freeStateParameters;
52  
53      /** States with various number of additional parameters. */
54      private final List<FieldSpacecraftState<Gradient>> gStates;
55  
56      /** Simple constructor.
57       * @param freeStateParameters number of free parameters
58       */
59      protected AbstractGradientConverter(final int freeStateParameters) {
60          this.freeStateParameters = freeStateParameters;
61          this.gStates             = new ArrayList<>();
62      }
63  
64      /** Get the number of free state parameters.
65       * @return number of free state parameters
66       */
67      public int getFreeStateParameters() {
68          return freeStateParameters;
69      }
70  
71      /** Initialize first state with 0 parameters.
72       * @param zeroParametersState state with zero parameters
73       * @since 11.2
74       */
75      public void initStates(final FieldSpacecraftState<Gradient> zeroParametersState) {
76          gStates.clear();
77          gStates.add(zeroParametersState);
78      }
79  
80      /** Add zero derivatives.
81       * @param original original scalar
82       * @param freeParameters total number of free parameters in the gradient
83       * @return extended scalar
84       */
85      private Gradient extend(final Gradient original, final int freeParameters) {
86          final double[] originalDerivatives = original.getGradient();
87          final double[] extendedDerivatives = new double[freeParameters];
88          System.arraycopy(originalDerivatives, 0, extendedDerivatives, 0, originalDerivatives.length);
89          return new Gradient(original.getValue(), extendedDerivatives);
90      }
91  
92      /** Add zero derivatives.
93       * @param original original date
94       * @param freeParameters total number of free parameters in the gradient
95       * @return extended date
96       */
97      private FieldAbsoluteDate<Gradient> extend(final FieldAbsoluteDate<Gradient> original, final int freeParameters) {
98          final AbsoluteDate date = original.toAbsoluteDate();
99          return new FieldAbsoluteDate<>(date, extend(original.durationFrom(date), freeParameters));
100     }
101 
102     /** Add zero derivatives.
103      * @param original original vector
104      * @param freeParameters total number of free parameters in the gradient
105      * @return extended vector
106      */
107     private FieldVector3D<Gradient> extend(final FieldVector3D<Gradient> original, final int freeParameters) {
108         return new FieldVector3D<>(extend(original.getX(), freeParameters),
109                                    extend(original.getY(), freeParameters),
110                                    extend(original.getZ(), freeParameters));
111     }
112 
113     /** Add zero derivatives.
114      * @param original original rotation
115      * @param freeParameters total number of free parameters in the gradient
116      * @return extended rotation
117      */
118     private FieldRotation<Gradient> extend(final FieldRotation<Gradient> original, final int freeParameters) {
119         return new FieldRotation<>(extend(original.getQ0(), freeParameters),
120                                    extend(original.getQ1(), freeParameters),
121                                    extend(original.getQ2(), freeParameters),
122                                    extend(original.getQ3(), freeParameters),
123                                    false);
124     }
125 
126     /** Add zero derivatives.
127      * @param original original angular coordinates
128      * @param freeParameters total number of free parameters in the gradient
129      * @return extended angular coordinates
130      */
131     private TimeStampedFieldAngularCoordinates<Gradient> extend(final TimeStampedFieldAngularCoordinates<Gradient> original,
132                                                                 final int freeParameters) {
133         return new TimeStampedFieldAngularCoordinates<>(extend(original.getDate(), freeParameters),
134                                                         extend(original.getRotation(), freeParameters),
135                                                         extend(original.getRotationRate(), freeParameters),
136                                                         extend(original.getRotationAcceleration(), freeParameters));
137 
138     }
139 
140     /** Add zero derivatives.
141      * @param original original attitude
142      * @param freeParameters total number of free parameters in the gradient
143      * @return extended rotation
144      */
145     private FieldAttitude<Gradient> extend(final FieldAttitude<Gradient> original, final int freeParameters) {
146         return new FieldAttitude<>(original.getReferenceFrame(),
147                                    extend(original.getOrientation(), freeParameters));
148     }
149 
150     /** Add zero derivatives.
151      * @param orbit original orbit
152      * @param freeParameters number of free parameters
153      * @return gradient orbit
154      * @since 14.0
155      */
156     private FieldOrbit<Gradient> extend(final FieldOrbit<Gradient> orbit, final int freeParameters) {
157 
158         final PositionAngleType positionAngleType = orbit instanceof PositionAngleBased<?> pab ?
159                                                     pab.getCachedPositionAngleType() :
160                                                     PositionAngleType.MEAN;
161 
162         // original state
163         final Gradient[] originalState = new Gradient[6];
164         orbit.getType().mapOrbitToArray(orbit, positionAngleType, originalState, null);
165 
166         // extended state
167         final Gradient[] extendedState = new Gradient[originalState.length];
168         for (int i = 0; i < originalState.length; i++) {
169             extendedState[i] = extend(originalState[i], freeParameters);
170         }
171 
172         return orbit.getType().mapArrayToOrbit(extendedState, null, positionAngleType,
173                                                extend(orbit.getDate(), freeParameters),
174                                                extend(orbit.getMu(), freeParameters),
175                                                orbit.getFrame());
176 
177     }
178 
179     /** Add zero derivatives.
180      * @param pv original Cartesian coordinates
181      * @param freeParameters number of free parameters
182      * @return gradient Cartesian coordinates
183      * @since 14.0
184      */
185     private FieldPVCoordinates<Gradient> extend(final FieldPVCoordinates<Gradient> pv, final int freeParameters) {
186         return new FieldPVCoordinates<>(extend(pv.getPosition(),     freeParameters),
187                                         extend(pv.getVelocity(),     freeParameters),
188                                         extend(pv.getAcceleration(), freeParameters));
189     }
190 
191     /** Add zero derivatives.
192      * @param apv original Cartesian coordinates
193      * @param freeParameters number of free parameters
194      * @return gradient Cartesian coordinates
195      * @since 14.0
196      */
197     private FieldAbsolutePVCoordinates<Gradient> extend(final FieldAbsolutePVCoordinates<Gradient> apv,
198                                                         final int freeParameters) {
199         return new FieldAbsolutePVCoordinates<>(apv.getFrame(),
200                                                 extend(apv.getDate(), freeParameters),
201                                                 extend(apv.getPVCoordinates(), freeParameters));
202     }
203 
204     /** Process a state into a Gradient version without force model parameter.
205      * @param state state
206      * @param freeStateParameters number of free parameters
207      * @param provider attitude provider
208      * @return Gradient version of the state
209      * @since 12.0
210      */
211     public static FieldSpacecraftState<Gradient> buildBasicGradientSpacecraftState(final SpacecraftState state,
212                                                                                    final int freeStateParameters,
213                                                                                    final AttitudeProvider provider) {
214 
215         // Derivative field
216         final GradientField field = GradientField.getField(freeStateParameters);
217 
218         if (state.isOrbitDefined()) {
219             final CartesianOrbit cartesianOrbit = (CartesianOrbit) OrbitType.CARTESIAN.convertType(state.getOrbit());
220             final SpacecraftState cartesianState = new SpacecraftState(cartesianOrbit, state.getAttitude()).withMass(state.getMass());
221             return DerivativeStateUtils.buildSpacecraftStateGradient(field, cartesianState, provider);
222         } else {
223             return DerivativeStateUtils.buildSpacecraftStateGradient(field, state, provider);
224         }
225 
226     }
227 
228     /**
229      * Get the state with the number of parameters consistent with parametric model.
230      * @param parametricModel parametric model
231      * @return state with the number of parameters consistent with parametric model
232      */
233     public FieldSpacecraftState<Gradient> getState(final ParameterDriversProvider parametricModel) {
234 
235         // count the required number of parameters
236         int nbParams = 0;
237         for (final ParameterDriver driver : parametricModel.getParametersDrivers()) {
238             if (driver.isSelected()) {
239                 nbParams += driver.getNbOfValues();
240             }
241         }
242 
243         // fill in intermediate slots
244         while (gStates.size() < nbParams + 1) {
245             gStates.add(null);
246         }
247 
248         if (gStates.get(nbParams) == null) {
249             // it is the first time we need this number of parameters
250             // we need to create the state
251             final int freeParameters = freeStateParameters + nbParams;
252             final FieldSpacecraftState<Gradient> s0 = gStates.getFirst();
253 
254             // attitude
255             final FieldAttitude<Gradient> gAttitude = extend(s0.getAttitude(), freeParameters);
256 
257             // orbit or absolute position-velocity coordinates
258             final FieldSpacecraftState<Gradient> spacecraftState =
259                 s0.isOrbitDefined() ?
260                 new FieldSpacecraftState<>(extend(s0.getOrbit(), freeParameters), gAttitude) :
261                 new FieldSpacecraftState<>(extend(s0.getAbsPVA(), freeParameters), gAttitude);
262 
263             // mass
264             final Gradient gMass = extend(s0.getMass(), freeParameters);
265 
266             gStates.set(nbParams, spacecraftState.withMass(gMass));
267 
268         }
269 
270         return gStates.get(nbParams);
271 
272     }
273 
274     /** Get the parametric model parameters, return gradient values for each span of each driver (several gradient
275      * values for each parameter).
276      * Different from {@link #getParametersAtStateDate(FieldSpacecraftState, ParameterDriversProvider)}
277      * which return a Gradient list containing for each driver the gradient value at state date (only 1 gradient
278      * value for each parameter).
279      * @param state state as returned by {@link #getState(ParameterDriversProvider) getState(parametricModel)}
280      * @param parametricModel parametric model associated with the parameters
281      * @return parametric model parameters (for all span of each driver)
282      */
283     public Gradient[] getParameters(final FieldSpacecraftState<Gradient> state,
284                                     final ParameterDriversProvider parametricModel) {
285         final int freeParameters = state.getMass().getFreeParameters();
286         final List<ParameterDriver> drivers = parametricModel.getParametersDrivers();
287         int sizeDrivers = 0;
288         for ( ParameterDriver driver : drivers) {
289             sizeDrivers += driver.getNbOfValues();
290         }
291         final Gradient[] parameters = new Gradient[sizeDrivers];
292         int index = freeStateParameters;
293         int i = 0;
294         for (ParameterDriver driver : drivers) {
295             // Loop on the spans
296             for (Span<Double> span = driver.getValueSpanMap().getFirstSpan(); span != null; span = span.next()) {
297                 parameters[i++] = driver.isSelected() ?
298                                   Gradient.variable(freeParameters, index++, span.getData()) :
299                                   Gradient.constant(freeParameters, span.getData());
300             }
301         }
302         return parameters;
303     }
304 
305     /** Get the parametric model parameters, return gradient values at state date for each driver (only 1 gradient
306      * value for each parameter).
307      * Different from {@link #getParameters(FieldSpacecraftState, ParameterDriversProvider)}
308      * which return a Gradient list containing for each driver the gradient values for each span value (several gradient
309      * values for each parameter).
310      * @param state state as returned by {@link #getState(ParameterDriversProvider) getState(parametricModel)}
311      * @param parametricModel parametric model associated with the parameters
312      * @return parametric model parameters (for all span of each driver)
313      */
314     public Gradient[] getParametersAtStateDate(final FieldSpacecraftState<Gradient> state,
315                                                final ParameterDriversProvider parametricModel) {
316         final int freeParameters = state.getMass().getFreeParameters();
317         final List<ParameterDriver> drivers = parametricModel.getParametersDrivers();
318 
319         final AbsoluteDate date = state.getDate().toAbsoluteDate();
320         final Gradient[] parameters = new Gradient[drivers.size()];
321         int index = freeStateParameters;
322         int i = 0;
323         for (ParameterDriver driver : drivers) {
324             for (Span<String> span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) {
325                 if (span.getData().equals(driver.getNameSpan(date))) {
326                     parameters[i++] = driver.isSelected() ?
327                                       Gradient.variable(freeParameters, index, driver.getValue(date)) :
328                                       Gradient.constant(freeParameters, driver.getValue(date));
329                 }
330                 index = driver.isSelected() ? index + 1 : index;
331             }
332         }
333         return parameters;
334     }
335 
336 
337 }