ToleranceProvider.java

  1. /* Copyright 2022-2025 Romain Serra
  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;

  18. import org.hipparchus.CalculusFieldElement;
  19. import org.hipparchus.geometry.euclidean.threed.Vector3D;
  20. import org.hipparchus.util.FastMath;
  21. import org.orekit.errors.OrekitException;
  22. import org.orekit.errors.OrekitMessages;
  23. import org.orekit.orbits.FieldOrbit;
  24. import org.orekit.orbits.Orbit;
  25. import org.orekit.orbits.OrbitType;
  26. import org.orekit.orbits.PositionAngleType;
  27. import org.orekit.propagation.numerical.FieldNumericalPropagator;
  28. import org.orekit.propagation.numerical.NumericalPropagator;
  29. import org.orekit.utils.PVCoordinates;

  30. import java.util.Arrays;

  31. /**
  32.  * Interface to define integration tolerances for adaptive schemes (like the embedded Runge-Kutta ones) propagating
  33.  * the position-velocity vector (or an equivalent set of coordinates) and the mass, for a total of 7 primary dependent variables (in that order).
  34.  * The tolerances are given as an array of array: each row has 7 elements, whilst the first column is the absolute tolerances and the second the relative ones.
  35.  *
  36.  * @see NumericalPropagator
  37.  * @see FieldNumericalPropagator
  38.  * @see CartesianToleranceProvider
  39.  * @since 13.0
  40.  * @author Romain Serra
  41.  */
  42. public interface ToleranceProvider extends CartesianToleranceProvider {

  43.     /**
  44.      * Retrieve the integration tolerances given a reference orbit.
  45.      * @param referenceOrbit orbit
  46.      * @param propagationOrbitType orbit type for propagation (can be different from the input orbit one)
  47.      * @param positionAngleType reference position angle type
  48.      * @return absolute and relative tolerances
  49.      */
  50.     double[][] getTolerances(Orbit referenceOrbit, OrbitType propagationOrbitType,
  51.                              PositionAngleType positionAngleType);

  52.     /**
  53.      * Retrieve the integration tolerances given a reference orbit.
  54.      * @param referenceOrbit orbit
  55.      * @param propagationOrbitType orbit type for propagation (can be different from the input orbit one)
  56.      * @return absolute and relative tolerances
  57.      */
  58.     default double[][] getTolerances(Orbit referenceOrbit, OrbitType propagationOrbitType) {
  59.         return getTolerances(referenceOrbit, propagationOrbitType, NumericalPropagator.DEFAULT_POSITION_ANGLE_TYPE);
  60.     }

  61.     /**
  62.      * Retrieve the integration tolerances given a reference Field orbit.
  63.      * @param referenceOrbit orbit
  64.      * @param propagationOrbitType orbit type for propagation (can be different from the input orbit one)
  65.      * @param positionAngleType reference position angle type
  66.      * @param <T> field type
  67.      * @return absolute and relative tolerances
  68.      */
  69.     default <T extends CalculusFieldElement<T>> double[][] getTolerances(FieldOrbit<T> referenceOrbit,
  70.                                                                          OrbitType propagationOrbitType,
  71.                                                                          PositionAngleType positionAngleType) {
  72.         return getTolerances(referenceOrbit.toOrbit(), propagationOrbitType, positionAngleType);
  73.     }

  74.     /**
  75.      * Retrieve the integration tolerances given a reference Field orbit.
  76.      * @param referenceOrbit orbit
  77.      * @param propagationOrbitType orbit type for propagation (can be different from the input orbit one)
  78.      * @param <T> field type
  79.      * @return absolute and relative tolerances
  80.      */
  81.     default <T extends CalculusFieldElement<T>> double[][] getTolerances(FieldOrbit<T> referenceOrbit,
  82.                                                                          OrbitType propagationOrbitType) {
  83.         return getTolerances(referenceOrbit, propagationOrbitType, NumericalPropagator.DEFAULT_POSITION_ANGLE_TYPE);
  84.     }

  85.     /**
  86.      * Build a provider using a single value for absolute and respective tolerance respectively.
  87.      * @param absoluteTolerance absolute tolerance value to be used
  88.      * @param relativeTolerance relative tolerance value to be used
  89.      * @return tolerance provider
  90.      */
  91.     static ToleranceProvider of(final double absoluteTolerance, final double relativeTolerance) {

  92.         return new ToleranceProvider() {
  93.             @Override
  94.             public double[][] getTolerances(final Orbit referenceOrbit, final OrbitType propagationOrbitType,
  95.                                             final PositionAngleType positionAngleType) {
  96.                 return getTolerances();
  97.             }

  98.             @Override
  99.             public double[][] getTolerances(final Vector3D position, final Vector3D velocity) {
  100.                 return getTolerances();
  101.             }

  102.             /**
  103.              * Retrieve constant absolute and respective tolerances.
  104.              * @return tolerances
  105.              */
  106.             double[][] getTolerances() {
  107.                 final double[] absTol = new double[7];
  108.                 Arrays.fill(absTol, absoluteTolerance);
  109.                 final double[] relTol = new double[absTol.length];
  110.                 Arrays.fill(relTol, relativeTolerance);
  111.                 return new double[][] { absTol, relTol };
  112.             }
  113.         };
  114.     }

  115.     /**
  116.      * Build a provider based on a tolerance provider for Cartesian coordinates.
  117.      * <p> Orbits Jacobian matrices are used to get consistent errors on orbital parameters.
  118.      * <p>
  119.      *
  120.      * @param cartesianToleranceProvider tolerance provider dedicated to Cartesian propagation
  121.      * @return tolerance provider
  122.      */
  123.     static ToleranceProvider of(final CartesianToleranceProvider cartesianToleranceProvider) {
  124.         return new ToleranceProvider() {

  125.             @Override
  126.             public double[][] getTolerances(final Vector3D position, final Vector3D velocity) {
  127.                 return cartesianToleranceProvider.getTolerances(position, velocity);
  128.             }

  129.             @Override
  130.             public double[][] getTolerances(final Orbit referenceOrbit, final OrbitType propagationOrbitType,
  131.                                             final PositionAngleType positionAngleType) {
  132.                 // compute Cartesian-related tolerances
  133.                 final double[][] cartesianTolerances = getTolerances(referenceOrbit.getPosition(),
  134.                         referenceOrbit.getPVCoordinates().getVelocity());
  135.                 if (propagationOrbitType == OrbitType.CARTESIAN) {
  136.                     return cartesianTolerances;
  137.                 }

  138.                 final double[] cartAbsTol = cartesianTolerances[0];
  139.                 final double[] cartRelTol = cartesianTolerances[1];
  140.                 final double[] absTol = new double[7];
  141.                 final double[] relTol = absTol.clone();

  142.                 // convert the orbit to the desired type
  143.                 final double[][] jacobian = new double[6][6];
  144.                 final Orbit converted = propagationOrbitType.convertType(referenceOrbit);
  145.                 converted.getJacobianWrtCartesian(positionAngleType, jacobian);

  146.                 double minimumRel = cartRelTol[6];
  147.                 for (int i = 0; i < jacobian.length; ++i) {
  148.                     final double[] row = jacobian[i];
  149.                     absTol[i] = FastMath.abs(row[0]) * cartAbsTol[0] +
  150.                             FastMath.abs(row[1]) * cartAbsTol[1] +
  151.                             FastMath.abs(row[2]) * cartAbsTol[2] +
  152.                             FastMath.abs(row[3]) * cartAbsTol[3] +
  153.                             FastMath.abs(row[4]) * cartAbsTol[4] +
  154.                             FastMath.abs(row[5]) * cartAbsTol[5];
  155.                     if (Double.isNaN(absTol[i])) {
  156.                         throw new OrekitException(OrekitMessages.SINGULAR_JACOBIAN_FOR_ORBIT_TYPE, propagationOrbitType);
  157.                     }
  158.                     minimumRel = FastMath.min(minimumRel, cartRelTol[i]);
  159.                 }
  160.                 absTol[6] = cartAbsTol[6];

  161.                 Arrays.fill(relTol, 0, 6, minimumRel);
  162.                 relTol[6] = cartRelTol[6];
  163.                 return new double[][] {absTol, relTol};
  164.             }
  165.         };
  166.     }

  167.     /**
  168.      * Defines a default tolerance provider. It is consistent with values from previous versions of Orekit obtained via other APIs.
  169.      *
  170.      * <p>
  171.      * The tolerances are only <em>orders of magnitude</em>, and integrator tolerances
  172.      * are only local estimates, not global ones. So some care must be taken when using
  173.      * these tolerances. Setting 1mm as a position error does NOT mean the tolerances
  174.      * will guarantee a 1mm error position after several orbits integration.
  175.      * </p>
  176.      *
  177.      * @param dP expected position error
  178.      * @return tolerances
  179.      */
  180.     static ToleranceProvider getDefaultToleranceProvider(final double dP) {
  181.         return new ToleranceProvider() {
  182.             @Override
  183.             public double[][] getTolerances(final Orbit referenceOrbit, final OrbitType propagationOrbitType,
  184.                                             final PositionAngleType positionAngleType) {
  185.                 // Cartesian case
  186.                 final double[] relTol = new double[7];
  187.                 final double[] cartAbsTol = new double[7];
  188.                 final double relPos = dP / referenceOrbit.getPosition().getNorm();
  189.                 Arrays.fill(relTol, 0, relTol.length, relPos);
  190.                 Arrays.fill(cartAbsTol, 0, 3, dP);
  191.                 // estimate the scalar velocity error
  192.                 final PVCoordinates pv = referenceOrbit.getPVCoordinates();
  193.                 final double r2 = pv.getPosition().getNormSq();
  194.                 final double v  = pv.getVelocity().getNorm();
  195.                 final double dV = referenceOrbit.getMu() * dP / (v * r2);
  196.                 Arrays.fill(cartAbsTol, 3, 6, dV);
  197.                 cartAbsTol[6] = DEFAULT_ABSOLUTE_MASS_TOLERANCE;

  198.                 if (propagationOrbitType == OrbitType.CARTESIAN) {
  199.                     return new double[][] {cartAbsTol, relTol};
  200.                 }

  201.                 // convert the orbit to the desired type
  202.                 final double[] absTol = cartAbsTol.clone();
  203.                 final double[][] jacobian = new double[6][6];
  204.                 final Orbit converted = propagationOrbitType.convertType(referenceOrbit);
  205.                 converted.getJacobianWrtCartesian(PositionAngleType.TRUE, jacobian);

  206.                 for (int i = 0; i < jacobian.length; ++i) {
  207.                     final double[] row = jacobian[i];
  208.                     absTol[i] = FastMath.abs(row[0]) * cartAbsTol[0] +
  209.                             FastMath.abs(row[1]) * cartAbsTol[1] +
  210.                             FastMath.abs(row[2]) * cartAbsTol[2] +
  211.                             FastMath.abs(row[3]) * cartAbsTol[3] +
  212.                             FastMath.abs(row[4]) * cartAbsTol[4] +
  213.                             FastMath.abs(row[5]) * cartAbsTol[5];
  214.                     if (Double.isNaN(absTol[i])) {
  215.                         throw new OrekitException(OrekitMessages.SINGULAR_JACOBIAN_FOR_ORBIT_TYPE, propagationOrbitType);
  216.                     }
  217.                 }

  218.                 return new double[][] {absTol, relTol};
  219.             }

  220.             @Override
  221.             public double[][] getTolerances(final Vector3D position, final Vector3D velocity) {
  222.                 final double[] absTol = new double[7];
  223.                 final double[] relTol = absTol.clone();
  224.                 final double relPos = dP / position.getNorm();
  225.                 Arrays.fill(relTol, 0, relTol.length, relPos);
  226.                 Arrays.fill(absTol, 0, 3, dP);
  227.                 final double dV = relPos * velocity.getNorm();
  228.                 Arrays.fill(absTol, 3, 6, dV);
  229.                 absTol[6] = DEFAULT_ABSOLUTE_MASS_TOLERANCE;
  230.                 return new double[][] {absTol, relTol};
  231.             }
  232.         };
  233.     }
  234. }