CunninghamAttractionModel.java

  1. /* Copyright 2002-2013 CS Systèmes d'Information
  2.  * Licensed to CS Systèmes d'Information (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.forces.gravity;


  18. import java.util.Collections;

  19. import org.apache.commons.math3.analysis.differentiation.DerivativeStructure;
  20. import org.apache.commons.math3.geometry.euclidean.threed.FieldRotation;
  21. import org.apache.commons.math3.geometry.euclidean.threed.FieldVector3D;
  22. import org.apache.commons.math3.geometry.euclidean.threed.Vector3D;
  23. import org.apache.commons.math3.ode.AbstractParameterizable;
  24. import org.apache.commons.math3.util.FastMath;
  25. import org.orekit.errors.OrekitException;
  26. import org.orekit.errors.OrekitMessages;
  27. import org.orekit.forces.ForceModel;
  28. import org.orekit.forces.gravity.potential.GravityFieldFactory;
  29. import org.orekit.forces.gravity.potential.TideSystem;
  30. import org.orekit.forces.gravity.potential.TideSystemProvider;
  31. import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider;
  32. import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider.UnnormalizedSphericalHarmonics;
  33. import org.orekit.frames.Frame;
  34. import org.orekit.frames.Transform;
  35. import org.orekit.propagation.SpacecraftState;
  36. import org.orekit.propagation.events.EventDetector;
  37. import org.orekit.propagation.numerical.Jacobianizer;
  38. import org.orekit.propagation.numerical.ParameterConfiguration;
  39. import org.orekit.propagation.numerical.TimeDerivativesEquations;
  40. import org.orekit.time.AbsoluteDate;

  41. /** This class represents the gravitational field of a celestial body.
  42.  * <p>The algorithm implemented in this class has been designed by
  43.  * Leland E. Cunningham (Lockheed Missiles and Space Company, Sunnyvale
  44.  * and Astronomy Department University of California, Berkeley) in
  45.  * his 1969 paper: <em>On the computation of the spherical harmonic
  46.  * terms needed during the numerical integration of the orbital motion
  47.  * of an artificial satellite</em> (Celestial Mechanics 2, 1970).</p>
  48.  * <p>
  49.  * Note that this class can often not be used for high degrees (say
  50.  * above 90) as most modern gravity fields are provided as normalized
  51.  * coefficients and the un-normalization process to convert these
  52.  * coefficients underflows at degree and order 89. This class also
  53.  * does not provide analytical partial derivatives (it uses finite differences
  54.  * to compute them) and is much slower than {@link HolmesFeatherstoneAttractionModel}
  55.  * (even when no derivatives are computed). For all these reasons,
  56.  * it is recommended to use the {@link HolmesFeatherstoneAttractionModel
  57.  * Holmes-Featherstone model} rather than this class.
  58.  * </p>
  59.  * <p>
  60.  * As this class uses finite differences to compute derivatives, the steps for
  61.  * finite differences <strong>must</strong> be initialized by calling {@link
  62.  * #setSteps(double, double)} prior to use derivatives, otherwise an exception
  63.  * will be thrown by {@link #accelerationDerivatives(AbsoluteDate, Frame, FieldVector3D,
  64.  * FieldVector3D, FieldRotation, DerivativeStructure)} and by {@link
  65.  * #accelerationDerivatives(SpacecraftState, String)}.
  66.  * </p>
  67.  *
  68.  * @see HolmesFeatherstoneAttractionModel
  69.  * @author Fabien Maussion
  70.  * @author Luc Maisonobe
  71.  * @author V&eacute;ronique Pommier-Maurussane
  72.  */

  73. public class CunninghamAttractionModel
  74.     extends AbstractParameterizable implements ForceModel, TideSystemProvider {

  75.     /** Provider for the spherical harmonics. */
  76.     private final UnnormalizedSphericalHarmonicsProvider provider;

  77.     /** Central attraction coefficient. */
  78.     private double mu;

  79.     /** Rotating body. */
  80.     private final Frame bodyFrame;

  81.     /** Helper class computing acceleration derivatives. */
  82.     private Jacobianizer jacobianizer;

  83.     /** Creates a new instance.
  84.     * @param centralBodyFrame rotating body frame
  85.     * @param equatorialRadius reference equatorial radius of the potential
  86.     * @param mu central body attraction coefficient (m<sup>3</sup>/s<sup>2</sup>)
  87.     * @param C un-normalized coefficients array (cosine part)
  88.     * @param S un-normalized coefficients array (sine part)
  89.     * @deprecated since 6.0, replaced by {@link #CunninghamAttractionModel(Frame, UnnormalizedSphericalHarmonicsProvider)}
  90.     */
  91.     public CunninghamAttractionModel(final Frame centralBodyFrame,
  92.                                      final double equatorialRadius, final double mu,
  93.                                      final double[][] C, final double[][] S) {
  94.         this(centralBodyFrame, GravityFieldFactory.getUnnormalizedProvider(equatorialRadius, mu,
  95.                                                                            TideSystem.UNKNOWN, C, S));
  96.     }

  97.    /** Creates a new instance.
  98.    * @param centralBodyFrame rotating body frame
  99.    * @param provider provider for spherical harmonics
  100.    * @since 6.0
  101.    */
  102.     public CunninghamAttractionModel(final Frame centralBodyFrame,
  103.                                      final UnnormalizedSphericalHarmonicsProvider provider) {
  104.         super(NewtonianAttraction.CENTRAL_ATTRACTION_COEFFICIENT);

  105.         this.provider     = provider;
  106.         this.mu           = provider.getMu();
  107.         this.bodyFrame    = centralBodyFrame;
  108.         this.jacobianizer = null;

  109.     }

  110.     /** Set the step for finite differences with respect to spacecraft position.
  111.      * @param hPosition step used for finite difference computation
  112.      * with respect to spacecraft position (m)
  113.      * @param hMu step used for finite difference computation
  114.      * with respect to central attraction coefficient (m<sup>3</sup>/s<sup>2</sup>)
  115.      */
  116.     public void setSteps(final double hPosition, final double hMu) {
  117.         final ParameterConfiguration muConfig =
  118.                 new ParameterConfiguration(NewtonianAttraction.CENTRAL_ATTRACTION_COEFFICIENT, hMu);
  119.         jacobianizer = new Jacobianizer(this, mu, Collections.singletonList(muConfig), hPosition);
  120.     }

  121.     /** {@inheritDoc} */
  122.     public TideSystem getTideSystem() {
  123.         return provider.getTideSystem();
  124.     }

  125.     /** {@inheritDoc} */
  126.     public void addContribution(final SpacecraftState s, final TimeDerivativesEquations adder)
  127.         throws OrekitException {

  128.         // get the position in body frame
  129.         final AbsoluteDate date = s.getDate();
  130.         final UnnormalizedSphericalHarmonics harmonics = provider.onDate(date);
  131.         final Transform fromBodyFrame = bodyFrame.getTransformTo(s.getFrame(), date);
  132.         final Transform toBodyFrame   = fromBodyFrame.getInverse();
  133.         final Vector3D relative = toBodyFrame.transformPosition(s.getPVCoordinates().getPosition());

  134.         final double x = relative.getX();
  135.         final double y = relative.getY();
  136.         final double z = relative.getZ();

  137.         final double x2 = x * x;
  138.         final double y2 = y * y;
  139.         final double z2 = z * z;
  140.         final double r2 = x2 + y2 + z2;
  141.         final double r = FastMath.sqrt(r2);
  142.         final double equatorialRadius = provider.getAe();
  143.         if (r <= equatorialRadius) {
  144.             throw new OrekitException(OrekitMessages.TRAJECTORY_INSIDE_BRILLOUIN_SPHERE, r);
  145.         }

  146.         // define some intermediate variables
  147.         final double onR2 = 1 / r2;
  148.         final double onR3 = onR2 / r;
  149.         final double rEqOnR2  = equatorialRadius / r2;
  150.         final double rEqOnR4  = rEqOnR2 / r2;
  151.         final double rEq2OnR2 = equatorialRadius * rEqOnR2;

  152.         double cmx   = -x * rEqOnR2;
  153.         double cmy   = -y * rEqOnR2;
  154.         double cmz   = -z * rEqOnR2;

  155.         final double dx   = -2 * cmx;
  156.         final double dy   = -2 * cmy;
  157.         final double dz   = -2 * cmz;

  158.         // intermediate variables gradients
  159.         // since dcy/dx = dcx/dy, dcz/dx = dcx/dz and dcz/dy = dcy/dz,
  160.         // we reuse the existing variables

  161.         double dcmxdx = (x2 - y2 - z2) * rEqOnR4;
  162.         double dcmxdy =  dx * y * onR2;
  163.         double dcmxdz =  dx * z * onR2;
  164.         double dcmydy = (y2 - x2 - z2) * rEqOnR4;
  165.         double dcmydz =  dy * z * onR2;
  166.         double dcmzdz = (z2 - x2 - y2) * rEqOnR4;

  167.         final double ddxdx = -2 * dcmxdx;
  168.         final double ddxdy = -2 * dcmxdy;
  169.         final double ddxdz = -2 * dcmxdz;
  170.         final double ddydy = -2 * dcmydy;
  171.         final double ddydz = -2 * dcmydz;
  172.         final double ddzdz = -2 * dcmzdz;

  173.         final double donr2dx = -dx * rEqOnR2;
  174.         final double donr2dy = -dy * rEqOnR2;
  175.         final double donr2dz = -dz * rEqOnR2;

  176.         // potential coefficients (4 per matrix)
  177.         double vrn  = 0.0;
  178.         double vin  = 0.0;
  179.         double vrd  = 1.0 / (equatorialRadius * r);
  180.         double vid  = 0.0;
  181.         double vrn1 = 0.0;
  182.         double vin1 = 0.0;
  183.         double vrn2 = 0.0;
  184.         double vin2 = 0.0;

  185.         // gradient coefficients (4 per matrix)
  186.         double gradXVrn  = 0.0;
  187.         double gradXVin  = 0.0;
  188.         double gradXVrd  = -x * onR3 / equatorialRadius;
  189.         double gradXVid  = 0.0;
  190.         double gradXVrn1 = 0.0;
  191.         double gradXVin1 = 0.0;
  192.         double gradXVrn2 = 0.0;
  193.         double gradXVin2 = 0.0;

  194.         double gradYVrn  = 0.0;
  195.         double gradYVin  = 0.0;
  196.         double gradYVrd  = -y * onR3 / equatorialRadius;
  197.         double gradYVid  = 0.0;
  198.         double gradYVrn1 = 0.0;
  199.         double gradYVin1 = 0.0;
  200.         double gradYVrn2 = 0.0;
  201.         double gradYVin2 = 0.0;

  202.         double gradZVrn  = 0.0;
  203.         double gradZVin  = 0.0;
  204.         double gradZVrd  = -z * onR3 / equatorialRadius;
  205.         double gradZVid  = 0.0;
  206.         double gradZVrn1 = 0.0;
  207.         double gradZVin1 = 0.0;
  208.         double gradZVrn2 = 0.0;
  209.         double gradZVin2 = 0.0;

  210.         // acceleration coefficients
  211.         double vdX = 0.0;
  212.         double vdY = 0.0;
  213.         double vdZ = 0.0;

  214.         // start calculating
  215.         for (int m = 0; m <= provider.getMaxOrder(); m++) {

  216.             double cx = cmx;
  217.             double cy = cmy;
  218.             double cz = cmz;

  219.             double dcxdx = dcmxdx;
  220.             double dcxdy = dcmxdy;
  221.             double dcxdz = dcmxdz;
  222.             double dcydy = dcmydy;
  223.             double dcydz = dcmydz;
  224.             double dczdz = dcmzdz;

  225.             for (int n = m; n <= provider.getMaxDegree(); n++) {

  226.                 if (n == m) {
  227.                     // calculate the first element of the next column

  228.                     vrn      = equatorialRadius * vrd;
  229.                     vin      = equatorialRadius * vid;

  230.                     gradXVrn = equatorialRadius * gradXVrd;
  231.                     gradXVin = equatorialRadius * gradXVid;
  232.                     gradYVrn = equatorialRadius * gradYVrd;
  233.                     gradYVin = equatorialRadius * gradYVid;
  234.                     gradZVrn = equatorialRadius * gradZVrd;
  235.                     gradZVin = equatorialRadius * gradZVid;

  236.                     final double tmpGradXVrd = (cx + dx) * gradXVrd - (cy + dy) * gradXVid + (dcxdx + ddxdx) * vrd - (dcxdy + ddxdy) * vid;
  237.                     gradXVid = (cy + dy) * gradXVrd + (cx + dx) * gradXVid + (dcxdy + ddxdy) * vrd + (dcxdx + ddxdx) * vid;
  238.                     gradXVrd = tmpGradXVrd;

  239.                     final double tmpGradYVrd = (cx + dx) * gradYVrd - (cy + dy) * gradYVid + (dcxdy + ddxdy) * vrd - (dcydy + ddydy) * vid;
  240.                     gradYVid = (cy + dy) * gradYVrd + (cx + dx) * gradYVid + (dcydy + ddydy) * vrd + (dcxdy + ddxdy) * vid;
  241.                     gradYVrd = tmpGradYVrd;

  242.                     final double tmpGradZVrd = (cx + dx) * gradZVrd - (cy + dy) * gradZVid + (dcxdz + ddxdz) * vrd - (dcydz + ddydz) * vid;
  243.                     gradZVid = (cy + dy) * gradZVrd + (cx + dx) * gradZVid + (dcydz + ddydz) * vrd + (dcxdz + ddxdz) * vid;
  244.                     gradZVrd = tmpGradZVrd;

  245.                     final double tmpVrd = (cx + dx) * vrd - (cy + dy) * vid;
  246.                     vid = (cy + dy) * vrd + (cx + dx) * vid;
  247.                     vrd = tmpVrd;

  248.                 } else if (n == m + 1) {
  249.                     // calculate the second element of the column
  250.                     vrn = cz * vrn1;
  251.                     vin = cz * vin1;

  252.                     gradXVrn = cz * gradXVrn1 + dcxdz * vrn1;
  253.                     gradXVin = cz * gradXVin1 + dcxdz * vin1;

  254.                     gradYVrn = cz * gradYVrn1 + dcydz * vrn1;
  255.                     gradYVin = cz * gradYVin1 + dcydz * vin1;

  256.                     gradZVrn = cz * gradZVrn1 + dczdz * vrn1;
  257.                     gradZVin = cz * gradZVin1 + dczdz * vin1;

  258.                 } else {
  259.                     // calculate the other elements of the column
  260.                     final double inv   = 1.0 / (n - m);
  261.                     final double coeff = n + m - 1.0;

  262.                     vrn = (cz * vrn1 - coeff * rEq2OnR2 * vrn2) * inv;
  263.                     vin = (cz * vin1 - coeff * rEq2OnR2 * vin2) * inv;

  264.                     gradXVrn = (cz * gradXVrn1 - coeff * rEq2OnR2 * gradXVrn2 + dcxdz * vrn1 - coeff * donr2dx * vrn2) * inv;
  265.                     gradXVin = (cz * gradXVin1 - coeff * rEq2OnR2 * gradXVin2 + dcxdz * vin1 - coeff * donr2dx * vin2) * inv;
  266.                     gradYVrn = (cz * gradYVrn1 - coeff * rEq2OnR2 * gradYVrn2 + dcydz * vrn1 - coeff * donr2dy * vrn2) * inv;
  267.                     gradYVin = (cz * gradYVin1 - coeff * rEq2OnR2 * gradYVin2 + dcydz * vin1 - coeff * donr2dy * vin2) * inv;
  268.                     gradZVrn = (cz * gradZVrn1 - coeff * rEq2OnR2 * gradZVrn2 + dczdz * vrn1 - coeff * donr2dz * vrn2) * inv;
  269.                     gradZVin = (cz * gradZVin1 - coeff * rEq2OnR2 * gradZVin2 + dczdz * vin1 - coeff * donr2dz * vin2) * inv;
  270.                 }

  271.                 // increment variables
  272.                 cx += dx;
  273.                 cy += dy;
  274.                 cz += dz;

  275.                 dcxdx += ddxdx;
  276.                 dcxdy += ddxdy;
  277.                 dcxdz += ddxdz;
  278.                 dcydy += ddydy;
  279.                 dcydz += ddydz;
  280.                 dczdz += ddzdz;

  281.                 vrn2 = vrn1;
  282.                 vin2 = vin1;
  283.                 gradXVrn2 = gradXVrn1;
  284.                 gradXVin2 = gradXVin1;
  285.                 gradYVrn2 = gradYVrn1;
  286.                 gradYVin2 = gradYVin1;
  287.                 gradZVrn2 = gradZVrn1;
  288.                 gradZVin2 = gradZVin1;

  289.                 vrn1 = vrn;
  290.                 vin1 = vin;
  291.                 gradXVrn1 = gradXVrn;
  292.                 gradXVin1 = gradXVin;
  293.                 gradYVrn1 = gradYVrn;
  294.                 gradYVin1 = gradYVin;
  295.                 gradZVrn1 = gradZVrn;
  296.                 gradZVin1 = gradZVin;

  297.                 // compute the acceleration due to the Cnm and Snm coefficients
  298.                 // ignoring the central attraction
  299.                 if (n > 0) {
  300.                     final double cnm = harmonics.getUnnormalizedCnm(n, m);
  301.                     final double snm = harmonics.getUnnormalizedSnm(n, m);
  302.                     vdX += cnm * gradXVrn + snm * gradXVin;
  303.                     vdY += cnm * gradYVrn + snm * gradYVin;
  304.                     vdZ += cnm * gradZVrn + snm * gradZVin;
  305.                 }

  306.             }

  307.             // increment variables
  308.             cmx += dx;
  309.             cmy += dy;
  310.             cmz += dz;

  311.             dcmxdx += ddxdx;
  312.             dcmxdy += ddxdy;
  313.             dcmxdz += ddxdz;
  314.             dcmydy += ddydy;
  315.             dcmydz += ddydz;
  316.             dcmzdz += ddzdz;

  317.         }

  318.         // compute acceleration in inertial frame
  319.         final Vector3D acceleration =
  320.             fromBodyFrame.transformVector(new Vector3D(mu * vdX, mu * vdY, mu * vdZ));
  321.         adder.addXYZAcceleration(acceleration.getX(), acceleration.getY(), acceleration.getZ());

  322.     }

  323.     /** {@inheritDoc} */
  324.     public FieldVector3D<DerivativeStructure> accelerationDerivatives(final AbsoluteDate date, final  Frame frame,
  325.                                                                       final FieldVector3D<DerivativeStructure> position,
  326.                                                                       final FieldVector3D<DerivativeStructure> velocity,
  327.                                                                       final FieldRotation<DerivativeStructure> rotation,
  328.                                                                       final DerivativeStructure mass)
  329.         throws OrekitException {
  330.         if (jacobianizer == null) {
  331.             throw new OrekitException(OrekitMessages.STEPS_NOT_INITIALIZED_FOR_FINITE_DIFFERENCES);
  332.         }
  333.         return jacobianizer.accelerationDerivatives(date, frame, position, velocity, rotation, mass);
  334.     }

  335.     /** {@inheritDoc} */
  336.     public FieldVector3D<DerivativeStructure> accelerationDerivatives(final SpacecraftState s, final String paramName)
  337.         throws OrekitException {
  338.         if (jacobianizer == null) {
  339.             throw new OrekitException(OrekitMessages.STEPS_NOT_INITIALIZED_FOR_FINITE_DIFFERENCES);
  340.         }
  341.         return jacobianizer.accelerationDerivatives(s, paramName);
  342.     }

  343.     /** {@inheritDoc} */
  344.     public EventDetector[] getEventsDetectors() {
  345.         return new EventDetector[0];
  346.     }

  347.     /** {@inheritDoc} */
  348.     public double getParameter(final String name)
  349.         throws IllegalArgumentException {
  350.         complainIfNotSupported(name);
  351.         return mu;
  352.     }

  353.     /** {@inheritDoc} */
  354.     public void setParameter(final String name, final double value)
  355.         throws IllegalArgumentException {
  356.         complainIfNotSupported(name);
  357.         mu = value;
  358.     }

  359. }