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