001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    
018    package org.apache.commons.math.ode.events;
019    
020    import java.util.ArrayList;
021    import java.util.Collection;
022    import java.util.Collections;
023    import java.util.List;
024    
025    import org.apache.commons.math.ConvergenceException;
026    import org.apache.commons.math.ode.DerivativeException;
027    import org.apache.commons.math.ode.IntegratorException;
028    import org.apache.commons.math.ode.sampling.StepInterpolator;
029    
030    /** This class manages several {@link EventHandler event handlers} during integration.
031     *
032     * @see EventHandler
033     * @see EventState
034     * @version $Revision: 786881 $ $Date: 2009-06-20 14:53:08 -0400 (Sat, 20 Jun 2009) $
035     * @since 1.2
036     */
037    
038    public class CombinedEventsManager {
039    
040        /** Events states. */
041        private final List<EventState> states;
042    
043        /** First active event. */
044        private EventState first;
045    
046        /** Initialization indicator. */
047        private boolean initialized;
048    
049        /** Simple constructor.
050         * Create an empty manager
051         */
052        public CombinedEventsManager() {
053            states      = new ArrayList<EventState>();
054            first       = null;
055            initialized = false;
056        }
057    
058        /** Add an events handler.
059         * @param handler event handler
060         * @param maxCheckInterval maximal time interval between events
061         * checks (this interval prevents missing sign changes in
062         * case the integration steps becomes very large)
063         * @param convergence convergence threshold in the event time search
064         * @param maxIterationCount upper limit of the iteration count in
065         * the event time search
066         * @see #getEventsHandlers()
067         * @see #clearEventsHandlers()
068         */
069        public void addEventHandler(final EventHandler handler, final double maxCheckInterval,
070                                    final double convergence, final int maxIterationCount) {
071            states.add(new EventState(handler, maxCheckInterval,
072                                      convergence, maxIterationCount));
073        }
074    
075        /** Get all the events handlers that have been added to the manager.
076         * @return an unmodifiable collection of the added event handlers
077         * @see #addEventHandler(EventHandler, double, double, int)
078         * @see #clearEventsHandlers()
079         * @see #getEventsStates()
080         */
081        public Collection<EventHandler> getEventsHandlers() {
082            final List<EventHandler> list = new ArrayList<EventHandler>();
083            for (EventState state : states) {
084                list.add(state.getEventHandler());
085            }
086            return Collections.unmodifiableCollection(list);
087        }
088    
089        /** Remove all the events handlers that have been added to the manager.
090         * @see #addEventHandler(EventHandler, double, double, int)
091         * @see #getEventsHandlers()
092         */
093        public void clearEventsHandlers() {
094            states.clear();
095        }
096    
097        /** Get all the events state wrapping the handlers that have been added to the manager.
098         * @return a collection of the events states
099         * @see #getEventsHandlers()
100         */
101        public Collection<EventState> getEventsStates() {
102            return states;
103        }
104    
105        /** Check if the manager does not manage any event handlers.
106         * @return true if manager is empty
107         */
108        public boolean isEmpty() {
109            return states.isEmpty();
110        }
111    
112        /** Evaluate the impact of the proposed step on all managed
113         * event handlers.
114         * @param interpolator step interpolator for the proposed step
115         * @return true if at least one event handler triggers an event
116         * before the end of the proposed step (this implies the step should
117         * be rejected)
118         * @exception DerivativeException if the interpolator fails to
119         * compute the function somewhere within the step
120         * @exception IntegratorException if an event cannot be located
121         */
122        public boolean evaluateStep(final StepInterpolator interpolator)
123        throws DerivativeException, IntegratorException {
124    
125            try {
126    
127                first = null;
128                if (states.isEmpty()) {
129                    // there is nothing to do, return now to avoid setting the
130                    // interpolator time (and hence avoid unneeded calls to the
131                    // user function due to interpolator finalization)
132                    return false;
133                }
134    
135                if (! initialized) {
136    
137                    // initialize the events states
138                    final double t0 = interpolator.getPreviousTime();
139                    interpolator.setInterpolatedTime(t0);
140                    final double [] y = interpolator.getInterpolatedState();
141                    for (EventState state : states) {
142                        state.reinitializeBegin(t0, y);
143                    }
144    
145                    initialized = true;
146    
147                }
148    
149                // check events occurrence
150                for (EventState state : states) {
151    
152                    if (state.evaluateStep(interpolator)) {
153                        if (first == null) {
154                            first = state;
155                        } else {
156                            if (interpolator.isForward()) {
157                                if (state.getEventTime() < first.getEventTime()) {
158                                    first = state;
159                                }
160                            } else {
161                                if (state.getEventTime() > first.getEventTime()) {
162                                    first = state;
163                                }
164                            }
165                        }
166                    }
167    
168                }
169    
170                return first != null;
171    
172            } catch (EventException se) {
173                throw new IntegratorException(se);
174            } catch (ConvergenceException ce) {
175                throw new IntegratorException(ce);
176            }
177    
178        }
179    
180        /** Get the occurrence time of the first event triggered in the
181         * last evaluated step.
182         * @return occurrence time of the first event triggered in the last
183         * evaluated step, or </code>Double.NaN</code> if no event is
184         * triggered
185         */
186        public double getEventTime() {
187            return (first == null) ? Double.NaN : first.getEventTime();
188        }
189    
190        /** Inform the event handlers that the step has been accepted
191         * by the integrator.
192         * @param t value of the independent <i>time</i> variable at the
193         * end of the step
194         * @param y array containing the current value of the state vector
195         * at the end of the step
196         * @exception IntegratorException if the value of one of the
197         * events states cannot be evaluated
198         */
199        public void stepAccepted(final double t, final double[] y)
200        throws IntegratorException {
201            try {
202                for (EventState state : states) {
203                    state.stepAccepted(t, y);
204                }
205            } catch (EventException se) {
206                throw new IntegratorException(se);
207            }
208        }
209    
210        /** Check if the integration should be stopped at the end of the
211         * current step.
212         * @return true if the integration should be stopped
213         */
214        public boolean stop() {
215            for (EventState state : states) {
216                if (state.stop()) {
217                    return true;
218                }
219            }
220            return false;
221        }
222    
223        /** Let the event handlers reset the state if they want.
224         * @param t value of the independent <i>time</i> variable at the
225         * beginning of the next step
226         * @param y array were to put the desired state vector at the beginning
227         * of the next step
228         * @return true if the integrator should reset the derivatives too
229         * @exception IntegratorException if one of the events states
230         * that should reset the state fails to do it
231         */
232        public boolean reset(final double t, final double[] y)
233            throws IntegratorException {
234            try {
235                boolean resetDerivatives = false;
236                for (EventState state : states) {
237                    if (state.reset(t, y)) {
238                        resetDerivatives = true;
239                    }
240                }
241                return resetDerivatives;
242            } catch (EventException se) {
243                throw new IntegratorException(se);
244            }
245        }
246    
247    }