AbstractBatchLSModel.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.estimation.leastsquares;

  18. import java.util.ArrayList;
  19. import java.util.Arrays;
  20. import java.util.Collections;
  21. import java.util.HashMap;
  22. import java.util.IdentityHashMap;
  23. import java.util.List;
  24. import java.util.Map;

  25. import org.hipparchus.linear.Array2DRowRealMatrix;
  26. import org.hipparchus.linear.ArrayRealVector;
  27. import org.hipparchus.linear.MatrixUtils;
  28. import org.hipparchus.linear.RealMatrix;
  29. import org.hipparchus.linear.RealVector;
  30. import org.hipparchus.optim.nonlinear.vector.leastsquares.MultivariateJacobianFunction;
  31. import org.hipparchus.util.FastMath;
  32. import org.hipparchus.util.Incrementor;
  33. import org.hipparchus.util.Pair;
  34. import org.orekit.estimation.measurements.EstimatedMeasurement;
  35. import org.orekit.estimation.measurements.ObservedMeasurement;
  36. import org.orekit.orbits.Orbit;
  37. import org.orekit.propagation.MatricesHarvester;
  38. import org.orekit.propagation.Propagator;
  39. import org.orekit.propagation.PropagatorsParallelizer;
  40. import org.orekit.propagation.SpacecraftState;
  41. import org.orekit.propagation.conversion.OrbitDeterminationPropagatorBuilder;
  42. import org.orekit.propagation.integration.AbstractJacobiansMapper;
  43. import org.orekit.propagation.sampling.MultiSatStepHandler;
  44. import org.orekit.time.AbsoluteDate;
  45. import org.orekit.time.ChronologicalComparator;
  46. import org.orekit.utils.ParameterDriver;
  47. import org.orekit.utils.ParameterDriversList;
  48. import org.orekit.utils.ParameterDriversList.DelegatingDriver;

  49. /** Bridge between {@link ObservedMeasurement measurements} and {@link
  50.  * org.hipparchus.optim.nonlinear.vector.leastsquares.LeastSquaresProblem
  51.  * least squares problems}.
  52.  * @author Luc Maisonobe
  53.  * @author Bryan Cazabonne
  54.  * @author Thomas Paulet
  55.  * @since 11.0
  56.  */
  57. public abstract class AbstractBatchLSModel implements MultivariateJacobianFunction {

  58.     /** Builders for propagators. */
  59.     private final OrbitDeterminationPropagatorBuilder[] builders;

  60.     /** Array of each builder's selected orbit drivers.
  61.      * @since 11.1
  62.      */
  63.     private final ParameterDriversList[] estimatedOrbitalParameters;

  64.     /** Array of each builder's selected propagation drivers. */
  65.     private final ParameterDriversList[] estimatedPropagationParameters;

  66.     /** Estimated measurements parameters. */
  67.     private final ParameterDriversList estimatedMeasurementsParameters;

  68.     /** Measurements. */
  69.     private final List<ObservedMeasurement<?>> measurements;

  70.     /** Start columns for each estimated orbit. */
  71.     private final int[] orbitsStartColumns;

  72.     /** End columns for each estimated orbit. */
  73.     private final int[] orbitsEndColumns;

  74.     /** Map for propagation parameters columns. */
  75.     private final Map<String, Integer> propagationParameterColumns;

  76.     /** Map for measurements parameters columns. */
  77.     private final Map<String, Integer> measurementParameterColumns;

  78.     /** Last evaluations. */
  79.     private final Map<ObservedMeasurement<?>, EstimatedMeasurement<?>> evaluations;

  80.     /** Observer to be notified at orbit changes. */
  81.     private final ModelObserver observer;

  82.     /** Counter for the evaluations. */
  83.     private Incrementor evaluationsCounter;

  84.     /** Counter for the iterations. */
  85.     private Incrementor iterationsCounter;

  86.     /** Date of the first enabled measurement. */
  87.     private AbsoluteDate firstDate;

  88.     /** Date of the last enabled measurement. */
  89.     private AbsoluteDate lastDate;

  90.     /** Boolean indicating if the propagation will go forward or backward. */
  91.     private final boolean forwardPropagation;

  92.     /** Model function value. */
  93.     private RealVector value;

  94.     /** Harvesters for extracting State Transition Matrices and Jacobians from integrated states.
  95.      * @since 11.1
  96.      */
  97.     private final MatricesHarvester[] harvesters;

  98.     /** Model function Jacobian. */
  99.     private RealMatrix jacobian;

  100.     /**
  101.      * Constructor.
  102.      * @param propagatorBuilders builders to use for propagation
  103.      * @param measurements measurements
  104.      * @param estimatedMeasurementsParameters estimated measurements parameters
  105.      * @param harvesters harvesters for matrices (ignored since 11.1)
  106.      * @param observer observer to be notified at model calls
  107.      * @deprecated as of 11.1, replaced by [@link #AbstractBatchLSModel(OrbitDeterminationPropagatorBuilder[],
  108.      * List, ParameterDriversList, ModelObserver)}
  109.      */
  110.     @Deprecated
  111.     public AbstractBatchLSModel(final OrbitDeterminationPropagatorBuilder[] propagatorBuilders,
  112.                                 final List<ObservedMeasurement<?>> measurements,
  113.                                 final ParameterDriversList estimatedMeasurementsParameters,
  114.                                 final MatricesHarvester[] harvesters,
  115.                                 final ModelObserver observer) {
  116.         this(propagatorBuilders, measurements, estimatedMeasurementsParameters, observer);
  117.     }

  118.     /**
  119.      * Constructor.
  120.      * @param propagatorBuilders builders to use for propagation
  121.      * @param measurements measurements
  122.      * @param estimatedMeasurementsParameters estimated measurements parameters
  123.      * @param observer observer to be notified at model calls
  124.      */
  125.     public AbstractBatchLSModel(final OrbitDeterminationPropagatorBuilder[] propagatorBuilders,
  126.                                 final List<ObservedMeasurement<?>> measurements,
  127.                                 final ParameterDriversList estimatedMeasurementsParameters,
  128.                                 final ModelObserver observer) {

  129.         this.builders                        = propagatorBuilders.clone();
  130.         this.measurements                    = measurements;
  131.         this.estimatedMeasurementsParameters = estimatedMeasurementsParameters;
  132.         this.measurementParameterColumns     = new HashMap<>(estimatedMeasurementsParameters.getDrivers().size());
  133.         this.estimatedOrbitalParameters      = new ParameterDriversList[builders.length];
  134.         this.estimatedPropagationParameters  = new ParameterDriversList[builders.length];
  135.         this.evaluations                     = new IdentityHashMap<>(measurements.size());
  136.         this.observer                        = observer;
  137.         this.harvesters                      = new MatricesHarvester[builders.length];

  138.         // allocate vector and matrix
  139.         int rows = 0;
  140.         for (final ObservedMeasurement<?> measurement : measurements) {
  141.             rows += measurement.getDimension();
  142.         }

  143.         this.orbitsStartColumns = new int[builders.length];
  144.         this.orbitsEndColumns   = new int[builders.length];
  145.         int columns = 0;
  146.         for (int i = 0; i < builders.length; ++i) {
  147.             this.orbitsStartColumns[i] = columns;
  148.             for (final ParameterDriver driver : builders[i].getOrbitalParametersDrivers().getDrivers()) {
  149.                 if (driver.isSelected()) {
  150.                     ++columns;
  151.                 }
  152.             }
  153.             this.orbitsEndColumns[i] = columns;
  154.         }

  155.         // Gather all the propagation drivers names in a list
  156.         final List<String> estimatedPropagationParametersNames = new ArrayList<>();
  157.         for (int i = 0; i < builders.length; ++i) {
  158.             // The index i in array estimatedPropagationParameters (attribute of the class) is populated
  159.             // when the first call to getSelectedPropagationDriversForBuilder(i) is made
  160.             for (final DelegatingDriver delegating : getSelectedPropagationDriversForBuilder(i).getDrivers()) {
  161.                 final String driverName = delegating.getName();
  162.                 // Add the driver name if it has not been added yet
  163.                 if (!estimatedPropagationParametersNames.contains(driverName)) {
  164.                     estimatedPropagationParametersNames.add(driverName);
  165.                 }
  166.             }
  167.         }
  168.         // Populate the map of propagation drivers' columns and update the total number of columns
  169.         propagationParameterColumns = new HashMap<>(estimatedPropagationParametersNames.size());
  170.         for (final String driverName : estimatedPropagationParametersNames) {
  171.             propagationParameterColumns.put(driverName, columns);
  172.             ++columns;
  173.         }

  174.         // Populate the map of measurement drivers' columns and update the total number of columns
  175.         for (final ParameterDriver parameter : estimatedMeasurementsParameters.getDrivers()) {
  176.             measurementParameterColumns.put(parameter.getName(), columns);
  177.             ++columns;
  178.         }

  179.         // Initialize point and value
  180.         value    = new ArrayRealVector(rows);
  181.         jacobian = MatrixUtils.createRealMatrix(rows, columns);

  182.         // Decide whether the propagation will be done forward or backward.
  183.         // Minimize the duration between first measurement treated and orbit determination date
  184.         // Propagator builder number 0 holds the reference date for orbit determination
  185.         final AbsoluteDate refDate = builders[0].getInitialOrbitDate();

  186.         // Sort the measurement list chronologically
  187.         measurements.sort(new ChronologicalComparator());
  188.         firstDate = measurements.get(0).getDate();
  189.         lastDate  = measurements.get(measurements.size() - 1).getDate();

  190.         // Decide the direction of propagation
  191.         if (FastMath.abs(refDate.durationFrom(firstDate)) <= FastMath.abs(refDate.durationFrom(lastDate))) {
  192.             // Propagate forward from firstDate
  193.             forwardPropagation = true;
  194.         } else {
  195.             // Propagate backward from lastDate
  196.             forwardPropagation = false;
  197.         }
  198.     }

  199.     /** Set the counter for evaluations.
  200.      * @param evaluationsCounter counter for evaluations
  201.      */
  202.     public void setEvaluationsCounter(final Incrementor evaluationsCounter) {
  203.         this.evaluationsCounter = evaluationsCounter;
  204.     }

  205.     /** Set the counter for iterations.
  206.      * @param iterationsCounter counter for iterations
  207.      */
  208.     public void setIterationsCounter(final Incrementor iterationsCounter) {
  209.         this.iterationsCounter = iterationsCounter;
  210.     }

  211.     /** Return the forward propagation flag.
  212.      * @return the forward propagation flag
  213.      */
  214.     public boolean isForwardPropagation() {
  215.         return forwardPropagation;
  216.     }

  217.     /** Configure the propagator to compute derivatives.
  218.      * @param propagator {@link Propagator} to configure
  219.      * @return harvester harvester to retrive the State Transition Matrix and Jacobian Matrix
  220.      */
  221.     protected MatricesHarvester configureHarvester(final Propagator propagator) {
  222.         // FIXME: this default implementation is only intended for version 11.1 to delegate to a deprecated method
  223.         // it should be removed in 12.0 when configureDerivatives is removed
  224.         return configureDerivatives(propagator);
  225.     }

  226.     /** Configure the propagator to compute derivatives.
  227.      * @param propagators {@link Propagator} to configure
  228.      * @return mapper for this propagator
  229.      * @deprecated as of 11.1, replaced by {@link #configureHarvester(Propagator)}
  230.      */
  231.     @Deprecated
  232.     protected abstract AbstractJacobiansMapper configureDerivatives(Propagator propagators);

  233.     /** Configure the current estimated orbits.
  234.      * <p>
  235.      * For DSST orbit determination, short period derivatives are also calculated.
  236.      * </p>
  237.      * @param harvester harvester for matrices
  238.      * @param propagator the orbit propagator
  239.      * @return the current estimated orbits
  240.      */
  241.     protected abstract Orbit configureOrbits(MatricesHarvester harvester, Propagator propagator);

  242.     /** {@inheritDoc} */
  243.     @Override
  244.     public Pair<RealVector, RealMatrix> value(final RealVector point) {

  245.         // Set up the propagators parallelizer
  246.         final Propagator[] propagators = createPropagators(point);
  247.         final Orbit[] orbits = new Orbit[propagators.length];
  248.         for (int i = 0; i < propagators.length; ++i) {
  249.             harvesters[i] = configureHarvester(propagators[i]);
  250.             orbits[i]     = configureOrbits(harvesters[i], propagators[i]);
  251.         }
  252.         final PropagatorsParallelizer parallelizer =
  253.                         new PropagatorsParallelizer(Arrays.asList(propagators), configureMeasurements(point));

  254.         // Reset value and Jacobian
  255.         evaluations.clear();
  256.         value.set(0.0);
  257.         for (int i = 0; i < jacobian.getRowDimension(); ++i) {
  258.             for (int j = 0; j < jacobian.getColumnDimension(); ++j) {
  259.                 jacobian.setEntry(i, j, 0.0);
  260.             }
  261.         }

  262.         // Run the propagation, gathering residuals on the fly
  263.         if (isForwardPropagation()) {
  264.             // Propagate forward from firstDate
  265.             parallelizer.propagate(firstDate.shiftedBy(-1.0), lastDate.shiftedBy(+1.0));
  266.         } else {
  267.             // Propagate backward from lastDate
  268.             parallelizer.propagate(lastDate.shiftedBy(+1.0), firstDate.shiftedBy(-1.0));
  269.         }

  270.         observer.modelCalled(orbits, evaluations);

  271.         return new Pair<RealVector, RealMatrix>(value, jacobian);

  272.     }

  273.     /** Get the selected orbital drivers for a propagatorBuilder.
  274.      * @param iBuilder index of the builder in the builders' array
  275.      * @return the list of selected orbital drivers for propagatorBuilder of index iBuilder
  276.      * @since 11.1
  277.      */
  278.     public ParameterDriversList getSelectedOrbitalParametersDriversForBuilder(final int iBuilder) {

  279.         // Lazy evaluation, create the list only if it hasn't been created yet
  280.         if (estimatedOrbitalParameters[iBuilder] == null) {

  281.             // Gather the drivers
  282.             final ParameterDriversList selectedOrbitalDrivers = new ParameterDriversList();
  283.             for (final DelegatingDriver delegating : builders[iBuilder].getOrbitalParametersDrivers().getDrivers()) {
  284.                 if (delegating.isSelected()) {
  285.                     for (final ParameterDriver driver : delegating.getRawDrivers()) {
  286.                         selectedOrbitalDrivers.add(driver);
  287.                     }
  288.                 }
  289.             }

  290.             // Add the list of selected orbital parameters drivers to the array
  291.             estimatedOrbitalParameters[iBuilder] = selectedOrbitalDrivers;
  292.         }
  293.         return estimatedOrbitalParameters[iBuilder];
  294.     }

  295.     /** Get the selected propagation drivers for a propagatorBuilder.
  296.      * @param iBuilder index of the builder in the builders' array
  297.      * @return the list of selected propagation drivers for propagatorBuilder of index iBuilder
  298.      */
  299.     public ParameterDriversList getSelectedPropagationDriversForBuilder(final int iBuilder) {

  300.         // Lazy evaluation, create the list only if it hasn't been created yet
  301.         if (estimatedPropagationParameters[iBuilder] == null) {

  302.             // Gather the drivers
  303.             final ParameterDriversList selectedPropagationDrivers = new ParameterDriversList();
  304.             for (final DelegatingDriver delegating : builders[iBuilder].getPropagationParametersDrivers().getDrivers()) {
  305.                 if (delegating.isSelected()) {
  306.                     for (final ParameterDriver driver : delegating.getRawDrivers()) {
  307.                         selectedPropagationDrivers.add(driver);
  308.                     }
  309.                 }
  310.             }

  311.             // List of propagation drivers are sorted in the BatchLSEstimator class.
  312.             // Hence we need to sort this list so the parameters' indexes match
  313.             selectedPropagationDrivers.sort();

  314.             // Add the list of selected propagation drivers to the array
  315.             estimatedPropagationParameters[iBuilder] = selectedPropagationDrivers;
  316.         }
  317.         return estimatedPropagationParameters[iBuilder];
  318.     }

  319.     /** Create the propagators and parameters corresponding to an evaluation point.
  320.      * @param point evaluation point
  321.      * @return an array of new propagators
  322.      */
  323.     public Propagator[] createPropagators(final RealVector point) {

  324.         final Propagator[] propagators = new Propagator[builders.length];

  325.         // Set up the propagators
  326.         for (int i = 0; i < builders.length; ++i) {

  327.             // Get the number of selected orbital drivers in the builder
  328.             final int nbOrb    = orbitsEndColumns[i] - orbitsStartColumns[i];

  329.             // Get the list of selected propagation drivers in the builder and its size
  330.             final ParameterDriversList selectedPropagationDrivers = getSelectedPropagationDriversForBuilder(i);
  331.             final int nbParams = selectedPropagationDrivers.getNbParams();

  332.             // Init the array of normalized parameters for the builder
  333.             final double[] propagatorArray = new double[nbOrb + nbParams];

  334.             // Add the orbital drivers normalized values
  335.             for (int j = 0; j < nbOrb; ++j) {
  336.                 propagatorArray[j] = point.getEntry(orbitsStartColumns[i] + j);
  337.             }

  338.             // Add the propagation drivers normalized values
  339.             for (int j = 0; j < nbParams; ++j) {
  340.                 propagatorArray[nbOrb + j] =
  341.                                 point.getEntry(propagationParameterColumns.get(selectedPropagationDrivers.getDrivers().get(j).getName()));
  342.             }

  343.             // Build the propagator
  344.             propagators[i] = builders[i].buildPropagator(propagatorArray);
  345.         }

  346.         return propagators;

  347.     }

  348.     /** Fetch a measurement that was evaluated during propagation.
  349.      * @param index index of the measurement first component
  350.      * @param evaluation measurement evaluation
  351.      */
  352.     public void fetchEvaluatedMeasurement(final int index, final EstimatedMeasurement<?> evaluation) {

  353.         // States and observed measurement
  354.         final SpacecraftState[]      evaluationStates    = evaluation.getStates();
  355.         final ObservedMeasurement<?> observedMeasurement = evaluation.getObservedMeasurement();

  356.         // compute weighted residuals
  357.         evaluations.put(observedMeasurement, evaluation);
  358.         if (evaluation.getStatus() == EstimatedMeasurement.Status.REJECTED) {
  359.             return;
  360.         }

  361.         final double[] evaluated = evaluation.getEstimatedValue();
  362.         final double[] observed  = observedMeasurement.getObservedValue();
  363.         final double[] sigma     = observedMeasurement.getTheoreticalStandardDeviation();
  364.         final double[] weight    = evaluation.getObservedMeasurement().getBaseWeight();
  365.         for (int i = 0; i < evaluated.length; ++i) {
  366.             value.setEntry(index + i, weight[i] * (evaluated[i] - observed[i]) / sigma[i]);
  367.         }

  368.         for (int k = 0; k < evaluationStates.length; ++k) {

  369.             final int p = observedMeasurement.getSatellites().get(k).getPropagatorIndex();

  370.             // partial derivatives of the current Cartesian coordinates with respect to current orbital state
  371.             final double[][] aCY = new double[6][6];
  372.             final Orbit currentOrbit = evaluationStates[k].getOrbit();
  373.             currentOrbit.getJacobianWrtParameters(builders[p].getPositionAngle(), aCY);
  374.             final RealMatrix dCdY = new Array2DRowRealMatrix(aCY, false);

  375.             // Jacobian of the measurement with respect to current orbital state
  376.             final RealMatrix dMdC = new Array2DRowRealMatrix(evaluation.getStateDerivatives(k), false);
  377.             final RealMatrix dMdY = dMdC.multiply(dCdY);

  378.             // Jacobian of the measurement with respect to initial orbital state
  379.             final ParameterDriversList selectedOrbitalDrivers = getSelectedOrbitalParametersDriversForBuilder(p);
  380.             final int nbOrbParams = selectedOrbitalDrivers.getNbParams();
  381.             if (nbOrbParams > 0) {
  382.                 final RealMatrix dYdY0 = harvesters[p].getStateTransitionMatrix(evaluationStates[k]);
  383.                 final RealMatrix dMdY0 = dMdY.multiply(dYdY0);
  384.                 for (int i = 0; i < dMdY0.getRowDimension(); ++i) {
  385.                     int jOrb = orbitsStartColumns[p];
  386.                     for (int j = 0; j < dMdY0.getColumnDimension(); ++j) {
  387.                         final ParameterDriver driver = selectedOrbitalDrivers.getDrivers().get(j);
  388.                         jacobian.setEntry(index + i, jOrb++,
  389.                                           weight[i] * dMdY0.getEntry(i, j) / sigma[i] * driver.getScale());
  390.                     }
  391.                 }
  392.             }

  393.             // Jacobian of the measurement with respect to propagation parameters
  394.             final ParameterDriversList selectedPropagationDrivers = getSelectedPropagationDriversForBuilder(p);
  395.             final int nbParams = selectedPropagationDrivers.getNbParams();
  396.             if (nbParams > 0) {
  397.                 final RealMatrix dYdPp = harvesters[p].getParametersJacobian(evaluationStates[k]);
  398.                 final RealMatrix dMdPp = dMdY.multiply(dYdPp);
  399.                 for (int i = 0; i < dMdPp.getRowDimension(); ++i) {
  400.                     for (int j = 0; j < nbParams; ++j) {
  401.                         final ParameterDriver delegating = selectedPropagationDrivers.getDrivers().get(j);
  402.                         jacobian.addToEntry(index + i, propagationParameterColumns.get(delegating.getName()),
  403.                                             weight[i] * dMdPp.getEntry(i, j) / sigma[i] * delegating.getScale());
  404.                     }
  405.                 }
  406.             }
  407.         }
  408.         // Jacobian of the measurement with respect to measurements parameters
  409.         for (final ParameterDriver driver : observedMeasurement.getParametersDrivers()) {
  410.             if (driver.isSelected()) {
  411.                 final double[] aMPm = evaluation.getParameterDerivatives(driver);
  412.                 for (int i = 0; i < aMPm.length; ++i) {
  413.                     jacobian.setEntry(index + i, measurementParameterColumns.get(driver.getName()),
  414.                                       weight[i] * aMPm[i] / sigma[i] * driver.getScale());
  415.                 }
  416.             }
  417.         }

  418.     }

  419.     /** Configure the multi-satellites handler to handle measurements.
  420.      * @param point evaluation point
  421.      * @return multi-satellites handler to handle measurements
  422.      */
  423.     private MultiSatStepHandler configureMeasurements(final RealVector point) {

  424.         // Set up the measurement parameters
  425.         int index = orbitsEndColumns[builders.length - 1] + propagationParameterColumns.size();
  426.         for (final ParameterDriver parameter : estimatedMeasurementsParameters.getDrivers()) {
  427.             parameter.setNormalizedValue(point.getEntry(index++));
  428.         }

  429.         // Set up measurements handler
  430.         final List<PreCompensation> precompensated = new ArrayList<>();
  431.         for (final ObservedMeasurement<?> measurement : measurements) {
  432.             if (measurement.isEnabled()) {
  433.                 precompensated.add(new PreCompensation(measurement, evaluations.get(measurement)));
  434.             }
  435.         }
  436.         precompensated.sort(new ChronologicalComparator());

  437.         // Assign first and last date
  438.         firstDate = precompensated.get(0).getDate();
  439.         lastDate  = precompensated.get(precompensated.size() - 1).getDate();

  440.         // Reverse the list in case of backward propagation
  441.         if (!forwardPropagation) {
  442.             Collections.reverse(precompensated);
  443.         }

  444.         return new MeasurementHandler(this, precompensated);

  445.     }

  446.     /** Get the iterations count.
  447.      * @return iterations count
  448.      */
  449.     public int getIterationsCount() {
  450.         return iterationsCounter.getCount();
  451.     }

  452.     /** Get the evaluations count.
  453.      * @return evaluations count
  454.      */
  455.     public int getEvaluationsCount() {
  456.         return evaluationsCounter.getCount();
  457.     }

  458. }