Alfano2005.java

/* Copyright 2002-2024 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.ssa.collision.shorttermencounter.probability.twod;

import org.hipparchus.CalculusFieldElement;
import org.hipparchus.special.Erf;
import org.hipparchus.util.FastMath;
import org.orekit.ssa.metrics.FieldProbabilityOfCollision;
import org.orekit.ssa.metrics.ProbabilityOfCollision;

/**
 * Compute the probability of collision using the method described in :"S. Alfano. A numerical implementation of spherical
 * objet collision probability. Journal of Astronautical Sciences, 53(1), January-March 2005."
 * <p>
 * It assumes :
 *     <ul>
 *         <li>Short encounter leading to a linear relative motion.</li>
 *         <li>Spherical collision object.</li>
 *         <li>Uncorrelated positional covariance.</li>
 *         <li>Gaussian distribution of the position uncertainties.</li>
 *         <li>Deterministic velocity i.e. no velocity uncertainties.</li>
 *     </ul>
 * <p>
 * Also, it has been implemented using a simpson integration scheme as explained in his paper.
 *
 * @author Vincent Cucchietti
 * @since 12.0
 */
public class Alfano2005 extends AbstractShortTermEncounter2DPOCMethod {

    /** Empty constructor. */
    public Alfano2005() {
        super(ShortTermEncounter2DPOCMethodType.ALFANO_2005.name());
    }

    /** {@inheritDoc} */
    public ProbabilityOfCollision compute(final double xm, final double ym, final double sigmaX, final double sigmaY,
                                          final double radius) {
        // Computing development order M
        final int developmentOrderM = computeOrderM(xm, ym, sigmaX, sigmaY, radius);

        // Computing x step
        final double xStep = radius / (2 * developmentOrderM);

        // Computing initial x0
        final double x0 = 0.015 * xStep - radius;

        // 1 : m0
        final double m0 = 2. * getRecurrentPart(x0, xm, ym, sigmaX, sigmaY, radius);

        // 2 : mEven
        double mEvenSum = 0.;
        for (int i = 1; i < developmentOrderM; i++) {
            final double x2i = 2. * i * xStep - radius;
            mEvenSum += getRecurrentPart(x2i, xm, ym, sigmaX, sigmaY, radius);
        }

        final double otherTerm = FastMath.exp(-xm * xm / (2 * sigmaX * sigmaX)) * (
                Erf.erf((-ym + radius) / (FastMath.sqrt(2) * sigmaY)) -
                        Erf.erf((-ym - radius) / (FastMath.sqrt(2) * sigmaY)));

        final double mEven = 2. * (mEvenSum + otherTerm);

        // 3 : mOdd
        double mOddSum = 0.;
        for (int i = 1; i <= developmentOrderM; i++) {
            final double x2i_1 = (2. * i - 1.) * xStep - radius;
            mOddSum += getRecurrentPart(x2i_1, xm, ym, sigmaX, sigmaY, radius);
        }

        final double mOdd = 4. * mOddSum;

        // Output
        final double factor = xStep / (3. * sigmaX * FastMath.sqrt(8. * FastMath.PI));

        final double value = factor * (m0 + mEven + mOdd);

        return new ProbabilityOfCollision(value, getName(), isAMaximumProbabilityOfCollisionMethod());
    }

    /** {@inheritDoc} */
    @Override
    public <T extends CalculusFieldElement<T>> FieldProbabilityOfCollision<T> compute(final T xm, final T ym,
                                                                                      final T sigmaX, final T sigmaY,
                                                                                      final T radius) {
        final T zero = xm.getField().getZero();

        // Computing development order M
        final int developmentOrderM = computeOrderM(xm, ym, sigmaX, sigmaY, radius);

        // Computing x step
        final T xStep = radius.multiply(0.5 / developmentOrderM);

        // Computing initial x0
        final T x0 = xStep.multiply(0.015).subtract(radius);

        // 1 : m0
        final T m0 = getRecurrentPart(x0, xm, ym, sigmaX, sigmaY, radius).multiply(2.);

        // 2 : mEven
        T mEvenSum = zero;
        for (int i = 1; i < developmentOrderM; i++) {
            final T x2i = xStep.multiply(2. * i).subtract(radius);
            mEvenSum = mEvenSum.add(getRecurrentPart(x2i, xm, ym, sigmaX, sigmaY, radius));
        }

        final T rootTwoSigmaY = sigmaY.multiply(FastMath.sqrt(2));

        final T otherTerm = xm.square().divide(sigmaX.multiply(sigmaX).multiply(-2.)).exp()
                              .multiply(Erf.erf(radius.subtract(ym).divide(rootTwoSigmaY))
                                           .subtract(Erf.erf(radius.add(ym).negate().divide(rootTwoSigmaY))));

        final T mEven = mEvenSum.add(otherTerm).multiply(2.);

        // 3 : mOdd
        T mOddSum = zero;
        for (int i = 1; i <= developmentOrderM; i++) {
            final T x2i_1 = xStep.multiply(2. * i - 1).subtract(radius);
            mOddSum = mOddSum.add(getRecurrentPart(x2i_1, xm, ym, sigmaX, sigmaY, radius));
        }

        final T mOdd = mOddSum.multiply(4.);

        // Output
        final T factor = xStep.divide(sigmaX.multiply(3 * FastMath.sqrt(8. * FastMath.PI)));

        final T value = factor.multiply(m0.add(mEven).add(mOdd));

        return new FieldProbabilityOfCollision<>(value, getName(), isAMaximumProbabilityOfCollisionMethod());
    }

    /**
     * Get the development order M from inputs.
     *
     * @param xm other collision object projected position onto the collision plane in the rotated encounter frame (m)
     * @param ym other collision object projected position onto the collision plane in the rotated encounter frame (m)
     * @param sigmaX square root of the smallest eigen value of the diagonalized combined covariance matrix projected onto
     * the collision plane
     * @param sigmaY square root of the biggest eigen value of the diagonalized combined covariance matrix projected onto the
     * collision plane
     * @param radius sum of primary and secondary collision object equivalent sphere radii (m)
     *
     * @return development order M
     */
    private int computeOrderM(final double xm, final double ym, final double sigmaX, final double sigmaY,
                              final double radius) {
        final int M = (int) (5 * radius / (FastMath.min(FastMath.min(sigmaX, sigmaY),
                                                        FastMath.min(sigmaX, FastMath.sqrt(xm * xm + ym * ym)))));
        final int LOWER_LIMIT = 10;
        final int UPPER_LIMIT = 100000;

        if (M >= UPPER_LIMIT) {
            return UPPER_LIMIT;
        } else if (M <= LOWER_LIMIT) {
            return LOWER_LIMIT;
        } else {
            return M;
        }
    }

    /**
     * Get the development order M from inputs.
     *
     * @param xm other collision object projected position onto the collision plane in the rotated encounter frame x-axis
     * (m)
     * @param ym other collision object projected position onto the collision plane in the rotated encounter frame y-axis
     * (m)
     * @param sigmaX square root of the smallest eigen value of the diagonalized combined covariance matrix projected onto
     * the collision plane
     * @param sigmaY square root of the biggest eigen value of the diagonalized combined covariance matrix projected onto the
     * collision plane
     * @param radius sum of primary and secondary collision object equivalent sphere radii (m)
     * @param <T> type of the field elements
     *
     * @return development order M
     */
    private <T extends CalculusFieldElement<T>> int computeOrderM(final T xm, final T ym, final T sigmaX, final T sigmaY,
                                                                  final T radius) {

        final double xmR = xm.getReal();
        final double ymR = ym.getReal();
        final int M = (int) (radius.getReal() * 5. / FastMath.min(FastMath.min(sigmaX.getReal(), sigmaY.getReal()),
                                                                  FastMath.min(sigmaX.getReal(),
                                                                               FastMath.sqrt(xmR * xmR + ymR * ymR))));

        final int lowerLimit = 10;
        final int upperLimit = 10000;

        if (M >= upperLimit) {
            return upperLimit;
        } else if (M <= lowerLimit) {
            return lowerLimit;
        } else {
            return M;
        }
    }

    /**
     * Get the recurrent equation from Alfano's method.
     *
     * @param x step
     * @param xm other collision object projected position onto the collision plane in the rotated encounter frame (m)
     * @param ym other collision object projected position onto the collision plane in the rotated encounter frame (m)
     * @param sigmaX square root of the smallest eigen value of the diagonalized combined covariance matrix projected onto
     * the collision plane
     * @param sigmaY square root of the biggest eigen value of the diagonalized combined covariance matrix projected onto the
     * collision plane
     * @param radius sum of primary and secondary collision object equivalent sphere radius (m)
     *
     * @return recurrent equation from Alfano's method
     */
    private double getRecurrentPart(final double x, final double xm, final double ym, final double sigmaX,
                                    final double sigmaY, final double radius) {
        return (Erf.erf((-ym + FastMath.sqrt(radius * radius - x * x)) / (FastMath.sqrt(2) * sigmaY)) -
                Erf.erf((-ym - FastMath.sqrt(radius * radius - x * x)) / (FastMath.sqrt(2) * sigmaY))) *
                (FastMath.exp(-(x - xm) * (x - xm) / (2. * sigmaX * sigmaX)) +
                        FastMath.exp(-(x + xm) * (x + xm) / (2. * sigmaX * sigmaX)));
    }

    /**
     * Get the recurrent equation from Alfano's method.
     *
     * @param x step
     * @param xm other collision object projected position onto the collision plane in the rotated encounter frame (m)
     * @param ym other collision object projected position onto the collision plane in the rotated encounter frame (m)
     * @param sigmaX square root of the smallest eigen value of the diagonalized combined covariance matrix projected onto
     * the collision plane
     * @param sigmaY square root of the biggest eigen value of the diagonalized combined covariance matrix projected onto the
     * collision plane
     * @param radius sum of primary and secondary collision object equivalent sphere radius (m)
     * @param <T> type of the field elements
     *
     * @return recurrent equation from Alfano's method
     */
    private <T extends CalculusFieldElement<T>> T getRecurrentPart(final T x, final T xm, final T ym, final T sigmaX,
                                                                   final T sigmaY, final T radius) {
        final T minusTwoSigmaXSquared          = sigmaX.square().multiply(-2.);
        final T radiusSquaredMinusXSquaredSQRT = radius.square().subtract(x.square()).sqrt();
        final T rootTwoSigmaY                  = sigmaY.multiply(FastMath.sqrt(2));
        final T xMinusXm                       = x.subtract(xm);
        final T xPlusXm                        = x.add(xm);

        return Erf.erf(radiusSquaredMinusXSquaredSQRT.subtract(ym).divide(rootTwoSigmaY)).subtract(
                          Erf.erf(radiusSquaredMinusXSquaredSQRT.add(ym).negate().divide(rootTwoSigmaY)))
                  .multiply(xMinusXm.square().divide(minusTwoSigmaXSquared).exp()
                                    .add(xPlusXm.square().divide(minusTwoSigmaXSquared).exp()));
    }

    /** {@inheritDoc} */
    @Override
    public ShortTermEncounter2DPOCMethodType getType() {
        return ShortTermEncounter2DPOCMethodType.ALFANO_2005;
    }

}