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<T>
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<T>
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 }