LeastSquaresConverter.java
- /* Copyright 2002-2025 CS GROUP
- * Licensed to CS GROUP (CS) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * CS licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- package org.orekit.propagation.conversion.osc2mean;
- import java.util.function.UnaryOperator;
- import org.hipparchus.CalculusFieldElement;
- import org.hipparchus.Field;
- import org.hipparchus.analysis.differentiation.Gradient;
- import org.hipparchus.analysis.differentiation.GradientField;
- import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
- import org.hipparchus.geometry.euclidean.threed.Vector3D;
- import org.hipparchus.linear.DiagonalMatrix;
- import org.hipparchus.linear.FieldVector;
- import org.hipparchus.linear.MatrixUtils;
- import org.hipparchus.linear.RealMatrix;
- import org.hipparchus.linear.RealVector;
- import org.hipparchus.optim.ConvergenceChecker;
- import org.hipparchus.optim.SimpleVectorValueChecker;
- import org.hipparchus.optim.nonlinear.vector.leastsquares.LeastSquaresBuilder;
- import org.hipparchus.optim.nonlinear.vector.leastsquares.LeastSquaresFactory;
- import org.hipparchus.optim.nonlinear.vector.leastsquares.LeastSquaresOptimizer;
- import org.hipparchus.optim.nonlinear.vector.leastsquares.LeastSquaresProblem;
- import org.hipparchus.optim.nonlinear.vector.leastsquares.MultivariateJacobianFunction;
- import org.hipparchus.util.Pair;
- import org.orekit.orbits.CartesianOrbit;
- import org.orekit.orbits.FieldCartesianOrbit;
- import org.orekit.orbits.FieldKeplerianOrbit;
- import org.orekit.orbits.FieldOrbit;
- import org.orekit.orbits.Orbit;
- import org.orekit.time.FieldAbsoluteDate;
- import org.orekit.utils.FieldPVCoordinates;
- import org.orekit.utils.PVCoordinates;
- /**
- * Class enabling conversion from osculating to mean orbit
- * for a given theory using a least-squares algorithm.
- *
- * @author Pascal Parraud
- * @since 13.0
- */
- public class LeastSquaresConverter implements OsculatingToMeanConverter {
- /** Default convergence threshold. */
- public static final double DEFAULT_THRESHOLD = 1e-4;
- /** Default maximum number of iterations. */
- public static final int DEFAULT_MAX_ITERATIONS = 1000;
- /** Mean theory used. */
- private MeanTheory theory;
- /** Convergence threshold. */
- private double threshold;
- /** Maximum number of iterations. */
- private int maxIterations;
- /** Optimizer used. */
- private LeastSquaresOptimizer optimizer;
- /** Convergence checker for optimization algorithm. */
- private ConvergenceChecker<LeastSquaresProblem.Evaluation> checker;
- /** RMS. */
- private double rms;
- /** Number of iterations performed. */
- private int iterationsNb;
- /**
- * Default constructor.
- * <p>
- * The mean theory and the optimizer must be set before converting.
- */
- public LeastSquaresConverter() {
- this(null, null, DEFAULT_THRESHOLD, DEFAULT_MAX_ITERATIONS);
- }
- /**
- * Constructor.
- * <p>
- * The optimizer must be set before converting.
- *
- * @param theory mean theory to be used
- */
- public LeastSquaresConverter(final MeanTheory theory) {
- this(theory, null, DEFAULT_THRESHOLD, DEFAULT_MAX_ITERATIONS);
- }
- /**
- * Constructor.
- * @param theory mean theory to be used
- * @param optimizer optimizer to be used
- */
- public LeastSquaresConverter(final MeanTheory theory,
- final LeastSquaresOptimizer optimizer) {
- this(theory, optimizer, DEFAULT_THRESHOLD, DEFAULT_MAX_ITERATIONS);
- }
- /**
- * Constructor.
- * <p>
- * The mean theory and the optimizer must be set before converting.
- *
- * @param threshold convergence threshold
- * @param maxIterations maximum number of iterations
- */
- public LeastSquaresConverter(final double threshold,
- final int maxIterations) {
- this(null, null, threshold, maxIterations);
- }
- /**
- * Constructor.
- * @param theory mean theory to be used
- * @param optimizer optimizer to be used
- * @param threshold convergence threshold
- * @param maxIterations maximum number of iterations
- */
- public LeastSquaresConverter(final MeanTheory theory,
- final LeastSquaresOptimizer optimizer,
- final double threshold,
- final int maxIterations) {
- setMeanTheory(theory);
- setOptimizer(optimizer);
- setThreshold(threshold);
- setMaxIterations(maxIterations);
- }
- /** {@inheritDoc} */
- @Override
- public MeanTheory getMeanTheory() {
- return theory;
- }
- /** {@inheritDoc} */
- @Override
- public void setMeanTheory(final MeanTheory meanTheory) {
- this.theory = meanTheory;
- }
- /**
- * Gets the optimizer.
- * @return the optimizer
- */
- public LeastSquaresOptimizer getOptimizer() {
- return optimizer;
- }
- /**
- * Sets the optimizer.
- * @param optimizer the optimizer
- */
- public void setOptimizer(final LeastSquaresOptimizer optimizer) {
- this.optimizer = optimizer;
- }
- /**
- * Gets the convergence threshold.
- * @return convergence threshold
- */
- public double getThreshold() {
- return threshold;
- }
- /**
- * Sets the convergence threshold.
- * @param threshold convergence threshold
- */
- public void setThreshold(final double threshold) {
- this.threshold = threshold;
- final SimpleVectorValueChecker svvc = new SimpleVectorValueChecker(-1.0, threshold);
- this.checker = LeastSquaresFactory.evaluationChecker(svvc);
- }
- /**
- * Gets the maximum number of iterations.
- * @return maximum number of iterations
- */
- public int getMaxIterations() {
- return maxIterations;
- }
- /**
- * Sets maximum number of iterations.
- * @param maxIterations maximum number of iterations
- */
- public void setMaxIterations(final int maxIterations) {
- this.maxIterations = maxIterations;
- }
- /**
- * Gets the RMS for the last conversion.
- * @return the RMS
- */
- public double getRMS() {
- return rms;
- }
- /**
- * Gets the number of iterations performed by the last conversion.
- * @return number of iterations
- */
- public int getIterationsNb() {
- return iterationsNb;
- }
- /** {@inheritDoc}
- * Uses a least-square algorithm.
- */
- @Override
- public Orbit convertToMean(final Orbit osculating) {
- // Initialize conversion
- final Orbit initialized = theory.preprocessing(osculating);
- // State vector
- final RealVector stateVector = MatrixUtils.createRealVector(6);
- // Position/Velocity
- final Vector3D position = initialized.getPVCoordinates().getPosition();
- final Vector3D velocity = initialized.getPVCoordinates().getVelocity();
- // Fill state vector
- stateVector.setEntry(0, position.getX());
- stateVector.setEntry(1, position.getY());
- stateVector.setEntry(2, position.getZ());
- stateVector.setEntry(3, velocity.getX());
- stateVector.setEntry(4, velocity.getY());
- stateVector.setEntry(5, velocity.getZ());
- // Create the initial guess of the least squares problem
- final RealVector startState = MatrixUtils.createRealVector(6);
- startState.setSubVector(0, stateVector.getSubVector(0, 6));
- // Weights
- final double[] weights = new double[6];
- final double velocityWeight = initialized.getPVCoordinates().getVelocity().getNorm() *
- initialized.getPVCoordinates().getPosition().getNormSq() / initialized.getMu();
- for (int i = 0; i < 3; i++) {
- weights[i] = 1.0;
- weights[i + 3] = velocityWeight;
- }
- // Constructs the least squares problem
- final LeastSquaresProblem problem = new LeastSquaresBuilder().
- maxIterations(maxIterations).
- maxEvaluations(Integer.MAX_VALUE).
- checker(checker).
- model(new ModelFunction(initialized)).
- weight(new DiagonalMatrix(weights)).
- target(stateVector).
- start(startState).
- build();
- // Solve least squares
- final LeastSquaresOptimizer.Optimum optimum = optimizer.optimize(problem);
- // Stores some results
- rms = optimum.getRMS();
- iterationsNb = optimum.getIterations();
- // Builds the estimated mean orbit
- final Vector3D pEstimated = new Vector3D(optimum.getPoint().getSubVector(0, 3).toArray());
- final Vector3D vEstimated = new Vector3D(optimum.getPoint().getSubVector(3, 3).toArray());
- final Orbit mean = new CartesianOrbit(new PVCoordinates(pEstimated, vEstimated),
- initialized.getFrame(), initialized.getDate(),
- initialized.getMu());
- // Returns the mean orbit
- return theory.postprocessing(osculating, mean);
- }
- /** {@inheritDoc}
- * Uses a least-square algorithm.
- */
- @Override
- public <T extends CalculusFieldElement<T>> FieldOrbit<T> convertToMean(final FieldOrbit<T> osculating) {
- throw new UnsupportedOperationException();
- }
- /** Model function for the least squares problem.
- * Provides the Jacobian of the function computing position/velocity at the point.
- */
- private class ModelFunction implements MultivariateJacobianFunction {
- /** Osculating orbit as Cartesian. */
- private final FieldCartesianOrbit<Gradient> fieldOsc;
- /**
- * Constructor.
- * @param osculating osculating orbit
- */
- ModelFunction(final Orbit osculating) {
- // Conversion to field orbit
- final Field<Gradient> field = GradientField.getField(6);
- this.fieldOsc = new FieldCartesianOrbit<>(field, osculating);
- }
- /** {@inheritDoc} */
- @Override
- public Pair<RealVector, RealMatrix> value(final RealVector point) {
- final RealVector objectiveOscState = MatrixUtils.createRealVector(6);
- final RealMatrix objectiveJacobian = MatrixUtils.createRealMatrix(6, 6);
- getTransformedAndJacobian(state -> mean2Osc(state), point,
- objectiveOscState, objectiveJacobian);
- return new Pair<>(objectiveOscState, objectiveJacobian);
- }
- /**
- * Fill model.
- * @param operator state vector propagation
- * @param state state vector
- * @param transformed value to fill
- * @param jacobian Jacobian to fill
- */
- private void getTransformedAndJacobian(final UnaryOperator<FieldVector<Gradient>> operator,
- final RealVector state, final RealVector transformed,
- final RealMatrix jacobian) {
- // State dimension
- final int stateDim = state.getDimension();
- // Initialise the state as field to calculate the gradient
- final GradientField field = GradientField.getField(stateDim);
- final FieldVector<Gradient> fieldState = MatrixUtils.createFieldVector(field, stateDim);
- for (int i = 0; i < stateDim; ++i) {
- fieldState.setEntry(i, Gradient.variable(stateDim, i, state.getEntry(i)));
- }
- // Call operator
- final FieldVector<Gradient> fieldTransformed = operator.apply(fieldState);
- // Output dimension
- final int outDim = fieldTransformed.getDimension();
- // Extract transform and Jacobian as real values
- for (int i = 0; i < outDim; ++i) {
- transformed.setEntry(i, fieldTransformed.getEntry(i).getReal());
- jacobian.setRow(i, fieldTransformed.getEntry(i).getGradient());
- }
- }
- /**
- * Operator to compute an osculating state from a mean state.
- * @param mean mean state vector
- * @return osculating state vector
- */
- private FieldVector<Gradient> mean2Osc(final FieldVector<Gradient> mean) {
- // Epoch
- final FieldAbsoluteDate<Gradient> epoch = fieldOsc.getDate();
- // Field
- final Field<Gradient> field = epoch.getField();
- // Extract mean state
- final FieldVector3D<Gradient> pos = new FieldVector3D<>(mean.getSubVector(0, 3).toArray());
- final FieldVector3D<Gradient> vel = new FieldVector3D<>(mean.getSubVector(3, 3).toArray());
- final FieldPVCoordinates<Gradient> pvMean = new FieldPVCoordinates<>(pos, vel);
- final FieldKeplerianOrbit<Gradient> oMean = new FieldKeplerianOrbit<>(pvMean,
- fieldOsc.getFrame(),
- fieldOsc.getDate(),
- fieldOsc.getMu());
- // Propagate to epoch
- final FieldOrbit<Gradient> oOsc = theory.meanToOsculating(oMean);
- final FieldPVCoordinates<Gradient> pvOsc = oOsc.getPVCoordinates(oMean.getFrame());
- // Osculating
- final FieldVector<Gradient> osculating = MatrixUtils.createFieldVector(field, 6);
- osculating.setEntry(0, pvOsc.getPosition().getX());
- osculating.setEntry(1, pvOsc.getPosition().getY());
- osculating.setEntry(2, pvOsc.getPosition().getZ());
- osculating.setEntry(3, pvOsc.getVelocity().getX());
- osculating.setEntry(4, pvOsc.getVelocity().getY());
- osculating.setEntry(5, pvOsc.getVelocity().getZ());
- // Return
- return osculating;
- }
- }
- }