JacobianPropagatorConverter.java

  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. import java.util.List;

  19. import org.hipparchus.analysis.MultivariateVectorFunction;
  20. import org.hipparchus.linear.ArrayRealVector;
  21. import org.hipparchus.linear.MatrixUtils;
  22. import org.hipparchus.linear.RealMatrix;
  23. import org.hipparchus.linear.RealVector;
  24. import org.hipparchus.optim.nonlinear.vector.leastsquares.MultivariateJacobianFunction;
  25. import org.hipparchus.util.Pair;
  26. import org.orekit.errors.OrekitException;
  27. import org.orekit.errors.OrekitMessages;
  28. import org.orekit.orbits.OrbitType;
  29. import org.orekit.propagation.MatricesHarvester;
  30. import org.orekit.propagation.SpacecraftState;
  31. import org.orekit.propagation.numerical.NumericalPropagator;
  32. import org.orekit.propagation.sampling.OrekitStepHandler;
  33. import org.orekit.propagation.sampling.OrekitStepInterpolator;
  34. import org.orekit.time.AbsoluteDate;
  35. import org.orekit.utils.PVCoordinates;
  36. import org.orekit.utils.ParameterDriver;
  37. import org.orekit.utils.ParameterDriversList;
  38. import org.orekit.utils.TimeSpanMap.Span;

  39. /** Propagator converter using the real Jacobian.
  40.  * @author Pascal Parraud
  41.  * @since 6.0
  42.  */
  43. public class JacobianPropagatorConverter extends AbstractPropagatorConverter {

  44.     /** Numerical propagator builder. */
  45.     private final NumericalPropagatorBuilder builder;

  46.     /** Simple constructor.
  47.      * @param builder builder for adapted propagator, it <em>must</em>
  48.      * be configured to generate {@link OrbitType#CARTESIAN} states
  49.      * @param threshold absolute threshold for optimization algorithm
  50.      * @param maxIterations maximum number of iterations for fitting
  51.      */
  52.     public JacobianPropagatorConverter(final NumericalPropagatorBuilder builder,
  53.                                        final double threshold,
  54.                                        final int maxIterations) {
  55.         super(builder, threshold, maxIterations);
  56.         if (builder.getOrbitType() != OrbitType.CARTESIAN) {
  57.             throw new OrekitException(OrekitMessages.ORBIT_TYPE_NOT_ALLOWED,
  58.                                       builder.getOrbitType(), OrbitType.CARTESIAN);
  59.         }
  60.         this.builder = builder;
  61.     }

  62.     /** {@inheritDoc} */
  63.     protected MultivariateVectorFunction getObjectiveFunction() {
  64.         return point -> {
  65.             final NumericalPropagator propagator  = builder.buildPropagator(point);
  66.             final ValuesHandler handler = new ValuesHandler();
  67.             propagator.getMultiplexer().add(handler);
  68.             final List<SpacecraftState> sample = getSample();
  69.             propagator.propagate(sample.get(sample.size() - 1).getDate().shiftedBy(10.0));
  70.             return handler.value;
  71.         };
  72.     }

  73.     /** {@inheritDoc} */
  74.     protected MultivariateJacobianFunction getModel() {
  75.         return point -> {
  76.             final NumericalPropagator propagator  = builder.buildPropagator(point.toArray());
  77.             final JacobianHandler handler = new JacobianHandler(propagator, point.getDimension());
  78.             propagator.getMultiplexer().add(handler);
  79.             final List<SpacecraftState> sample = getSample();
  80.             propagator.propagate(sample.get(sample.size() - 1).getDate().shiftedBy(10.0));
  81.             return new Pair<>(handler.value, handler.jacobian);
  82.         };
  83.     }

  84.     /** Handler for picking up values at sample dates.
  85.      * <p>
  86.      * This class is heavily based on org.orekit.estimation.leastsquares.MeasurementHandler.
  87.      * </p>
  88.      * @since 11.1
  89.      */
  90.     private class ValuesHandler implements OrekitStepHandler {

  91.         /** Values vector. */
  92.         private final double[] value;

  93.         /** Number of the next measurement. */
  94.         private int number;

  95.         /** Index of the next component in the model. */
  96.         private int index;

  97.         /** Simple constructor.
  98.          */
  99.         ValuesHandler() {
  100.             this.value = new double[getTargetSize()];
  101.         }

  102.         /** {@inheritDoc} */
  103.         @Override
  104.         public void init(final SpacecraftState initialState, final AbsoluteDate target) {
  105.             number = 0;
  106.             index  = 0;
  107.         }

  108.         /** {@inheritDoc} */
  109.         @Override
  110.         public void handleStep(final OrekitStepInterpolator interpolator) {

  111.             while (number < getSample().size()) {

  112.                 // Consider the next sample to handle
  113.                 final SpacecraftState next = getSample().get(number);

  114.                 // Current state date
  115.                 final AbsoluteDate currentDate = interpolator.getCurrentState().getDate();
  116.                 if (next.getDate().compareTo(currentDate) > 0) {
  117.                     return;
  118.                 }

  119.                 final PVCoordinates pv = interpolator.getInterpolatedState(next.getDate()).getPVCoordinates(getFrame());
  120.                 value[index++] = pv.getPosition().getX();
  121.                 value[index++] = pv.getPosition().getY();
  122.                 value[index++] = pv.getPosition().getZ();
  123.                 if (!isOnlyPosition()) {
  124.                     value[index++] = pv.getVelocity().getX();
  125.                     value[index++] = pv.getVelocity().getY();
  126.                     value[index++] = pv.getVelocity().getZ();
  127.                 }

  128.                 // prepare handling of next measurement
  129.                 ++number;

  130.             }

  131.         }

  132.     }

  133.     /** Handler for picking up Jacobians at sample dates.
  134.      * <p>
  135.      * This class is heavily based on org.orekit.estimation.leastsquares.MeasurementHandler.
  136.      * </p>
  137.      * @since 11.1
  138.      */
  139.     private class JacobianHandler implements OrekitStepHandler {

  140.         /** Values vector. */
  141.         private final RealVector value;

  142.         /** Jacobian matrix. */
  143.         private final RealMatrix jacobian;

  144.         /** State size (3 or 6). */
  145.         private final int stateSize;

  146.         /** Matrices harvester. */
  147.         private final MatricesHarvester harvester;

  148.         /** Number of the next measurement. */
  149.         private int number;

  150.         /** Index of the next Jacobian component in the model. */
  151.         private int index;

  152.         /** Simple constructor.
  153.          * @param propagator propagator
  154.          * @param columns number of columns of the Jacobian matrix
  155.          */
  156.         JacobianHandler(final NumericalPropagator propagator, final int columns) {
  157.             this.value     = new ArrayRealVector(getTargetSize());
  158.             this.jacobian  = MatrixUtils.createRealMatrix(getTargetSize(), columns);
  159.             this.stateSize = isOnlyPosition() ? 3 : 6;
  160.             this.harvester = propagator.setupMatricesComputation("converter-partials", null, null);
  161.         }

  162.         /** {@inheritDoc} */
  163.         @Override
  164.         public void init(final SpacecraftState initialState, final AbsoluteDate target) {
  165.             number = 0;
  166.             index  = 0;
  167.         }

  168.         /** {@inheritDoc} */
  169.         @Override
  170.         public void handleStep(final OrekitStepInterpolator interpolator) {

  171.             while (number < getSample().size()) {

  172.                 // Consider the next sample to handle
  173.                 final SpacecraftState next = getSample().get(number);

  174.                 // Current state date
  175.                 final AbsoluteDate currentDate = interpolator.getCurrentState().getDate();
  176.                 if (next.getDate().compareTo(currentDate) > 0) {
  177.                     return;
  178.                 }

  179.                 fillRows(index, interpolator.getInterpolatedState(next.getDate()),
  180.                          builder.getOrbitalParametersDrivers());

  181.                 // prepare handling of next measurement
  182.                 ++number;
  183.                 index += stateSize;

  184.             }

  185.         }

  186.         /** Fill up a few Jacobian rows (either 6 or 3 depending on velocities used or not).
  187.          * @param row first row index
  188.          * @param state spacecraft state
  189.          * @param orbitalParameters drivers for the orbital parameters
  190.          */
  191.         private void fillRows(final int row,
  192.                               final SpacecraftState state,
  193.                               final ParameterDriversList orbitalParameters) {

  194.             // value part
  195.             final PVCoordinates pv = state.getPVCoordinates(getFrame());
  196.             value.setEntry(row,     pv.getPosition().getX());
  197.             value.setEntry(row + 1, pv.getPosition().getY());
  198.             value.setEntry(row + 2, pv.getPosition().getZ());
  199.             if (!isOnlyPosition()) {
  200.                 value.setEntry(row + 3, pv.getVelocity().getX());
  201.                 value.setEntry(row + 4, pv.getVelocity().getY());
  202.                 value.setEntry(row + 5, pv.getVelocity().getZ());
  203.             }

  204.             // Jacobian part
  205.             final RealMatrix dYdY0 = harvester.getStateTransitionMatrix(state);
  206.             final RealMatrix dYdP  = harvester.getParametersJacobian(state);
  207.             for (int k = 0; k < stateSize; k++) {
  208.                 int column = 0;
  209.                 for (int j = 0; j < orbitalParameters.getNbParams(); ++j) {
  210.                     final ParameterDriver driver = orbitalParameters.getDrivers().get(j);
  211.                     if (driver.isSelected()) {
  212.                         jacobian.setEntry(row + k, column++, dYdY0.getEntry(k, j) * driver.getScale());
  213.                     }
  214.                 }
  215.                 if (dYdP != null) {
  216.                     for (int j = 0; j < dYdP.getColumnDimension(); ++j) {
  217.                         final String name = harvester.getJacobiansColumnsNames().get(j);
  218.                         for (final ParameterDriver driver : builder.getPropagationParametersDrivers().getDrivers()) {

  219.                             for (Span<String> span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) {
  220.                                 if (name.equals(span.getData())) {
  221.                                     jacobian.setEntry(row + k, column++, dYdP.getEntry(k, j) * driver.getScale());
  222.                                 }
  223.                             }
  224.                         }
  225.                     }
  226.                 }
  227.             }
  228.         }

  229.     }

  230. }