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.events;
18  
19  import java.util.Arrays;
20  import java.util.List;
21  import java.util.SortedSet;
22  import java.util.TreeSet;
23  import java.util.stream.Stream;
24  
25  import org.hipparchus.CalculusFieldElement;
26  import org.hipparchus.Field;
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.events.intervals.AdaptableInterval;
31  import org.orekit.propagation.events.intervals.DateDetectionAdaptableIntervalFactory;
32  import org.orekit.propagation.events.intervals.FieldAdaptableInterval;
33  import org.orekit.propagation.semianalytical.dsst.forces.DSSTForceModel;
34  import org.orekit.time.AbsoluteDate;
35  import org.orekit.time.ChronologicalComparator;
36  import org.orekit.time.FieldAbsoluteDate;
37  import org.orekit.time.FieldTimeStamped;
38  import org.orekit.time.TimeInterval;
39  import org.orekit.time.TimeStamped;
40  import org.orekit.utils.drivers.ParameterDriver;
41  
42  /** Interface for building event detectors for force models and maneuver parameters.
43   * <p>
44   * Objects implementing this interface are mainly {@link ForceModel} and {@link DSSTForceModel}.
45   * </p>
46   * @author Luc Maisonobe
47   * @author Melina Vanel
48   * @author Maxime Journot
49   * @since 12.0
50   */
51  public interface EventDetectorsProvider {
52  
53      /** Accuracy of switching events dates (s). */
54      double DATATION_ACCURACY = DateDetector.DEFAULT_THRESHOLD;
55  
56      /** Get the discrete events related to the model.
57       * <p>This method is not intended to be called several time, only once by a propagator,
58       * as it has the side effect of rebuilding the events detectors when called
59       * </p>
60       * @return stream of event detectors
61       */
62      Stream<EventDetector> getEventDetectors();
63  
64      /** Get the discrete events related to the model.
65       * <p>This method is not intended to be called several time, only once by a propagator,
66       * as it has the side effect of rebuilding the events detectors when called
67       * </p>
68       * @param field field to which the state belongs
69       * @param <T> extends CalculusFieldElement&lt;T&gt;
70       * @return stream of event detectors
71       */
72      <T extends CalculusFieldElement<T>> Stream<FieldEventDetector<T>> getFieldEventDetectors(Field<T> field);
73  
74      /** Get the discrete events related to the model from a list of {@link ParameterDriver}.
75       * <p>Date detectors are used to cleanly stop the propagator and reset
76       * the state derivatives at transition dates (if any) of the parameter drivers.
77       * </p>
78       * <p>This method is not intended to be called several times, only once by a propagator,
79       * as it has the side effect of rebuilding the events detectors when called.
80       * </p>
81       * @param parameterDrivers list of parameter drivers
82       * @return stream of event detectors
83       */
84      default Stream<EventDetector> getEventDetectors(final List<ParameterDriver> parameterDrivers) {
85          // If force model does not have parameter Driver, an empty stream is given as results
86          final SortedSet<AbsoluteDate> transitionDates = new TreeSet<>(new ChronologicalComparator());
87          for (final ParameterDriver driver : parameterDrivers) {
88              final TimeInterval validity = driver.getValidity();
89              if (validity.getStartDate().isFinite()) {
90                  transitionDates.add(validity.getStartDate());
91              }
92              if (validity.getEndDate().isFinite()) {
93                  transitionDates.add(validity.getEndDate());
94              }
95          }
96          // Either force model does not have any parameter driver or only contains parameter driver with only 1 span
97          if (transitionDates.isEmpty()) {
98              return Stream.empty();
99  
100         } else {
101             // Create the date detector containing all transition dates and return it
102             final DateDetector detector = getDateDetector(transitionDates.toArray(new AbsoluteDate[0]));
103             return Stream.of(detector);
104         }
105     }
106 
107     /** Get the discrete events related to the model from a list of {@link ParameterDriver}.
108      * <p>Date detectors are used to cleanly stop the propagator and reset
109      * the state derivatives at transition dates (if any) of the parameter drivers.
110      * </p>
111      * <p>This method is not intended to be called several times, only once by a propagator,
112      * as it has the side effect of rebuilding the events detectors when called.
113      * </p>
114      * @param parameterDrivers list of parameter drivers
115      * @param field field to which the state belongs
116      * @param <T> extends CalculusFieldElement&lt;T&gt;
117      * @return stream of event detectors
118      */
119     default <T extends CalculusFieldElement<T>> Stream<FieldEventDetector<T>> getFieldEventDetectors(final Field<T> field,
120                                                                                                      final List<ParameterDriver> parameterDrivers) {
121         // If force model does not have parameter Driver, an empty stream is given as results
122         final SortedSet<AbsoluteDate> transitionDates = new TreeSet<>(new ChronologicalComparator());
123         for (ParameterDriver driver : parameterDrivers) {
124             final TimeInterval validity = driver.getValidity();
125             if (validity.getStartDate().isFinite()) {
126                 transitionDates.add(validity.getStartDate());
127             }
128             if (validity.getEndDate().isFinite()) {
129                 transitionDates.add(validity.getEndDate());
130             }
131         }
132         // Either force model does not have any parameter driver or only contains parameter driver with only 1 span
133         if (transitionDates.isEmpty()) {
134             return Stream.empty();
135 
136         } else {
137             // Initialize the date detector
138             final FieldDateDetector<T> datesDetector =
139                 getFieldDateDetector(field, transitionDates.toArray(new AbsoluteDate[0]));
140             // Return the detectors
141             return Stream.of(datesDetector);
142         }
143     }
144 
145     /**
146      * Method building dates' detector.
147      * @param timeStampeds dates to detect
148      * @return dates detector
149      * @since 13.0
150      */
151     default DateDetector getDateDetector(final TimeStamped... timeStampeds) {
152         final AdaptableInterval maxCheck =
153             DateDetectionAdaptableIntervalFactory.getDatesDetectionInterval(timeStampeds);
154         final double minGap = DateDetectionAdaptableIntervalFactory.getMinGap(timeStampeds) / 2;
155         final DateDetector dateDetector = new DateDetector().
156                                           withMaxCheck(maxCheck).
157                                           withMinGap(minGap).
158                                           withThreshold(DATATION_ACCURACY).
159                                           withHandler(new ResetDerivativesOnEvent());
160         final SortedSet<AbsoluteDate> sortedDates = new TreeSet<>(new ChronologicalComparator());
161         sortedDates.addAll(Arrays.stream(timeStampeds).map(TimeStamped::getDate).toList());
162         for (final AbsoluteDate date : sortedDates) {
163             dateDetector.addEventDate(date);
164         }
165         return dateDetector;
166     }
167 
168     /**
169      * Method building dates' detector.
170      * @param field field
171      * @param timeStampeds dates to detect
172      * @param <T> field type
173      * @return dates detector
174      * @since 13.0
175      */
176     default <T extends CalculusFieldElement<T>> FieldDateDetector<T> getFieldDateDetector(final Field<T> field,
177                                                                                           final TimeStamped... timeStampeds) {
178         @SuppressWarnings("unchecked")
179         final FieldAdaptableInterval<T> maxCheck =
180             DateDetectionAdaptableIntervalFactory.getDatesDetectionFieldInterval(
181                 Arrays.stream(timeStampeds).
182                        map(timeStamped -> new FieldAbsoluteDate<>(field, timeStamped.getDate())).
183                        toArray(FieldTimeStamped[]::new));
184         final double minGap = DateDetectionAdaptableIntervalFactory.getMinGap(timeStampeds) / 2;
185         final FieldDateDetector<T> fieldDateDetector = new FieldDateDetector<>(field).
186                                                        withHandler(new FieldResetDerivativesOnEvent<>()).
187                                                        withMaxCheck(maxCheck).
188                                                        withMinGap(minGap).
189                                                        withThreshold(field.getZero().newInstance(DATATION_ACCURACY));
190         final SortedSet<AbsoluteDate> sortedDates = new TreeSet<>(new ChronologicalComparator());
191         sortedDates.addAll(Arrays.stream(timeStampeds).map(TimeStamped::getDate).toList());
192         for (final AbsoluteDate date : sortedDates) {
193             fieldDateDetector.addEventDate(new FieldAbsoluteDate<>(field, date));
194         }
195         return fieldDateDetector;
196     }
197 
198 }