JacobianPropagatorConverter.java

  1. /* Copyright 2002-2022 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. /** Propagator converter using the real Jacobian.
  39.  * @author Pascal Parraud
  40.  * @since 6.0
  41.  */
  42. public class JacobianPropagatorConverter extends AbstractPropagatorConverter {

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  129.             }

  130.         }

  131.     }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  183.             }

  184.         }

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

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

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

  225.         }

  226.     }

  227. }