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 }