LeastSquaresConverter.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.conversion.osc2mean;

  18. import java.util.function.UnaryOperator;

  19. import org.hipparchus.CalculusFieldElement;
  20. import org.hipparchus.Field;
  21. import org.hipparchus.analysis.differentiation.Gradient;
  22. import org.hipparchus.analysis.differentiation.GradientField;
  23. import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
  24. import org.hipparchus.geometry.euclidean.threed.Vector3D;
  25. import org.hipparchus.linear.DiagonalMatrix;
  26. import org.hipparchus.linear.FieldVector;
  27. import org.hipparchus.linear.MatrixUtils;
  28. import org.hipparchus.linear.RealMatrix;
  29. import org.hipparchus.linear.RealVector;
  30. import org.hipparchus.optim.ConvergenceChecker;
  31. import org.hipparchus.optim.SimpleVectorValueChecker;
  32. import org.hipparchus.optim.nonlinear.vector.leastsquares.LeastSquaresBuilder;
  33. import org.hipparchus.optim.nonlinear.vector.leastsquares.LeastSquaresFactory;
  34. import org.hipparchus.optim.nonlinear.vector.leastsquares.LeastSquaresOptimizer;
  35. import org.hipparchus.optim.nonlinear.vector.leastsquares.LeastSquaresProblem;
  36. import org.hipparchus.optim.nonlinear.vector.leastsquares.MultivariateJacobianFunction;
  37. import org.hipparchus.util.Pair;
  38. import org.orekit.orbits.CartesianOrbit;
  39. import org.orekit.orbits.FieldCartesianOrbit;
  40. import org.orekit.orbits.FieldKeplerianOrbit;
  41. import org.orekit.orbits.FieldOrbit;
  42. import org.orekit.orbits.Orbit;
  43. import org.orekit.time.FieldAbsoluteDate;
  44. import org.orekit.utils.FieldPVCoordinates;
  45. import org.orekit.utils.PVCoordinates;

  46. /**
  47.  * Class enabling conversion from osculating to mean orbit
  48.  * for a given theory using a least-squares algorithm.
  49.  *
  50.  * @author Pascal Parraud
  51.  * @since 13.0
  52.  */
  53. public class LeastSquaresConverter implements OsculatingToMeanConverter {

  54.     /** Default convergence threshold. */
  55.     public static final double DEFAULT_THRESHOLD   = 1e-4;

  56.     /** Default maximum number of iterations. */
  57.     public static final int DEFAULT_MAX_ITERATIONS = 1000;

  58.     /** Mean theory used. */
  59.     private MeanTheory theory;

  60.     /** Convergence threshold. */
  61.     private double threshold;

  62.     /** Maximum number of iterations. */
  63.     private int maxIterations;

  64.     /** Optimizer used. */
  65.     private LeastSquaresOptimizer optimizer;

  66.     /** Convergence checker for optimization algorithm. */
  67.     private ConvergenceChecker<LeastSquaresProblem.Evaluation> checker;

  68.     /** RMS. */
  69.     private double rms;

  70.     /** Number of iterations performed. */
  71.     private int iterationsNb;

  72.     /**
  73.      * Default constructor.
  74.      * <p>
  75.      * The mean theory and the optimizer must be set before converting.
  76.      */
  77.     public LeastSquaresConverter() {
  78.         this(null, null, DEFAULT_THRESHOLD, DEFAULT_MAX_ITERATIONS);
  79.     }

  80.     /**
  81.      * Constructor.
  82.      * <p>
  83.      * The optimizer must be set before converting.
  84.      *
  85.      * @param theory mean theory to be used
  86.      */
  87.     public LeastSquaresConverter(final MeanTheory theory) {
  88.         this(theory, null, DEFAULT_THRESHOLD, DEFAULT_MAX_ITERATIONS);
  89.     }

  90.     /**
  91.      * Constructor.
  92.      * @param theory mean theory to be used
  93.      * @param optimizer optimizer to be used
  94.      */
  95.     public LeastSquaresConverter(final MeanTheory theory,
  96.                                  final LeastSquaresOptimizer optimizer) {
  97.         this(theory, optimizer, DEFAULT_THRESHOLD, DEFAULT_MAX_ITERATIONS);
  98.     }

  99.     /**
  100.      * Constructor.
  101.      * <p>
  102.      * The mean theory and the optimizer must be set before converting.
  103.      *
  104.      * @param threshold     convergence threshold
  105.      * @param maxIterations maximum number of iterations
  106.      */
  107.     public LeastSquaresConverter(final double threshold,
  108.                                  final int maxIterations) {
  109.         this(null, null, threshold, maxIterations);
  110.     }

  111.     /**
  112.      * Constructor.
  113.      * @param theory        mean theory to be used
  114.      * @param optimizer     optimizer to be used
  115.      * @param threshold     convergence threshold
  116.      * @param maxIterations maximum number of iterations
  117.      */
  118.     public LeastSquaresConverter(final MeanTheory theory,
  119.                                  final LeastSquaresOptimizer optimizer,
  120.                                  final double threshold,
  121.                                  final int maxIterations) {
  122.         setMeanTheory(theory);
  123.         setOptimizer(optimizer);
  124.         setThreshold(threshold);
  125.         setMaxIterations(maxIterations);
  126.     }

  127.     /** {@inheritDoc} */
  128.     @Override
  129.     public MeanTheory getMeanTheory() {
  130.         return theory;
  131.     }

  132.     /** {@inheritDoc} */
  133.     @Override
  134.     public void setMeanTheory(final MeanTheory meanTheory) {
  135.         this.theory = meanTheory;
  136.     }

  137.     /**
  138.      * Gets the optimizer.
  139.      * @return the optimizer
  140.      */
  141.     public LeastSquaresOptimizer getOptimizer() {
  142.         return optimizer;
  143.     }

  144.     /**
  145.      * Sets the optimizer.
  146.      * @param optimizer the optimizer
  147.      */
  148.     public void setOptimizer(final LeastSquaresOptimizer optimizer) {
  149.         this.optimizer = optimizer;
  150.     }

  151.     /**
  152.      * Gets the convergence threshold.
  153.      * @return convergence threshold
  154.      */
  155.     public double getThreshold() {
  156.         return threshold;
  157.     }

  158.     /**
  159.      * Sets the convergence threshold.
  160.      * @param threshold convergence threshold
  161.      */
  162.     public void setThreshold(final double threshold) {
  163.         this.threshold = threshold;
  164.         final SimpleVectorValueChecker svvc = new SimpleVectorValueChecker(-1.0, threshold);
  165.         this.checker = LeastSquaresFactory.evaluationChecker(svvc);
  166.     }

  167.     /**
  168.      * Gets the maximum number of iterations.
  169.      * @return maximum number of iterations
  170.      */
  171.     public int getMaxIterations() {
  172.         return maxIterations;
  173.     }

  174.     /**
  175.      * Sets maximum number of iterations.
  176.      * @param maxIterations maximum number of iterations
  177.      */
  178.     public void setMaxIterations(final int maxIterations) {
  179.         this.maxIterations = maxIterations;
  180.     }

  181.     /**
  182.      * Gets the RMS for the last conversion.
  183.      * @return the RMS
  184.      */
  185.     public double getRMS() {
  186.         return rms;
  187.     }

  188.     /**
  189.      * Gets the number of iterations performed by the last conversion.
  190.      * @return number of iterations
  191.      */
  192.     public int getIterationsNb() {
  193.         return iterationsNb;
  194.     }

  195.     /** {@inheritDoc}
  196.      *  Uses a least-square algorithm.
  197.      */
  198.     @Override
  199.     public Orbit convertToMean(final Orbit osculating) {

  200.         // Initialize conversion
  201.         final Orbit initialized = theory.preprocessing(osculating);

  202.         // State vector
  203.         final RealVector stateVector = MatrixUtils.createRealVector(6);

  204.         // Position/Velocity
  205.         final Vector3D position = initialized.getPVCoordinates().getPosition();
  206.         final Vector3D velocity = initialized.getPVCoordinates().getVelocity();

  207.         // Fill state vector
  208.         stateVector.setEntry(0, position.getX());
  209.         stateVector.setEntry(1, position.getY());
  210.         stateVector.setEntry(2, position.getZ());
  211.         stateVector.setEntry(3, velocity.getX());
  212.         stateVector.setEntry(4, velocity.getY());
  213.         stateVector.setEntry(5, velocity.getZ());

  214.         // Create the initial guess of the least squares problem
  215.         final RealVector startState = MatrixUtils.createRealVector(6);
  216.         startState.setSubVector(0, stateVector.getSubVector(0, 6));

  217.         // Weights
  218.         final double[] weights = new double[6];
  219.         final double velocityWeight = initialized.getPVCoordinates().getVelocity().getNorm() *
  220.                                       initialized.getPVCoordinates().getPosition().getNormSq() / initialized.getMu();
  221.         for (int i = 0; i < 3; i++) {
  222.             weights[i] = 1.0;
  223.             weights[i + 3] = velocityWeight;
  224.         }

  225.         // Constructs the least squares problem
  226.         final LeastSquaresProblem problem = new LeastSquaresBuilder().
  227.                                             maxIterations(maxIterations).
  228.                                             maxEvaluations(Integer.MAX_VALUE).
  229.                                             checker(checker).
  230.                                             model(new ModelFunction(initialized)).
  231.                                             weight(new DiagonalMatrix(weights)).
  232.                                             target(stateVector).
  233.                                             start(startState).
  234.                                             build();

  235.         // Solve least squares
  236.         final LeastSquaresOptimizer.Optimum optimum = optimizer.optimize(problem);

  237.         // Stores some results
  238.         rms = optimum.getRMS();
  239.         iterationsNb = optimum.getIterations();

  240.         // Builds the estimated mean orbit
  241.         final Vector3D pEstimated = new Vector3D(optimum.getPoint().getSubVector(0, 3).toArray());
  242.         final Vector3D vEstimated = new Vector3D(optimum.getPoint().getSubVector(3, 3).toArray());
  243.         final Orbit mean = new CartesianOrbit(new PVCoordinates(pEstimated, vEstimated),
  244.                                               initialized.getFrame(), initialized.getDate(),
  245.                                               initialized.getMu());

  246.         // Returns the mean orbit
  247.         return theory.postprocessing(osculating, mean);
  248.     }

  249.     /** {@inheritDoc}
  250.      *  Uses a least-square algorithm.
  251.      */
  252.     @Override
  253.     public <T extends CalculusFieldElement<T>> FieldOrbit<T> convertToMean(final FieldOrbit<T> osculating) {
  254.         throw new UnsupportedOperationException();
  255.     }

  256.     /** Model function for the least squares problem.
  257.      * Provides the Jacobian of the function computing position/velocity at the point.
  258.      */
  259.     private class ModelFunction implements MultivariateJacobianFunction {

  260.         /** Osculating orbit as Cartesian. */
  261.         private final FieldCartesianOrbit<Gradient> fieldOsc;

  262.         /**
  263.          * Constructor.
  264.          * @param osculating osculating orbit
  265.          */
  266.         ModelFunction(final Orbit osculating) {
  267.             // Conversion to field orbit
  268.             final Field<Gradient> field = GradientField.getField(6);
  269.             this.fieldOsc = new FieldCartesianOrbit<>(field, osculating);
  270.         }

  271.         /**  {@inheritDoc} */
  272.         @Override
  273.         public Pair<RealVector, RealMatrix> value(final RealVector point) {
  274.             final RealVector objectiveOscState = MatrixUtils.createRealVector(6);
  275.             final RealMatrix objectiveJacobian = MatrixUtils.createRealMatrix(6, 6);
  276.             getTransformedAndJacobian(state -> mean2Osc(state), point,
  277.                                       objectiveOscState, objectiveJacobian);
  278.             return new Pair<>(objectiveOscState, objectiveJacobian);
  279.         }

  280.         /**
  281.          * Fill model.
  282.          * @param operator state vector propagation
  283.          * @param state state vector
  284.          * @param transformed value to fill
  285.          * @param jacobian Jacobian to fill
  286.          */
  287.         private void getTransformedAndJacobian(final UnaryOperator<FieldVector<Gradient>> operator,
  288.                                                final RealVector state, final RealVector transformed,
  289.                                                final RealMatrix jacobian) {

  290.             // State dimension
  291.             final int stateDim = state.getDimension();

  292.             // Initialise the state as field to calculate the gradient
  293.             final GradientField field = GradientField.getField(stateDim);
  294.             final FieldVector<Gradient> fieldState = MatrixUtils.createFieldVector(field, stateDim);
  295.             for (int i = 0; i < stateDim; ++i) {
  296.                 fieldState.setEntry(i, Gradient.variable(stateDim, i, state.getEntry(i)));
  297.             }

  298.             // Call operator
  299.             final FieldVector<Gradient> fieldTransformed = operator.apply(fieldState);

  300.             // Output dimension
  301.             final int outDim = fieldTransformed.getDimension();

  302.             // Extract transform and Jacobian as real values
  303.             for (int i = 0; i < outDim; ++i) {
  304.                 transformed.setEntry(i, fieldTransformed.getEntry(i).getReal());
  305.                 jacobian.setRow(i, fieldTransformed.getEntry(i).getGradient());
  306.             }

  307.         }

  308.         /**
  309.          * Operator to compute an osculating state from a mean state.
  310.          * @param mean mean state vector
  311.          * @return osculating state vector
  312.          */
  313.         private FieldVector<Gradient> mean2Osc(final FieldVector<Gradient> mean) {
  314.             // Epoch
  315.             final FieldAbsoluteDate<Gradient> epoch = fieldOsc.getDate();

  316.             // Field
  317.             final Field<Gradient> field = epoch.getField();

  318.             // Extract mean state
  319.             final FieldVector3D<Gradient> pos = new FieldVector3D<>(mean.getSubVector(0, 3).toArray());
  320.             final FieldVector3D<Gradient> vel = new FieldVector3D<>(mean.getSubVector(3, 3).toArray());
  321.             final FieldPVCoordinates<Gradient> pvMean = new FieldPVCoordinates<>(pos, vel);
  322.             final FieldKeplerianOrbit<Gradient> oMean = new FieldKeplerianOrbit<>(pvMean,
  323.                                                                                   fieldOsc.getFrame(),
  324.                                                                                   fieldOsc.getDate(),
  325.                                                                                   fieldOsc.getMu());

  326.             // Propagate to epoch
  327.             final FieldOrbit<Gradient> oOsc = theory.meanToOsculating(oMean);
  328.             final FieldPVCoordinates<Gradient> pvOsc = oOsc.getPVCoordinates(oMean.getFrame());

  329.             // Osculating
  330.             final FieldVector<Gradient> osculating = MatrixUtils.createFieldVector(field, 6);
  331.             osculating.setEntry(0, pvOsc.getPosition().getX());
  332.             osculating.setEntry(1, pvOsc.getPosition().getY());
  333.             osculating.setEntry(2, pvOsc.getPosition().getZ());
  334.             osculating.setEntry(3, pvOsc.getVelocity().getX());
  335.             osculating.setEntry(4, pvOsc.getVelocity().getY());
  336.             osculating.setEntry(5, pvOsc.getVelocity().getZ());

  337.             // Return
  338.             return osculating;

  339.         }

  340.     }

  341. }