AbstractAnalyticalMatricesHarvester.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.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.AdditionalDataProvider;
  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
  45.         implements AdditionalDataProvider<double[]> {

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  144.         final AbstractAnalyticalGradientConverter converter           = getGradientConverter();
  145.         final FieldAbstractAnalyticalPropagator<Gradient> gPropagator = converter.getPropagator();

  146.         // Compute Jacobian
  147.         final AbsoluteDate                   target     = reference.getDate();
  148.         final FieldAbsoluteDate<Gradient>    start      = gPropagator.getInitialState().getDate();
  149.         final double                         dt         = target.durationFrom(start.toAbsoluteDate());
  150.         final FieldSpacecraftState<Gradient> state      = gPropagator.getInitialState();
  151.         final Gradient[]                     parameters = converter.getParameters(state, converter);
  152.         final FieldOrbit<Gradient>           gOrbit     = gPropagator.propagateOrbit(start.shiftedBy(dt), parameters);
  153.         final FieldPVCoordinates<Gradient>   gPv        = gOrbit.getPVCoordinates();

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

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

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

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

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

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

  192.     }

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

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

  210.     /** {@inheritDoc} */
  211.     @Override
  212.     public OrbitType getOrbitType() {
  213.         // Set to CARTESIAN because analytical gradient converters uses Cartesian representation
  214.         return OrbitType.CARTESIAN;
  215.     }

  216.     /** {@inheritDoc} */
  217.     @Override
  218.     public PositionAngleType getPositionAngleType() {
  219.         // Irrelevant: set a default value
  220.         return PositionAngleType.MEAN;
  221.     }

  222.     /**
  223.      * Get the gradient converter related to the analytical orbit propagator.
  224.      * @return the gradient converter
  225.      */
  226.     public abstract AbstractAnalyticalGradientConverter getGradientConverter();

  227. }