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