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 }