AbstractFieldTimeInterpolator.java

  1. /* Copyright 2002-2025 CS GROUP
  2.  * Licensed to CS GROUP (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.time;

  18. import org.hipparchus.CalculusFieldElement;
  19. import org.hipparchus.Field;
  20. import org.hipparchus.util.FastMath;
  21. import org.orekit.errors.OrekitIllegalArgumentException;
  22. import org.orekit.errors.OrekitInternalError;
  23. import org.orekit.errors.OrekitMessages;
  24. import org.orekit.utils.ImmutableFieldTimeStampedCache;

  25. import java.util.ArrayList;
  26. import java.util.Collection;
  27. import java.util.Collections;
  28. import java.util.List;
  29. import java.util.Optional;
  30. import java.util.stream.Collectors;
  31. import java.util.stream.Stream;

  32. /**
  33.  * Abstract class for time interpolator.
  34.  *
  35.  * @param <T> interpolated time stamped type
  36.  * @param <KK> type of the field element
  37.  *
  38.  * @author Vincent Cucchietti
  39.  */
  40. public abstract class AbstractFieldTimeInterpolator<T extends FieldTimeStamped<KK>, KK extends CalculusFieldElement<KK>>
  41.         implements FieldTimeInterpolator<T, KK> {

  42.     /** Default extrapolation time threshold: 1ms. */
  43.     public static final double DEFAULT_EXTRAPOLATION_THRESHOLD_SEC = 1e-3;

  44.     /** Default number of interpolation points. */
  45.     public static final int DEFAULT_INTERPOLATION_POINTS = 2;

  46.     /** The extrapolation threshold beyond which the propagation will fail. */
  47.     private final double extrapolationThreshold;

  48.     /** Neighbor size. */
  49.     private final int interpolationPoints;

  50.     /**
  51.      * Constructor.
  52.      *
  53.      * @param interpolationPoints number of interpolation points
  54.      * @param extrapolationThreshold extrapolation threshold beyond which the propagation will fail
  55.      */
  56.     public AbstractFieldTimeInterpolator(final int interpolationPoints, final double extrapolationThreshold) {
  57.         this.interpolationPoints    = interpolationPoints;
  58.         this.extrapolationThreshold = extrapolationThreshold;
  59.     }

  60.     /**
  61.      * Method checking if given interpolator is compatible with given sample size.
  62.      *
  63.      * @param interpolator interpolator
  64.      * @param sampleSize sample size
  65.      * @param <T> type of the field elements
  66.      */
  67.     public static <T extends CalculusFieldElement<T>> void checkInterpolatorCompatibilityWithSampleSize(
  68.             final FieldTimeInterpolator<? extends FieldTimeStamped<T>, T> interpolator,
  69.             final int sampleSize) {

  70.         // Retrieve all sub-interpolators (or a singleton list with given interpolator if there are no sub-interpolators)
  71.         final List<FieldTimeInterpolator<? extends FieldTimeStamped<T>, T>> subInterpolators =
  72.                 interpolator.getSubInterpolators();
  73.         for (final FieldTimeInterpolator<? extends FieldTimeStamped<T>, T> subInterpolator : subInterpolators) {
  74.             if (sampleSize < subInterpolator.getNbInterpolationPoints()) {
  75.                 throw new OrekitIllegalArgumentException(OrekitMessages.NOT_ENOUGH_DATA, sampleSize);
  76.             }
  77.         }
  78.     }

  79.     /** {@inheritDoc} */
  80.     @Override
  81.     public T interpolate(final FieldAbsoluteDate<KK> interpolationDate, final Stream<T> sample) {
  82.         return interpolate(interpolationDate, sample.collect(Collectors.toList()));
  83.     }

  84.     /** {@inheritDoc}. */
  85.     @Override
  86.     public T interpolate(final FieldAbsoluteDate<KK> interpolationDate, final Collection<T> sample) {
  87.         final InterpolationData interpolationData = new InterpolationData(interpolationDate, sample);
  88.         return interpolate(interpolationData);
  89.     }

  90.     /**
  91.      * Get the central date to use to find neighbors while taking into account extrapolation threshold.
  92.      *
  93.      * @param date interpolation date
  94.      * @param cachedSamples cached samples
  95.      * @param threshold extrapolation threshold
  96.      * @param <T> type of time stamped element
  97.      * @param <KK> type of calculus field element
  98.      *
  99.      * @return central date to use to find neighbors
  100.      * @since 12.0.1
  101.      */
  102.     public static <T extends FieldTimeStamped<KK>, KK extends CalculusFieldElement<KK>> FieldAbsoluteDate<KK> getCentralDate(
  103.             final FieldAbsoluteDate<KK> date,
  104.             final ImmutableFieldTimeStampedCache<T, KK> cachedSamples,
  105.             final double threshold) {
  106.         return getCentralDate(
  107.                 date,
  108.                 cachedSamples.getEarliest().getDate(),
  109.                 cachedSamples.getLatest().getDate(),
  110.                 threshold);
  111.     }

  112.     /**
  113.      * Get the central date to use to find neighbors while taking into account extrapolation threshold.
  114.      *
  115.      * @param date interpolation date
  116.      * @param minDate earliest date in the sample.
  117.      * @param maxDate latest date in the sample.
  118.      * @param threshold extrapolation threshold
  119.      * @param <KK> type of calculus field element
  120.      *
  121.      * @return central date to use to find neighbors
  122.      * @since 12.0.1
  123.      */
  124.     public static <KK extends CalculusFieldElement<KK>> FieldAbsoluteDate<KK> getCentralDate(
  125.             final FieldAbsoluteDate<KK> date,
  126.             final FieldAbsoluteDate<KK> minDate,
  127.             final FieldAbsoluteDate<KK> maxDate,
  128.             final double threshold) {
  129.         final FieldAbsoluteDate<KK> central;

  130.         if (date.compareTo(minDate) < 0 && FastMath.abs(date.durationFrom(minDate)).getReal() <= threshold) {
  131.             // avoid TimeStampedCacheException as we are still within the tolerance before minDate
  132.             central = minDate;
  133.         } else if (date.compareTo(maxDate) > 0 && FastMath.abs(date.durationFrom(maxDate)).getReal() <= threshold) {
  134.             // avoid TimeStampedCacheException as we are still within the tolerance after maxDate
  135.             central = maxDate;
  136.         } else {
  137.             central = date;
  138.         }

  139.         return central;
  140.     }

  141.     /** {@inheritDoc} */
  142.     public List<FieldTimeInterpolator<? extends FieldTimeStamped<KK>, KK>> getSubInterpolators() {
  143.         return Collections.singletonList(this);
  144.     }

  145.     /** {@inheritDoc} */
  146.     public int getNbInterpolationPoints() {
  147.         final List<FieldTimeInterpolator<? extends FieldTimeStamped<KK>, KK>> subInterpolators = getSubInterpolators();
  148.         // In case the interpolator does not have sub interpolators
  149.         if (subInterpolators.size() == 1) {
  150.             return interpolationPoints;
  151.         }
  152.         // Otherwise find maximum number of interpolation points among sub interpolators
  153.         else {
  154.             final Optional<Integer> optionalMaxNbInterpolationPoints =
  155.                     subInterpolators.stream().map(FieldTimeInterpolator::getNbInterpolationPoints).max(Integer::compareTo);
  156.             if (optionalMaxNbInterpolationPoints.isPresent()) {
  157.                 return optionalMaxNbInterpolationPoints.get();
  158.             } else {
  159.                 // This should never happen
  160.                 throw new OrekitInternalError(null);
  161.             }
  162.         }
  163.     }

  164.     /** {@inheritDoc} */
  165.     public double getExtrapolationThreshold() {
  166.         return extrapolationThreshold;
  167.     }

  168.     /**
  169.      * Add all lowest level sub interpolators to the sub interpolator list.
  170.      *
  171.      * @param subInterpolator optional sub interpolator to add
  172.      * @param subInterpolators list of sub interpolators
  173.      * @param <S> type of the field element
  174.      */
  175.     protected <S extends CalculusFieldElement<S>> void addOptionalSubInterpolatorIfDefined(
  176.             final FieldTimeInterpolator<? extends FieldTimeStamped<S>, S> subInterpolator,
  177.             final List<FieldTimeInterpolator<? extends FieldTimeStamped<S>, S>> subInterpolators) {
  178.         // Add all lowest level sub interpolators
  179.         if (subInterpolator != null) {
  180.             subInterpolators.addAll(subInterpolator.getSubInterpolators());
  181.         }
  182.     }

  183.     /**
  184.      * Interpolate instance from given interpolation data.
  185.      *
  186.      * @param interpolationData interpolation data
  187.      *
  188.      * @return interpolated instance from given interpolation data.
  189.      */
  190.     protected abstract T interpolate(InterpolationData interpolationData);

  191.     /**
  192.      * Get the time parameter which lies between [0:1] by normalizing the difference between interpolating time and previous
  193.      * date by the Δt between tabulated values.
  194.      *
  195.      * @param interpolatingTime time at which we want to interpolate a value (between previous and next tabulated dates)
  196.      * @param previousDate previous tabulated value date
  197.      * @param nextDate next tabulated value date
  198.      *
  199.      * @return time parameter which lies between [0:1]
  200.      */
  201.     protected KK getTimeParameter(final FieldAbsoluteDate<KK> interpolatingTime,
  202.                                   final FieldAbsoluteDate<KK> previousDate,
  203.                                   final FieldAbsoluteDate<KK> nextDate) {

  204.         return interpolatingTime.durationFrom(previousDate).divide(nextDate.getDate().durationFrom(previousDate));
  205.     }

  206.     /**
  207.      * Nested class used to store interpolation data.
  208.      * <p>
  209.      * It makes the interpolator thread safe.
  210.      */
  211.     public class InterpolationData {

  212.         /** Interpolation date. */
  213.         private final FieldAbsoluteDate<KK> interpolationDate;

  214.         /** Unmodifiable list of neighbors. */
  215.         private final List<T> neighborList;

  216.         /** Field of the element. */
  217.         private final Field<KK> field;

  218.         /** Fielded zero. */
  219.         private final KK zero;

  220.         /** Fielded one. */
  221.         private final KK one;

  222.         /**
  223.          * Constructor.
  224.          *
  225.          * @param interpolationDate interpolation date
  226.          * @param sample time stamped sample
  227.          */
  228.         protected InterpolationData(final FieldAbsoluteDate<KK> interpolationDate, final Collection<T> sample) {
  229.             // Handle specific case that is not handled by the immutable time stamped cache constructor
  230.             if (sample.isEmpty()) {
  231.                 throw new OrekitIllegalArgumentException(OrekitMessages.NOT_ENOUGH_DATA, 0);
  232.             }

  233.             // TODO performance: create neighborsList without copying sample.
  234.             if (sample.size() == interpolationPoints) {
  235.                 // shortcut for simple case
  236.                 // copy list to make neighborList immutable
  237.                 this.neighborList = Collections.unmodifiableList(new ArrayList<>(sample));
  238.             } else {
  239.                 // else, select sample.

  240.                 // Create immutable time stamped cache
  241.                 final ImmutableFieldTimeStampedCache<T, KK> cachedSamples =
  242.                         new ImmutableFieldTimeStampedCache<>(interpolationPoints, sample);

  243.                 // Find neighbors
  244.                 final FieldAbsoluteDate<KK> central =
  245.                         AbstractFieldTimeInterpolator.getCentralDate(
  246.                                 interpolationDate,
  247.                                 cachedSamples,
  248.                                 extrapolationThreshold);
  249.                 final Stream<T> neighborsStream = cachedSamples.getNeighbors(central);

  250.                 // Convert to unmodifiable list
  251.                 neighborList = Collections.unmodifiableList(neighborsStream.collect(Collectors.toList()));
  252.             }

  253.             // Extract field and useful terms
  254.             this.field = interpolationDate.getField();
  255.             this.zero = field.getZero();
  256.             this.one = field.getOne();

  257.             // Store interpolation date
  258.             this.interpolationDate = interpolationDate;
  259.         }

  260.         /** Get interpolation date.
  261.          * @return interpolation date
  262.          */
  263.         public FieldAbsoluteDate<KK> getInterpolationDate() {
  264.             return interpolationDate;
  265.         }

  266.         /** Get neighbor list.
  267.          * @return neighbor list
  268.          */
  269.         public List<T> getNeighborList() {
  270.             return neighborList;
  271.         }

  272.         /** Get field.
  273.          * @return field
  274.          */
  275.         public Field<KK> getField() {
  276.             return field;
  277.         }

  278.         /** Get zero.
  279.          * @return zero
  280.          */
  281.         public KK getZero() {
  282.             return zero;
  283.         }

  284.         /** Get one.
  285.          * @return one
  286.          */
  287.         public KK getOne() {
  288.             return one;
  289.         }
  290.     }
  291. }