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