1   /* Copyright 2002-2022 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.CalculusFieldElement;
28  import org.hipparchus.Field;
29  import org.orekit.attitudes.AttitudeProvider;
30  import org.orekit.errors.OrekitException;
31  import org.orekit.errors.OrekitMessages;
32  import org.orekit.frames.Frame;
33  import org.orekit.propagation.sampling.FieldStepHandlerMultiplexer;
34  import org.orekit.time.FieldAbsoluteDate;
35  import org.orekit.utils.FieldArrayDictionary;
36  import org.orekit.utils.FieldTimeSpanMap;
37  import org.orekit.utils.TimeStampedFieldPVCoordinates;
38  
39  /** Common handling of {@link Propagator} methods for analytical propagators.
40   * <p>
41   * This abstract class allows to provide easily the full set of {@link Propagator}
42   * methods, including all propagation modes support and discrete events support for
43   * any simple propagation method.
44   * </p>
45   * @param <T> the type of the field elements
46   * @author Luc Maisonobe
47   */
48  public abstract class FieldAbstractPropagator<T extends CalculusFieldElement<T>> implements FieldPropagator<T> {
49  
50      /** Multiplexer for step handlers. */
51      private FieldStepHandlerMultiplexer<T> multiplexer;
52  
53      /** Start date. */
54      private FieldAbsoluteDate<T> startDate;
55  
56      /** Attitude provider. */
57      private AttitudeProvider attitudeProvider;
58  
59      /** Additional state providers. */
60      private final List<FieldAdditionalStateProvider<T>> additionalStateProviders;
61  
62      /** States managed by neither additional equations nor state providers. */
63      private final Map<String, FieldTimeSpanMap<T[], T>> unmanagedStates;
64  
65      /** Field used.*/
66      private final Field<T> field;
67  
68      /** Initial state. */
69      private FieldSpacecraftState<T> initialState;
70  
71      /** Build a new instance.
72       * @param field setting the field
73       */
74      protected FieldAbstractPropagator(final Field<T> field) {
75          this.field               = field;
76          multiplexer              = new FieldStepHandlerMultiplexer<>();
77          additionalStateProviders = new ArrayList<>();
78          unmanagedStates          = new HashMap<>();
79      }
80  
81      /** Set a start date.
82       * @param startDate start date
83       */
84      protected void setStartDate(final FieldAbsoluteDate<T> startDate) {
85          this.startDate = startDate;
86      }
87  
88      /** Get the start date.
89       * @return start date
90       */
91      protected FieldAbsoluteDate<T> getStartDate() {
92          return startDate;
93      }
94  
95      /**  {@inheritDoc} */
96      public AttitudeProvider getAttitudeProvider() {
97          return attitudeProvider;
98      }
99  
100     /**  {@inheritDoc} */
101     public void setAttitudeProvider(final AttitudeProvider attitudeProvider) {
102         this.attitudeProvider = attitudeProvider;
103     }
104 
105     /** Field getter.
106      * @return field used*/
107     public Field<T> getField() {
108         return field;
109     }
110 
111     /** {@inheritDoc} */
112     public FieldSpacecraftState<T> getInitialState() {
113         return initialState;
114     }
115 
116     /** {@inheritDoc} */
117     public Frame getFrame() {
118         return initialState.getFrame();
119     }
120 
121     /** {@inheritDoc} */
122     public void resetInitialState(final FieldSpacecraftState<T> state) {
123         initialState = state;
124         setStartDate(state.getDate());
125     }
126 
127     /** {@inheritDoc} */
128     public FieldStepHandlerMultiplexer<T> getMultiplexer() {
129         return multiplexer;
130     }
131 
132     /** {@inheritDoc} */
133     public void addAdditionalStateProvider(final FieldAdditionalStateProvider<T> additionalStateProvider) {
134 
135         // check if the name is already used
136         if (isAdditionalStateManaged(additionalStateProvider.getName())) {
137             // this additional state is already registered, complain
138             throw new OrekitException(OrekitMessages.ADDITIONAL_STATE_NAME_ALREADY_IN_USE,
139                                       additionalStateProvider.getName());
140         }
141 
142         // this is really a new name, add it
143         additionalStateProviders.add(additionalStateProvider);
144 
145     }
146 
147     /** {@inheritDoc} */
148     public List<FieldAdditionalStateProvider<T>> getAdditionalStateProviders() {
149         return Collections.unmodifiableList(additionalStateProviders);
150     }
151 
152     /** Update state by adding unmanaged states.
153      * @param original original state
154      * @return updated state, with unmanaged states included
155      * @see #updateAdditionalStates(FieldSpacecraftState)
156      */
157     protected FieldSpacecraftState<T> updateUnmanagedStates(final FieldSpacecraftState<T> original) {
158 
159         // start with original state,
160         // which may already contain additional states, for example in interpolated ephemerides
161         FieldSpacecraftState<T> updated = original;
162 
163         // update the states not managed by providers
164         for (final Map.Entry<String, FieldTimeSpanMap<T[], T>> entry : unmanagedStates.entrySet()) {
165             updated = updated.addAdditionalState(entry.getKey(),
166                                                  entry.getValue().get(original.getDate()));
167         }
168 
169         return updated;
170 
171     }
172 
173     /** Update state by adding all additional states.
174      * @param original original state
175      * @return updated state, with all additional states included
176      * @see #addAdditionalStateProvider(FieldAdditionalStateProvider)
177      */
178     protected FieldSpacecraftState<T> updateAdditionalStates(final FieldSpacecraftState<T> original) {
179 
180         // start with original state and unmanaged states
181         FieldSpacecraftState<T> updated = updateUnmanagedStates(original);
182 
183         // set up queue for providers
184         final Queue<FieldAdditionalStateProvider<T>> pending = new LinkedList<>(getAdditionalStateProviders());
185 
186         // update the additional states managed by providers, taking care of dependencies
187         int yieldCount = 0;
188         while (!pending.isEmpty()) {
189             final FieldAdditionalStateProvider<T> provider = pending.remove();
190             if (provider.yield(updated)) {
191                 // this generator has to wait for another one,
192                 // we put it again in the pending queue
193                 pending.add(provider);
194                 if (++yieldCount >= pending.size()) {
195                     // all pending providers yielded!, they probably need data not yet initialized
196                     // we let the propagation proceed, if these data are really needed right now
197                     // an appropriate exception will be triggered when caller tries to access them
198                     break;
199                 }
200             } else {
201                 // we can use this provider right now
202                 updated    = updated.addAdditionalState(provider.getName(), provider.getAdditionalState(updated));
203                 yieldCount = 0;
204             }
205         }
206 
207         return updated;
208 
209     }
210 
211     /** {@inheritDoc} */
212     public boolean isAdditionalStateManaged(final String name) {
213         for (final FieldAdditionalStateProvider<T> provider : additionalStateProviders) {
214             if (provider.getName().equals(name)) {
215                 return true;
216             }
217         }
218         return false;
219     }
220 
221     /** {@inheritDoc} */
222     public String[] getManagedAdditionalStates() {
223         final String[] managed = new String[additionalStateProviders.size()];
224         for (int i = 0; i < managed.length; ++i) {
225             managed[i] = additionalStateProviders.get(i).getName();
226         }
227         return managed;
228     }
229 
230     /** {@inheritDoc} */
231     public FieldSpacecraftState<T> propagate(final FieldAbsoluteDate<T> target) {
232         if (startDate == null) {
233             startDate = getInitialState().getDate();
234         }
235         return propagate(startDate, target);
236     }
237 
238     /** {@inheritDoc} */
239     public TimeStampedFieldPVCoordinates<T> getPVCoordinates(final FieldAbsoluteDate<T> date, final Frame frame) {
240         return propagate(date).getPVCoordinates(frame);
241     }
242 
243     /** Initialize propagation.
244      * @since 10.1
245      */
246     protected void initializePropagation() {
247 
248         unmanagedStates.clear();
249 
250         if (initialState != null) {
251             // there is an initial state
252             // (null initial states occur for example in interpolated ephemerides)
253             // copy the additional states present in initialState but otherwise not managed
254             for (final FieldArrayDictionary<T>.Entry initial : initialState.getAdditionalStatesValues().getData()) {
255                 if (!isAdditionalStateManaged(initial.getKey())) {
256                     // this additional state is in the initial state, but is unknown to the propagator
257                     // we store it in a way event handlers may change it
258                     unmanagedStates.put(initial.getKey(),
259                                         new FieldTimeSpanMap<>(initial.getValue(),
260                                                                initialState.getDate().getField()));
261                 }
262             }
263         }
264     }
265 
266     /** Notify about a state change.
267      * @param state new state
268      */
269     protected void stateChanged(final FieldSpacecraftState<T> state) {
270         final FieldAbsoluteDate<T> date    = state.getDate();
271         final boolean              forward = date.durationFrom(getStartDate()).getReal() >= 0.0;
272         for (final  FieldArrayDictionary<T>.Entry changed : state.getAdditionalStatesValues().getData()) {
273             final FieldTimeSpanMap<T[], T> tsm = unmanagedStates.get(changed.getKey());
274             if (tsm != null) {
275                 // this is an unmanaged state
276                 if (forward) {
277                     tsm.addValidAfter(changed.getValue(), date);
278                 } else {
279                     tsm.addValidBefore(changed.getValue(), date);
280                 }
281             }
282         }
283     }
284 
285 }