/* Copyright 2002-2017 CS Systèmes d'Information * Licensed to CS Systèmes d'Information (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.semianalytical.dsst.forces; import java.io.NotSerializableException; import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.SortedSet; import java.util.TreeMap; import org.hipparchus.analysis.differentiation.DSFactory; import org.hipparchus.analysis.differentiation.DerivativeStructure; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.util.FastMath; import org.orekit.attitudes.AttitudeProvider; import org.orekit.bodies.CelestialBody; import org.orekit.errors.OrekitException; import org.orekit.orbits.Orbit; import org.orekit.propagation.SpacecraftState; import org.orekit.propagation.events.EventDetector; import org.orekit.propagation.semianalytical.dsst.utilities.AuxiliaryElements; import org.orekit.propagation.semianalytical.dsst.utilities.CjSjCoefficient; import org.orekit.propagation.semianalytical.dsst.utilities.CoefficientsFactory; import org.orekit.propagation.semianalytical.dsst.utilities.CoefficientsFactory.NSKey; import org.orekit.propagation.semianalytical.dsst.utilities.JacobiPolynomials; import org.orekit.propagation.semianalytical.dsst.utilities.ShortPeriodicsInterpolatedCoefficient; import org.orekit.propagation.semianalytical.dsst.utilities.UpperBounds; import org.orekit.propagation.semianalytical.dsst.utilities.hansen.HansenThirdBodyLinear; import org.orekit.time.AbsoluteDate; import org.orekit.utils.TimeSpanMap; /** Third body attraction perturbation to the * {@link org.orekit.propagation.semianalytical.dsst.DSSTPropagator DSSTPropagator}. * * @author Romain Di Costanzo * @author Pascal Parraud */ public class DSSTThirdBody implements DSSTForceModel { /** Max power for summation. */ private static final int MAX_POWER = 22; /** Truncation tolerance for big, eccentric orbits. */ private static final double BIG_TRUNCATION_TOLERANCE = 1.e-1; /** Truncation tolerance for small orbits. */ private static final double SMALL_TRUNCATION_TOLERANCE = 1.9e-6; /** Number of points for interpolation. */ private static final int INTERPOLATION_POINTS = 3; /** Maximum power for eccentricity used in short periodic computation. */ private static final int MAX_ECCPOWER_SP = 4; /** Retrograde factor I. * <p> * DSST model needs equinoctial orbit as internal representation. * Classical equinoctial elements have discontinuities when inclination * is close to zero. In this representation, I = +1. <br> * To avoid this discontinuity, another representation exists and equinoctial * elements can be expressed in a different way, called "retrograde" orbit. * This implies I = -1. <br> * As Orekit doesn't implement the retrograde orbit, I is always set to +1. * But for the sake of consistency with the theory, the retrograde factor * has been kept in the formulas. * </p> */ private static final int I = 1; /** The 3rd body to consider. */ private final CelestialBody body; /** Standard gravitational parameter μ for the body in m³/s². */ private final double gm; /** Factorial. */ private final double[] fact; /** V<sub>ns</sub> coefficients. */ private final TreeMap<NSKey, Double> Vns; /** Distance from center of mass of the central body to the 3rd body. */ private double R3; /** Short period terms. */ private ThirdBodyShortPeriodicCoefficients shortPeriods; // Equinoctial elements (according to DSST notation) /** a. */ private double a; /** e<sub>x</sub>. */ private double k; /** e<sub>y</sub>. */ private double h; /** h<sub>x</sub>. */ private double q; /** h<sub>y</sub>. */ private double p; /** Eccentricity. */ private double ecc; // Direction cosines of the symmetry axis /** α. */ private double alpha; /** β. */ private double beta; /** γ. */ private double gamma; // Common factors for potential computation /** A = n * a². */ private double A; /** B = sqrt(1 - e²). */ private double B; /** C = 1 + p² + q². */ private double C; /** B². */ private double BB; /** B³. */ private double BBB; /** The mean motion (n). */ private double meanMotion; /** Χ = 1 / sqrt(1 - e²) = 1 / B. */ private double X; /** Χ². */ private double XX; /** Χ³. */ private double XXX; /** -2 * a / A. */ private double m2aoA; /** B / A. */ private double BoA; /** 1 / (A * B). */ private double ooAB; /** -C / (2 * A * B). */ private double mCo2AB; /** B / A(1 + B). */ private double BoABpo; /** Max power for a/R3 in the serie expansion. */ private int maxAR3Pow; /** Max power for e in the serie expansion. */ private int maxEccPow; /** Max power for e in the serie expansion (for short periodics). */ private int maxEccPowShort; /** Max frequency of F. */ private int maxFreqF; /** An array that contains the objects needed to build the Hansen coefficients. <br/> * The index is s */ private final HansenThirdBodyLinear[] hansenObjects; /** The current value of the U function. <br/> * Needed for the short periodic contribution */ private double U; /** Qns coefficients. */ private double[][] Qns; /** a / R3 up to power maxAR3Pow. */ private double[] aoR3Pow; /** mu3 / R3. */ private double muoR3; /** b = 1 / (1 + sqrt(1 - e²)) = 1 / (1 + B).*/ private double b; /** h * Χ³. */ private double hXXX; /** k * Χ³. */ private double kXXX; /** Factory for the DerivativeStructure instances. */ private final DSFactory factory; /** Complete constructor. * @param body the 3rd body to consider * @see org.orekit.bodies.CelestialBodyFactory */ public DSSTThirdBody(final CelestialBody body) { this.body = body; this.gm = body.getGM(); this.maxAR3Pow = Integer.MIN_VALUE; this.maxEccPow = Integer.MIN_VALUE; this.Vns = CoefficientsFactory.computeVns(MAX_POWER); // Factorials computation final int dim = 2 * MAX_POWER; this.fact = new double[dim]; fact[0] = 1.; for (int i = 1; i < dim; i++) { fact[i] = i * fact[i - 1]; } //Initialise the HansenCoefficient generator this.hansenObjects = new HansenThirdBodyLinear[MAX_POWER + 1]; for (int s = 0; s <= MAX_POWER; s++) { this.hansenObjects[s] = new HansenThirdBodyLinear(MAX_POWER, s); } this.factory = new DSFactory(1, 1); } /** Get third body. * @return third body */ public CelestialBody getBody() { return body; } /** Computes the highest power of the eccentricity and the highest power * of a/R3 to appear in the truncated analytical power series expansion. * <p> * This method computes the upper value for the 3rd body potential and * determines the maximal powers for the eccentricity and a/R3 producing * potential terms bigger than a defined tolerance. * </p> * @param aux auxiliary elements related to the current orbit * @param meanOnly only mean elements will be used for the propagation * @throws OrekitException if some specific error occurs */ @Override public List<ShortPeriodTerms> initialize(final AuxiliaryElements aux, final boolean meanOnly) throws OrekitException { // Initializes specific parameters. initializeStep(aux); // Truncation tolerance. final double aor = a / R3; final double tol = ( aor > .3 || (aor > .15 && ecc > .25) ) ? BIG_TRUNCATION_TOLERANCE : SMALL_TRUNCATION_TOLERANCE; // Utilities for truncation // Set a lower bound for eccentricity final double eo2 = FastMath.max(0.0025, ecc / 2.); final double x2o2 = XX / 2.; final double[] eccPwr = new double[MAX_POWER]; final double[] chiPwr = new double[MAX_POWER]; eccPwr[0] = 1.; chiPwr[0] = X; for (int i = 1; i < MAX_POWER; i++) { eccPwr[i] = eccPwr[i - 1] * eo2; chiPwr[i] = chiPwr[i - 1] * x2o2; } // Auxiliary quantities. final double ao2rxx = aor / (2. * XX); double xmuarn = ao2rxx * ao2rxx * gm / (X * R3); double term = 0.; // Compute max power for a/R3 and e. maxAR3Pow = 2; maxEccPow = 0; int n = 2; int m = 2; int nsmd2 = 0; do { // Upper bound for Tnm. term = xmuarn * (fact[n + m] / (fact[nsmd2] * fact[nsmd2 + m])) * (fact[n + m + 1] / (fact[m] * fact[n + 1])) * (fact[n - m + 1] / fact[n + 1]) * eccPwr[m] * UpperBounds.getDnl(XX, chiPwr[m], n + 2, m); if (term < tol) { if (m == 0) { break; } else if (m < 2) { xmuarn *= ao2rxx; m = 0; n++; nsmd2++; } else { m -= 2; nsmd2++; } } else { maxAR3Pow = n; maxEccPow = FastMath.max(m, maxEccPow); xmuarn *= ao2rxx; m++; n++; } } while (n < MAX_POWER); maxEccPow = FastMath.min(maxAR3Pow, maxEccPow); // allocate the array aoR3Pow aoR3Pow = new double[maxAR3Pow + 1]; maxFreqF = maxAR3Pow + 1; maxEccPowShort = MAX_ECCPOWER_SP; Qns = CoefficientsFactory.computeQns(gamma, maxAR3Pow, FastMath.max(maxEccPow, maxEccPowShort)); final int jMax = maxAR3Pow + 1; shortPeriods = new ThirdBodyShortPeriodicCoefficients(jMax, INTERPOLATION_POINTS, maxFreqF, body.getName(), new TimeSpanMap<Slot>(new Slot(jMax, INTERPOLATION_POINTS))); final List<ShortPeriodTerms> list = new ArrayList<ShortPeriodTerms>(); list.add(shortPeriods); return list; } /** {@inheritDoc} */ @Override public void initializeStep(final AuxiliaryElements aux) throws OrekitException { // Equinoctial elements a = aux.getSma(); k = aux.getK(); h = aux.getH(); q = aux.getQ(); p = aux.getP(); // Eccentricity ecc = aux.getEcc(); // Distance from center of mass of the central body to the 3rd body final Vector3D bodyPos = body.getPVCoordinates(aux.getDate(), aux.getFrame()).getPosition(); R3 = bodyPos.getNorm(); // Direction cosines final Vector3D bodyDir = bodyPos.normalize(); alpha = bodyDir.dotProduct(aux.getVectorF()); beta = bodyDir.dotProduct(aux.getVectorG()); gamma = bodyDir.dotProduct(aux.getVectorW()); // Equinoctial coefficients A = aux.getA(); B = aux.getB(); C = aux.getC(); meanMotion = aux.getMeanMotion(); //Χ<sup>-2</sup>. BB = B * B; //Χ<sup>-3</sup>. BBB = BB * B; //b = 1 / (1 + B) b = 1. / (1. + B); // Χ X = 1. / B; XX = X * X; XXX = X * XX; // -2 * a / A m2aoA = -2. * a / A; // B / A BoA = B / A; // 1 / AB ooAB = 1. / (A * B); // -C / 2AB mCo2AB = -C * ooAB / 2.; // B / A(1 + B) BoABpo = BoA / (1. + B); // mu3 / R3 muoR3 = gm / R3; //h * Χ³ hXXX = h * XXX; //k * Χ³ kXXX = k * XXX; } /** {@inheritDoc} */ @Override public double[] getMeanElementRate(final SpacecraftState currentState) { // Qns coefficients Qns = CoefficientsFactory.computeQns(gamma, maxAR3Pow, maxEccPow); // a / R3 up to power maxAR3Pow final double aoR3 = a / R3; aoR3Pow[0] = 1.; for (int i = 1; i <= maxAR3Pow; i++) { aoR3Pow[i] = aoR3 * aoR3Pow[i - 1]; } // Compute potential U derivatives final double[] dU = computeUDerivatives(); final double dUda = dU[0]; final double dUdk = dU[1]; final double dUdh = dU[2]; final double dUdAl = dU[3]; final double dUdBe = dU[4]; final double dUdGa = dU[5]; // Compute cross derivatives [Eq. 2.2-(8)] // U(alpha,gamma) = alpha * dU/dgamma - gamma * dU/dalpha final double UAlphaGamma = alpha * dUdGa - gamma * dUdAl; // U(beta,gamma) = beta * dU/dgamma - gamma * dU/dbeta final double UBetaGamma = beta * dUdGa - gamma * dUdBe; // Common factor final double pUAGmIqUBGoAB = (p * UAlphaGamma - I * q * UBetaGamma) * ooAB; // Compute mean elements rates [Eq. 3.1-(1)] final double da = 0.; final double dh = BoA * dUdk + k * pUAGmIqUBGoAB; final double dk = -BoA * dUdh - h * pUAGmIqUBGoAB; final double dp = mCo2AB * UBetaGamma; final double dq = mCo2AB * UAlphaGamma * I; final double dM = m2aoA * dUda + BoABpo * (h * dUdh + k * dUdk) + pUAGmIqUBGoAB; return new double[] {da, dk, dh, dq, dp, dM}; } /** {@inheritDoc} */ @Override public void updateShortPeriodTerms(final SpacecraftState... meanStates) throws OrekitException { final Slot slot = shortPeriods.createSlot(meanStates); for (final SpacecraftState meanState : meanStates) { initializeStep(new AuxiliaryElements(meanState.getOrbit(), I)); // a / R3 up to power maxAR3Pow final double aoR3 = a / R3; aoR3Pow[0] = 1.; for (int i = 1; i <= maxAR3Pow; i++) { aoR3Pow[i] = aoR3 * aoR3Pow[i - 1]; } // Qns coefficients Qns = CoefficientsFactory.computeQns(gamma, maxAR3Pow, FastMath.max(maxEccPow, maxEccPowShort)); final GeneratingFunctionCoefficients gfCoefs = new GeneratingFunctionCoefficients(maxAR3Pow, MAX_ECCPOWER_SP, maxAR3Pow + 1); //Compute additional quantities // 2 * a / An final double ax2oAn = -m2aoA / meanMotion; // B / An final double BoAn = BoA / meanMotion; // 1 / ABn final double ooABn = ooAB / meanMotion; // C / 2ABn final double Co2ABn = -mCo2AB / meanMotion; // B / (A * (1 + B) * n) final double BoABpon = BoABpo / meanMotion; // -3 / n²a² = -3 / nA final double m3onA = -3 / (A * meanMotion); //Compute the C<sub>i</sub><sup>j</sup> and S<sub>i</sub><sup>j</sup> coefficients. for (int j = 1; j < slot.cij.length; j++) { // First compute the C<sub>i</sub><sup>j</sup> coefficients final double[] currentCij = new double[6]; // Compute the cross derivatives operator : final double SAlphaGammaCj = alpha * gfCoefs.getdSdgammaCj(j) - gamma * gfCoefs.getdSdalphaCj(j); final double SAlphaBetaCj = alpha * gfCoefs.getdSdbetaCj(j) - beta * gfCoefs.getdSdalphaCj(j); final double SBetaGammaCj = beta * gfCoefs.getdSdgammaCj(j) - gamma * gfCoefs.getdSdbetaCj(j); final double ShkCj = h * gfCoefs.getdSdkCj(j) - k * gfCoefs.getdSdhCj(j); final double pSagmIqSbgoABnCj = (p * SAlphaGammaCj - I * q * SBetaGammaCj) * ooABn; final double ShkmSabmdSdlCj = ShkCj - SAlphaBetaCj - gfCoefs.getdSdlambdaCj(j); currentCij[0] = ax2oAn * gfCoefs.getdSdlambdaCj(j); currentCij[1] = -(BoAn * gfCoefs.getdSdhCj(j) + h * pSagmIqSbgoABnCj + k * BoABpon * gfCoefs.getdSdlambdaCj(j)); currentCij[2] = BoAn * gfCoefs.getdSdkCj(j) + k * pSagmIqSbgoABnCj - h * BoABpon * gfCoefs.getdSdlambdaCj(j); currentCij[3] = Co2ABn * (q * ShkmSabmdSdlCj - I * SAlphaGammaCj); currentCij[4] = Co2ABn * (p * ShkmSabmdSdlCj - SBetaGammaCj); currentCij[5] = -ax2oAn * gfCoefs.getdSdaCj(j) + BoABpon * (h * gfCoefs.getdSdhCj(j) + k * gfCoefs.getdSdkCj(j)) + pSagmIqSbgoABnCj + m3onA * gfCoefs.getSCj(j); // add the computed coefficients to the interpolators slot.cij[j].addGridPoint(meanState.getDate(), currentCij); // Compute the S<sub>i</sub><sup>j</sup> coefficients final double[] currentSij = new double[6]; // Compute the cross derivatives operator : final double SAlphaGammaSj = alpha * gfCoefs.getdSdgammaSj(j) - gamma * gfCoefs.getdSdalphaSj(j); final double SAlphaBetaSj = alpha * gfCoefs.getdSdbetaSj(j) - beta * gfCoefs.getdSdalphaSj(j); final double SBetaGammaSj = beta * gfCoefs.getdSdgammaSj(j) - gamma * gfCoefs.getdSdbetaSj(j); final double ShkSj = h * gfCoefs.getdSdkSj(j) - k * gfCoefs.getdSdhSj(j); final double pSagmIqSbgoABnSj = (p * SAlphaGammaSj - I * q * SBetaGammaSj) * ooABn; final double ShkmSabmdSdlSj = ShkSj - SAlphaBetaSj - gfCoefs.getdSdlambdaSj(j); currentSij[0] = ax2oAn * gfCoefs.getdSdlambdaSj(j); currentSij[1] = -(BoAn * gfCoefs.getdSdhSj(j) + h * pSagmIqSbgoABnSj + k * BoABpon * gfCoefs.getdSdlambdaSj(j)); currentSij[2] = BoAn * gfCoefs.getdSdkSj(j) + k * pSagmIqSbgoABnSj - h * BoABpon * gfCoefs.getdSdlambdaSj(j); currentSij[3] = Co2ABn * (q * ShkmSabmdSdlSj - I * SAlphaGammaSj); currentSij[4] = Co2ABn * (p * ShkmSabmdSdlSj - SBetaGammaSj); currentSij[5] = -ax2oAn * gfCoefs.getdSdaSj(j) + BoABpon * (h * gfCoefs.getdSdhSj(j) + k * gfCoefs.getdSdkSj(j)) + pSagmIqSbgoABnSj + m3onA * gfCoefs.getSSj(j); // add the computed coefficients to the interpolators slot.sij[j].addGridPoint(meanState.getDate(), currentSij); if (j == 1) { //Compute the C⁰ coefficients using Danielson 2.5.2-15a. final double[] value = new double[6]; for (int i = 0; i < 6; ++i) { value[i] = currentCij[i] * k / 2. + currentSij[i] * h / 2.; } slot.cij[0].addGridPoint(meanState.getDate(), value); } } } } /** {@inheritDoc} */ @Override public EventDetector[] getEventsDetectors() { return null; } /** Compute potential derivatives. * @return derivatives of the potential with respect to orbital parameters */ private double[] computeUDerivatives() { // Gs and Hs coefficients final double[][] GsHs = CoefficientsFactory.computeGsHs(k, h, alpha, beta, maxEccPow); // Initialise U. U = 0.; // Potential derivatives double dUda = 0.; double dUdk = 0.; double dUdh = 0.; double dUdAl = 0.; double dUdBe = 0.; double dUdGa = 0.; for (int s = 0; s <= maxEccPow; s++) { // initialise the Hansen roots this.hansenObjects[s].computeInitValues(B, BB, BBB); // Get the current Gs coefficient final double gs = GsHs[0][s]; // Compute Gs partial derivatives from 3.1-(9) double dGsdh = 0.; double dGsdk = 0.; double dGsdAl = 0.; double dGsdBe = 0.; if (s > 0) { // First get the G(s-1) and the H(s-1) coefficients final double sxGsm1 = s * GsHs[0][s - 1]; final double sxHsm1 = s * GsHs[1][s - 1]; // Then compute derivatives dGsdh = beta * sxGsm1 - alpha * sxHsm1; dGsdk = alpha * sxGsm1 + beta * sxHsm1; dGsdAl = k * sxGsm1 - h * sxHsm1; dGsdBe = h * sxGsm1 + k * sxHsm1; } // Kronecker symbol (2 - delta(0,s)) final double delta0s = (s == 0) ? 1. : 2.; for (int n = FastMath.max(2, s); n <= maxAR3Pow; n++) { // (n - s) must be even if ((n - s) % 2 == 0) { // Extract data from previous computation : final double kns = this.hansenObjects[s].getValue(n, B); final double dkns = this.hansenObjects[s].getDerivative(n, B); final double vns = Vns.get(new NSKey(n, s)); final double coef0 = delta0s * aoR3Pow[n] * vns; final double coef1 = coef0 * Qns[n][s]; final double coef2 = coef1 * kns; // dQns/dGamma = Q(n, s + 1) from Equation 3.1-(8) // for n = s, Q(n, n + 1) = 0. (Cefola & Broucke, 1975) final double dqns = (n == s) ? 0. : Qns[n][s + 1]; //Compute U: U += coef2 * gs; // Compute dU / da : dUda += coef2 * n * gs; // Compute dU / dh dUdh += coef1 * (kns * dGsdh + hXXX * gs * dkns); // Compute dU / dk dUdk += coef1 * (kns * dGsdk + kXXX * gs * dkns); // Compute dU / dAlpha dUdAl += coef2 * dGsdAl; // Compute dU / dBeta dUdBe += coef2 * dGsdBe; // Compute dU / dGamma dUdGa += coef0 * kns * dqns * gs; } } } // multiply by mu3 / R3 U *= muoR3; return new double[] { dUda * muoR3 / a, dUdk * muoR3, dUdh * muoR3, dUdAl * muoR3, dUdBe * muoR3, dUdGa * muoR3 }; } /** {@inheritDoc} */ @Override public void registerAttitudeProvider(final AttitudeProvider provider) { //nothing is done since this contribution is not sensitive to attitude } /** Computes the C<sup>j</sup> and S<sup>j</sup> coefficients Danielson 4.2-(15,16) * and their derivatives. * <p> * CS Mathematical Report $3.5.3.2 * </p> */ private class FourierCjSjCoefficients { /** The coefficients G<sub>n, s</sub> and their derivatives. */ private final GnsCoefficients gns; /** the coefficients e<sup>-|j-s|</sup>*w<sub>j</sub><sup>n, s</sup> and their derivatives by h and k. */ private final WnsjEtomjmsCoefficient wnsjEtomjmsCoefficient; /** The terms containing the coefficients C<sub>j</sub> and S<sub>j</sub> of (α, β) or (k, h). */ private final CjSjAlphaBetaKH ABDECoefficients; /** The Fourier coefficients C<sup>j</sup> and their derivatives. * <p> * Each column of the matrix contains the following values: <br/> * - C<sup>j</sup> <br/> * - dC<sup>j</sup> / da <br/> * - dC<sup>j</sup> / dk <br/> * - dC<sup>j</sup> / dh <br/> * - dC<sup>j</sup> / dα <br/> * - dC<sup>j</sup> / dβ <br/> * - dC<sup>j</sup> / dγ <br/> * </p> */ private final double[][] cj; /** The S<sup>j</sup> coefficients and their derivatives. * <p> * Each column of the matrix contains the following values: <br/> * - S<sup>j</sup> <br/> * - dS<sup>j</sup> / da <br/> * - dS<sup>j</sup> / dk <br/> * - dS<sup>j</sup> / dh <br/> * - dS<sup>j</sup> / dα <br/> * - dS<sup>j</sup> / dβ <br/> * - dS<sup>j</sup> / dγ <br/> * </p> */ private final double[][] sj; /** The Coefficients C<sup>j</sup><sub>,λ</sub>. * <p> * See Danielson 4.2-21 * </p> */ private final double[] cjlambda; /** The Coefficients S<sup>j</sup><sub>,λ</sub>. * <p> * See Danielson 4.2-21 * </p> */ private final double[] sjlambda; /** Maximum value for n. */ private final int nMax; /** Maximum value for s. */ private final int sMax; /** Maximum value for j. */ private final int jMax; /** * Private constructor. * * @param nMax maximum value for n index * @param sMax maximum value for s index * @param jMax maximum value for j index */ FourierCjSjCoefficients(final int nMax, final int sMax, final int jMax) { //Save parameters this.nMax = nMax; this.sMax = sMax; this.jMax = jMax; //Create objects wnsjEtomjmsCoefficient = new WnsjEtomjmsCoefficient(); ABDECoefficients = new CjSjAlphaBetaKH(); gns = new GnsCoefficients(nMax, sMax); //create arays this.cj = new double[7][jMax + 1]; this.sj = new double[7][jMax + 1]; this.cjlambda = new double[jMax]; this.sjlambda = new double[jMax]; computeCoefficients(); } /** * Compute all coefficients. */ private void computeCoefficients() { for (int j = 1; j <= jMax; j++) { // initialise the coefficients for (int i = 0; i <= 6; i++) { cj[i][j] = 0.; sj[i][j] = 0.; } if (j < jMax) { // initialise the C<sup>j</sup><sub>,λ</sub> and S<sup>j</sup><sub>,λ</sub> coefficients cjlambda[j] = 0.; sjlambda[j] = 0.; } for (int s = 0; s <= sMax; s++) { // Compute the coefficients A<sub>j, s</sub>, B<sub>j, s</sub>, D<sub>j, s</sub> and E<sub>j, s</sub> ABDECoefficients.computeCoefficients(j, s); // compute starting value for n final int minN = FastMath.max(2, FastMath.max(j - 1, s)); for (int n = minN; n <= nMax; n++) { // check if n-s is even if ((n - s) % 2 == 0) { // compute the coefficient e<sup>-|j-s|</sup>*w<sub>j</sub><sup>n+1, s</sup> and its derivatives final double[] wjnp1semjms = wnsjEtomjmsCoefficient.computeWjnsEmjmsAndDeriv(j, s, n + 1); // compute the coefficient e<sup>-|j-s|</sup>*w<sub>-j</sub><sup>n+1, s</sup> and its derivatives final double[] wmjnp1semjms = wnsjEtomjmsCoefficient.computeWjnsEmjmsAndDeriv(-j, s, n + 1); // compute common factors final double coef1 = -(wjnp1semjms[0] * ABDECoefficients.getCoefA() + wmjnp1semjms[0] * ABDECoefficients.getCoefB()); final double coef2 = wjnp1semjms[0] * ABDECoefficients.getCoefD() + wmjnp1semjms[0] * ABDECoefficients.getCoefE(); //Compute C<sup>j</sup> cj[0][j] += gns.getGns(n, s) * coef1; //Compute dC<sup>j</sup> / da cj[1][j] += gns.getdGnsda(n, s) * coef1; //Compute dC<sup>j</sup> / dk cj[2][j] += -gns.getGns(n, s) * ( wjnp1semjms[1] * ABDECoefficients.getCoefA() + wjnp1semjms[0] * ABDECoefficients.getdCoefAdk() + wmjnp1semjms[1] * ABDECoefficients.getCoefB() + wmjnp1semjms[0] * ABDECoefficients.getdCoefBdk() ); //Compute dC<sup>j</sup> / dh cj[3][j] += -gns.getGns(n, s) * ( wjnp1semjms[2] * ABDECoefficients.getCoefA() + wjnp1semjms[0] * ABDECoefficients.getdCoefAdh() + wmjnp1semjms[2] * ABDECoefficients.getCoefB() + wmjnp1semjms[0] * ABDECoefficients.getdCoefBdh() ); //Compute dC<sup>j</sup> / dα cj[4][j] += -gns.getGns(n, s) * ( wjnp1semjms[0] * ABDECoefficients.getdCoefAdalpha() + wmjnp1semjms[0] * ABDECoefficients.getdCoefBdalpha() ); //Compute dC<sup>j</sup> / dβ cj[5][j] += -gns.getGns(n, s) * ( wjnp1semjms[0] * ABDECoefficients.getdCoefAdbeta() + wmjnp1semjms[0] * ABDECoefficients.getdCoefBdbeta() ); //Compute dC<sup>j</sup> / dγ cj[6][j] += gns.getdGnsdgamma(n, s) * coef1; //Compute S<sup>j</sup> sj[0][j] += gns.getGns(n, s) * coef2; //Compute dS<sup>j</sup> / da sj[1][j] += gns.getdGnsda(n, s) * coef2; //Compute dS<sup>j</sup> / dk sj[2][j] += gns.getGns(n, s) * ( wjnp1semjms[1] * ABDECoefficients.getCoefD() + wjnp1semjms[0] * ABDECoefficients.getdCoefDdk() + wmjnp1semjms[1] * ABDECoefficients.getCoefE() + wmjnp1semjms[0] * ABDECoefficients.getdCoefEdk() ); //Compute dS<sup>j</sup> / dh sj[3][j] += gns.getGns(n, s) * ( wjnp1semjms[2] * ABDECoefficients.getCoefD() + wjnp1semjms[0] * ABDECoefficients.getdCoefDdh() + wmjnp1semjms[2] * ABDECoefficients.getCoefE() + wmjnp1semjms[0] * ABDECoefficients.getdCoefEdh() ); //Compute dS<sup>j</sup> / dα sj[4][j] += gns.getGns(n, s) * ( wjnp1semjms[0] * ABDECoefficients.getdCoefDdalpha() + wmjnp1semjms[0] * ABDECoefficients.getdCoefEdalpha() ); //Compute dS<sup>j</sup> / dβ sj[5][j] += gns.getGns(n, s) * ( wjnp1semjms[0] * ABDECoefficients.getdCoefDdbeta() + wmjnp1semjms[0] * ABDECoefficients.getdCoefEdbeta() ); //Compute dS<sup>j</sup> / dγ sj[6][j] += gns.getdGnsdgamma(n, s) * coef2; //Check if n is greater or equal to j and j is at most jMax-1 if (n >= j && j < jMax) { // compute the coefficient e<sup>-|j-s|</sup>*w<sub>j</sub><sup>n, s</sup> and its derivatives final double[] wjnsemjms = wnsjEtomjmsCoefficient.computeWjnsEmjmsAndDeriv(j, s, n); // compute the coefficient e<sup>-|j-s|</sup>*w<sub>-j</sub><sup>n, s</sup> and its derivatives final double[] wmjnsemjms = wnsjEtomjmsCoefficient.computeWjnsEmjmsAndDeriv(-j, s, n); //Compute C<sup>j</sup><sub>,λ</sub> cjlambda[j] += gns.getGns(n, s) * (wjnsemjms[0] * ABDECoefficients.getCoefD() + wmjnsemjms[0] * ABDECoefficients.getCoefE()); //Compute S<sup>j</sup><sub>,λ</sub> sjlambda[j] += gns.getGns(n, s) * (wjnsemjms[0] * ABDECoefficients.getCoefA() + wmjnsemjms[0] * ABDECoefficients.getCoefB()); } } } } // Divide by j for (int i = 0; i <= 6; i++) { cj[i][j] /= j; sj[i][j] /= j; } } //The C⁰ coefficients are not computed here. //They are evaluated at the final point. //C⁰<sub>,λ</sub> cjlambda[0] = k * cjlambda[1] / 2. + h * sjlambda[1] / 2.; } /** Get the Fourier coefficient C<sup>j</sup>. * @param j j index * @return C<sup>j</sup> */ public double getCj(final int j) { return cj[0][j]; } /** Get the derivative dC<sup>j</sup>/da. * @param j j index * @return dC<sup>j</sup>/da */ public double getdCjda(final int j) { return cj[1][j]; } /** Get the derivative dC<sup>j</sup>/dk. * @param j j index * @return dC<sup>j</sup>/dk */ public double getdCjdk(final int j) { return cj[2][j]; } /** Get the derivative dC<sup>j</sup>/dh. * @param j j index * @return dC<sup>j</sup>/dh */ public double getdCjdh(final int j) { return cj[3][j]; } /** Get the derivative dC<sup>j</sup>/dα. * @param j j index * @return dC<sup>j</sup>/dα */ public double getdCjdalpha(final int j) { return cj[4][j]; } /** Get the derivative dC<sup>j</sup>/dβ. * @param j j index * @return dC<sup>j</sup>/dβ */ public double getdCjdbeta(final int j) { return cj[5][j]; } /** Get the derivative dC<sup>j</sup>/dγ. * @param j j index * @return dC<sup>j</sup>/dγ */ public double getdCjdgamma(final int j) { return cj[6][j]; } /** Get the Fourier coefficient S<sup>j</sup>. * @param j j index * @return S<sup>j</sup> */ public double getSj(final int j) { return sj[0][j]; } /** Get the derivative dS<sup>j</sup>/da. * @param j j index * @return dS<sup>j</sup>/da */ public double getdSjda(final int j) { return sj[1][j]; } /** Get the derivative dS<sup>j</sup>/dk. * @param j j index * @return dS<sup>j</sup>/dk */ public double getdSjdk(final int j) { return sj[2][j]; } /** Get the derivative dS<sup>j</sup>/dh. * @param j j index * @return dS<sup>j</sup>/dh */ public double getdSjdh(final int j) { return sj[3][j]; } /** Get the derivative dS<sup>j</sup>/dα. * @param j j index * @return dS<sup>j</sup>/dα */ public double getdSjdalpha(final int j) { return sj[4][j]; } /** Get the derivative dS<sup>j</sup>/dβ. * @param j j index * @return dS<sup>j</sup>/dβ */ public double getdSjdbeta(final int j) { return sj[5][j]; } /** Get the derivative dS<sup>j</sup>/dγ. * @param j j index * @return dS<sup>j</sup>/dγ */ public double getdSjdgamma(final int j) { return sj[6][j]; } /** Get the coefficient C⁰<sub>,λ</sub>. * @return C⁰<sub>,λ</sub> */ public double getC0Lambda() { return cjlambda[0]; } /** Get the coefficient C<sup>j</sup><sub>,λ</sub>. * @param j j index * @return C<sup>j</sup><sub>,λ</sub> */ public double getCjLambda(final int j) { if (j < 1 || j >= jMax) { return 0.; } return cjlambda[j]; } /** Get the coefficient S<sup>j</sup><sub>,λ</sub>. * @param j j index * @return S<sup>j</sup><sub>,λ</sub> */ public double getSjLambda(final int j) { if (j < 1 || j >= jMax) { return 0.; } return sjlambda[j]; } } /** This class covers the coefficients e<sup>-|j-s|</sup>*w<sub>j</sub><sup>n, s</sup> and their derivatives by h and k. * * <p> * Starting from Danielson 4.2-9,10,11 and taking into account that fact that: <br /> * c = e / (1 + (1 - e²)<sup>1/2</sup>) = e / (1 + B) = e * b <br/> * the expression e<sup>-|j-s|</sup>*w<sub>j</sub><sup>n, s</sup> * can be written as: <br/ > * - for |s| > |j| <br /> * e<sup>-|j-s|</sup>*w<sub>j</sub><sup>n, s</sup> = * (((n + s)!(n - s)!)/((n + j)!(n - j)!)) * * (-b)<sup>|j-s|</sup> * * ((1 - c²)<sup>n-|s|</sup>/(1 + c²)<sup>n</sup>) * * P<sub>n-|s|</sub><sup>|j-s|, |j+s|</sup>(χ) <br /> * <br /> * - for |s| <= |j| <br /> * e<sup>-|j-s|</sup>*w<sub>j</sub><sup>n, s</sup> = * (-b)<sup>|j-s|</sup> * * ((1 - c²)<sup>n-|j|</sup>/(1 + c²)<sup>n</sup>) * * P<sub>n-|j|</sub><sup>|j-s|, |j+s|</sup>(χ) * </p> * * @author Lucian Barbulescu */ private class WnsjEtomjmsCoefficient { /** The value c. * <p> * c = e / (1 + (1 - e²)<sup>1/2</sup>) = e / (1 + B) = e * b <br/> * </p> * */ private final double c; /** c².*/ private final double c2; /** db / dh. */ private final double dbdh; /** db / dk. */ private final double dbdk; /** de / dh. */ private final double dedh; /** de / dk. */ private final double dedk; /** dc / dh = e * db/dh + b * de/dh. */ private final double dcdh; /** dc / dk = e * db/dk + b * de/dk. */ private final double dcdk; /** The values (1 - c²)<sup>n</sup>. <br /> * The maximum possible value for the power is N + 1 */ private final double[] omc2tn; /** The values (1 + c²)<sup>n</sup>. <br /> * The maximum possible value for the power is N + 1 */ private final double[] opc2tn; /** The values b<sup>|j-s|</sup>. */ private final double[] btjms; /** * Standard constructor. */ WnsjEtomjmsCoefficient() { //initialise fields c = ecc * b; c2 = c * c; //b² * χ final double b2Chi = b * b * X; //Compute derivatives of b dbdh = h * b2Chi; dbdk = k * b2Chi; //Compute derivatives of e dedh = h / ecc; dedk = k / ecc; //Compute derivatives of c dcdh = ecc * dbdh + b * dedh; dcdk = ecc * dbdk + b * dedk; //Compute the powers (1 - c²)<sup>n</sup> and (1 + c²)<sup>n</sup> omc2tn = new double[maxAR3Pow + maxFreqF + 2]; opc2tn = new double[maxAR3Pow + maxFreqF + 2]; final double omc2 = 1. - c2; final double opc2 = 1. + c2; omc2tn[0] = 1.; opc2tn[0] = 1.; for (int i = 1; i <= maxAR3Pow + maxFreqF + 1; i++) { omc2tn[i] = omc2tn[i - 1] * omc2; opc2tn[i] = opc2tn[i - 1] * opc2; } //Compute the powers of b btjms = new double[maxAR3Pow + maxFreqF + 1]; btjms[0] = 1.; for (int i = 1; i <= maxAR3Pow + maxFreqF; i++) { btjms[i] = btjms[i - 1] * b; } } /** Compute the value of the coefficient e<sup>-|j-s|</sup>*w<sub>j</sub><sup>n, s</sup> and its derivatives by h and k. <br /> * * @param j j index * @param s s index * @param n n index * @return an array containing the value of the coefficient at index 0, the derivative by k at index 1 and the derivative by h at index 2 */ public double[] computeWjnsEmjmsAndDeriv(final int j, final int s, final int n) { final double[] wjnsemjms = new double[] {0., 0., 0.}; // |j| final int absJ = FastMath.abs(j); // |s| final int absS = FastMath.abs(s); // |j - s| final int absJmS = FastMath.abs(j - s); // |j + s| final int absJpS = FastMath.abs(j + s); //The lower index of P. Also the power of (1 - c²) final int l; // the factorial ratio coefficient or 1. if |s| <= |j| final double factCoef; if (absS > absJ) { factCoef = (fact[n + s] / fact[n + j]) * (fact[n - s] / fact[n - j]); l = n - absS; } else { factCoef = 1.; l = n - absJ; } // (-1)<sup>|j-s|</sup> final double sign = absJmS % 2 != 0 ? -1. : 1.; //(1 - c²)<sup>n-|s|</sup> / (1 + c²)<sup>n</sup> final double coef1 = omc2tn[l] / opc2tn[n]; //-b<sup>|j-s|</sup> final double coef2 = sign * btjms[absJmS]; // P<sub>l</sub><sup>|j-s|, |j+s|</sup>(χ) final DerivativeStructure jac = JacobiPolynomials.getValue(l, absJmS, absJpS, factory.variable(0, X)); // the derivative of coef1 by c final double dcoef1dc = -coef1 * 2. * c * (((double) n) / opc2tn[1] + ((double) l) / omc2tn[1]); // the derivative of coef1 by h final double dcoef1dh = dcoef1dc * dcdh; // the derivative of coef1 by k final double dcoef1dk = dcoef1dc * dcdk; // the derivative of coef2 by b final double dcoef2db = absJmS == 0 ? 0 : sign * (double) absJmS * btjms[absJmS - 1]; // the derivative of coef2 by h final double dcoef2dh = dcoef2db * dbdh; // the derivative of coef2 by k final double dcoef2dk = dcoef2db * dbdk; // the jacobi polinomial value final double jacobi = jac.getValue(); // the derivative of the Jacobi polynomial by h final double djacobidh = jac.getPartialDerivative(1) * hXXX; // the derivative of the Jacobi polynomial by k final double djacobidk = jac.getPartialDerivative(1) * kXXX; //group the above coefficients to limit the mathematical operations final double term1 = factCoef * coef1 * coef2; final double term2 = factCoef * coef1 * jacobi; final double term3 = factCoef * coef2 * jacobi; //compute e<sup>-|j-s|</sup>*w<sub>j</sub><sup>n, s</sup> and its derivatives by k and h wjnsemjms[0] = term1 * jacobi; wjnsemjms[1] = dcoef1dk * term3 + dcoef2dk * term2 + djacobidk * term1; wjnsemjms[2] = dcoef1dh * term3 + dcoef2dh * term2 + djacobidh * term1; return wjnsemjms; } } /** The G<sub>n,s</sub> coefficients and their derivatives. * <p> * See Danielson, 4.2-17 * * @author Lucian Barbulescu */ private class GnsCoefficients { /** Maximum value for n index. */ private final int nMax; /** Maximum value for s index. */ private final int sMax; /** The coefficients G<sub>n,s</sub>. */ private final double gns[][]; /** The derivatives of the coefficients G<sub>n,s</sub> by a. */ private final double dgnsda[][]; /** The derivatives of the coefficients G<sub>n,s</sub> by γ. */ private final double dgnsdgamma[][]; /** Standard constructor. * * @param nMax maximum value for n indes * @param sMax maximum value for s index */ GnsCoefficients(final int nMax, final int sMax) { this.nMax = nMax; this.sMax = sMax; final int rows = nMax + 1; final int columns = sMax + 1; this.gns = new double[rows][columns]; this.dgnsda = new double[rows][columns]; this.dgnsdgamma = new double[rows][columns]; // Generate the coefficients generateCoefficients(); } /** * Compute the coefficient G<sub>n,s</sub> and its derivatives. * <p> * Only the derivatives by a and γ are computed as all others are 0 * </p> */ private void generateCoefficients() { for (int s = 0; s <= sMax; s++) { // The n index is always at least the maximum between 2 and s final int minN = FastMath.max(2, s); for (int n = minN; n <= nMax; n++) { // compute the coefficients only if (n - s) % 2 == 0 if ( (n - s) % 2 == 0 ) { // Kronecker symbol (2 - delta(0,s)) final double delta0s = (s == 0) ? 1. : 2.; final double vns = Vns.get(new NSKey(n, s)); final double coef0 = delta0s * aoR3Pow[n] * vns * muoR3; final double coef1 = coef0 * Qns[n][s]; // dQns/dGamma = Q(n, s + 1) from Equation 3.1-(8) // for n = s, Q(n, n + 1) = 0. (Cefola & Broucke, 1975) final double dqns = (n == s) ? 0. : Qns[n][s + 1]; //Compute the coefficient and its derivatives. this.gns[n][s] = coef1; this.dgnsda[n][s] = coef1 * n / a; this.dgnsdgamma[n][s] = coef0 * dqns; } else { // the coefficient and its derivatives is 0 this.gns[n][s] = 0.; this.dgnsda[n][s] = 0.; this.dgnsdgamma[n][s] = 0.; } } } } /** Get the coefficient G<sub>n,s</sub>. * * @param n n index * @param s s index * @return the coefficient G<sub>n,s</sub> */ public double getGns(final int n, final int s) { return this.gns[n][s]; } /** Get the derivative dG<sub>n,s</sub> / da. * * @param n n index * @param s s index * @return the derivative dG<sub>n,s</sub> / da */ public double getdGnsda(final int n, final int s) { return this.dgnsda[n][s]; } /** Get the derivative dG<sub>n,s</sub> / dγ. * * @param n n index * @param s s index * @return the derivative dG<sub>n,s</sub> / dγ */ public double getdGnsdgamma(final int n, final int s) { return this.dgnsdgamma[n][s]; } } /** This class computes the terms containing the coefficients C<sub>j</sub> and S<sub>j</sub> of (α, β) or (k, h). * * <p> * The following terms and their derivatives by k, h, alpha and beta are considered: <br/ > * - sign(j-s) * C<sub>s</sub>(α, β) * S<sub>|j-s|</sub>(k, h) + S<sub>s</sub>(α, β) * C<sub>|j-s|</sub>(k, h) <br /> * - C<sub>s</sub>(α, β) * S<sub>j+s</sub>(k, h) - S<sub>s</sub>(α, β) * C<sub>j+s</sub>(k, h) <br /> * - C<sub>s</sub>(α, β) * C<sub>|j-s|</sub>(k, h) - sign(j-s) * S<sub>s</sub>(α, β) * S<sub>|j-s|</sub>(k, h) <br /> * - C<sub>s</sub>(α, β) * C<sub>j+s</sub>(k, h) + S<sub>s</sub>(α, β) * S<sub>j+s</sub>(k, h) <br /> * For the ease of usage the above terms are renamed A<sub>js</sub>, B<sub>js</sub>, D<sub>js</sub> and E<sub>js</sub> respectively <br /> * See the CS Mathematical report $3.5.3.2 for more details * </p> * @author Lucian Barbulescu */ private class CjSjAlphaBetaKH { /** The C<sub>j</sub>(k, h) and the S<sub>j</sub>(k, h) series. */ private final CjSjCoefficient cjsjkh; /** The C<sub>j</sub>(α, β) and the S<sub>j</sub>(α, β) series. */ private final CjSjCoefficient cjsjalbe; /** The coeficient sign(j-s) * C<sub>s</sub>(α, β) * S<sub>|j-s|</sub>(k, h) + S<sub>s</sub>(α, β) * C<sub>|j-s|</sub>(k, h) * and its derivative by k, h, α and β. */ private final double coefAandDeriv[]; /** The coeficient C<sub>s</sub>(α, β) * S<sub>j+s</sub>(k, h) - S<sub>s</sub>(α, β) * C<sub>j+s</sub>(k, h) * and its derivative by k, h, α and β. */ private final double coefBandDeriv[]; /** The coeficient C<sub>s</sub>(α, β) * C<sub>|j-s|</sub>(k, h) - sign(j-s) * S<sub>s</sub>(α, β) * S<sub>|j-s|</sub>(k, h) * and its derivative by k, h, α and β. */ private final double coefDandDeriv[]; /** The coeficient C<sub>s</sub>(α, β) * C<sub>j+s</sub>(k, h) + S<sub>s</sub>(α, β) * S<sub>j+s</sub>(k, h) * and its derivative by k, h, α and β. */ private final double coefEandDeriv[]; /** * Standard constructor. */ CjSjAlphaBetaKH() { cjsjkh = new CjSjCoefficient(k, h); cjsjalbe = new CjSjCoefficient(alpha, beta); coefAandDeriv = new double[5]; coefBandDeriv = new double[5]; coefDandDeriv = new double[5]; coefEandDeriv = new double[5]; } /** Compute the coefficients and their derivatives for a given (j,s) pair. * @param j j index * @param s s index */ public void computeCoefficients(final int j, final int s) { // sign of j-s final int sign = j < s ? -1 : 1; //|j-s| final int absJmS = FastMath.abs(j - s); //j+s final int jps = j + s; //Compute the coefficient A and its derivatives coefAandDeriv[0] = sign * cjsjalbe.getCj(s) * cjsjkh.getSj(absJmS) + cjsjalbe.getSj(s) * cjsjkh.getCj(absJmS); coefAandDeriv[1] = sign * cjsjalbe.getCj(s) * cjsjkh.getDsjDk(absJmS) + cjsjalbe.getSj(s) * cjsjkh.getDcjDk(absJmS); coefAandDeriv[2] = sign * cjsjalbe.getCj(s) * cjsjkh.getDsjDh(absJmS) + cjsjalbe.getSj(s) * cjsjkh.getDcjDh(absJmS); coefAandDeriv[3] = sign * cjsjalbe.getDcjDk(s) * cjsjkh.getSj(absJmS) + cjsjalbe.getDsjDk(s) * cjsjkh.getCj(absJmS); coefAandDeriv[4] = sign * cjsjalbe.getDcjDh(s) * cjsjkh.getSj(absJmS) + cjsjalbe.getDsjDh(s) * cjsjkh.getCj(absJmS); //Compute the coefficient B and its derivatives coefBandDeriv[0] = cjsjalbe.getCj(s) * cjsjkh.getSj(jps) - cjsjalbe.getSj(s) * cjsjkh.getCj(jps); coefBandDeriv[1] = cjsjalbe.getCj(s) * cjsjkh.getDsjDk(jps) - cjsjalbe.getSj(s) * cjsjkh.getDcjDk(jps); coefBandDeriv[2] = cjsjalbe.getCj(s) * cjsjkh.getDsjDh(jps) - cjsjalbe.getSj(s) * cjsjkh.getDcjDh(jps); coefBandDeriv[3] = cjsjalbe.getDcjDk(s) * cjsjkh.getSj(jps) - cjsjalbe.getDsjDk(s) * cjsjkh.getCj(jps); coefBandDeriv[4] = cjsjalbe.getDcjDh(s) * cjsjkh.getSj(jps) - cjsjalbe.getDsjDh(s) * cjsjkh.getCj(jps); //Compute the coefficient D and its derivatives coefDandDeriv[0] = cjsjalbe.getCj(s) * cjsjkh.getCj(absJmS) - sign * cjsjalbe.getSj(s) * cjsjkh.getSj(absJmS); coefDandDeriv[1] = cjsjalbe.getCj(s) * cjsjkh.getDcjDk(absJmS) - sign * cjsjalbe.getSj(s) * cjsjkh.getDsjDk(absJmS); coefDandDeriv[2] = cjsjalbe.getCj(s) * cjsjkh.getDcjDh(absJmS) - sign * cjsjalbe.getSj(s) * cjsjkh.getDsjDh(absJmS); coefDandDeriv[3] = cjsjalbe.getDcjDk(s) * cjsjkh.getCj(absJmS) - sign * cjsjalbe.getDsjDk(s) * cjsjkh.getSj(absJmS); coefDandDeriv[4] = cjsjalbe.getDcjDh(s) * cjsjkh.getCj(absJmS) - sign * cjsjalbe.getDsjDh(s) * cjsjkh.getSj(absJmS); //Compute the coefficient E and its derivatives coefEandDeriv[0] = cjsjalbe.getCj(s) * cjsjkh.getCj(jps) + cjsjalbe.getSj(s) * cjsjkh.getSj(jps); coefEandDeriv[1] = cjsjalbe.getCj(s) * cjsjkh.getDcjDk(jps) + cjsjalbe.getSj(s) * cjsjkh.getDsjDk(jps); coefEandDeriv[2] = cjsjalbe.getCj(s) * cjsjkh.getDcjDh(jps) + cjsjalbe.getSj(s) * cjsjkh.getDsjDh(jps); coefEandDeriv[3] = cjsjalbe.getDcjDk(s) * cjsjkh.getCj(jps) + cjsjalbe.getDsjDk(s) * cjsjkh.getSj(jps); coefEandDeriv[4] = cjsjalbe.getDcjDh(s) * cjsjkh.getCj(jps) + cjsjalbe.getDsjDh(s) * cjsjkh.getSj(jps); } /** Get the value of coefficient A<sub>j,s</sub>. * * @return the coefficient A<sub>j,s</sub> */ public double getCoefA() { return coefAandDeriv[0]; } /** Get the value of coefficient dA<sub>j,s</sub>/dk. * * @return the coefficient dA<sub>j,s</sub>/dk */ public double getdCoefAdk() { return coefAandDeriv[1]; } /** Get the value of coefficient dA<sub>j,s</sub>/dh. * * @return the coefficient dA<sub>j,s</sub>/dh */ public double getdCoefAdh() { return coefAandDeriv[2]; } /** Get the value of coefficient dA<sub>j,s</sub>/dα. * * @return the coefficient dA<sub>j,s</sub>/dα */ public double getdCoefAdalpha() { return coefAandDeriv[3]; } /** Get the value of coefficient dA<sub>j,s</sub>/dβ. * * @return the coefficient dA<sub>j,s</sub>/dβ */ public double getdCoefAdbeta() { return coefAandDeriv[4]; } /** Get the value of coefficient B<sub>j,s</sub>. * * @return the coefficient B<sub>j,s</sub> */ public double getCoefB() { return coefBandDeriv[0]; } /** Get the value of coefficient dB<sub>j,s</sub>/dk. * * @return the coefficient dB<sub>j,s</sub>/dk */ public double getdCoefBdk() { return coefBandDeriv[1]; } /** Get the value of coefficient dB<sub>j,s</sub>/dh. * * @return the coefficient dB<sub>j,s</sub>/dh */ public double getdCoefBdh() { return coefBandDeriv[2]; } /** Get the value of coefficient dB<sub>j,s</sub>/dα. * * @return the coefficient dB<sub>j,s</sub>/dα */ public double getdCoefBdalpha() { return coefBandDeriv[3]; } /** Get the value of coefficient dB<sub>j,s</sub>/dβ. * * @return the coefficient dB<sub>j,s</sub>/dβ */ public double getdCoefBdbeta() { return coefBandDeriv[4]; } /** Get the value of coefficient D<sub>j,s</sub>. * * @return the coefficient D<sub>j,s</sub> */ public double getCoefD() { return coefDandDeriv[0]; } /** Get the value of coefficient dD<sub>j,s</sub>/dk. * * @return the coefficient dD<sub>j,s</sub>/dk */ public double getdCoefDdk() { return coefDandDeriv[1]; } /** Get the value of coefficient dD<sub>j,s</sub>/dh. * * @return the coefficient dD<sub>j,s</sub>/dh */ public double getdCoefDdh() { return coefDandDeriv[2]; } /** Get the value of coefficient dD<sub>j,s</sub>/dα. * * @return the coefficient dD<sub>j,s</sub>/dα */ public double getdCoefDdalpha() { return coefDandDeriv[3]; } /** Get the value of coefficient dD<sub>j,s</sub>/dβ. * * @return the coefficient dD<sub>j,s</sub>/dβ */ public double getdCoefDdbeta() { return coefDandDeriv[4]; } /** Get the value of coefficient E<sub>j,s</sub>. * * @return the coefficient E<sub>j,s</sub> */ public double getCoefE() { return coefEandDeriv[0]; } /** Get the value of coefficient dE<sub>j,s</sub>/dk. * * @return the coefficient dE<sub>j,s</sub>/dk */ public double getdCoefEdk() { return coefEandDeriv[1]; } /** Get the value of coefficient dE<sub>j,s</sub>/dh. * * @return the coefficient dE<sub>j,s</sub>/dh */ public double getdCoefEdh() { return coefEandDeriv[2]; } /** Get the value of coefficient dE<sub>j,s</sub>/dα. * * @return the coefficient dE<sub>j,s</sub>/dα */ public double getdCoefEdalpha() { return coefEandDeriv[3]; } /** Get the value of coefficient dE<sub>j,s</sub>/dβ. * * @return the coefficient dE<sub>j,s</sub>/dβ */ public double getdCoefEdbeta() { return coefEandDeriv[4]; } } /** This class computes the coefficients for the generating function S and its derivatives. * <p> * The form of the generating functions is: <br> * S = C⁰ + Σ<sub>j=1</sub><sup>N+1</sup>(C<sup>j</sup> * cos(jF) + S<sup>j</sup> * sin(jF)) <br> * The coefficients C⁰, C<sup>j</sup>, S<sup>j</sup> are the Fourrier coefficients * presented in Danielson 4.2-14,15 except for the case j=1 where * C¹ = C¹<sub>Fourier</sub> - hU and * S¹ = S¹<sub>Fourier</sub> + kU <br> * Also the coefficients of the derivatives of S by a, k, h, α, β, γ and λ * are computed end expressed in a similar manner. The formulas used are 4.2-19, 20, 23, 24 * </p> * @author Lucian Barbulescu */ private class GeneratingFunctionCoefficients { /** The Fourier coefficients as presented in Danielson 4.2-14,15. */ private final FourierCjSjCoefficients cjsjFourier; /** Maximum value of j index. */ private final int jMax; /** The coefficients C<sup>j</sup> of the function S and its derivatives. * <p> * The index j belongs to the interval [0,jMax]. The coefficient C⁰ is the free coefficient.<br> * Each column of the matrix contains the coefficient corresponding to the following functions: <br/> * - S <br/> * - dS / da <br/> * - dS / dk <br/> * - dS / dh <br/> * - dS / dα <br/> * - dS / dβ <br/> * - dS / dγ <br/> * - dS / dλ * </p> */ private final double[][] cjCoefs; /** The coefficients S<sup>j</sup> of the function S and its derivatives. * <p> * The index j belongs to the interval [0,jMax].<br> * Each column of the matrix contains the coefficient corresponding to the following functions: <br/> * - S <br/> * - dS / da <br/> * - dS / dk <br/> * - dS / dh <br/> * - dS / dα <br/> * - dS / dβ <br/> * - dS / dγ <br/> * - dS / dλ * </p> */ private final double[][] sjCoefs; /** * Standard constructor. * * @param nMax maximum value of n index * @param sMax maximum value of s index * @param jMax maximum value of j index */ GeneratingFunctionCoefficients(final int nMax, final int sMax, final int jMax) { this.jMax = jMax; this.cjsjFourier = new FourierCjSjCoefficients(nMax, sMax, jMax); this.cjCoefs = new double[8][jMax + 1]; this.sjCoefs = new double[8][jMax + 1]; computeGeneratingFunctionCoefficients(); } /** * Compute the coefficients for the generating function S and its derivatives. */ private void computeGeneratingFunctionCoefficients() { // Compute potential U and derivatives final double[] dU = computeUDerivatives(); //Compute the C<sup>j</sup> coefficients for (int j = 1; j <= jMax; j++) { //Compute the C<sup>j</sup> coefficients cjCoefs[0][j] = cjsjFourier.getCj(j); cjCoefs[1][j] = cjsjFourier.getdCjda(j); cjCoefs[2][j] = cjsjFourier.getdCjdk(j) - (cjsjFourier.getSjLambda(j - 1) - cjsjFourier.getSjLambda(j + 1)) / 2; cjCoefs[3][j] = cjsjFourier.getdCjdh(j) - (cjsjFourier.getCjLambda(j - 1) + cjsjFourier.getCjLambda(j + 1)) / 2; cjCoefs[4][j] = cjsjFourier.getdCjdalpha(j); cjCoefs[5][j] = cjsjFourier.getdCjdbeta(j); cjCoefs[6][j] = cjsjFourier.getdCjdgamma(j); cjCoefs[7][j] = cjsjFourier.getCjLambda(j); //Compute the S<sup>j</sup> coefficients sjCoefs[0][j] = cjsjFourier.getSj(j); sjCoefs[1][j] = cjsjFourier.getdSjda(j); sjCoefs[2][j] = cjsjFourier.getdSjdk(j) + (cjsjFourier.getCjLambda(j - 1) - cjsjFourier.getCjLambda(j + 1)) / 2; sjCoefs[3][j] = cjsjFourier.getdSjdh(j) - (cjsjFourier.getSjLambda(j - 1) + cjsjFourier.getSjLambda(j + 1)) / 2; sjCoefs[4][j] = cjsjFourier.getdSjdalpha(j); sjCoefs[5][j] = cjsjFourier.getdSjdbeta(j); sjCoefs[6][j] = cjsjFourier.getdSjdgamma(j); sjCoefs[7][j] = cjsjFourier.getSjLambda(j); //In the special case j == 1 there are some additional terms to be added if (j == 1) { //Additional terms for C<sup>j</sup> coefficients cjCoefs[0][j] += -h * U; cjCoefs[1][j] += -h * dU[0]; cjCoefs[2][j] += -h * dU[1]; cjCoefs[3][j] += -(h * dU[2] + U + cjsjFourier.getC0Lambda()); cjCoefs[4][j] += -h * dU[3]; cjCoefs[5][j] += -h * dU[4]; cjCoefs[6][j] += -h * dU[5]; //Additional terms for S<sup>j</sup> coefficients sjCoefs[0][j] += k * U; sjCoefs[1][j] += k * dU[0]; sjCoefs[2][j] += k * dU[1] + U + cjsjFourier.getC0Lambda(); sjCoefs[3][j] += k * dU[2]; sjCoefs[4][j] += k * dU[3]; sjCoefs[5][j] += k * dU[4]; sjCoefs[6][j] += k * dU[5]; } } } /** Get the coefficient C<sup>j</sup> for the function S. * <br> * Possible values for j are within the interval [0,jMax]. * The value 0 is used to obtain the free coefficient C⁰ * @param j j index * @return C<sup>j</sup> for the function S */ public double getSCj(final int j) { return cjCoefs[0][j]; } /** Get the coefficient S<sup>j</sup> for the function S. * <br> * Possible values for j are within the interval [1,jMax]. * @param j j index * @return S<sup>j</sup> for the function S */ public double getSSj(final int j) { return sjCoefs[0][j]; } /** Get the coefficient C<sup>j</sup> for the derivative dS/da. * <br> * Possible values for j are within the interval [0,jMax]. * The value 0 is used to obtain the free coefficient C⁰ * @param j j index * @return C<sup>j</sup> for the function dS/da */ public double getdSdaCj(final int j) { return cjCoefs[1][j]; } /** Get the coefficient S<sup>j</sup> for the derivative dS/da. * <br> * Possible values for j are within the interval [1,jMax]. * @param j j index * @return S<sup>j</sup> for the derivative dS/da */ public double getdSdaSj(final int j) { return sjCoefs[1][j]; } /** Get the coefficient C<sup>j</sup> for the derivative dS/dk * <br> * Possible values for j are within the interval [0,jMax]. * The value 0 is used to obtain the free coefficient C⁰ * @param j j index * @return C<sup>j</sup> for the function dS/dk */ public double getdSdkCj(final int j) { return cjCoefs[2][j]; } /** Get the coefficient S<sup>j</sup> for the derivative dS/dk. * <br> * Possible values for j are within the interval [1,jMax]. * @param j j index * @return S<sup>j</sup> for the derivative dS/dk */ public double getdSdkSj(final int j) { return sjCoefs[2][j]; } /** Get the coefficient C<sup>j</sup> for the derivative dS/dh * <br> * Possible values for j are within the interval [0,jMax]. * The value 0 is used to obtain the free coefficient C⁰ * @param j j index * @return C<sup>j</sup> for the function dS/dh */ public double getdSdhCj(final int j) { return cjCoefs[3][j]; } /** Get the coefficient S<sup>j</sup> for the derivative dS/dh. * <br> * Possible values for j are within the interval [1,jMax]. * @param j j index * @return S<sup>j</sup> for the derivative dS/dh */ public double getdSdhSj(final int j) { return sjCoefs[3][j]; } /** Get the coefficient C<sup>j</sup> for the derivative dS/dα * <br> * Possible values for j are within the interval [0,jMax]. * The value 0 is used to obtain the free coefficient C⁰ * @param j j index * @return C<sup>j</sup> for the function dS/dα */ public double getdSdalphaCj(final int j) { return cjCoefs[4][j]; } /** Get the coefficient S<sup>j</sup> for the derivative dS/dα. * <br> * Possible values for j are within the interval [1,jMax]. * @param j j index * @return S<sup>j</sup> for the derivative dS/dα */ public double getdSdalphaSj(final int j) { return sjCoefs[4][j]; } /** Get the coefficient C<sup>j</sup> for the derivative dS/dβ * <br> * Possible values for j are within the interval [0,jMax]. * The value 0 is used to obtain the free coefficient C⁰ * @param j j index * @return C<sup>j</sup> for the function dS/dβ */ public double getdSdbetaCj(final int j) { return cjCoefs[5][j]; } /** Get the coefficient S<sup>j</sup> for the derivative dS/dβ. * <br> * Possible values for j are within the interval [1,jMax]. * @param j j index * @return S<sup>j</sup> for the derivative dS/dβ */ public double getdSdbetaSj(final int j) { return sjCoefs[5][j]; } /** Get the coefficient C<sup>j</sup> for the derivative dS/dγ * <br> * Possible values for j are within the interval [0,jMax]. * The value 0 is used to obtain the free coefficient C⁰ * @param j j index * @return C<sup>j</sup> for the function dS/dγ */ public double getdSdgammaCj(final int j) { return cjCoefs[6][j]; } /** Get the coefficient S<sup>j</sup> for the derivative dS/dγ. * <br> * Possible values for j are within the interval [1,jMax]. * @param j j index * @return S<sup>j</sup> for the derivative dS/dγ */ public double getdSdgammaSj(final int j) { return sjCoefs[6][j]; } /** Get the coefficient C<sup>j</sup> for the derivative dS/dλ * <br> * Possible values for j are within the interval [0,jMax]. * The value 0 is used to obtain the free coefficient C⁰ * @param j j index * @return C<sup>j</sup> for the function dS/dλ */ public double getdSdlambdaCj(final int j) { return cjCoefs[7][j]; } /** Get the coefficient S<sup>j</sup> for the derivative dS/dλ. * <br> * Possible values for j are within the interval [1,jMax]. * @param j j index * @return S<sup>j</sup> for the derivative dS/dλ */ public double getdSdlambdaSj(final int j) { return sjCoefs[7][j]; } } /** * The coefficients used to compute the short periodic contribution for the Third body perturbation. * <p> * The short periodic contribution for the Third Body is expressed in Danielson 4.2-25.<br> * The coefficients C<sub>i</sub>⁰, C<sub>i</sub><sup>j</sup>, S<sub>i</sub><sup>j</sup> * are computed by replacing the corresponding values in formula 2.5.5-10. * </p> * @author Lucian Barbulescu */ private static class ThirdBodyShortPeriodicCoefficients implements ShortPeriodTerms { /** Serializable UID. */ private static final long serialVersionUID = 20151119L; /** Maximal value for j. */ private final int jMax; /** Number of points used in the interpolation process. */ private final int interpolationPoints; /** Max frequency of F. */ private final int maxFreqF; /** Coefficients prefix. */ private final String prefix; /** All coefficients slots. */ private final transient TimeSpanMap<Slot> slots; /** * Standard constructor. * @param interpolationPoints number of points used in the interpolation process * @param jMax maximal value for j * @param maxFreqF Max frequency of F * @param bodyName third body name * @param slots all coefficients slots */ ThirdBodyShortPeriodicCoefficients(final int jMax, final int interpolationPoints, final int maxFreqF, final String bodyName, final TimeSpanMap<Slot> slots) { this.jMax = jMax; this.interpolationPoints = interpolationPoints; this.maxFreqF = maxFreqF; this.prefix = "DSST-3rd-body-" + bodyName + "-"; this.slots = slots; } /** Get the slot valid for some date. * @param meanStates mean states defining the slot * @return slot valid at the specified date */ public Slot createSlot(final SpacecraftState... meanStates) { final Slot slot = new Slot(jMax, interpolationPoints); final AbsoluteDate first = meanStates[0].getDate(); final AbsoluteDate last = meanStates[meanStates.length - 1].getDate(); if (first.compareTo(last) <= 0) { slots.addValidAfter(slot, first); } else { slots.addValidBefore(slot, first); } return slot; } /** {@inheritDoc} */ @Override public double[] value(final Orbit meanOrbit) { // select the coefficients slot final Slot slot = slots.get(meanOrbit.getDate()); // the current eccentric longitude final double F = meanOrbit.getLE(); //initialize the short periodic contribution with the corresponding C⁰ coeficient final double[] shortPeriodic = slot.cij[0].value(meanOrbit.getDate()); // Add the cos and sin dependent terms for (int j = 1; j <= maxFreqF; j++) { //compute cos and sin final double cosjF = FastMath.cos(j * F); final double sinjF = FastMath.sin(j * F); final double[] c = slot.cij[j].value(meanOrbit.getDate()); final double[] s = slot.sij[j].value(meanOrbit.getDate()); for (int i = 0; i < 6; i++) { shortPeriodic[i] += c[i] * cosjF + s[i] * sinjF; } } return shortPeriodic; } /** {@inheritDoc} */ @Override public String getCoefficientsKeyPrefix() { return prefix; } /** {@inheritDoc} * <p> * For third body attraction forces,there are maxFreqF + 1 cj coefficients, * maxFreqF sj coefficients where maxFreqF depends on the orbit. * The j index is the integer multiplier for the eccentric longitude argument * in the cj and sj coefficients. * </p> */ @Override public Map<String, double[]> getCoefficients(final AbsoluteDate date, final Set<String> selected) throws OrekitException { // select the coefficients slot final Slot slot = slots.get(date); final Map<String, double[]> coefficients = new HashMap<String, double[]>(2 * maxFreqF + 1); storeIfSelected(coefficients, selected, slot.cij[0].value(date), "c", 0); for (int j = 1; j <= maxFreqF; j++) { storeIfSelected(coefficients, selected, slot.cij[j].value(date), "c", j); storeIfSelected(coefficients, selected, slot.sij[j].value(date), "s", j); } return coefficients; } /** Put a coefficient in a map if selected. * @param map map to populate * @param selected set of coefficients that should be put in the map * (empty set means all coefficients are selected) * @param value coefficient value * @param id coefficient identifier * @param indices list of coefficient indices */ private void storeIfSelected(final Map<String, double[]> map, final Set<String> selected, final double[] value, final String id, final int... indices) { final StringBuilder keyBuilder = new StringBuilder(getCoefficientsKeyPrefix()); keyBuilder.append(id); for (int index : indices) { keyBuilder.append('[').append(index).append(']'); } final String key = keyBuilder.toString(); if (selected.isEmpty() || selected.contains(key)) { map.put(key, value); } } /** Replace the instance with a data transfer object for serialization. * @return data transfer object that will be serialized * @exception NotSerializableException if an additional state provider is not serializable */ private Object writeReplace() throws NotSerializableException { // slots transitions final SortedSet<TimeSpanMap.Transition<Slot>> transitions = slots.getTransitions(); final AbsoluteDate[] transitionDates = new AbsoluteDate[transitions.size()]; final Slot[] allSlots = new Slot[transitions.size() + 1]; int i = 0; for (final TimeSpanMap.Transition<Slot> transition : transitions) { if (i == 0) { // slot before the first transition allSlots[i] = transition.getBefore(); } if (i < transitionDates.length) { transitionDates[i] = transition.getDate(); allSlots[++i] = transition.getAfter(); } } return new DataTransferObject(jMax, interpolationPoints, maxFreqF, prefix, transitionDates, allSlots); } /** Internal class used only for serialization. */ private static class DataTransferObject implements Serializable { /** Serializable UID. */ private static final long serialVersionUID = 20160319L; /** Maximum value for j index. */ private final int jMax; /** Number of points used in the interpolation process. */ private final int interpolationPoints; /** Max frequency of F. */ private final int maxFreqF; /** Coefficients prefix. */ private final String prefix; /** Transitions dates. */ private final AbsoluteDate[] transitionDates; /** All slots. */ private final Slot[] allSlots; /** Simple constructor. * @param jMax maximum value for j index * @param interpolationPoints number of points used in the interpolation process * @param maxFreqF max frequency of F * @param prefix prefix for coefficients keys * @param transitionDates transitions dates * @param allSlots all slots */ DataTransferObject(final int jMax, final int interpolationPoints, final int maxFreqF, final String prefix, final AbsoluteDate[] transitionDates, final Slot[] allSlots) { this.jMax = jMax; this.interpolationPoints = interpolationPoints; this.maxFreqF = maxFreqF; this.prefix = prefix; this.transitionDates = transitionDates; this.allSlots = allSlots; } /** Replace the deserialized data transfer object with a {@link ThirdBodyShortPeriodicCoefficients}. * @return replacement {@link ThirdBodyShortPeriodicCoefficients} */ private Object readResolve() { final TimeSpanMap<Slot> slots = new TimeSpanMap<Slot>(allSlots[0]); for (int i = 0; i < transitionDates.length; ++i) { slots.addValidAfter(allSlots[i + 1], transitionDates[i]); } return new ThirdBodyShortPeriodicCoefficients(jMax, interpolationPoints, maxFreqF, prefix, slots); } } } /** Coefficients valid for one time slot. */ private static class Slot implements Serializable { /** Serializable UID. */ private static final long serialVersionUID = 20160319L; /** The coefficients C<sub>i</sub><sup>j</sup>. * <p> * The index order is cij[j][i] <br/> * i corresponds to the equinoctial element, as follows: <br/> * - i=0 for a <br/> * - i=1 for k <br/> * - i=2 for h <br/> * - i=3 for q <br/> * - i=4 for p <br/> * - i=5 for λ <br/> * </p> */ private final ShortPeriodicsInterpolatedCoefficient[] cij; /** The coefficients S<sub>i</sub><sup>j</sup>. * <p> * The index order is sij[j][i] <br/> * i corresponds to the equinoctial element, as follows: <br/> * - i=0 for a <br/> * - i=1 for k <br/> * - i=2 for h <br/> * - i=3 for q <br/> * - i=4 for p <br/> * - i=5 for λ <br/> * </p> */ private final ShortPeriodicsInterpolatedCoefficient[] sij; /** Simple constructor. * @param jMax maximum value for j index * @param interpolationPoints number of points used in the interpolation process */ Slot(final int jMax, final int interpolationPoints) { // allocate the coefficients arrays cij = new ShortPeriodicsInterpolatedCoefficient[jMax + 1]; sij = new ShortPeriodicsInterpolatedCoefficient[jMax + 1]; for (int j = 0; j <= jMax; j++) { cij[j] = new ShortPeriodicsInterpolatedCoefficient(interpolationPoints); sij[j] = new ShortPeriodicsInterpolatedCoefficient(interpolationPoints); } } } }