1   /* Copyright 2002-2021 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.forces.empirical;
18  
19  import java.util.ArrayList;
20  import java.util.Collections;
21  import java.util.List;
22  import java.util.NavigableSet;
23  import java.util.stream.Stream;
24  
25  import org.hipparchus.Field;
26  import org.hipparchus.CalculusFieldElement;
27  import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
28  import org.hipparchus.geometry.euclidean.threed.Vector3D;
29  import org.hipparchus.util.MathArrays;
30  import org.orekit.attitudes.Attitude;
31  import org.orekit.attitudes.AttitudeProvider;
32  import org.orekit.attitudes.FieldAttitude;
33  import org.orekit.forces.AbstractForceModel;
34  import org.orekit.propagation.FieldSpacecraftState;
35  import org.orekit.propagation.SpacecraftState;
36  import org.orekit.propagation.events.EventDetector;
37  import org.orekit.propagation.events.FieldEventDetector;
38  import org.orekit.time.AbsoluteDate;
39  import org.orekit.time.FieldAbsoluteDate;
40  import org.orekit.utils.ParameterDriver;
41  import org.orekit.utils.TimeSpanMap;
42  import org.orekit.utils.TimeSpanMap.Span;
43  import org.orekit.utils.TimeSpanMap.Transition;
44  
45  /** Time span parametric acceleration model.
46   *  <p>
47   *  This class is closely related to {@link org.orekit.forces.empirical.ParametricAcceleration ParametricAcceleration} class.<br>
48   *  The difference is that it has a {@link TimeSpanMap} of {@link AccelerationModel} objects as attribute
49   *  instead of a single {@link AccelerationModel} object. <br>
50   *  The idea behind this model is to allow the user to design a parametric acceleration model that can see its physical parameters
51   *  change with time, at dates chosen by the user. <br>
52   *  </p>
53   *  <p>
54   *  This is a behavior that can be sought in precise orbit determination.<br>
55   *  Indeed for this type of application, the empirical parameters must be revalued at
56   *  each new orbit.
57   *  </p>
58   *  <b>Usage</b>:<ul>
59   *  <li><u>Construction</u>: constructor takes an acceleration direction, an attitude mode (or an inertial flag) and
60   *  an AccelerationModel model.<br>
61   *  This last model will be your initial AccelerationModel model and it will be initially valid for the whole time line.<br>
62   *  The real validity of this first entry will be truncated as other AccelerationModel models are added.
63   *  <li><u>Time spans</u>: AccelerationModel models are added using methods {@link #addAccelerationModelValidAfter(AccelerationModel, AbsoluteDate)}
64   *   or {@link #addAccelerationModelValidBefore(AccelerationModel, AbsoluteDate)}.<br>
65   *   Recommendations are the same than the ones in {@link TimeSpanMap}, meaning: <ul>
66   *   <li>As an entry is added, it truncates the validity of the neighboring entries already present in the map;
67   *   <li><b>The transition dates should be entered only once</b>. Repeating a transition date will lead to unexpected result and is not supported;
68   *   <li>It is advised to order your AccelerationModel models chronologically when adding them to avoid any confusion.
69   *   </ul>
70   *   <li><u>Naming the parameter drivers</u>: It is strongly advised to give a custom name to the {@link ParameterDriver}(s)
71   *   of each AccelerationModel model that is added to the object. This will allow you keeping track of the evolution of your models.<br>
72   *   Different names are mandatory to differentiate the different drivers.<br>
73   *   Since there is no default name for acceleration model parameters, you must handle the driver names to consider
74   *   different names when adding a new acceleration model.
75   *   </ul>
76   * @author Bryan Cazabonne
77   * @since 10.3
78   */
79  public class TimeSpanParametricAcceleration extends AbstractForceModel {
80  
81      /** Prefix for dates before in the parameter drivers' name. */
82      public static final String DATE_BEFORE = " - Before ";
83  
84      /** Prefix for dates after in the parameter drivers' name. */
85      public static final String DATE_AFTER = " - After ";
86  
87      /** Direction of the acceleration in defining frame. */
88      private final Vector3D direction;
89  
90      /** Flag for inertial acceleration direction. */
91      private final boolean isInertial;
92  
93      /** The attitude to override, if set. */
94      private final AttitudeProvider attitudeOverride;
95  
96      /** TimeSpanMap of AccelerationModel objects. */
97      private final TimeSpanMap<AccelerationModel> accelerationModelTimeSpanMap;
98  
99      /** Simple constructor.
100      * @param direction acceleration direction in overridden spacecraft frame
101      * @param isInertial if true, direction is defined in the same inertial
102      * frame used for propagation (i.e. {@link SpacecraftState#getFrame()}),
103      * otherwise direction is defined in spacecraft frame (i.e. using the
104      * propagation {@link
105      * org.orekit.propagation.Propagator#setAttitudeProvider(AttitudeProvider)
106      * attitude law})
107      * @param accelerationModel acceleration model used to compute the contribution of the empirical acceleration
108      */
109     public TimeSpanParametricAcceleration(final Vector3D direction,
110                                           final boolean isInertial,
111                                           final AccelerationModel accelerationModel) {
112         this(direction, isInertial, null, accelerationModel);
113     }
114 
115     /** Simple constructor.
116      * @param direction acceleration direction in overridden spacecraft frame
117      * frame used for propagation (i.e. {@link SpacecraftState#getFrame()}),
118      * otherwise direction is defined in spacecraft frame (i.e. using the
119      * propagation {@link
120      * org.orekit.propagation.Propagator#setAttitudeProvider(AttitudeProvider)
121      * attitude law})
122      * @param attitudeOverride provider for attitude used to compute acceleration
123      * @param accelerationModel acceleration model used to compute the contribution of the empirical acceleration
124      */
125     public TimeSpanParametricAcceleration(final Vector3D direction,
126                                           final AttitudeProvider attitudeOverride,
127                                           final AccelerationModel accelerationModel) {
128         this(direction, false, attitudeOverride, accelerationModel);
129     }
130 
131     /** Simple constructor.
132      * @param direction acceleration direction in overridden spacecraft frame
133      * @param isInertial if true, direction is defined in the same inertial
134      * frame used for propagation (i.e. {@link SpacecraftState#getFrame()}),
135      * otherwise direction is defined in spacecraft frame (i.e. using the
136      * propagation {@link
137      * org.orekit.propagation.Propagator#setAttitudeProvider(AttitudeProvider)
138      * attitude law})
139      * @param attitudeOverride provider for attitude used to compute acceleration
140      * @param accelerationModel acceleration model used to compute the contribution of the empirical acceleration
141      */
142     private TimeSpanParametricAcceleration(final Vector3D direction,
143                                            final boolean isInertial,
144                                            final AttitudeProvider attitudeOverride,
145                                            final AccelerationModel accelerationModel) {
146         this.direction                    = direction;
147         this.isInertial                   = isInertial;
148         this.attitudeOverride             = attitudeOverride;
149         this.accelerationModelTimeSpanMap = new TimeSpanMap<>(accelerationModel);
150     }
151 
152     /** {@inheritDoc} */
153     @Override
154     public void init(final SpacecraftState initialState, final AbsoluteDate target) {
155         accelerationModelTimeSpanMap.forEach(accelerationModel -> accelerationModel.init(initialState, target));
156     }
157 
158     /** Add an AccelerationModel entry valid before a limit date.<br>
159      * <p>
160      * Using <code>addAccelerationModelValidBefore(entry, t)</code> will make <code>entry</code>
161      * valid in ]-∞, t[ (note the open bracket).
162      * <p>
163      * <b>WARNING</b>: Since there is no default name for acceleration model parameters,
164      * the user must handle itself the driver names to consider different names
165      * (i.e. different parameters) when adding a new acceleration model.
166      * @param accelerationModel AccelerationModel entry
167      * @param latestValidityDate date before which the entry is valid
168      * (must be different from <b>all</b> dates already used for transitions)
169      */
170     public void addAccelerationModelValidBefore(final AccelerationModel accelerationModel, final AbsoluteDate latestValidityDate) {
171         accelerationModelTimeSpanMap.addValidBefore(accelerationModel, latestValidityDate);
172     }
173 
174     /** Add a AccelerationModel entry valid after a limit date.<br>
175      * <p>
176      * Using <code>addAccelerationModelValidAfter(entry, t)</code> will make <code>entry</code>
177      * valid in [t, +∞[ (note the closed bracket).
178      * <p>
179      * <b>WARNING</b>: Since there is no default name for acceleration model parameters,
180      * the user must handle itself the driver names to consider different names
181      * (i.e. different parameters) when adding a new acceleration model.
182      * @param accelerationModel AccelerationModel entry
183      * @param earliestValidityDate date after which the entry is valid
184      * (must be different from <b>all</b> dates already used for transitions)
185      */
186     public void addAccelerationModelValidAfter(final AccelerationModel accelerationModel, final AbsoluteDate earliestValidityDate) {
187         accelerationModelTimeSpanMap.addValidAfter(accelerationModel, earliestValidityDate);
188     }
189 
190     /** Get the {@link AccelerationModel} model valid at a date.
191      * @param date the date of validity
192      * @return the AccelerationModel model valid at date
193      */
194     public AccelerationModel getAccelerationModel(final AbsoluteDate date) {
195         return accelerationModelTimeSpanMap.get(date);
196     }
197 
198     /** Get the {@link AccelerationModel} {@link Span} containing a specified date.
199      * @param date date belonging to the desired time span
200      * @return the AccelerationModel time span containing the specified date
201      */
202     public Span<AccelerationModel> getAccelerationModelSpan(final AbsoluteDate date) {
203         return accelerationModelTimeSpanMap.getSpan(date);
204     }
205 
206     /** Extract a range of the {@link AccelerationModel} map.
207      * <p>
208      * The object returned will be a new independent instance that will contain
209      * only the transitions that lie in the specified range.
210      * </p>
211      * See the {@link TimeSpanMap#extractRange TimeSpanMap.extractRange method} for more.
212      * @param start earliest date at which a transition is included in the range
213      * (may be set to {@link AbsoluteDate#PAST_INFINITY} to keep all early transitions)
214      * @param end latest date at which a transition is included in the r
215      * (may be set to {@link AbsoluteDate#FUTURE_INFINITY} to keep all late transitions)
216      * @return a new TimeSpanMap instance of AccelerationModel with all transitions restricted to the specified range
217      */
218     public TimeSpanMap<AccelerationModel> extractAccelerationModelRange(final AbsoluteDate start, final AbsoluteDate end) {
219         return accelerationModelTimeSpanMap.extractRange(start, end);
220     }
221 
222     /** Get the {@link Transition}s of the acceleration model time span map.
223      * @return the {@link Transition}s for the acceleration model time span map
224      */
225     public NavigableSet<Transition<AccelerationModel>> getTransitions() {
226         return accelerationModelTimeSpanMap.getTransitions();
227     }
228 
229     /** {@inheritDoc} */
230     @Override
231     public boolean dependsOnPositionOnly() {
232         return isInertial;
233     }
234 
235     /** {@inheritDoc} */
236     @Override
237     public Vector3D acceleration(final SpacecraftState state,
238                                  final double[] parameters) {
239 
240         // Date
241         final AbsoluteDate date = state.getDate();
242 
243         // Compute inertial direction
244         final Vector3D inertialDirection;
245         if (isInertial) {
246             // the acceleration direction is already defined in the inertial frame
247             inertialDirection = direction;
248         } else {
249             final Attitude attitude;
250             if (attitudeOverride == null) {
251                 // the acceleration direction is defined in spacecraft frame as set by the propagator
252                 attitude = state.getAttitude();
253             } else {
254                 // the acceleration direction is defined in a dedicated frame
255                 attitude = attitudeOverride.getAttitude(state.getOrbit(), date, state.getFrame());
256             }
257             inertialDirection = attitude.getRotation().applyInverseTo(direction);
258         }
259 
260         // Extract the proper parameters valid at date from the input array
261         final double[] extractedParameters = extractParameters(parameters, date);
262 
263         // Compute and return the parametric acceleration
264         return new Vector3D(getAccelerationModel(date).signedAmplitude(state, extractedParameters), inertialDirection);
265 
266     }
267 
268     /** {@inheritDoc} */
269     @Override
270     public <T extends CalculusFieldElement<T>> FieldVector3D<T> acceleration(final FieldSpacecraftState<T> state,
271                                                                          final T[] parameters) {
272 
273         // Date
274         final FieldAbsoluteDate<T> date = state.getDate();
275 
276         // Compute inertial direction
277         final FieldVector3D<T> inertialDirection;
278         if (isInertial) {
279             // the acceleration direction is already defined in the inertial frame
280             inertialDirection = new FieldVector3D<>(state.getDate().getField(), direction);
281         } else {
282             final FieldAttitude<T> attitude;
283             if (attitudeOverride == null) {
284                 // the acceleration direction is defined in spacecraft frame as set by the propagator
285                 attitude = state.getAttitude();
286             } else {
287                 // the acceleration direction is defined in a dedicated frame
288                 attitude = attitudeOverride.getAttitude(state.getOrbit(), date, state.getFrame());
289             }
290             inertialDirection = attitude.getRotation().applyInverseTo(direction);
291         }
292 
293         // Extract the proper parameters valid at date from the input array
294         final T[] extractedParameters = extractParameters(parameters, date);
295 
296         // Compute and return the parametric acceleration
297         return new FieldVector3D<>(getAccelerationModel(date.toAbsoluteDate()).signedAmplitude(state, extractedParameters), inertialDirection);
298 
299     }
300 
301     /** {@inheritDoc} */
302     @Override
303     public Stream<EventDetector> getEventsDetectors() {
304         return Stream.empty();
305     }
306 
307     /** {@inheritDoc} */
308     @Override
309     public <T extends CalculusFieldElement<T>> Stream<FieldEventDetector<T>> getFieldEventsDetectors(final Field<T> field) {
310         return Stream.empty();
311     }
312 
313     /** {@inheritDoc}
314      * <p>
315      * All the parameter drivers of all AccelerationModel models are returned in an array.
316      * Models are ordered chronologically.
317      * </p>
318      */
319     @Override
320     public List<ParameterDriver> getParametersDrivers() {
321 
322         // Get all transitions from the TimeSpanMap
323         final List<ParameterDriver> listParameterDrivers = new ArrayList<>();
324         final NavigableSet<Transition<AccelerationModel>> accelerationModelTransitions =  getTransitions();
325 
326         // Loop on the transitions
327         for (Transition<AccelerationModel> transition : accelerationModelTransitions) {
328             // Add all the "before" parameter drivers of each transition
329             for (ParameterDriver driver : transition.getBefore().getParametersDrivers()) {
330                 // Add the driver only if the name does not exist already
331                 if (!findByName(listParameterDrivers, driver.getName())) {
332                     listParameterDrivers.add(driver);
333                 }
334             }
335         }
336 
337         // Finally, add the "after" parameter drivers of the last transition
338         for (ParameterDriver driver : accelerationModelTransitions.last().getAfter().getParametersDrivers()) {
339             // Adds only if the name does not exist already
340             if (!findByName(listParameterDrivers, driver.getName())) {
341                 listParameterDrivers.add(driver);
342             }
343         }
344 
345         // Return an array of parameter drivers with no duplicated name
346         return Collections.unmodifiableList(listParameterDrivers);
347 
348     }
349 
350     /** Extract the proper parameter drivers' values from the array in input of the
351      * {@link #acceleration(SpacecraftState, double[]) acceleration} method.
352      *  Parameters are filtered given an input date.
353      * @param parameters the input parameters array
354      * @param date the date
355      * @return the parameters given the date
356      */
357     public double[] extractParameters(final double[] parameters, final AbsoluteDate date) {
358 
359         // Get the acceleration model parameter drivers of the date
360         final List<ParameterDriver> empiricalParameterDriver = getAccelerationModel(date).getParametersDrivers();
361 
362         // Find out the indexes of the parameters in the whole array of parameters
363         final List<ParameterDriver> allParameters = getParametersDrivers();
364         final double[] outParameters = new double[empiricalParameterDriver.size()];
365         int index = 0;
366         for (int i = 0; i < allParameters.size(); i++) {
367             final String driverName = allParameters.get(i).getName();
368             for (ParameterDriver accDriver : empiricalParameterDriver) {
369                 if (accDriver.getName().equals(driverName)) {
370                     outParameters[index++] = parameters[i];
371                 }
372             }
373         }
374         return outParameters;
375     }
376 
377     /** Extract the proper parameter drivers' values from the array in input of the
378      * {@link #acceleration(FieldSpacecraftState, CalculusFieldElement[]) acceleration} method.
379      *  Parameters are filtered given an input date.
380      * @param parameters the input parameters array
381      * @param date the date
382      * @param <T> extends CalculusFieldElement
383      * @return the parameters given the date
384      */
385     public <T extends CalculusFieldElement<T>> T[] extractParameters(final T[] parameters,
386                                                                  final FieldAbsoluteDate<T> date) {
387 
388         // Get the acceleration parameter drivers of the date
389         final List<ParameterDriver> empiricalParameterDriver = getAccelerationModel(date.toAbsoluteDate()).getParametersDrivers();
390 
391         // Find out the indexes of the parameters in the whole array of parameters
392         final List<ParameterDriver> allParameters = getParametersDrivers();
393         final T[] outParameters = MathArrays.buildArray(date.getField(), empiricalParameterDriver.size());
394         int index = 0;
395         for (int i = 0; i < allParameters.size(); i++) {
396             final String driverName = allParameters.get(i).getName();
397             for (ParameterDriver accDriver : empiricalParameterDriver) {
398                 if (accDriver.getName().equals(driverName)) {
399                     outParameters[index++] = parameters[i];
400                 }
401             }
402         }
403         return outParameters;
404     }
405 
406     /** Find if a parameter driver with a given name already exists in a list of parameter drivers.
407      * @param driversList the list of parameter drivers
408      * @param name the parameter driver's name to filter with
409      * @return true if the name was found, false otherwise
410      */
411     private boolean findByName(final List<ParameterDriver> driversList, final String name) {
412         for (final ParameterDriver d : driversList) {
413             if (d.getName().equals(name)) {
414                 return true;
415             }
416         }
417         return false;
418     }
419 
420 }