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