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