AbstractPropagator.java

  1. /* Copyright 2002-2024 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 java.util.ArrayList;
  19. import java.util.Collections;
  20. import java.util.HashMap;
  21. import java.util.LinkedList;
  22. import java.util.List;
  23. import java.util.Map;
  24. import java.util.Queue;

  25. import org.hipparchus.linear.RealMatrix;
  26. import org.orekit.attitudes.AttitudeProvider;
  27. import org.orekit.errors.OrekitException;
  28. import org.orekit.errors.OrekitMessages;
  29. import org.orekit.frames.Frame;
  30. import org.orekit.propagation.sampling.StepHandlerMultiplexer;
  31. import org.orekit.time.AbsoluteDate;
  32. import org.orekit.utils.DoubleArrayDictionary;
  33. import org.orekit.utils.TimeSpanMap;

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

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

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

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

  49.     /** Providers for additional states. */
  50.     private final List<AdditionalStateProvider> additionalStateProviders;

  51.     /** States managed by no generators. */
  52.     private final Map<String, TimeSpanMap<double[]>> unmanagedStates;

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

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

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

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

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

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

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

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

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

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

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

  102.     /** {@inheritDoc} */
  103.     @Override
  104.     public void addAdditionalStateProvider(final AdditionalStateProvider provider) {

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

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

  113.     }

  114.     /** {@inheritDoc} */
  115.     @Override
  116.     public List<AdditionalStateProvider> getAdditionalStateProviders() {
  117.         return Collections.unmodifiableList(additionalStateProviders);
  118.     }

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

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

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

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

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

  160.         // update the states not managed by providers
  161.         for (final Map.Entry<String, TimeSpanMap<double[]>> entry : unmanagedStates.entrySet()) {
  162.             updated = updated.addAdditionalState(entry.getKey(),
  163.                                                  entry.getValue().get(original.getDate()));
  164.         }

  165.         return updated;

  166.     }

  167.     /** Update state by adding all additional states.
  168.      * @param original original state
  169.      * @return updated state, with all additional states included
  170.      * (including {@link #updateUnmanagedStates(SpacecraftState) unmanaged} states)
  171.      * @see #addAdditionalStateProvider(AdditionalStateProvider)
  172.      * @see #updateUnmanagedStates(SpacecraftState)
  173.      */
  174.     protected SpacecraftState updateAdditionalStates(final SpacecraftState original) {

  175.         // start with original state and unmanaged states
  176.         SpacecraftState updated = updateUnmanagedStates(original);

  177.         // set up queue for providers
  178.         final Queue<AdditionalStateProvider> pending = new LinkedList<>(getAdditionalStateProviders());

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

  199.         return updated;

  200.     }

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

  211.     /** {@inheritDoc} */
  212.     public boolean isAdditionalStateManaged(final String name) {
  213.         for (final AdditionalStateProvider provider : additionalStateProviders) {
  214.             if (provider.getName().equals(name)) {
  215.                 return true;
  216.             }
  217.         }
  218.         return false;
  219.     }

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

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

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

  239.         unmanagedStates.clear();

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

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

  271. }