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; 19 20 import java.util.ArrayList; 21 import java.util.Collection; 22 import java.util.Collections; 23 import java.util.Comparator; 24 import java.util.Iterator; 25 import java.util.List; 26 import java.util.SortedSet; 27 import java.util.TreeSet; 28 29 import org.apache.commons.math.ConvergenceException; 30 import org.apache.commons.math.MaxEvaluationsExceededException; 31 import org.apache.commons.math.exception.util.LocalizedFormats; 32 import org.apache.commons.math.ode.events.CombinedEventsManager; 33 import org.apache.commons.math.ode.events.EventException; 34 import org.apache.commons.math.ode.events.EventHandler; 35 import org.apache.commons.math.ode.events.EventState; 36 import org.apache.commons.math.ode.sampling.AbstractStepInterpolator; 37 import org.apache.commons.math.ode.sampling.StepHandler; 38 import org.apache.commons.math.util.FastMath; 39 import org.apache.commons.math.util.MathUtils; 40 41 /** 42 * Base class managing common boilerplate for all integrators. 43 * @version $Revision: 1073267 $ $Date: 2011-02-22 10:06:20 +0100 (mar. 22 fvr. 2011) $ 44 * @since 2.0 45 */ 46 public abstract class AbstractIntegrator implements FirstOrderIntegrator { 47 48 /** Step handler. */ 49 protected Collection<StepHandler> stepHandlers; 50 51 /** Current step start time. */ 52 protected double stepStart; 53 54 /** Current stepsize. */ 55 protected double stepSize; 56 57 /** Indicator for last step. */ 58 protected boolean isLastStep; 59 60 /** Indicator that a state or derivative reset was triggered by some event. */ 61 protected boolean resetOccurred; 62 63 /** Events states. */ 64 private Collection<EventState> eventsStates; 65 66 /** Initialization indicator of events states. */ 67 private boolean statesInitialized; 68 69 /** Name of the method. */ 70 private final String name; 71 72 /** Maximal number of evaluations allowed. */ 73 private int maxEvaluations; 74 75 /** Number of evaluations already performed. */ 76 private int evaluations; 77 78 /** Differential equations to integrate. */ 79 private transient FirstOrderDifferentialEquations equations; 80 81 /** Build an instance. 82 * @param name name of the method 83 */ 84 public AbstractIntegrator(final String name) { 85 this.name = name; 86 stepHandlers = new ArrayList<StepHandler>(); 87 stepStart = Double.NaN; 88 stepSize = Double.NaN; 89 eventsStates = new ArrayList<EventState>(); 90 statesInitialized = false; 91 setMaxEvaluations(-1); 92 resetEvaluations(); 93 } 94 95 /** Build an instance with a null name. 96 */ 97 protected AbstractIntegrator() { 98 this(null); 99 } 100 101 /** {@inheritDoc} */ 102 public String getName() { 103 return name; 104 } 105 106 /** {@inheritDoc} */ 107 public void addStepHandler(final StepHandler handler) { 108 stepHandlers.add(handler); 109 } 110 111 /** {@inheritDoc} */ 112 public Collection<StepHandler> getStepHandlers() { 113 return Collections.unmodifiableCollection(stepHandlers); 114 } 115 116 /** {@inheritDoc} */ 117 public void clearStepHandlers() { 118 stepHandlers.clear(); 119 } 120 121 /** {@inheritDoc} */ 122 public void addEventHandler(final EventHandler handler, 123 final double maxCheckInterval, 124 final double convergence, 125 final int maxIterationCount) { 126 eventsStates.add(new EventState(handler, maxCheckInterval, convergence, maxIterationCount)); 127 } 128 129 /** {@inheritDoc} */ 130 public Collection<EventHandler> getEventHandlers() { 131 final List<EventHandler> list = new ArrayList<EventHandler>(); 132 for (EventState state : eventsStates) { 133 list.add(state.getEventHandler()); 134 } 135 return Collections.unmodifiableCollection(list); 136 } 137 138 /** {@inheritDoc} */ 139 public void clearEventHandlers() { 140 eventsStates.clear(); 141 } 142 143 /** Check if dense output is needed. 144 * @return true if there is at least one event handler or if 145 * one of the step handlers requires dense output 146 */ 147 protected boolean requiresDenseOutput() { 148 if (!eventsStates.isEmpty()) { 149 return true; 150 } 151 for (StepHandler handler : stepHandlers) { 152 if (handler.requiresDenseOutput()) { 153 return true; 154 } 155 } 156 return false; 157 } 158 159 /** {@inheritDoc} */ 160 public double getCurrentStepStart() { 161 return stepStart; 162 } 163 164 /** {@inheritDoc} */ 165 public double getCurrentSignedStepsize() { 166 return stepSize; 167 } 168 169 /** {@inheritDoc} */ 170 public void setMaxEvaluations(int maxEvaluations) { 171 this.maxEvaluations = (maxEvaluations < 0) ? Integer.MAX_VALUE : maxEvaluations; 172 } 173 174 /** {@inheritDoc} */ 175 public int getMaxEvaluations() { 176 return maxEvaluations; 177 } 178 179 /** {@inheritDoc} */ 180 public int getEvaluations() { 181 return evaluations; 182 } 183 184 /** Reset the number of evaluations to zero. 185 */ 186 protected void resetEvaluations() { 187 evaluations = 0; 188 } 189 190 /** Set the differential equations. 191 * @param equations differential equations to integrate 192 * @see #computeDerivatives(double, double[], double[]) 193 */ 194 protected void setEquations(final FirstOrderDifferentialEquations equations) { 195 this.equations = equations; 196 } 197 198 /** Compute the derivatives and check the number of evaluations. 199 * @param t current value of the independent <I>time</I> variable 200 * @param y array containing the current value of the state vector 201 * @param yDot placeholder array where to put the time derivative of the state vector 202 * @throws DerivativeException this user-defined exception should be used if an error is 203 * is triggered by user code 204 */ 205 public void computeDerivatives(final double t, final double[] y, final double[] yDot) 206 throws DerivativeException { 207 if (++evaluations > maxEvaluations) { 208 throw new DerivativeException(new MaxEvaluationsExceededException(maxEvaluations)); 209 } 210 equations.computeDerivatives(t, y, yDot); 211 } 212 213 /** Set the stateInitialized flag. 214 * <p>This method must be called by integrators with the value 215 * {@code false} before they start integration, so a proper lazy 216 * initialization is done automatically on the first step.</p> 217 * @param stateInitialized new value for the flag 218 * @since 2.2 219 */ 220 protected void setStateInitialized(final boolean stateInitialized) { 221 this.statesInitialized = stateInitialized; 222 } 223 224 /** Accept a step, triggering events and step handlers. 225 * @param interpolator step interpolator 226 * @param y state vector at step end time, must be reset if an event 227 * asks for resetting or if an events stops integration during the step 228 * @param yDot placeholder array where to put the time derivative of the state vector 229 * @param tEnd final integration time 230 * @return time at end of step 231 * @throws DerivativeException this exception is propagated to the caller if 232 * the underlying user function triggers one 233 * @exception IntegratorException if the value of one event state cannot be evaluated 234 * @since 2.2 235 */ 236 protected double acceptStep(final AbstractStepInterpolator interpolator, 237 final double[] y, final double[] yDot, final double tEnd) 238 throws DerivativeException, IntegratorException { 239 240 try { 241 double previousT = interpolator.getGlobalPreviousTime(); 242 final double currentT = interpolator.getGlobalCurrentTime(); 243 resetOccurred = false; 244 245 // initialize the events states if needed 246 if (! statesInitialized) { 247 for (EventState state : eventsStates) { 248 state.reinitializeBegin(interpolator); 249 } 250 statesInitialized = true; 251 } 252 253 // search for next events that may occur during the step 254 final int orderingSign = interpolator.isForward() ? +1 : -1; 255 SortedSet<EventState> occuringEvents = new TreeSet<EventState>(new Comparator<EventState>() { 256 257 /** {@inheritDoc} */ 258 public int compare(EventState es0, EventState es1) { 259 return orderingSign * Double.compare(es0.getEventTime(), es1.getEventTime()); 260 } 261 262 }); 263 264 for (final EventState state : eventsStates) { 265 if (state.evaluateStep(interpolator)) { 266 // the event occurs during the current step 267 occuringEvents.add(state); 268 } 269 } 270 271 while (!occuringEvents.isEmpty()) { 272 273 // handle the chronologically first event 274 final Iterator<EventState> iterator = occuringEvents.iterator(); 275 final EventState currentEvent = iterator.next(); 276 iterator.remove(); 277 278 // restrict the interpolator to the first part of the step, up to the event 279 final double eventT = currentEvent.getEventTime(); 280 interpolator.setSoftPreviousTime(previousT); 281 interpolator.setSoftCurrentTime(eventT); 282 283 // trigger the event 284 interpolator.setInterpolatedTime(eventT); 285 final double[] eventY = interpolator.getInterpolatedState(); 286 currentEvent.stepAccepted(eventT, eventY); 287 isLastStep = currentEvent.stop(); 288 289 // handle the first part of the step, up to the event 290 for (final StepHandler handler : stepHandlers) { 291 handler.handleStep(interpolator, isLastStep); 292 } 293 294 if (isLastStep) { 295 // the event asked to stop integration 296 System.arraycopy(eventY, 0, y, 0, y.length); 297 return eventT; 298 } 299 300 if (currentEvent.reset(eventT, eventY)) { 301 // some event handler has triggered changes that 302 // invalidate the derivatives, we need to recompute them 303 System.arraycopy(eventY, 0, y, 0, y.length); 304 computeDerivatives(eventT, y, yDot); 305 resetOccurred = true; 306 return eventT; 307 } 308 309 // prepare handling of the remaining part of the step 310 previousT = eventT; 311 interpolator.setSoftPreviousTime(eventT); 312 interpolator.setSoftCurrentTime(currentT); 313 314 // check if the same event occurs again in the remaining part of the step 315 if (currentEvent.evaluateStep(interpolator)) { 316 // the event occurs during the current step 317 occuringEvents.add(currentEvent); 318 } 319 320 } 321 322 interpolator.setInterpolatedTime(currentT); 323 final double[] currentY = interpolator.getInterpolatedState(); 324 for (final EventState state : eventsStates) { 325 state.stepAccepted(currentT, currentY); 326 isLastStep = isLastStep || state.stop(); 327 } 328 isLastStep = isLastStep || MathUtils.equals(currentT, tEnd, 1); 329 330 // handle the remaining part of the step, after all events if any 331 for (StepHandler handler : stepHandlers) { 332 handler.handleStep(interpolator, isLastStep); 333 } 334 335 return currentT; 336 } catch (EventException se) { 337 final Throwable cause = se.getCause(); 338 if ((cause != null) && (cause instanceof DerivativeException)) { 339 throw (DerivativeException) cause; 340 } 341 throw new IntegratorException(se); 342 } catch (ConvergenceException ce) { 343 throw new IntegratorException(ce); 344 } 345 346 } 347 348 /** Perform some sanity checks on the integration parameters. 349 * @param ode differential equations set 350 * @param t0 start time 351 * @param y0 state vector at t0 352 * @param t target time for the integration 353 * @param y placeholder where to put the state vector 354 * @exception IntegratorException if some inconsistency is detected 355 */ 356 protected void sanityChecks(final FirstOrderDifferentialEquations ode, 357 final double t0, final double[] y0, 358 final double t, final double[] y) 359 throws IntegratorException { 360 361 if (ode.getDimension() != y0.length) { 362 throw new IntegratorException( 363 LocalizedFormats.DIMENSIONS_MISMATCH_SIMPLE, ode.getDimension(), y0.length); 364 } 365 366 if (ode.getDimension() != y.length) { 367 throw new IntegratorException( 368 LocalizedFormats.DIMENSIONS_MISMATCH_SIMPLE, ode.getDimension(), y.length); 369 } 370 371 if (FastMath.abs(t - t0) <= 1.0e-12 * FastMath.max(FastMath.abs(t0), FastMath.abs(t))) { 372 throw new IntegratorException( 373 LocalizedFormats.TOO_SMALL_INTEGRATION_INTERVAL, 374 FastMath.abs(t - t0)); 375 } 376 377 } 378 379 /** Add an event handler for end time checking. 380 * <p>This method can be used to simplify handling of integration end time. 381 * It leverages the nominal stop condition with the exceptional stop 382 * conditions.</p> 383 * @param startTime integration start time 384 * @param endTime desired end time 385 * @param manager manager containing the user-defined handlers 386 * @return a new manager containing all the user-defined handlers plus a 387 * dedicated manager triggering a stop event at entTime 388 * @deprecated as of 2.2, this method is not used any more 389 */ 390 @Deprecated 391 protected CombinedEventsManager addEndTimeChecker(final double startTime, 392 final double endTime, 393 final CombinedEventsManager manager) { 394 CombinedEventsManager newManager = new CombinedEventsManager(); 395 for (final EventState state : manager.getEventsStates()) { 396 newManager.addEventHandler(state.getEventHandler(), 397 state.getMaxCheckInterval(), 398 state.getConvergence(), 399 state.getMaxIterationCount()); 400 } 401 newManager.addEventHandler(new EndTimeChecker(endTime), 402 Double.POSITIVE_INFINITY, 403 FastMath.ulp(FastMath.max(FastMath.abs(startTime), FastMath.abs(endTime))), 404 100); 405 return newManager; 406 } 407 408 /** Specialized event handler to stop integration. 409 * @deprecated as of 2.2, this class is not used anymore 410 */ 411 @Deprecated 412 private static class EndTimeChecker implements EventHandler { 413 414 /** Desired end time. */ 415 private final double endTime; 416 417 /** Build an instance. 418 * @param endTime desired time 419 */ 420 public EndTimeChecker(final double endTime) { 421 this.endTime = endTime; 422 } 423 424 /** {@inheritDoc} */ 425 public int eventOccurred(double t, double[] y, boolean increasing) { 426 return STOP; 427 } 428 429 /** {@inheritDoc} */ 430 public double g(double t, double[] y) { 431 return t - endTime; 432 } 433 434 /** {@inheritDoc} */ 435 public void resetState(double t, double[] y) { 436 } 437 438 } 439 440 } 441