CartesianToleranceProvider.java

/* Copyright 2022-2024 Romain Serra
 * 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;

import org.hipparchus.CalculusFieldElement;
import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
import org.hipparchus.geometry.euclidean.threed.Vector3D;
import org.hipparchus.util.FastMath;
import org.orekit.orbits.CartesianOrbit;
import org.orekit.orbits.FieldCartesianOrbit;
import org.orekit.propagation.numerical.FieldNumericalPropagator;
import org.orekit.propagation.numerical.NumericalPropagator;
import org.orekit.utils.AbsolutePVCoordinates;
import org.orekit.utils.FieldAbsolutePVCoordinates;

import java.util.Arrays;


/**
 * Interface to define integration tolerances for adaptive schemes (like the embedded Runge-Kutta ones) propagating
 * the position-velocity vector and the mass, for a total of 7 primary dependent variables (in that order).
 * 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.
 *
 * @see NumericalPropagator
 * @see FieldNumericalPropagator
 * @see CartesianToleranceProvider
 * @since 13.0
 * @author Romain Serra
 */
public interface CartesianToleranceProvider {

    /** Default absolute tolerance for mass integration. */
    double DEFAULT_ABSOLUTE_MASS_TOLERANCE = 1e-6;

    /**
     * Retrieve the integration tolerances given reference position and velocity vectors.
     * @param position reference position vector
     * @param velocity reference velocity vector
     * @return absolute and relative tolerances
     */
    double[][] getTolerances(Vector3D position, Vector3D velocity);

    /**
     * Retrieve the integration tolerances given reference position and velocity Field vectors.
     * @param position reference position vector
     * @param velocity reference velocity vector
     * @param <T> field type
     * @return absolute and relative tolerances
     */
    default <T extends CalculusFieldElement<T>> double[][] getTolerances(FieldVector3D<T> position,
                                                                         FieldVector3D<T> velocity) {
        return getTolerances(position.toVector3D(), velocity.toVector3D());
    }

    /**
     * Retrieve the integration tolerances given a reference Cartesian orbit.
     * @param cartesianOrbit reference orbit
     * @return absolute and relative tolerances
     */
    default double[][] getTolerances(CartesianOrbit cartesianOrbit) {
        return getTolerances(cartesianOrbit.getPosition(), cartesianOrbit.getPVCoordinates().getVelocity());
    }

    /**
     * Retrieve the integration tolerances given a reference Cartesian orbit.
     * @param cartesianOrbit reference orbit
     * @param <T> field type
     * @return absolute and relative tolerances
     */
    default <T extends CalculusFieldElement<T>> double[][] getTolerances(FieldCartesianOrbit<T> cartesianOrbit) {
        return getTolerances(cartesianOrbit.toOrbit());
    }

    /**
     * Retrieve the integration tolerances given a reference absolute position-velocity vector.
     * @param absolutePVCoordinates reference position-velocity
     * @return absolute and relative tolerances
     */
    default double[][] getTolerances(AbsolutePVCoordinates absolutePVCoordinates) {
        return getTolerances(absolutePVCoordinates.getPosition(), absolutePVCoordinates.getPVCoordinates().getVelocity());
    }

    /**
     * Retrieve the integration tolerances given a reference absolute position-velocity vector.
     * @param absolutePVCoordinates reference position-velocity
     * @param <T> field type
     * @return absolute and relative tolerances
     */
    default <T extends CalculusFieldElement<T>> double[][] getTolerances(FieldAbsolutePVCoordinates<T> absolutePVCoordinates) {
        return getTolerances(absolutePVCoordinates.toAbsolutePVCoordinates());
    }

    /**
     * Build a provider based on expected errors for position, velocity and mass respectively.
     *
     * <p>
     * The tolerances are only <em>orders of magnitude</em>, and integrator tolerances
     * are only local estimates, not global ones. So some care must be taken when using
     * these tolerances. Setting 1mm as a position error does NOT mean the tolerances
     * will guarantee a 1mm error position after several orbits integration.
     * </p>
     *
     * @param dP expected position error
     * @param dV expected velocity error
     * @param dM expected mass error
     * @return tolerance provider
     */
    static CartesianToleranceProvider of(final double dP, final double dV, final double dM) {
        return (position, velocity) -> {
            final double[] absTol = new double[7];
            final double[] relTol = absTol.clone();
            Arrays.fill(absTol, 0, 3, dP);
            Arrays.fill(absTol, 3, 6, dV);
            absTol[6] = dM;
            final double relPos = dP / position.getNorm();
            Arrays.fill(relTol, 0, 3, relPos);
            final double relVel = dV / velocity.getNorm();
            Arrays.fill(relTol, 3, 6, relVel);
            relTol[6] = FastMath.min(relPos, relVel);
            return new double[][] { absTol, relTol };
        };
    }

    /**
     * Build a provider based on expected errors for position only.
     *
     * <p>
     * The tolerances are only <em>orders of magnitude</em>, and integrator tolerances
     * are only local estimates, not global ones. So some care must be taken when using
     * these tolerances. Setting 1mm as a position error does NOT mean the tolerances
     * will guarantee a 1mm error position after several orbits integration.
     * </p>
     *
     * @param dP expected position error
     * @return tolerance provider
     */
    static CartesianToleranceProvider of(final double dP) {
        return (position, velocity) -> {
            final double[] absTol = new double[7];
            final double[] relTol = absTol.clone();
            final double relPos = dP / position.getNorm();
            final double dV = relPos * velocity.getNorm();
            Arrays.fill(absTol, 0, 3, dP);
            Arrays.fill(absTol, 3, 6, dV);
            absTol[6] = DEFAULT_ABSOLUTE_MASS_TOLERANCE;
            Arrays.fill(relTol, 0, 7, relPos);
            return new double[][] { absTol, relTol };
        };
    }
}