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