AbstractAnalyticalMatricesHarvester.java

  1. /* Copyright 2002-2024 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.analytical;

  18. import java.util.Arrays;
  19. import java.util.List;

  20. import org.hipparchus.analysis.differentiation.Gradient;
  21. import org.hipparchus.linear.MatrixUtils;
  22. import org.hipparchus.linear.RealMatrix;
  23. import org.orekit.orbits.FieldOrbit;
  24. import org.orekit.orbits.OrbitType;
  25. import org.orekit.orbits.PositionAngleType;
  26. import org.orekit.propagation.AbstractMatricesHarvester;
  27. import org.orekit.propagation.AdditionalStateProvider;
  28. import org.orekit.propagation.FieldSpacecraftState;
  29. import org.orekit.propagation.SpacecraftState;
  30. import org.orekit.time.AbsoluteDate;
  31. import org.orekit.time.FieldAbsoluteDate;
  32. import org.orekit.utils.DoubleArrayDictionary;
  33. import org.orekit.utils.FieldPVCoordinates;
  34. import org.orekit.utils.ParameterDriver;
  35. import org.orekit.utils.TimeSpanMap;
  36. import org.orekit.utils.TimeSpanMap.Span;

  37. /**
  38.  * Base class harvester between two-dimensional Jacobian
  39.  * matrices and analytical orbit propagator.
  40.  * @author Thomas Paulet
  41.  * @author Bryan Cazabonne
  42.  * @since 11.1
  43.  */
  44. public abstract class AbstractAnalyticalMatricesHarvester extends AbstractMatricesHarvester implements AdditionalStateProvider {

  45.     /** Columns names for parameters. */
  46.     private List<String> columnsNames;

  47.     /** Epoch of the last computed state transition matrix. */
  48.     private AbsoluteDate epoch;

  49.     /** Analytical derivatives that apply to State Transition Matrix. */
  50.     private final double[][] analyticalDerivativesStm;

  51.     /** Analytical derivatives that apply to Jacobians columns. */
  52.     private final DoubleArrayDictionary analyticalDerivativesJacobianColumns;

  53.     /** Propagator bound to this harvester. */
  54.     private final AbstractAnalyticalPropagator propagator;

  55.     /** Simple constructor.
  56.      * <p>
  57.      * The arguments for initial matrices <em>must</em> be compatible with the
  58.      * {@link org.orekit.orbits.OrbitType orbit type}
  59.      * and {@link PositionAngleType position angle} that will be used by propagator
  60.      * </p>
  61.      * @param propagator propagator bound to this harvester
  62.      * @param stmName State Transition Matrix state name
  63.      * @param initialStm initial State Transition Matrix ∂Y/∂Y₀,
  64.      * if null (which is the most frequent case), assumed to be 6x6 identity
  65.      * @param initialJacobianColumns initial columns of the Jacobians matrix with respect to parameters,
  66.      * if null or if some selected parameters are missing from the dictionary, the corresponding
  67.      * initial column is assumed to be 0
  68.      */
  69.     protected AbstractAnalyticalMatricesHarvester(final AbstractAnalyticalPropagator propagator, final String stmName,
  70.                                                   final RealMatrix initialStm, final DoubleArrayDictionary initialJacobianColumns) {
  71.         super(stmName, initialStm, initialJacobianColumns);
  72.         this.propagator                           = propagator;
  73.         this.epoch                                = propagator.getInitialState().getDate();
  74.         this.columnsNames                         = null;
  75.         this.analyticalDerivativesStm             = getInitialStateTransitionMatrix().getData();
  76.         this.analyticalDerivativesJacobianColumns = new DoubleArrayDictionary();
  77.     }

  78.     /** {@inheritDoc} */
  79.     @Override
  80.     public List<String> getJacobiansColumnsNames() {
  81.         return columnsNames == null ? propagator.getJacobiansColumnsNames() : columnsNames;
  82.     }

  83.     /** {@inheritDoc} */
  84.     @Override
  85.     public void freezeColumnsNames() {
  86.         columnsNames = getJacobiansColumnsNames();
  87.     }

  88.     /** {@inheritDoc} */
  89.     @Override
  90.     public String getName() {
  91.         return getStmName();
  92.     }

  93.     /** {@inheritDoc} */
  94.     @Override
  95.     public double[] getAdditionalState(final SpacecraftState state) {
  96.         // Update the partial derivatives if needed
  97.         updateDerivativesIfNeeded(state);
  98.         // Return the state transition matrix in an array
  99.         return toArray(analyticalDerivativesStm);
  100.     }

  101.     /** {@inheritDoc} */
  102.     @Override
  103.     public RealMatrix getStateTransitionMatrix(final SpacecraftState state) {
  104.         // Check if additional state is defined
  105.         if (!state.hasAdditionalState(getName())) {
  106.             return null;
  107.         }
  108.         // Return the state transition matrix
  109.         return toRealMatrix(state.getAdditionalState(getName()));
  110.     }

  111.     /** {@inheritDoc} */
  112.     @Override
  113.     public RealMatrix getParametersJacobian(final SpacecraftState state) {
  114.         // Update the partial derivatives if needed
  115.         updateDerivativesIfNeeded(state);

  116.         // Estimated parameters
  117.         final List<String> names = getJacobiansColumnsNames();
  118.         if (names == null || names.isEmpty()) {
  119.             return null;
  120.         }

  121.         // Initialize Jacobian
  122.         final RealMatrix dYdP = MatrixUtils.createRealMatrix(STATE_DIMENSION, names.size());

  123.         // Add the derivatives
  124.         for (int j = 0; j < names.size(); ++j) {
  125.             final double[] column = analyticalDerivativesJacobianColumns.get(names.get(j));
  126.             if (column != null) {
  127.                 for (int i = 0; i < STATE_DIMENSION; i++) {
  128.                     dYdP.addToEntry(i, j, column[i]);
  129.                 }
  130.             }
  131.         }

  132.         // Return
  133.         return dYdP;
  134.     }

  135.     /** {@inheritDoc} */
  136.     @Override
  137.     public void setReferenceState(final SpacecraftState reference) {

  138.         // reset derivatives to zero
  139.         for (final double[] row : analyticalDerivativesStm) {
  140.             Arrays.fill(row, 0.0);
  141.         }
  142.         analyticalDerivativesJacobianColumns.clear();

  143.         final AbstractAnalyticalGradientConverter converter           = getGradientConverter();
  144.         final FieldSpacecraftState<Gradient> gState                   = converter.getState();
  145.         final Gradient[] gParameters                                  = converter.getParameters(gState, converter);
  146.         final FieldAbstractAnalyticalPropagator<Gradient> gPropagator = converter.getPropagator(gState, gParameters);

  147.         // Compute Jacobian
  148.         final AbsoluteDate target               = reference.getDate();
  149.         final FieldAbsoluteDate<Gradient> start = gPropagator.getInitialState().getDate();
  150.         final double dt                         = target.durationFrom(start.toAbsoluteDate());
  151.         final FieldOrbit<Gradient> gOrbit       = gPropagator.propagateOrbit(start.shiftedBy(dt), gParameters);
  152.         final FieldPVCoordinates<Gradient> gPv  = gOrbit.getPVCoordinates();

  153.         final double[] derivativesX   = gPv.getPosition().getX().getGradient();
  154.         final double[] derivativesY   = gPv.getPosition().getY().getGradient();
  155.         final double[] derivativesZ   = gPv.getPosition().getZ().getGradient();
  156.         final double[] derivativesVx  = gPv.getVelocity().getX().getGradient();
  157.         final double[] derivativesVy  = gPv.getVelocity().getY().getGradient();
  158.         final double[] derivativesVz  = gPv.getVelocity().getZ().getGradient();

  159.         // Update Jacobian with respect to state
  160.         addToRow(derivativesX,  0);
  161.         addToRow(derivativesY,  1);
  162.         addToRow(derivativesZ,  2);
  163.         addToRow(derivativesVx, 3);
  164.         addToRow(derivativesVy, 4);
  165.         addToRow(derivativesVz, 5);

  166.         // Partial derivatives of the state with respect to propagation parameters
  167.         int paramsIndex = converter.getFreeStateParameters();
  168.         for (ParameterDriver driver : converter.getParametersDrivers()) {
  169.             if (driver.isSelected()) {

  170.                 final TimeSpanMap<String> driverNameSpanMap = driver.getNamesSpanMap();
  171.                 // for each span (for each estimated value) corresponding name is added
  172.                 for (Span<String> span = driverNameSpanMap.getFirstSpan(); span != null; span = span.next()) {
  173.                     // get the partials derivatives for this driver
  174.                     DoubleArrayDictionary.Entry entry = analyticalDerivativesJacobianColumns.getEntry(span.getData());
  175.                     if (entry == null) {
  176.                         // create an entry filled with zeroes
  177.                         analyticalDerivativesJacobianColumns.put(span.getData(), new double[STATE_DIMENSION]);
  178.                         entry = analyticalDerivativesJacobianColumns.getEntry(span.getData());
  179.                     }

  180.                     // add the contribution of the current force model
  181.                     entry.increment(new double[] {
  182.                         derivativesX[paramsIndex], derivativesY[paramsIndex], derivativesZ[paramsIndex],
  183.                         derivativesVx[paramsIndex], derivativesVy[paramsIndex], derivativesVz[paramsIndex]
  184.                     });
  185.                     ++paramsIndex;
  186.                 }
  187.             }
  188.         }

  189.         // Update the epoch of the last computed partial derivatives
  190.         epoch = target;

  191.     }

  192.     /** Update the partial derivatives (if needed).
  193.      * @param state current spacecraft state
  194.      */
  195.     private void updateDerivativesIfNeeded(final SpacecraftState state) {
  196.         if (!state.getDate().isEqualTo(epoch)) {
  197.             setReferenceState(state);
  198.         }
  199.     }

  200.     /** Fill State Transition Matrix rows.
  201.      * @param derivatives derivatives of a component
  202.      * @param index component index
  203.      */
  204.     private void addToRow(final double[] derivatives, final int index) {
  205.         for (int i = 0; i < 6; i++) {
  206.             analyticalDerivativesStm[index][i] += derivatives[i];
  207.         }
  208.     }

  209.     /** Convert an array to a matrix (6x6 dimension).
  210.      * @param array input array
  211.      * @return the corresponding matrix
  212.      */
  213.     private RealMatrix toRealMatrix(final double[] array) {
  214.         final RealMatrix matrix = MatrixUtils.createRealMatrix(STATE_DIMENSION, STATE_DIMENSION);
  215.         int index = 0;
  216.         for (int i = 0; i < STATE_DIMENSION; ++i) {
  217.             for (int j = 0; j < STATE_DIMENSION; ++j) {
  218.                 matrix.setEntry(i, j, array[index++]);
  219.             }
  220.         }
  221.         return matrix;
  222.     }

  223.     /** Set the STM data into an array.
  224.      * @param matrix STM matrix
  225.      * @return an array containing the STM data
  226.      */
  227.     private double[] toArray(final double[][] matrix) {
  228.         final double[] array = new double[STATE_DIMENSION * STATE_DIMENSION];
  229.         int index = 0;
  230.         for (int i = 0; i < STATE_DIMENSION; ++i) {
  231.             final double[] row = matrix[i];
  232.             for (int j = 0; j < STATE_DIMENSION; ++j) {
  233.                 array[index++] = row[j];
  234.             }
  235.         }
  236.         return array;
  237.     }

  238.     /** {@inheritDoc} */
  239.     @Override
  240.     public OrbitType getOrbitType() {
  241.         // Set to CARTESIAN because analytical gradient converter uses cartesian representation
  242.         return OrbitType.CARTESIAN;
  243.     }

  244.     /** {@inheritDoc} */
  245.     @Override
  246.     public PositionAngleType getPositionAngleType() {
  247.         // Irrelevant: set a default value
  248.         return PositionAngleType.MEAN;
  249.     }

  250.     /**
  251.      * Get the gradient converter related to the analytical orbit propagator.
  252.      * @return the gradient converter
  253.      */
  254.     public abstract AbstractAnalyticalGradientConverter getGradientConverter();

  255. }