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.events;
18  
19  import java.lang.reflect.Array;
20  import java.util.ArrayList;
21  import java.util.List;
22  import java.util.stream.Stream;
23  
24  import org.hipparchus.CalculusFieldElement;
25  import org.hipparchus.Field;
26  import org.hipparchus.ode.events.Action;
27  import org.hipparchus.util.FastMath;
28  import org.orekit.forces.ForceModel;
29  import org.orekit.propagation.semianalytical.dsst.forces.DSSTForceModel;
30  import org.orekit.time.AbsoluteDate;
31  import org.orekit.time.FieldAbsoluteDate;
32  import org.orekit.time.FieldTimeStamped;
33  import org.orekit.time.TimeStamped;
34  import org.orekit.utils.ParameterDriver;
35  
36  /** Interface for building event detectors for force models and maneuver parameters.
37   *
38   * <p>
39   * Objects implementing this interface are mainly {@link ForceModel} and {@link DSSTForceModel}.
40   *
41   * @author Luc Maisonobe
42   * @author Melina Vanel
43   * @author Maxime Journot
44   * @since 12.0
45   */
46  public interface EventDetectorsProvider {
47  
48      /** Accuracy of switching events dates (s). */
49      double DATATION_ACCURACY = 1.0e-10;
50  
51      /** Get the discrete events related to the model.
52       *
53       * <p><b>This method is not intended to be called several time, only once by a propagator</b>,
54       * as it has the side effect of rebuilding the events detectors when called
55       *
56       * @return stream of event detectors
57       */
58      Stream<EventDetector> getEventDetectors();
59  
60      /** Get the discrete events related to the model.
61       *
62       * <p><b>This method is not intended to be called several time, only once by a propagator</b>,
63       * as it has the side effect of rebuilding the events detectors when called
64       *
65       * @param field field to which the state belongs
66       * @param <T> extends CalculusFieldElement&lt;T&gt;
67       * @return stream of event detectors
68       */
69      <T extends CalculusFieldElement<T>> Stream<FieldEventDetector<T>> getFieldEventDetectors(Field<T> field);
70  
71      /** Get the discrete events related to the model from a list of {@link ParameterDriver}
72       *
73       * <p>Date detectors are used to cleanly stop the propagator and reset
74       * the state derivatives at transition dates (if any) of the parameter drivers.
75       *
76       * <p><b>This method is not intended to be called several times, only once by a propagator</b>,
77       * as it has the side effect of rebuilding the events detectors when called.
78       *
79       * @param parameterDrivers list of parameter drivers
80       * @return stream of event detectors
81       */
82      default Stream<EventDetector> getEventDetectors(List<ParameterDriver> parameterDrivers) {
83          // If force model does not have parameter Driver, an empty stream is given as results
84          final ArrayList<TimeStamped> transitionDates = new ArrayList<>();
85          for (final ParameterDriver driver : parameterDrivers) {
86              // Get the transitions' dates from the TimeSpanMap
87              for (AbsoluteDate date : driver.getTransitionDates()) {
88                  transitionDates.add(date);
89              }
90          }
91          // Either force model does not have any parameter driver or only contains parameter driver with only 1 span
92          if (transitionDates.size() == 0) {
93              return Stream.empty();
94  
95          } else {
96              // Sort transition dates chronologically
97              transitionDates.sort(null);
98  
99              // Find shortest duration between 2 consecutive dates
100             double shortestDuration = AbstractDetector.DEFAULT_MAXCHECK;
101             for (int i = 1; i < transitionDates.size(); i++) {
102                 // Duration from current to previous date
103                 shortestDuration = FastMath.min(shortestDuration,
104                                                 transitionDates.get(i).durationFrom(transitionDates.get(i - 1)));
105             }
106 
107             // Create the date detector containing all transition dates and return it
108             // Max check set to half the shortest duration between 2 consecutive dates
109             final DateDetector datesDetector = new DateDetector(transitionDates.toArray(new TimeStamped[0])).
110                             withMaxCheck(0.5 * shortestDuration).
111                             withMinGap(0.5 * shortestDuration).
112                             withThreshold(DATATION_ACCURACY).
113                             withHandler((state, d, increasing) -> {
114                                 return Action.RESET_DERIVATIVES;
115                             });
116             return Stream.of(datesDetector);
117         }
118     }
119 
120     /** Get the discrete events related to the model from a list of {@link ParameterDriver}
121      *
122      * <p>Date detectors are used to cleanly stop the propagator and reset
123      * the state derivatives at transition dates (if any) of the parameter drivers.
124      *
125      * <p><b>This method is not intended to be called several times, only once by a propagator</b>,
126      * as it has the side effect of rebuilding the events detectors when called.
127      *
128      * @param parameterDrivers list of parameter drivers
129      * @param field field to which the state belongs
130      * @param <T> extends CalculusFieldElement&lt;T&gt;
131      * @return stream of event detectors
132      */
133     default <T extends CalculusFieldElement<T>> Stream<FieldEventDetector<T>> getFieldEventDetectors(Field<T> field, List<ParameterDriver> parameterDrivers) {
134         // If force model does not have parameter Driver, an empty stream is given as results
135         final ArrayList<AbsoluteDate> transitionDates = new ArrayList<>();
136         for (ParameterDriver driver : parameterDrivers) {
137             // Get the transitions' dates from the TimeSpanMap
138             for (AbsoluteDate date : driver.getTransitionDates()) {
139                 transitionDates.add(date);
140             }
141         }
142         // Either force model does not have any parameter driver or only contains parameter driver with only 1 span
143         if (transitionDates.size() == 0) {
144             return Stream.empty();
145 
146         } else {
147             // Sort transition dates chronologically
148             transitionDates.sort(null);
149 
150             // Find shortest duration between 2 consecutive dates
151             double shortestDuration = AbstractDetector.DEFAULT_MAXCHECK;
152             for (int i = 1; i < transitionDates.size(); i++) {
153                 // Duration from current to previous date
154                 shortestDuration = FastMath.min(shortestDuration,
155                                                 transitionDates.get(i).durationFrom(transitionDates.get(i - 1)));
156             }
157 
158             // Initialize the date detector
159             // Max check set to half the shortest duration between 2 consecutive dates
160             @SuppressWarnings("unchecked")
161             final FieldDateDetector<T> datesDetector =
162                             new FieldDateDetector<>(field, (FieldTimeStamped<T>[]) Array.newInstance(FieldTimeStamped.class, 0)).
163                             withMaxCheck(0.5 * shortestDuration).
164                             withMinGap(0.5 * shortestDuration).
165                             withThreshold(field.getZero().newInstance(DATATION_ACCURACY)).
166                             withHandler(( state, d, increasing) -> {
167                                 return Action.RESET_DERIVATIVES;
168                             });
169             // Add all transitions' dates to the date detector
170             for (int i = 0; i < transitionDates.size(); i++) {
171                 datesDetector.addEventDate(new FieldAbsoluteDate<>(field, transitionDates.get(i)));
172             }
173             // Return the detectors
174             return Stream.of(datesDetector);
175         }
176     }
177 }