PoissonSeries.java

  1. /* Copyright 2002-2018 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.data;

  18. import java.util.HashMap;
  19. import java.util.Map;

  20. import org.hipparchus.RealFieldElement;
  21. import org.hipparchus.util.MathArrays;

  22. /**
  23.  * Class representing a Poisson series for nutation or ephemeris computations.
  24.  * <p>
  25.  * A Poisson series is composed of a time polynomial part and a non-polynomial
  26.  * part which consist in summation series. The {@link SeriesTerm series terms}
  27.  * are harmonic functions (combination of sines and cosines) of polynomial
  28.  * <em>arguments</em>. The polynomial arguments are combinations of luni-solar or
  29.  * planetary {@link BodiesElements elements}.
  30.  * </p>
  31.  * @author Luc Maisonobe
  32.  * @see PoissonSeriesParser
  33.  * @see SeriesTerm
  34.  * @see PolynomialNutation
  35.  */
  36. public class PoissonSeries {

  37.     /** Polynomial part. */
  38.     private final PolynomialNutation polynomial;

  39.     /** Non-polynomial series. */
  40.     private final Map<Long, SeriesTerm> series;

  41.     /** Build a Poisson series from an IERS table file.
  42.      * @param polynomial polynomial part (may be null)
  43.      * @param series non-polynomial part
  44.      */
  45.     public PoissonSeries(final PolynomialNutation polynomial, final Map<Long, SeriesTerm> series) {
  46.         this.polynomial = polynomial;
  47.         this.series     = series;
  48.     }

  49.     /** Get the polynomial part of the series.
  50.      * @return polynomial part of the series.
  51.      */
  52.     public PolynomialNutation getPolynomial() {
  53.         return polynomial;
  54.     }

  55.     /** Get the number of different terms in the non-polynomial part.
  56.      * @return number of different terms in the non-polynomial part
  57.      */
  58.     public int getNonPolynomialSize() {
  59.         return series.size();
  60.     }

  61.     /** Evaluate the value of the series.
  62.      * @param elements bodies elements for nutation
  63.      * @return value of the series
  64.      */
  65.     public double value(final BodiesElements elements) {

  66.         // polynomial part
  67.         final double p = polynomial.value(elements.getTC());

  68.         // non-polynomial part
  69.         // compute sum accurately, using Møller-Knuth TwoSum algorithm without branching
  70.         // the following statements must NOT be simplified, they rely on floating point
  71.         // arithmetic properties (rounding and representable numbers)
  72.         double npHigh = 0;
  73.         double npLow  = 0;
  74.         for (final Map.Entry<Long, SeriesTerm> entry : series.entrySet()) {
  75.             final double v       = entry.getValue().value(elements)[0];
  76.             final double sum     = npHigh + v;
  77.             final double sPrime  = sum - v;
  78.             final double tPrime  = sum - sPrime;
  79.             final double deltaS  = npHigh  - sPrime;
  80.             final double deltaT  = v - tPrime;
  81.             npLow  += deltaS   + deltaT;
  82.             npHigh  = sum;
  83.         }

  84.         // add the polynomial and the non-polynomial parts
  85.         return p + (npHigh + npLow);

  86.     }

  87.     /** Evaluate the value of the series.
  88.      * @param elements bodies elements for nutation
  89.      * @param <T> type fo the field elements
  90.      * @return value of the series
  91.      */
  92.     public <T extends RealFieldElement<T>> T value(final FieldBodiesElements<T> elements) {

  93.         // polynomial part
  94.         final T tc = elements.getTC();
  95.         final T p  = polynomial.value(tc);

  96.         // non-polynomial part
  97.         T sum = tc.getField().getZero();
  98.         for (final Map.Entry<Long, SeriesTerm> entry : series.entrySet()) {
  99.             sum = sum.add(entry.getValue().value(elements)[0]);
  100.         }

  101.         // add the polynomial and the non-polynomial parts
  102.         return p.add(sum);

  103.     }

  104.     /** This interface represents a fast evaluator for Poisson series.
  105.      * @see PoissonSeries#compile(PoissonSeries...)
  106.      * @since 6.1
  107.      */
  108.     public interface  CompiledSeries {

  109.         /** Evaluate a set of Poisson series.
  110.          * @param elements bodies elements for nutation
  111.          * @return value of the series
  112.          */
  113.         double[] value(BodiesElements elements);

  114.         /** Evaluate time derivative of a set of Poisson series.
  115.          * @param elements bodies elements for nutation
  116.          * @return time derivative of the series
  117.          */
  118.         double[] derivative(BodiesElements elements);

  119.         /** Evaluate a set of Poisson series.
  120.          * @param elements bodies elements for nutation
  121.          * @param <S> the type of the field elements
  122.          * @return value of the series
  123.          */
  124.         <S extends RealFieldElement<S>> S[] value(FieldBodiesElements<S> elements);

  125.         /** Evaluate time derivative of a set of Poisson series.
  126.          * @param elements bodies elements for nutation
  127.          * @param <S> the type of the field elements
  128.          * @return time derivative of the series
  129.          */
  130.         <S extends RealFieldElement<S>> S[] derivative(FieldBodiesElements<S> elements);

  131.     }

  132.     /** Join several nutation series, for fast simultaneous evaluation.
  133.      * @param poissonSeries Poisson series to join
  134.      * @return a single function that evaluates all series together
  135.      * @since 6.1
  136.      */
  137.     @SafeVarargs
  138.     public static CompiledSeries compile(final PoissonSeries... poissonSeries) {

  139.         // store all polynomials
  140.         final PolynomialNutation[] polynomials = new PolynomialNutation[poissonSeries.length];
  141.         for (int i = 0; i < polynomials.length; ++i) {
  142.             polynomials[i] = poissonSeries[i].polynomial;
  143.         }

  144.         // gather all series terms
  145.         final Map<Long, SeriesTerm> joinedMap = new HashMap<Long, SeriesTerm>();
  146.         for (final PoissonSeries ps : poissonSeries) {
  147.             for (Map.Entry<Long, SeriesTerm> entry : ps.series.entrySet()) {
  148.                 final long key = entry.getKey();
  149.                 if (!joinedMap.containsKey(key)) {

  150.                     // retrieve all Delaunay and planetary multipliers from the key
  151.                     final int[] m = NutationCodec.decode(key);

  152.                     // prepare a new term, ready to handle the required dimension
  153.                     final SeriesTerm term =
  154.                             SeriesTerm.buildTerm(m[0],
  155.                                                  m[1], m[2], m[3], m[4], m[5],
  156.                                                  m[6], m[7], m[8], m[9], m[10], m[11], m[12], m[13], m[14]);
  157.                     term.add(poissonSeries.length - 1, -1, Double.NaN, Double.NaN);

  158.                     // store it
  159.                     joinedMap.put(key, term);

  160.                 }
  161.             }
  162.         }

  163.         // join series by sharing terms, in order to speed up evaluation
  164.         // which is dominated by the computation of sine/cosine in each term
  165.         for (int i = 0; i < poissonSeries.length; ++i) {
  166.             for (final Map.Entry<Long, SeriesTerm> entry : poissonSeries[i].series.entrySet()) {
  167.                 final SeriesTerm singleTerm = entry.getValue();
  168.                 final SeriesTerm joinedTerm = joinedMap.get(entry.getKey());
  169.                 for (int degree = 0; degree <= singleTerm.getDegree(0); ++degree) {
  170.                     joinedTerm.add(i, degree,
  171.                                    singleTerm.getSinCoeff(0, degree),
  172.                                    singleTerm.getCosCoeff(0, degree));
  173.                 }
  174.             }
  175.         }

  176.         // use a single array for faster access
  177.         final SeriesTerm[] joinedTerms = new SeriesTerm[joinedMap.size()];
  178.         int index = 0;
  179.         for (final Map.Entry<Long, SeriesTerm> entry : joinedMap.entrySet()) {
  180.             joinedTerms[index++] = entry.getValue();
  181.         }

  182.         return new CompiledSeries() {

  183.             /** {@inheritDoc} */
  184.             @Override
  185.             public double[] value(final BodiesElements elements) {

  186.                 // non-polynomial part
  187.                 // compute sum accurately, using Møller-Knuth TwoSum algorithm without branching
  188.                 // the following statements must NOT be simplified, they rely on floating point
  189.                 // arithmetic properties (rounding and representable numbers)
  190.                 final double[] npHigh = new double[polynomials.length];
  191.                 final double[] npLow  = new double[polynomials.length];
  192.                 for (final SeriesTerm term : joinedTerms) {
  193.                     final double[] termValue = term.value(elements);
  194.                     for (int i = 0; i < termValue.length; ++i) {
  195.                         final double v       = termValue[i];
  196.                         final double sum     = npHigh[i] + v;
  197.                         final double sPrime  = sum - v;
  198.                         final double tPrime  = sum - sPrime;
  199.                         final double deltaS  = npHigh[i]  - sPrime;
  200.                         final double deltaT  = v - tPrime;
  201.                         npLow[i]  += deltaS   + deltaT;
  202.                         npHigh[i]  = sum;
  203.                     }
  204.                 }

  205.                 // add residual and polynomial part
  206.                 for (int i = 0; i < npHigh.length; ++i) {
  207.                     npHigh[i] += npLow[i] + polynomials[i].value(elements.getTC());
  208.                 }
  209.                 return npHigh;

  210.             }

  211.             /** {@inheritDoc} */
  212.             @Override
  213.             public double[] derivative(final BodiesElements elements) {

  214.                 // non-polynomial part
  215.                 final double[] v = new double[polynomials.length];
  216.                 for (final SeriesTerm term : joinedTerms) {
  217.                     final double[] termDerivative = term.derivative(elements);
  218.                     for (int i = 0; i < termDerivative.length; ++i) {
  219.                         v[i] += termDerivative[i];
  220.                     }
  221.                 }

  222.                 // add polynomial part
  223.                 for (int i = 0; i < v.length; ++i) {
  224.                     v[i] += polynomials[i].derivative(elements.getTC());
  225.                 }
  226.                 return v;

  227.             }

  228.             /** {@inheritDoc} */
  229.             @Override
  230.             public <S extends RealFieldElement<S>> S[] value(final FieldBodiesElements<S> elements) {

  231.                // non-polynomial part
  232.                 final S[] v = MathArrays.buildArray(elements.getTC().getField(), polynomials.length);
  233.                 for (final SeriesTerm term : joinedTerms) {
  234.                     final S[] termValue = term.value(elements);
  235.                     for (int i = 0; i < termValue.length; ++i) {
  236.                         v[i] = v[i].add(termValue[i]);
  237.                     }
  238.                 }

  239.                 // add polynomial part
  240.                 final S tc = elements.getTC();
  241.                 for (int i = 0; i < v.length; ++i) {
  242.                     v[i] = v[i].add(polynomials[i].value(tc));
  243.                 }
  244.                 return v;

  245.             }

  246.             /** {@inheritDoc} */
  247.             @Override
  248.             public <S extends RealFieldElement<S>> S[] derivative(final FieldBodiesElements<S> elements) {

  249.                // non-polynomial part
  250.                 final S[] v = MathArrays.buildArray(elements.getTC().getField(), polynomials.length);
  251.                 for (final SeriesTerm term : joinedTerms) {
  252.                     final S[] termDerivative = term.derivative(elements);
  253.                     for (int i = 0; i < termDerivative.length; ++i) {
  254.                         v[i] = v[i].add(termDerivative[i]);
  255.                     }
  256.                 }

  257.                 // add polynomial part
  258.                 final S tc = elements.getTC();
  259.                 for (int i = 0; i < v.length; ++i) {
  260.                     v[i] = v[i].add(polynomials[i].derivative(tc));
  261.                 }
  262.                 return v;

  263.             }

  264.         };

  265.     }

  266. }