Flattener.java

/* Copyright 2002-2022 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.forces.gravity.potential;

import org.hipparchus.util.FastMath;
import org.orekit.errors.OrekitException;
import org.orekit.errors.OrekitMessages;

/** Utility for converting between (degree, order) indices and one-dimensional flatten index.
 * <p>
 * The outer loop in {@link org.orekit.forces.gravity.HolmesFeatherstoneAttractionModel}
 * if in decreasing order and the inner loop is in increasing degree (starting
 * from the diagonal). This utility converts (degree, order) indices into a flatten index
 * in a one-dimensional array that increases as these loops are performed.
 * This means the first element of the one-dimensional array corresponds to diagonal
 * element at maximum order and the last element corresponds to order 0 and maximum degree.
 * </p>
 * @author Luc Maisonobe
 * @since 11.1
 */
class Flattener {

    /** Degree of the spherical harmonics. */
    private final int degree;

    /** Order of the spherical harmonics. */
    private final int order;

    /** Number of high order cells dropped in the triangular array. */
    private final int dropped;

    /** Simple constructor.
     * @param degree degree of the spherical harmonics
     * @param order order of the spherical harmonics
      */
    Flattener(final int degree, final int order) {
        this.degree  = degree;
        this.order   = order;
        this.dropped = (degree - order + 1) * (degree - order) / 2;
    }

    /** Get the degree of the spherical harmonics.
     * @return degree of the spherical harmonics
     */
    public int getDegree() {
        return degree;
    }

    /** Get the order of the spherical harmonics.
     * @return order of the spherical harmonics
     */
    public int getOrder() {
        return order;
    }

    /** Convert (degree, order) indices to one-dimensional flatten index.
     * <p>
     * As the outer loop in {@link org.orekit.forces.gravity.HolmesFeatherstoneAttractionModel}
     * if on decreasing order and the inner loop is in increasing degree (starting
     * from the diagonal), the flatten index increases as these loops are performed.
     * </p>
     * @param n degree index (must be within range, otherwise an exception is thrown)
     * @param m order index (must be within range, otherwise an exception is thrown)
     * @return one-dimensional flatten index
     * @see #withinRange(int, int)
     */
    public int index(final int n, final int m) {
        if (!withinRange(n, m)) {
            throw new OrekitException(OrekitMessages.WRONG_DEGREE_OR_ORDER, n, m, degree, order);
        }
        final int dm = degree - m;
        return dm * (dm + 1) / 2 + (n - m) - dropped;
    }

    /** Get the size of a flatten array sufficient to hold all coefficients.
     * @return size of a flatten array sufficient to hold all coefficients
     */
    public int arraySize() {
        return index(degree, 0) + 1;
    }

    /** Check if indices are within range.
     * @param n degree
     * @param m order
     * @return true if indices are within limits, false otherwise
     */
    public boolean withinRange(final int n, final int m) {
        return n >= 0 && n <= degree && m >= 0 && m <= FastMath.min(n, order);
    }

    /** Flatten a triangular array.
     * @param triangular triangular array to flatten
     * @return flatten array
     */
    public double[] flatten(final double[][] triangular) {
        final double[] flat = new double[arraySize()];
        for (int n = 0; n <= getDegree(); ++n) {
            for (int m = 0; m <= FastMath.min(n, getOrder()); ++m) {
                flat[index(n, m)] = triangular[n][m];
            }
        }
        return flat;
    }

}