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