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  
19  import java.util.ArrayList;
20  import java.util.Collections;
21  import java.util.HashMap;
22  import java.util.LinkedList;
23  import java.util.List;
24  import java.util.Map;
25  import java.util.Queue;
26  
27  import org.hipparchus.linear.RealMatrix;
28  import org.orekit.attitudes.AttitudeProvider;
29  import org.orekit.errors.OrekitException;
30  import org.orekit.errors.OrekitMessages;
31  import org.orekit.frames.Frame;
32  import org.orekit.propagation.sampling.StepHandlerMultiplexer;
33  import org.orekit.time.AbsoluteDate;
34  import org.orekit.utils.DoubleArrayDictionary;
35  import org.orekit.utils.TimeSpanMap;
36  
37  /** Common handling of {@link Propagator} methods for analytical propagators.
38   * <p>
39   * This abstract class allows to provide easily the full set of {@link Propagator}
40   * methods, including all propagation modes support and discrete events support for
41   * any simple propagation method.
42   * </p>
43   * @author Luc Maisonobe
44   */
45  public abstract class AbstractPropagator implements Propagator {
46  
47      /** Multiplexer for step handlers. */
48      private final StepHandlerMultiplexer multiplexer;
49  
50      /** Start date. */
51      private AbsoluteDate startDate;
52  
53      /** Attitude provider. */
54      private AttitudeProvider attitudeProvider;
55  
56      /** Providers for additional states. */
57      private final List<AdditionalStateProvider> additionalStateProviders;
58  
59      /** States managed by no generators. */
60      private final Map<String, TimeSpanMap<double[]>> unmanagedStates;
61  
62      /** Initial state. */
63      private SpacecraftState initialState;
64  
65      /** Harvester for State Transition Matrix and Jacobian matrix. */
66      private AbstractMatricesHarvester harvester;
67  
68      /** Build a new instance.
69       */
70      protected AbstractPropagator() {
71          multiplexer              = new StepHandlerMultiplexer();
72          additionalStateProviders = new ArrayList<>();
73          unmanagedStates          = new HashMap<>();
74          harvester                = null;
75      }
76  
77      /** Set a start date.
78       * @param startDate start date
79       */
80      protected void setStartDate(final AbsoluteDate startDate) {
81          this.startDate = startDate;
82      }
83  
84      /** Get the start date.
85       * @return start date
86       */
87      protected AbsoluteDate getStartDate() {
88          return startDate;
89      }
90  
91      /**  {@inheritDoc} */
92      public AttitudeProvider getAttitudeProvider() {
93          return attitudeProvider;
94      }
95  
96      /**  {@inheritDoc} */
97      public void setAttitudeProvider(final AttitudeProvider attitudeProvider) {
98          this.attitudeProvider = attitudeProvider;
99      }
100 
101     /** {@inheritDoc} */
102     public SpacecraftState getInitialState() {
103         return initialState;
104     }
105 
106     /** {@inheritDoc} */
107     public Frame getFrame() {
108         return initialState.getFrame();
109     }
110 
111     /** {@inheritDoc} */
112     public void resetInitialState(final SpacecraftState state) {
113         initialState = state;
114         setStartDate(state.getDate());
115     }
116 
117     /** {@inheritDoc} */
118     public StepHandlerMultiplexer getMultiplexer() {
119         return multiplexer;
120     }
121 
122     /** {@inheritDoc} */
123     @Override
124     public void addAdditionalStateProvider(final AdditionalStateProvider provider) {
125 
126         // check if the name is already used
127         if (isAdditionalStateManaged(provider.getName())) {
128             // this additional state is already registered, complain
129             throw new OrekitException(OrekitMessages.ADDITIONAL_STATE_NAME_ALREADY_IN_USE,
130                                       provider.getName());
131         }
132 
133         // this is really a new name, add it
134         additionalStateProviders.add(provider);
135 
136     }
137 
138     /** {@inheritDoc} */
139     @Override
140     public List<AdditionalStateProvider> getAdditionalStateProviders() {
141         return Collections.unmodifiableList(additionalStateProviders);
142     }
143 
144     /** {@inheritDoc} */
145     @Override
146     public MatricesHarvester setupMatricesComputation(final String stmName, final RealMatrix initialStm,
147                                                       final DoubleArrayDictionary initialJacobianColumns) {
148         if (stmName == null) {
149             throw new OrekitException(OrekitMessages.NULL_ARGUMENT, "stmName");
150         }
151         harvester = createHarvester(stmName, initialStm, initialJacobianColumns);
152         return harvester;
153     }
154 
155     /** Create the harvester suitable for propagator.
156      * @param stmName State Transition Matrix state name
157      * @param initialStm initial State Transition Matrix ∂Y/∂Y₀,
158      * if null (which is the most frequent case), assumed to be 6x6 identity
159      * @param initialJacobianColumns initial columns of the Jacobians matrix with respect to parameters,
160      * if null or if some selected parameters are missing from the dictionary, the corresponding
161      * initial column is assumed to be 0
162      * @return harvester to retrieve computed matrices during and after propagation
163      * @since 11.1
164      */
165     protected AbstractMatricesHarvester createHarvester(final String stmName, final RealMatrix initialStm,
166                                                         final DoubleArrayDictionary initialJacobianColumns) {
167         // FIXME: not implemented as of 11.1
168         throw new UnsupportedOperationException();
169     }
170 
171     /** Get the harvester.
172      * @return harvester, or null if it was not created
173      * @since 11.1
174      */
175     protected AbstractMatricesHarvester getHarvester() {
176         return harvester;
177     }
178 
179     /** Update state by adding unmanaged states.
180      * @param original original state
181      * @return updated state, with unmanaged states included
182      * @see #updateAdditionalStates(SpacecraftState)
183      */
184     protected SpacecraftState updateUnmanagedStates(final SpacecraftState original) {
185 
186         // start with original state,
187         // which may already contain additional states, for example in interpolated ephemerides
188         SpacecraftState updated = original;
189 
190         // update the states not managed by providers
191         for (final Map.Entry<String, TimeSpanMap<double[]>> entry : unmanagedStates.entrySet()) {
192             updated = updated.addAdditionalState(entry.getKey(),
193                                                  entry.getValue().get(original.getDate()));
194         }
195 
196         return updated;
197 
198     }
199 
200     /** Update state by adding all additional states.
201      * @param original original state
202      * @return updated state, with all additional states included
203      * (including {@link #updateUnmanagedStates(SpacecraftState) unmanaged} states)
204      * @see #addAdditionalStateProvider(AdditionalStateProvider)
205      * @see #updateUnmanagedStates(SpacecraftState)
206      */
207     protected SpacecraftState updateAdditionalStates(final SpacecraftState original) {
208 
209         // start with original state and unmanaged states
210         SpacecraftState updated = updateUnmanagedStates(original);
211 
212         // set up queue for providers
213         final Queue<AdditionalStateProvider> pending = new LinkedList<>(getAdditionalStateProviders());
214 
215         // update the additional states managed by providers, taking care of dependencies
216         int yieldCount = 0;
217         while (!pending.isEmpty()) {
218             final AdditionalStateProvider provider = pending.remove();
219             if (provider.yields(updated)) {
220                 // this generator has to wait for another one,
221                 // we put it again in the pending queue
222                 pending.add(provider);
223                 if (++yieldCount >= pending.size()) {
224                     // all pending providers yielded!, they probably need data not yet initialized
225                     // we let the propagation proceed, if these data are really needed right now
226                     // an appropriate exception will be triggered when caller tries to access them
227                     break;
228                 }
229             } else {
230                 // we can use this provider right now
231                 updated    = provider.update(updated);
232                 yieldCount = 0;
233             }
234         }
235 
236         return updated;
237 
238     }
239 
240     /**
241      * Initialize the additional state providers at the start of propagation.
242      * @param target date of propagation. Not equal to {@code initialState.getDate()}.
243      * @since 11.2
244      */
245     protected void initializeAdditionalStates(final AbsoluteDate target) {
246         for (final AdditionalStateProvider provider : additionalStateProviders) {
247             provider.init(initialState, target);
248         }
249     }
250 
251     /** {@inheritDoc} */
252     public boolean isAdditionalStateManaged(final String name) {
253         for (final AdditionalStateProvider provider : additionalStateProviders) {
254             if (provider.getName().equals(name)) {
255                 return true;
256             }
257         }
258         return false;
259     }
260 
261     /** {@inheritDoc} */
262     public String[] getManagedAdditionalStates() {
263         final String[] managed = new String[additionalStateProviders.size()];
264         for (int i = 0; i < managed.length; ++i) {
265             managed[i] = additionalStateProviders.get(i).getName();
266         }
267         return managed;
268     }
269 
270     /** {@inheritDoc} */
271     public SpacecraftState propagate(final AbsoluteDate target) {
272         if (startDate == null) {
273             startDate = getInitialState().getDate();
274         }
275         return propagate(startDate, target);
276     }
277 
278     /** Initialize propagation.
279      * @since 10.1
280      */
281     protected void initializePropagation() {
282 
283         unmanagedStates.clear();
284 
285         if (initialState != null) {
286             // there is an initial state
287             // (null initial states occur for example in interpolated ephemerides)
288             // copy the additional states present in initialState but otherwise not managed
289             for (final DoubleArrayDictionary.Entry initial : initialState.getAdditionalStatesValues().getData()) {
290                 if (!isAdditionalStateManaged(initial.getKey())) {
291                     // this additional state is in the initial state, but is unknown to the propagator
292                     // we store it in a way event handlers may change it
293                     unmanagedStates.put(initial.getKey(), new TimeSpanMap<>(initial.getValue()));
294                 }
295             }
296         }
297     }
298 
299     /** Notify about a state change.
300      * @param state new state
301      */
302     protected void stateChanged(final SpacecraftState state) {
303         final AbsoluteDate date    = state.getDate();
304         final boolean      forward = date.durationFrom(getStartDate()) >= 0.0;
305         for (final DoubleArrayDictionary.Entry changed : state.getAdditionalStatesValues().getData()) {
306             final TimeSpanMap<double[]> tsm = unmanagedStates.get(changed.getKey());
307             if (tsm != null) {
308                 // this is an unmanaged state
309                 if (forward) {
310                     tsm.addValidAfter(changed.getValue(), date, false);
311                 } else {
312                     tsm.addValidBefore(changed.getValue(), date, false);
313                 }
314             }
315         }
316     }
317 
318 }