View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF 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    *      https://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  
18  /*
19   * This is not the original file distributed by the Apache Software Foundation
20   * It has been modified by the Hipparchus project
21   */
22  
23  package org.hipparchus.ode.events;
24  
25  import java.util.Arrays;
26  
27  import org.hipparchus.analysis.UnivariateFunction;
28  import org.hipparchus.analysis.solvers.BracketedUnivariateSolver;
29  import org.hipparchus.ode.ODEState;
30  import org.hipparchus.ode.ODEStateAndDerivative;
31  
32  /** Wrapper used to detect only increasing or decreasing events.
33   *
34   * <p>General {@link ODEEventDetector events} are defined implicitly
35   * by a {@link ODEEventDetector#g(ODEStateAndDerivative) g function} crossing
36   * zero. This function needs to be continuous in the event neighborhood,
37   * and its sign must remain consistent between events. This implies that
38   * during an ODE integration, events triggered are alternately events
39   * for which the function increases from negative to positive values,
40   * and events for which the function decreases from positive to
41   * negative values.
42   * </p>
43   *
44   * <p>Sometimes, users are only interested in one type of event (say
45   * increasing events for example) and not in the other type. In these
46   * cases, looking precisely for all events location and triggering
47   * events that will later be ignored is a waste of computing time.</p>
48   *
49   * <p>Users can wrap a regular {@link ODEEventDetector event detector} in
50   * an instance of this class and provide this wrapping instance to
51   * the {@link org.hipparchus.ode.ODEIntegrator ODE solver}
52   * in order to avoid wasting time looking for uninteresting events.
53   * The wrapper will intercept the calls to the {@link
54   * ODEEventDetector#g(ODEStateAndDerivative) g function} and to the {@link
55   * ODEEventHandler#eventOccurred(ODEStateAndDerivative, ODEEventDetector, boolean)
56   * eventOccurred} method in order to ignore uninteresting events. The
57   * wrapped regular {@link ODEEventHandler event handler} will the see only
58   * the interesting events, i.e. either only {@code increasing} events or
59   * {@code decreasing} events. the number of calls to the {@link
60   * ODEEventDetector#g(ODEStateAndDerivative) g function} will also be reduced.</p>
61   * @param <T> type of the event detector
62   * @since 3.0
63   */
64  public class EventSlopeFilter<T extends ODEEventDetector> extends AbstractODEDetector<EventSlopeFilter<T>> {
65  
66      /** Number of past transformers updates stored. */
67      private static final int HISTORY_SIZE = 100;
68  
69      /** Wrapped event detector.
70       * @since 3.0
71       */
72      private final T rawDetector;
73  
74      /** Filter to use. */
75      private final FilterType filter;
76  
77      /** Transformers of the g function. */
78      private final Transformer[] transformers;
79  
80      /** Update time of the transformers. */
81      private final double[] updates;
82  
83      /** Indicator for forward integration. */
84      private boolean forward;
85  
86      /** Extreme time encountered so far. */
87      private double extremeT;
88  
89      /** Wrap an {@link ODEEventDetector event detector}.
90       * @param rawDetector event detector to wrap
91       * @param filter filter to use
92       * @since 3.0
93       */
94      public EventSlopeFilter(final T rawDetector, final FilterType filter) {
95          this(rawDetector.getMaxCheckInterval(), rawDetector.getMaxIterationCount(),
96               rawDetector.getSolver(), new LocalHandler<>(rawDetector.getHandler()),
97               rawDetector, filter);
98      }
99  
100     /** Private constructor with full parameters.
101      * <p>
102      * This constructor is private as users are expected to use the builder
103      * API with the various {@code withXxx()} methods to set up the instance
104      * in a readable manner without using a huge amount of parameters.
105      * </p>
106      * @param maxCheck maximum checking interval (s)
107      * @param maxIter maximum number of iterations in the event time search
108      * @param solver root-finding algorithm to use to detect state events
109      * @param handler event handler to call at event occurrences
110      * @param rawDetector event detector to wrap
111      * @param filter filter to use
112      */
113     private EventSlopeFilter(final AdaptableInterval maxCheck, final int maxIter,
114                              final BracketedUnivariateSolver<UnivariateFunction> solver,
115                              final ODEEventHandler handler,
116                              final T rawDetector, final FilterType filter) {
117         super(maxCheck, maxIter, solver, handler);
118         this.rawDetector  = rawDetector;
119         this.filter       = filter;
120         this.transformers = new Transformer[HISTORY_SIZE];
121         this.updates      = new double[HISTORY_SIZE];
122     }
123 
124     /** {@inheritDoc} */
125     @Override
126     protected EventSlopeFilter<T> create(final AdaptableInterval newMaxCheck, final int newMaxIter,
127                                          final BracketedUnivariateSolver<UnivariateFunction> newSolver,
128                                          final ODEEventHandler newHandler) {
129         return new EventSlopeFilter<>(newMaxCheck, newMaxIter, newSolver, newHandler,
130                                       rawDetector, filter);
131     }
132 
133     /**
134      * Get the wrapped raw detector.
135      * @return the wrapped raw detector
136      */
137     public T getDetector() {
138         return rawDetector;
139     }
140 
141     /**  {@inheritDoc} */
142     @Override
143     public void init(final ODEStateAndDerivative initialState, double finalTime) {
144 
145         // delegate to raw handler
146         rawDetector.init(initialState, finalTime);
147 
148         // initialize events triggering logic
149         forward  = finalTime >= initialState.getTime();
150         extremeT = forward ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY;
151         Arrays.fill(transformers, Transformer.UNINITIALIZED);
152         Arrays.fill(updates, extremeT);
153 
154     }
155 
156     /**  {@inheritDoc} */
157     @Override
158     public double g(final ODEStateAndDerivative state) {
159 
160         final double rawG = rawDetector.g(state);
161 
162         // search which transformer should be applied to g
163         if (forward) {
164             final int last = transformers.length - 1;
165             if (extremeT < state.getTime()) {
166                 // we are at the forward end of the history
167 
168                 // check if a new rough root has been crossed
169                 final Transformer previous = transformers[last];
170                 final Transformer next     = filter.selectTransformer(previous, rawG, forward);
171                 if (next != previous) {
172                     // there is a root somewhere between extremeT and t.
173                     // the new transformer is valid for t (this is how we have just computed
174                     // it above), but it is in fact valid on both sides of the root, so
175                     // it was already valid before t and even up to previous time. We store
176                     // the switch at extremeT for safety, to ensure the previous transformer
177                     // is not applied too close of the root
178                     System.arraycopy(updates,      1, updates,      0, last);
179                     System.arraycopy(transformers, 1, transformers, 0, last);
180                     updates[last]      = extremeT;
181                     transformers[last] = next;
182                 }
183 
184                 extremeT = state.getTime();
185 
186                 // apply the transform
187                 return next.transformed(rawG);
188 
189             } else {
190                 // we are in the middle of the history
191 
192                 // select the transformer
193                 for (int i = last; i > 0; --i) {
194                     if (updates[i] <= state.getTime()) {
195                         // apply the transform
196                         return transformers[i].transformed(rawG);
197                     }
198                 }
199 
200                 return transformers[0].transformed(rawG);
201 
202             }
203         } else {
204             if (state.getTime() < extremeT) {
205                 // we are at the backward end of the history
206 
207                 // check if a new rough root has been crossed
208                 final Transformer previous = transformers[0];
209                 final Transformer next     = filter.selectTransformer(previous, rawG, forward);
210                 if (next != previous) {
211                     // there is a root somewhere between extremeT and t.
212                     // the new transformer is valid for t (this is how we have just computed
213                     // it above), but it is in fact valid on both sides of the root, so
214                     // it was already valid before t and even up to previous time. We store
215                     // the switch at extremeT for safety, to ensure the previous transformer
216                     // is not applied too close of the root
217                     System.arraycopy(updates,      0, updates,      1, updates.length - 1);
218                     System.arraycopy(transformers, 0, transformers, 1, transformers.length - 1);
219                     updates[0]      = extremeT;
220                     transformers[0] = next;
221                 }
222 
223                 extremeT = state.getTime();
224 
225                 // apply the transform
226                 return next.transformed(rawG);
227 
228             } else {
229                 // we are in the middle of the history
230 
231                 // select the transformer
232                 for (int i = 0; i < updates.length - 1; ++i) {
233                     if (state.getTime() <= updates[i]) {
234                         // apply the transform
235                         return transformers[i].transformed(rawG);
236                     }
237                 }
238 
239                 return transformers[updates.length - 1].transformed(rawG);
240 
241             }
242        }
243 
244     }
245 
246     /** Local handler.
247      * @param <T> type of the event detector
248      */
249     private static class LocalHandler<T extends ODEEventDetector> implements ODEEventHandler {
250 
251         /** Raw handler. */
252         private final ODEEventHandler rawHandler;
253 
254         /** Simple constructor.
255          * @param rawHandler raw handler
256          */
257         LocalHandler(final ODEEventHandler rawHandler) {
258             this.rawHandler = rawHandler;
259         }
260 
261         /**  {@inheritDoc} */
262         @Override
263         public Action eventOccurred(final ODEStateAndDerivative state, final ODEEventDetector detector, final boolean increasing) {
264             // delegate to raw handler, fixing increasing status on the fly
265             @SuppressWarnings("unchecked")
266             final EventSlopeFilter<T> esf = (EventSlopeFilter<T>) detector;
267             return rawHandler.eventOccurred(state, esf, esf.filter.isTriggeredOnIncreasing());
268         }
269 
270         /**  {@inheritDoc} */
271         @Override
272         public ODEState resetState(final ODEEventDetector detector, final ODEStateAndDerivative state) {
273             // delegate to raw handler
274             @SuppressWarnings("unchecked")
275             final EventSlopeFilter<T> esf = (EventSlopeFilter<T>) detector;
276             return rawHandler.resetState(esf, state);
277         }
278 
279     }
280 
281 }