DSSTHarvester.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.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[STATE_DIMENSION][STATE_DIMENSION];
  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.         if (propagator.getPropagationType() == PropagationType.OSCULATING) {
  103.             // add the short period terms
  104.             for (int i = 0; i < STATE_DIMENSION; i++) {
  105.                 for (int j = 0; j < STATE_DIMENSION; j++) {
  106.                     stm.addToEntry(i, j, shortPeriodDerivativesStm[i][j]);
  107.                 }
  108.             }
  109.         }

  110.         return stm;

  111.     }

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

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

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

  125.         }

  126.         return jacobian;

  127.     }

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

  136.         // Initialize B1
  137.         final RealMatrix B1 = MatrixUtils.createRealMatrix(STATE_DIMENSION, STATE_DIMENSION);

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

  144.         // Return B1
  145.         return B1;

  146.     }

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

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

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

  177.         // Initialize B4
  178.         final List<String> names = getJacobiansColumnsNames();
  179.         final RealMatrix B4 = MatrixUtils.createRealMatrix(STATE_DIMENSION, names.size());

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

  187.         // Return B4
  188.         return B4;

  189.     }

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

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

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

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

  217.         // Converter
  218.         final DSSTGradientConverter converter = new DSSTGradientConverter(reference, propagator.getAttitudeProvider());

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

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

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

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

  237.         }

  238.     }

  239.     /** Update the short periodic terms for the "field" elements.
  240.      * @param reference current mean spacecraft state
  241.      */
  242.     @SuppressWarnings("unchecked")
  243.     public void updateFieldShortPeriodTerms(final SpacecraftState reference) {

  244.         // Converter
  245.         final DSSTGradientConverter converter = new DSSTGradientConverter(reference, propagator.getAttitudeProvider());

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

  248.             // Convert to Gradient
  249.             final FieldSpacecraftState<Gradient> dsState = converter.getState(forceModel);
  250.             final Gradient[] dsParameters = converter.getParameters(dsState, forceModel);

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

  253.         }

  254.     }

  255.     /** {@inheritDoc} */
  256.     @Override
  257.     public void setReferenceState(final SpacecraftState reference) {

  258.         // reset derivatives to zero
  259.         for (final double[] row : shortPeriodDerivativesStm) {
  260.             Arrays.fill(row, 0.0);
  261.         }

  262.         shortPeriodDerivativesJacobianColumns.clear();

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

  264.         // Compute Jacobian
  265.         for (final DSSTForceModel forceModel : propagator.getAllForceModels()) {

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

  278.             final double[] derivativesASP  = shortPeriod[0].getGradient();
  279.             final double[] derivativesExSP = shortPeriod[1].getGradient();
  280.             final double[] derivativesEySP = shortPeriod[2].getGradient();
  281.             final double[] derivativesHxSP = shortPeriod[3].getGradient();
  282.             final double[] derivativesHySP = shortPeriod[4].getGradient();
  283.             final double[] derivativesLSP  = shortPeriod[5].getGradient();

  284.             // update Jacobian with respect to state
  285.             addToRow(derivativesASP,  0);
  286.             addToRow(derivativesExSP, 1);
  287.             addToRow(derivativesEySP, 2);
  288.             addToRow(derivativesHxSP, 3);
  289.             addToRow(derivativesHySP, 4);
  290.             addToRow(derivativesLSP,  5);

  291.             int paramsIndex = converter.getFreeStateParameters();
  292.             for (ParameterDriver driver : forceModel.getParametersDrivers()) {
  293.                 if (driver.isSelected()) {

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

  296.                     for (Span<String> span = driverNameSpanMap.getFirstSpan(); span != null; span = span.next()) {
  297.                         // get the partials derivatives for this driver
  298.                         DoubleArrayDictionary.Entry entry = shortPeriodDerivativesJacobianColumns.getEntry(span.getData());
  299.                         if (entry == null) {
  300.                             // create an entry filled with zeroes
  301.                             shortPeriodDerivativesJacobianColumns.put(span.getData(), new double[STATE_DIMENSION]);
  302.                             entry = shortPeriodDerivativesJacobianColumns.getEntry(span.getData());
  303.                         }

  304.                         // add the contribution of the current force model
  305.                         entry.increment(new double[] {
  306.                             derivativesASP[paramsIndex], derivativesExSP[paramsIndex], derivativesEySP[paramsIndex],
  307.                             derivativesHxSP[paramsIndex], derivativesHySP[paramsIndex], derivativesLSP[paramsIndex]
  308.                         });
  309.                         ++paramsIndex;
  310.                     }
  311.                 }
  312.             }
  313.         }

  314.     }

  315.     /** Fill State Transition Matrix rows.
  316.      * @param derivatives derivatives of a component
  317.      * @param index component index (0 for a, 1 for ex, 2 for ey, 3 for hx, 4 for hy, 5 for l)
  318.      */
  319.     private void addToRow(final double[] derivatives, final int index) {
  320.         for (int i = 0; i < 6; i++) {
  321.             shortPeriodDerivativesStm[index][i] += derivatives[i];
  322.         }
  323.     }

  324.     /** {@inheritDoc} */
  325.     @Override
  326.     public OrbitType getOrbitType() {
  327.         return propagator.getOrbitType();
  328.     }

  329.     /** {@inheritDoc} */
  330.     @Override
  331.     public PositionAngleType getPositionAngleType() {
  332.         return propagator.getPositionAngleType();
  333.     }

  334. }