DSSTHarvester.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.semianalytical.dsst;

  18. import java.util.ArrayList;
  19. import java.util.Arrays;
  20. import java.util.IdentityHashMap;
  21. import java.util.List;
  22. import java.util.Map;

  23. import org.hipparchus.analysis.differentiation.Gradient;
  24. import org.hipparchus.linear.MatrixUtils;
  25. import org.hipparchus.linear.RealMatrix;
  26. import org.orekit.orbits.OrbitType;
  27. import org.orekit.orbits.PositionAngleType;
  28. import org.orekit.propagation.AbstractMatricesHarvester;
  29. import org.orekit.propagation.FieldSpacecraftState;
  30. import org.orekit.propagation.PropagationType;
  31. import org.orekit.propagation.SpacecraftState;
  32. import org.orekit.propagation.semianalytical.dsst.forces.DSSTForceModel;
  33. import org.orekit.propagation.semianalytical.dsst.forces.FieldShortPeriodTerms;
  34. import org.orekit.propagation.semianalytical.dsst.utilities.FieldAuxiliaryElements;
  35. import org.orekit.utils.DoubleArrayDictionary;
  36. import org.orekit.utils.ParameterDriver;
  37. import org.orekit.utils.TimeSpanMap;
  38. import org.orekit.utils.TimeSpanMap.Span;

  39. /** Harvester between two-dimensional Jacobian matrices and one-dimensional {@link
  40.  * SpacecraftState#getAdditionalState(String) additional state arrays}.
  41.  * @author Luc Maisonobe
  42.  * @author Bryan Cazabonne
  43.  * @since 11.1
  44.  */
  45. public class DSSTHarvester extends AbstractMatricesHarvester {

  46.     /** Retrograde factor I.
  47.      *  <p>
  48.      *  DSST model needs equinoctial orbit as internal representation.
  49.      *  Classical equinoctial elements have discontinuities when inclination
  50.      *  is close to zero. In this representation, I = +1. <br>
  51.      *  To avoid this discontinuity, another representation exists and equinoctial
  52.      *  elements can be expressed in a different way, called "retrograde" orbit.
  53.      *  This implies I = -1. <br>
  54.      *  As Orekit doesn't implement the retrograde orbit, I is always set to +1.
  55.      *  But for the sake of consistency with the theory, the retrograde factor
  56.      *  has been kept in the formulas.
  57.      *  </p>
  58.      */
  59.     private static final int I = 1;

  60.     /** Propagator bound to this harvester. */
  61.     private final DSSTPropagator propagator;

  62.     /** Derivatives of the short period terms that apply to State Transition Matrix.*/
  63.     private final double[][] shortPeriodDerivativesStm;

  64.     /** Derivatives of the short period terms that apply to Jacobians columns. */
  65.     private final DoubleArrayDictionary shortPeriodDerivativesJacobianColumns;

  66.     /** Columns names for parameters. */
  67.     private List<String> columnsNames;

  68.     /**
  69.      * Field short periodic terms. Key is the force model to which they pertain. Value is
  70.      * the terms. They need to be stored in a map because the DsstForceModel interface
  71.      * does not have a getter for the terms.
  72.      */
  73.     private final Map<DSSTForceModel, List<FieldShortPeriodTerms<Gradient>>>
  74.             fieldShortPeriodTerms;

  75.     /** Simple constructor.
  76.      * <p>
  77.      * The arguments for initial matrices <em>must</em> be compatible with the
  78.      * {@link org.orekit.orbits.OrbitType#EQUINOCTIAL equinoctial orbit type}
  79.      * and {@link PositionAngleType position angle} that will be used by propagator
  80.      * </p>
  81.      * @param propagator propagator bound to this harvester
  82.      * @param stmName State Transition Matrix state name
  83.      * @param initialStm initial State Transition Matrix ∂Y/∂Y₀,
  84.      * if null (which is the most frequent case), assumed to be 6x6 identity
  85.      * @param initialJacobianColumns initial columns of the Jacobians matrix with respect to parameters,
  86.      * if null or if some selected parameters are missing from the dictionary, the corresponding
  87.      * initial column is assumed to be 0
  88.      */
  89.     DSSTHarvester(final DSSTPropagator propagator, final String stmName,
  90.                   final RealMatrix initialStm, final DoubleArrayDictionary initialJacobianColumns) {
  91.         super(stmName, initialStm, initialJacobianColumns);
  92.         this.propagator                            = propagator;
  93.         this.shortPeriodDerivativesStm             = new double[getStateDimension()][getStateDimension()];
  94.         this.shortPeriodDerivativesJacobianColumns = new DoubleArrayDictionary();
  95.         // Use identity hash map to have the same behavior as a getter on the force model
  96.         this.fieldShortPeriodTerms                 = new IdentityHashMap<>();
  97.     }

  98.     /** {@inheritDoc} */
  99.     @Override
  100.     public RealMatrix getStateTransitionMatrix(final SpacecraftState state) {

  101.         final RealMatrix stm = super.getStateTransitionMatrix(state);

  102.         final int stateDimension = getStateDimension();
  103.         if (propagator.getPropagationType() == PropagationType.OSCULATING) {
  104.             // add the short period terms
  105.             for (int i = 0; i < stateDimension; i++) {
  106.                 for (int j = 0; j < stateDimension; j++) {
  107.                     stm.addToEntry(i, j, shortPeriodDerivativesStm[i][j]);
  108.                 }
  109.             }
  110.         }

  111.         return stm;

  112.     }

  113.     /** {@inheritDoc} */
  114.     @Override
  115.     public RealMatrix getParametersJacobian(final SpacecraftState state) {

  116.         final RealMatrix jacobian = super.getParametersJacobian(state);
  117.         if (jacobian != null && propagator.getPropagationType() == PropagationType.OSCULATING) {

  118.             // add the short period terms
  119.             final List<String> names = getJacobiansColumnsNames();
  120.             for (int j = 0; j < names.size(); ++j) {
  121.                 final double[] column = shortPeriodDerivativesJacobianColumns.get(names.get(j));
  122.                 for (int i = 0; i < getStateDimension(); i++) {
  123.                     jacobian.addToEntry(i, j, column[i]);
  124.                 }
  125.             }

  126.         }

  127.         return jacobian;

  128.     }

  129.     /** Get the Jacobian matrix B1 (B1 = ∂εη/∂Y).
  130.      * <p>
  131.      * B1 represents the partial derivatives of the short period motion
  132.      * with respect to the mean equinoctial elements.
  133.      * </p>
  134.      * @return the B1 jacobian matrix
  135.      */
  136.     public RealMatrix getB1() {

  137.         // Initialize B1
  138.         final int stateDimension = getStateDimension();
  139.         final RealMatrix B1 = MatrixUtils.createRealMatrix(stateDimension, stateDimension);

  140.         // add the short period terms
  141.         for (int i = 0; i < stateDimension; i++) {
  142.             for (int j = 0; j < stateDimension; j++) {
  143.                 B1.addToEntry(i, j, shortPeriodDerivativesStm[i][j]);
  144.             }
  145.         }

  146.         // Return B1
  147.         return B1;

  148.     }

  149.     /** Get the Jacobian matrix B2 (B2 = ∂Y/∂Y₀).
  150.      * <p>
  151.      * B2 represents the partial derivatives of the mean equinoctial elements
  152.      * with respect to the initial ones.
  153.      * </p>
  154.      * @param state spacecraft state
  155.      * @return the B2 jacobian matrix
  156.      */
  157.     public RealMatrix getB2(final SpacecraftState state) {
  158.         return super.getStateTransitionMatrix(state);
  159.     }

  160.     /** Get the Jacobian matrix B3 (B3 = ∂Y/∂P).
  161.      * <p>
  162.      * B3 represents the partial derivatives of the mean equinoctial elements
  163.      * with respect to the estimated propagation parameters.
  164.      * </p>
  165.      * @param state spacecraft state
  166.      * @return the B3 jacobian matrix
  167.      */
  168.     public RealMatrix getB3(final SpacecraftState state) {
  169.         return super.getParametersJacobian(state);
  170.     }

  171.     /** Get the Jacobian matrix B4 (B4 = ∂εη/∂c).
  172.      * <p>
  173.      * B4 represents the partial derivatives of the short period motion
  174.      * with respect to the estimated propagation parameters.
  175.      * </p>
  176.      * @return the B4 jacobian matrix
  177.      */
  178.     public RealMatrix getB4() {

  179.         // Initialize B4
  180.         final List<String> names = getJacobiansColumnsNames();
  181.         final RealMatrix B4 = MatrixUtils.createRealMatrix(getStateDimension(), names.size());

  182.         // add the short period terms
  183.         for (int j = 0; j < names.size(); ++j) {
  184.             final double[] column = shortPeriodDerivativesJacobianColumns.get(names.get(j));
  185.             for (int i = 0; i < getStateDimension(); i++) {
  186.                 B4.addToEntry(i, j, column[i]);
  187.             }
  188.         }

  189.         // Return B4
  190.         return B4;

  191.     }

  192.     /** Freeze the names of the Jacobian columns.
  193.      * <p>
  194.      * This method is called when proagation starts, i.e. when configuration is completed
  195.      * </p>
  196.      */
  197.     public void freezeColumnsNames() {
  198.         columnsNames = getJacobiansColumnsNames();
  199.     }

  200.     /** {@inheritDoc} */
  201.     @Override
  202.     public List<String> getJacobiansColumnsNames() {
  203.         return columnsNames == null ? propagator.getJacobiansColumnsNames() : columnsNames;
  204.     }

  205.     /** Initialize the short periodic terms for the "field" elements.
  206.      * @param reference current mean spacecraft state
  207.      */
  208.     public void initializeFieldShortPeriodTerms(final SpacecraftState reference) {
  209.         initializeFieldShortPeriodTerms(reference, propagator.getPropagationType());
  210.     }

  211.     /**
  212.      * Initialize the short periodic terms for the "field" elements.
  213.      *
  214.      * @param reference current mean spacecraft state
  215.      * @param type      MEAN or OSCULATING
  216.      */
  217.     public void initializeFieldShortPeriodTerms(final SpacecraftState reference,
  218.                                                 final PropagationType type) {

  219.         // Converter
  220.         final DSSTGradientConverter converter = new DSSTGradientConverter(reference, propagator.getAttitudeProvider());

  221.         // clear old values
  222.         // prevents duplicates or stale values when reusing a DSSTPropagator
  223.         fieldShortPeriodTerms.clear();

  224.         // Loop on force models
  225.         for (final DSSTForceModel forceModel : propagator.getAllForceModels()) {

  226.             // Convert to Gradient
  227.             final FieldSpacecraftState<Gradient> dsState = converter.getState(forceModel);
  228.             final Gradient[] dsParameters = converter.getParametersAtStateDate(dsState, forceModel);
  229.             final FieldAuxiliaryElements<Gradient> auxiliaryElements = new FieldAuxiliaryElements<>(dsState.getOrbit(), I);

  230.             // Initialize the "Field" short periodic terms, same mode as the propagator
  231.             final List<FieldShortPeriodTerms<Gradient>> terms =
  232.                     forceModel.initializeShortPeriodTerms(
  233.                             auxiliaryElements,
  234.                             type,
  235.                             dsParameters);
  236.             // create a copy of the list to protect against inadvertent modification
  237.             final List<FieldShortPeriodTerms<Gradient>> list;
  238.             synchronized (fieldShortPeriodTerms) {
  239.                 list = fieldShortPeriodTerms.computeIfAbsent(forceModel, x -> new ArrayList<>());
  240.             }
  241.             list.addAll(terms);

  242.         }

  243.     }

  244.     /** Update the short periodic terms for the "field" elements.
  245.      * @param reference current mean spacecraft state
  246.      */
  247.     @SuppressWarnings("unchecked")
  248.     public void updateFieldShortPeriodTerms(final SpacecraftState reference) {

  249.         // Converter
  250.         final DSSTGradientConverter converter = new DSSTGradientConverter(reference, propagator.getAttitudeProvider());

  251.         // Loop on force models
  252.         for (final DSSTForceModel forceModel : propagator.getAllForceModels()) {

  253.             // Convert to Gradient
  254.             final FieldSpacecraftState<Gradient> dsState = converter.getState(forceModel);
  255.             final Gradient[] dsParameters = converter.getParameters(dsState, forceModel);

  256.             // Update the short periodic terms for the current force model
  257.             forceModel.updateShortPeriodTerms(dsParameters, dsState);

  258.         }

  259.     }

  260.     /** {@inheritDoc} */
  261.     @Override
  262.     public void setReferenceState(final SpacecraftState reference) {

  263.         // reset derivatives to zero
  264.         for (final double[] row : shortPeriodDerivativesStm) {
  265.             Arrays.fill(row, 0.0);
  266.         }

  267.         shortPeriodDerivativesJacobianColumns.clear();

  268.         final DSSTGradientConverter converter = new DSSTGradientConverter(reference, propagator.getAttitudeProvider());

  269.         // Compute Jacobian
  270.         for (final DSSTForceModel forceModel : propagator.getAllForceModels()) {

  271.             final FieldSpacecraftState<Gradient> dsState = converter.getState(forceModel);
  272.             final Gradient zero = dsState.getDate().getField().getZero();
  273.             final Gradient[] shortPeriod = new Gradient[6];
  274.             Arrays.fill(shortPeriod, zero);
  275.             final List<FieldShortPeriodTerms<Gradient>> terms;
  276.             synchronized (fieldShortPeriodTerms) {
  277.                 terms = fieldShortPeriodTerms.computeIfAbsent(forceModel, x -> new ArrayList<>(0));
  278.             }
  279.             for (final FieldShortPeriodTerms<Gradient> spt : terms) {
  280.                 final Gradient[] spVariation = spt.value(dsState.getOrbit());
  281.                 for (int i = 0; i < spVariation .length; i++) {
  282.                     shortPeriod[i] = shortPeriod[i].add(spVariation[i]);
  283.                 }
  284.             }

  285.             final double[] derivativesASP  = shortPeriod[0].getGradient();
  286.             final double[] derivativesExSP = shortPeriod[1].getGradient();
  287.             final double[] derivativesEySP = shortPeriod[2].getGradient();
  288.             final double[] derivativesHxSP = shortPeriod[3].getGradient();
  289.             final double[] derivativesHySP = shortPeriod[4].getGradient();
  290.             final double[] derivativesLSP  = shortPeriod[5].getGradient();

  291.             // update Jacobian with respect to state
  292.             addToRow(derivativesASP,  0);
  293.             addToRow(derivativesExSP, 1);
  294.             addToRow(derivativesEySP, 2);
  295.             addToRow(derivativesHxSP, 3);
  296.             addToRow(derivativesHySP, 4);
  297.             addToRow(derivativesLSP,  5);

  298.             int paramsIndex = converter.getFreeStateParameters();
  299.             for (ParameterDriver driver : forceModel.getParametersDrivers()) {
  300.                 if (driver.isSelected()) {

  301.                     final TimeSpanMap<String> driverNameSpanMap = driver.getNamesSpanMap();
  302.                     // for each span (for each estimated value) corresponding name is added

  303.                     for (Span<String> span = driverNameSpanMap.getFirstSpan(); span != null; span = span.next()) {
  304.                         // get the partials derivatives for this driver
  305.                         DoubleArrayDictionary.Entry entry = shortPeriodDerivativesJacobianColumns.getEntry(span.getData());
  306.                         if (entry == null) {
  307.                             // create an entry filled with zeroes
  308.                             shortPeriodDerivativesJacobianColumns.put(span.getData(), new double[getStateDimension()]);
  309.                             entry = shortPeriodDerivativesJacobianColumns.getEntry(span.getData());
  310.                         }

  311.                         // add the contribution of the current force model
  312.                         entry.increment(new double[] {
  313.                             derivativesASP[paramsIndex], derivativesExSP[paramsIndex], derivativesEySP[paramsIndex],
  314.                             derivativesHxSP[paramsIndex], derivativesHySP[paramsIndex], derivativesLSP[paramsIndex]
  315.                         });
  316.                         ++paramsIndex;
  317.                     }
  318.                 }
  319.             }
  320.         }

  321.     }

  322.     /** Fill State Transition Matrix rows.
  323.      * @param derivatives derivatives of a component
  324.      * @param index component index (0 for a, 1 for ex, 2 for ey, 3 for hx, 4 for hy, 5 for l)
  325.      */
  326.     private void addToRow(final double[] derivatives, final int index) {
  327.         for (int i = 0; i < 6; i++) {
  328.             shortPeriodDerivativesStm[index][i] += derivatives[i];
  329.         }
  330.     }

  331.     /** {@inheritDoc} */
  332.     @Override
  333.     public OrbitType getOrbitType() {
  334.         return propagator.getOrbitType();
  335.     }

  336.     /** {@inheritDoc} */
  337.     @Override
  338.     public PositionAngleType getPositionAngleType() {
  339.         return propagator.getPositionAngleType();
  340.     }

  341. }