AbstractPropagator.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.propagation;

  18. import org.hipparchus.linear.RealMatrix;
  19. import org.orekit.attitudes.AttitudeProvider;
  20. import org.orekit.errors.OrekitException;
  21. import org.orekit.errors.OrekitMessages;
  22. import org.orekit.frames.Frame;
  23. import org.orekit.propagation.sampling.StepHandlerMultiplexer;
  24. import org.orekit.time.AbsoluteDate;
  25. import org.orekit.utils.DataDictionary;
  26. import org.orekit.utils.DoubleArrayDictionary;
  27. import org.orekit.utils.TimeSpanMap;

  28. import java.util.ArrayList;
  29. import java.util.Collections;
  30. import java.util.HashMap;
  31. import java.util.LinkedList;
  32. import java.util.List;
  33. import java.util.Map;
  34. import java.util.Queue;

  35. /** Common handling of {@link Propagator} methods for propagators.
  36.  * <p>
  37.  * This abstract class allows to provide easily the full set of {@link Propagator}
  38.  * methods, including all propagation modes support and discrete events support for
  39.  * any simple propagation method.
  40.  * </p>
  41.  * @author Luc Maisonobe
  42.  */
  43. public abstract class AbstractPropagator implements Propagator {

  44.     /** Multiplexer for step handlers. */
  45.     private final StepHandlerMultiplexer multiplexer;

  46.     /** Start date. */
  47.     private AbsoluteDate startDate;

  48.     /** Attitude provider. */
  49.     private AttitudeProvider attitudeProvider;

  50.     /** Providers for additional data. */
  51.     private final List<AdditionalDataProvider<?>> additionalDataProviders;

  52.     /** States managed by no generators. */
  53.     private final Map<String, TimeSpanMap<Object>> unmanagedStates;

  54.     /** Initial state. */
  55.     private SpacecraftState initialState;

  56.     /** Harvester for State Transition Matrix and Jacobian matrix. */
  57.     private AbstractMatricesHarvester harvester;

  58.     /** Build a new instance.
  59.      */
  60.     protected AbstractPropagator() {
  61.         multiplexer              = new StepHandlerMultiplexer();
  62.         additionalDataProviders  = new ArrayList<>();
  63.         unmanagedStates          = new HashMap<>();
  64.         harvester                = null;
  65.     }

  66.     /** Set a start date.
  67.      * @param startDate start date
  68.      */
  69.     protected void setStartDate(final AbsoluteDate startDate) {
  70.         this.startDate = startDate;
  71.     }

  72.     /** Get the start date.
  73.      * @return start date
  74.      */
  75.     protected AbsoluteDate getStartDate() {
  76.         return startDate;
  77.     }

  78.     /**  {@inheritDoc} */
  79.     public AttitudeProvider getAttitudeProvider() {
  80.         return attitudeProvider;
  81.     }

  82.     /**  {@inheritDoc} */
  83.     public void setAttitudeProvider(final AttitudeProvider attitudeProvider) {
  84.         this.attitudeProvider = attitudeProvider;
  85.     }

  86.     /** {@inheritDoc} */
  87.     public SpacecraftState getInitialState() {
  88.         return initialState;
  89.     }

  90.     /** {@inheritDoc} */
  91.     public Frame getFrame() {
  92.         return initialState.getFrame();
  93.     }

  94.     /** {@inheritDoc} */
  95.     public void resetInitialState(final SpacecraftState state) {
  96.         initialState = state;
  97.         setStartDate(state.getDate());
  98.     }

  99.     /** {@inheritDoc} */
  100.     public StepHandlerMultiplexer getMultiplexer() {
  101.         return multiplexer;
  102.     }

  103.     /** {@inheritDoc} */
  104.     @Override
  105.     public void addAdditionalDataProvider(final AdditionalDataProvider<?> provider) {

  106.         // check if the name is already used
  107.         if (isAdditionalDataManaged(provider.getName())) {
  108.             // this additional state is already registered, complain
  109.             throw new OrekitException(OrekitMessages.ADDITIONAL_STATE_NAME_ALREADY_IN_USE,
  110.                                       provider.getName());
  111.         }

  112.         // this is really a new name, add it
  113.         additionalDataProviders.add(provider);

  114.     }

  115.     /** {@inheritDoc} */
  116.     @Override
  117.     public List<AdditionalDataProvider<?>> getAdditionalDataProviders() {
  118.         return Collections.unmodifiableList(additionalDataProviders);
  119.     }

  120.     /** {@inheritDoc} */
  121.     @Override
  122.     public MatricesHarvester setupMatricesComputation(final String stmName, final RealMatrix initialStm,
  123.                                                       final DoubleArrayDictionary initialJacobianColumns) {
  124.         if (stmName == null) {
  125.             throw new OrekitException(OrekitMessages.NULL_ARGUMENT, "stmName");
  126.         }
  127.         harvester = createHarvester(stmName, initialStm, initialJacobianColumns);
  128.         return harvester;
  129.     }

  130.     /** Create the harvester suitable for propagator.
  131.      * @param stmName State Transition Matrix state name
  132.      * @param initialStm initial State Transition Matrix ∂Y/∂Y₀,
  133.      * if null (which is the most frequent case), assumed to be 6x6 identity
  134.      * @param initialJacobianColumns initial columns of the Jacobians matrix with respect to parameters,
  135.      * if null or if some selected parameters are missing from the dictionary, the corresponding
  136.      * initial column is assumed to be 0
  137.      * @return harvester to retrieve computed matrices during and after propagation
  138.      * @since 11.1
  139.      */
  140.     protected AbstractMatricesHarvester createHarvester(final String stmName, final RealMatrix initialStm,
  141.                                                         final DoubleArrayDictionary initialJacobianColumns) {
  142.         // FIXME: not implemented as of 11.1
  143.         throw new UnsupportedOperationException();
  144.     }

  145.     /** Get the harvester.
  146.      * @return harvester, or null if it was not created
  147.      * @since 11.1
  148.      */
  149.     protected AbstractMatricesHarvester getHarvester() {
  150.         return harvester;
  151.     }

  152.     /** Update state by adding unmanaged states.
  153.      * @param original original state
  154.      * @return updated state, with unmanaged states included
  155.      * @see #updateAdditionalData(SpacecraftState)
  156.      */
  157.     protected SpacecraftState updateUnmanagedData(final SpacecraftState original) {

  158.         // start with original state,
  159.         // which may already contain additional data, for example in interpolated ephemerides
  160.         SpacecraftState updated = original;

  161.         // update the data providers not managed by providers
  162.         for (final Map.Entry<String, TimeSpanMap<Object>> entry : unmanagedStates.entrySet()) {
  163.             updated = updated.addAdditionalData(entry.getKey(),
  164.                                                 entry.getValue().get(original.getDate()));
  165.         }

  166.         return updated;

  167.     }

  168.     /** Update state by adding all additional data.
  169.      * @param original original state
  170.      * @return updated state, with all additional data included
  171.      * (including {@link #updateUnmanagedData(SpacecraftState) unmanaged} data)
  172.      * @see #addAdditionalDataProvider(AdditionalDataProvider)
  173.      * @see #updateUnmanagedData(SpacecraftState)
  174.      */
  175.     public SpacecraftState updateAdditionalData(final SpacecraftState original) {

  176.         // start with original state and unmanaged data
  177.         SpacecraftState updated = updateUnmanagedData(original);

  178.         // set up queue for providers
  179.         final Queue<AdditionalDataProvider<?>> pending = new LinkedList<>(getAdditionalDataProviders());

  180.         // update the additional data managed by providers, taking care of dependencies
  181.         int yieldCount = 0;
  182.         while (!pending.isEmpty()) {
  183.             final AdditionalDataProvider<?> provider = pending.remove();
  184.             if (provider.yields(updated)) {
  185.                 // this generator has to wait for another one,
  186.                 // we put it again in the pending queue
  187.                 pending.add(provider);
  188.                 if (++yieldCount >= pending.size()) {
  189.                     // all pending providers yielded!, they probably need data not yet initialized
  190.                     // we let the propagation proceed, if these data are really needed right now
  191.                     // an appropriate exception will be triggered when caller tries to access them
  192.                     break;
  193.                 }
  194.             } else {
  195.                 // we can use this provider right now
  196.                 updated    = provider.update(updated);
  197.                 yieldCount = 0;
  198.             }
  199.         }

  200.         return updated;

  201.     }

  202.     /**
  203.      * Initialize the additional state providers at the start of propagation.
  204.      * @param target date of propagation. Not equal to {@code initialState.getDate()}.
  205.      * @since 11.2
  206.      */
  207.     protected void initializeAdditionalData(final AbsoluteDate target) {
  208.         for (final AdditionalDataProvider<?> provider : additionalDataProviders) {
  209.             provider.init(initialState, target);
  210.         }
  211.     }

  212.     /** {@inheritDoc} */
  213.     public boolean isAdditionalDataManaged(final String name) {
  214.         for (final AdditionalDataProvider<?> provider : additionalDataProviders) {
  215.             if (provider.getName().equals(name)) {
  216.                 return true;
  217.             }
  218.         }
  219.         return false;
  220.     }

  221.     /** {@inheritDoc} */
  222.     public String[] getManagedAdditionalData() {
  223.         final String[] managed = new String[additionalDataProviders.size()];
  224.         for (int i = 0; i < managed.length; ++i) {
  225.             managed[i] = additionalDataProviders.get(i).getName();
  226.         }
  227.         return managed;
  228.     }

  229.     /** {@inheritDoc} */
  230.     public SpacecraftState propagate(final AbsoluteDate target) {
  231.         if (startDate == null) {
  232.             startDate = getInitialState().getDate();
  233.         }
  234.         return propagate(startDate, target);
  235.     }

  236.     /** Initialize propagation.
  237.      * @since 10.1
  238.      */
  239.     protected void initializePropagation() {

  240.         unmanagedStates.clear();

  241.         if (initialState != null) {
  242.             // there is an initial state
  243.             // (null initial states occur for example in interpolated ephemerides)
  244.             // copy the additional data present in initialState but otherwise not managed
  245.             for (final DataDictionary.Entry initial : initialState.getAdditionalDataValues().getData()) {
  246.                 if (!isAdditionalDataManaged(initial.getKey())) {
  247.                     // this additional data is in the initial state, but is unknown to the propagator
  248.                     // we store it in a way event handlers may change it
  249.                     unmanagedStates.put(initial.getKey(), new TimeSpanMap<>(initial.getValue()));
  250.                 }
  251.             }
  252.         }
  253.     }

  254.     /** Notify about a state change.
  255.      * @param state new state
  256.      */
  257.     protected void stateChanged(final SpacecraftState state) {
  258.         final AbsoluteDate date    = state.getDate();
  259.         final boolean      forward = date.durationFrom(getStartDate()) >= 0.0;
  260.         for (final DataDictionary.Entry changed : state.getAdditionalDataValues().getData()) {
  261.             final TimeSpanMap<Object> tsm = unmanagedStates.get(changed.getKey());
  262.             if (tsm != null) {
  263.                 // this is an unmanaged state
  264.                 if (forward) {
  265.                     tsm.addValidAfter(changed.getValue(), date, false);
  266.                 } else {
  267.                     tsm.addValidBefore(changed.getValue(), date, false);
  268.                 }
  269.             }
  270.         }
  271.     }

  272. }