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.conversion;
18  
19  import org.hipparchus.analysis.MultivariateVectorFunction;
20  import org.hipparchus.linear.MatrixUtils;
21  import org.hipparchus.linear.RealMatrix;
22  import org.hipparchus.linear.RealVector;
23  import org.hipparchus.optim.nonlinear.vector.leastsquares.MultivariateJacobianFunction;
24  import org.hipparchus.util.Pair;
25  import org.orekit.errors.OrekitException;
26  import org.orekit.propagation.Propagator;
27  import org.orekit.propagation.SpacecraftState;
28  import org.orekit.utils.PVCoordinates;
29  
30  /** Propagator converter using finite differences to compute the Jacobian.
31   * @author Pascal Parraud
32   * @since 6.0
33   */
34  public class FiniteDifferencePropagatorConverter extends AbstractPropagatorConverter {
35  
36      /** Propagator builder. */
37      private final PropagatorBuilder builder;
38  
39      /** Simple constructor.
40       * @param factory builder for adapted propagator
41       * @param threshold absolute threshold for optimization algorithm
42       * @param maxIterations maximum number of iterations for fitting
43       */
44      public FiniteDifferencePropagatorConverter(final PropagatorBuilder factory,
45                                                 final double threshold,
46                                                 final int maxIterations) {
47          super(factory, threshold, maxIterations);
48          this.builder = factory;
49      }
50  
51      /** {@inheritDoc} */
52      protected MultivariateVectorFunction getObjectiveFunction() {
53          return new ObjectiveFunction();
54      }
55  
56      /** {@inheritDoc} */
57      protected MultivariateJacobianFunction getModel() {
58          return new ObjectiveFunctionJacobian();
59      }
60  
61      /** Internal class for computing position/velocity at sample points. */
62      private class ObjectiveFunction implements MultivariateVectorFunction {
63  
64          /** {@inheritDoc} */
65          public double[] value(final double[] arg)
66              throws IllegalArgumentException, OrekitException {
67              final Propagator propagator = builder.buildPropagator(arg);
68              final double[] eval = new double[getTargetSize()];
69              int k = 0;
70              for (SpacecraftState state : getSample()) {
71                  final PVCoordinates pv = propagator.getPVCoordinates(state.getDate(), getFrame());
72                  if (Double.isNaN(pv.getMomentum().getNorm())) {
73                      propagator.getPVCoordinates(state.getDate(), getFrame());
74                  }
75                  eval[k++] = pv.getPosition().getX();
76                  eval[k++] = pv.getPosition().getY();
77                  eval[k++] = pv.getPosition().getZ();
78                  if (!isOnlyPosition()) {
79                      eval[k++] = pv.getVelocity().getX();
80                      eval[k++] = pv.getVelocity().getY();
81                      eval[k++] = pv.getVelocity().getZ();
82                  }
83              }
84  
85              return eval;
86  
87          }
88      }
89  
90      /** Internal class for computing position/velocity Jacobian at sample points. */
91      private class ObjectiveFunctionJacobian implements MultivariateJacobianFunction {
92  
93          /** {@inheritDoc} */
94          public Pair<RealVector, RealMatrix> value(final RealVector point)
95              throws IllegalArgumentException, OrekitException {
96  
97              final double[] arg = point.toArray();
98              final MultivariateVectorFunction f = new ObjectiveFunction();
99  
100             final double[][] jacob = new double[getTargetSize()][arg.length];
101             final double[] eval = f.value(arg);
102             final double[] arg1 = new double[arg.length];
103             for (int j = 0; j < arg.length; j++) {
104                 System.arraycopy(arg, 0, arg1, 0, arg.length);
105                 arg1[j] += 1;
106                 final double[] eval1 = f.value(arg1);
107                 for (int t = 0; t < eval.length; t++) {
108                     jacob[t][j] = eval1[t] - eval[t];
109                 }
110             }
111 
112             return new Pair<>(MatrixUtils.createRealVector(eval),
113                     MatrixUtils.createRealMatrix(jacob));
114 
115         }
116 
117     }
118 
119 }
120