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 * 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 18 package org.apache.commons.math.ode.events; 19 20 import java.util.ArrayList; 21 import java.util.Collection; 22 import java.util.Collections; 23 import java.util.List; 24 25 import org.apache.commons.math.ConvergenceException; 26 import org.apache.commons.math.ode.DerivativeException; 27 import org.apache.commons.math.ode.IntegratorException; 28 import org.apache.commons.math.ode.sampling.StepInterpolator; 29 30 /** This class manages several {@link EventHandler event handlers} during integration. 31 * 32 * @see EventHandler 33 * @see EventState 34 * @version $Revision: 786881 $ $Date: 2009-06-20 14:53:08 -0400 (Sat, 20 Jun 2009) $ 35 * @since 1.2 36 */ 37 38 public class CombinedEventsManager { 39 40 /** Events states. */ 41 private final List<EventState> states; 42 43 /** First active event. */ 44 private EventState first; 45 46 /** Initialization indicator. */ 47 private boolean initialized; 48 49 /** Simple constructor. 50 * Create an empty manager 51 */ 52 public CombinedEventsManager() { 53 states = new ArrayList<EventState>(); 54 first = null; 55 initialized = false; 56 } 57 58 /** Add an events handler. 59 * @param handler event handler 60 * @param maxCheckInterval maximal time interval between events 61 * checks (this interval prevents missing sign changes in 62 * case the integration steps becomes very large) 63 * @param convergence convergence threshold in the event time search 64 * @param maxIterationCount upper limit of the iteration count in 65 * the event time search 66 * @see #getEventsHandlers() 67 * @see #clearEventsHandlers() 68 */ 69 public void addEventHandler(final EventHandler handler, final double maxCheckInterval, 70 final double convergence, final int maxIterationCount) { 71 states.add(new EventState(handler, maxCheckInterval, 72 convergence, maxIterationCount)); 73 } 74 75 /** Get all the events handlers that have been added to the manager. 76 * @return an unmodifiable collection of the added event handlers 77 * @see #addEventHandler(EventHandler, double, double, int) 78 * @see #clearEventsHandlers() 79 * @see #getEventsStates() 80 */ 81 public Collection<EventHandler> getEventsHandlers() { 82 final List<EventHandler> list = new ArrayList<EventHandler>(); 83 for (EventState state : states) { 84 list.add(state.getEventHandler()); 85 } 86 return Collections.unmodifiableCollection(list); 87 } 88 89 /** Remove all the events handlers that have been added to the manager. 90 * @see #addEventHandler(EventHandler, double, double, int) 91 * @see #getEventsHandlers() 92 */ 93 public void clearEventsHandlers() { 94 states.clear(); 95 } 96 97 /** Get all the events state wrapping the handlers that have been added to the manager. 98 * @return a collection of the events states 99 * @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 }