| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.apache.commons.math.ode; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.SortedSet; |
| import java.util.TreeSet; |
| |
| import org.apache.commons.math.ConvergenceException; |
| import org.apache.commons.math.MaxEvaluationsExceededException; |
| import org.apache.commons.math.exception.util.LocalizedFormats; |
| import org.apache.commons.math.ode.events.CombinedEventsManager; |
| import org.apache.commons.math.ode.events.EventException; |
| import org.apache.commons.math.ode.events.EventHandler; |
| import org.apache.commons.math.ode.events.EventState; |
| import org.apache.commons.math.ode.sampling.AbstractStepInterpolator; |
| import org.apache.commons.math.ode.sampling.StepHandler; |
| import org.apache.commons.math.util.FastMath; |
| import org.apache.commons.math.util.MathUtils; |
| |
| /** |
| * Base class managing common boilerplate for all integrators. |
| * @version $Revision: 1073267 $ $Date: 2011-02-22 10:06:20 +0100 (mar. 22 févr. 2011) $ |
| * @since 2.0 |
| */ |
| public abstract class AbstractIntegrator implements FirstOrderIntegrator { |
| |
| /** Step handler. */ |
| protected Collection<StepHandler> stepHandlers; |
| |
| /** Current step start time. */ |
| protected double stepStart; |
| |
| /** Current stepsize. */ |
| protected double stepSize; |
| |
| /** Indicator for last step. */ |
| protected boolean isLastStep; |
| |
| /** Indicator that a state or derivative reset was triggered by some event. */ |
| protected boolean resetOccurred; |
| |
| /** Events states. */ |
| private Collection<EventState> eventsStates; |
| |
| /** Initialization indicator of events states. */ |
| private boolean statesInitialized; |
| |
| /** Name of the method. */ |
| private final String name; |
| |
| /** Maximal number of evaluations allowed. */ |
| private int maxEvaluations; |
| |
| /** Number of evaluations already performed. */ |
| private int evaluations; |
| |
| /** Differential equations to integrate. */ |
| private transient FirstOrderDifferentialEquations equations; |
| |
| /** Build an instance. |
| * @param name name of the method |
| */ |
| public AbstractIntegrator(final String name) { |
| this.name = name; |
| stepHandlers = new ArrayList<StepHandler>(); |
| stepStart = Double.NaN; |
| stepSize = Double.NaN; |
| eventsStates = new ArrayList<EventState>(); |
| statesInitialized = false; |
| setMaxEvaluations(-1); |
| resetEvaluations(); |
| } |
| |
| /** Build an instance with a null name. |
| */ |
| protected AbstractIntegrator() { |
| this(null); |
| } |
| |
| /** {@inheritDoc} */ |
| public String getName() { |
| return name; |
| } |
| |
| /** {@inheritDoc} */ |
| public void addStepHandler(final StepHandler handler) { |
| stepHandlers.add(handler); |
| } |
| |
| /** {@inheritDoc} */ |
| public Collection<StepHandler> getStepHandlers() { |
| return Collections.unmodifiableCollection(stepHandlers); |
| } |
| |
| /** {@inheritDoc} */ |
| public void clearStepHandlers() { |
| stepHandlers.clear(); |
| } |
| |
| /** {@inheritDoc} */ |
| public void addEventHandler(final EventHandler handler, |
| final double maxCheckInterval, |
| final double convergence, |
| final int maxIterationCount) { |
| eventsStates.add(new EventState(handler, maxCheckInterval, convergence, maxIterationCount)); |
| } |
| |
| /** {@inheritDoc} */ |
| public Collection<EventHandler> getEventHandlers() { |
| final List<EventHandler> list = new ArrayList<EventHandler>(); |
| for (EventState state : eventsStates) { |
| list.add(state.getEventHandler()); |
| } |
| return Collections.unmodifiableCollection(list); |
| } |
| |
| /** {@inheritDoc} */ |
| public void clearEventHandlers() { |
| eventsStates.clear(); |
| } |
| |
| /** Check if dense output is needed. |
| * @return true if there is at least one event handler or if |
| * one of the step handlers requires dense output |
| */ |
| protected boolean requiresDenseOutput() { |
| if (!eventsStates.isEmpty()) { |
| return true; |
| } |
| for (StepHandler handler : stepHandlers) { |
| if (handler.requiresDenseOutput()) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /** {@inheritDoc} */ |
| public double getCurrentStepStart() { |
| return stepStart; |
| } |
| |
| /** {@inheritDoc} */ |
| public double getCurrentSignedStepsize() { |
| return stepSize; |
| } |
| |
| /** {@inheritDoc} */ |
| public void setMaxEvaluations(int maxEvaluations) { |
| this.maxEvaluations = (maxEvaluations < 0) ? Integer.MAX_VALUE : maxEvaluations; |
| } |
| |
| /** {@inheritDoc} */ |
| public int getMaxEvaluations() { |
| return maxEvaluations; |
| } |
| |
| /** {@inheritDoc} */ |
| public int getEvaluations() { |
| return evaluations; |
| } |
| |
| /** Reset the number of evaluations to zero. |
| */ |
| protected void resetEvaluations() { |
| evaluations = 0; |
| } |
| |
| /** Set the differential equations. |
| * @param equations differential equations to integrate |
| * @see #computeDerivatives(double, double[], double[]) |
| */ |
| protected void setEquations(final FirstOrderDifferentialEquations equations) { |
| this.equations = equations; |
| } |
| |
| /** Compute the derivatives and check the number of evaluations. |
| * @param t current value of the independent <I>time</I> variable |
| * @param y array containing the current value of the state vector |
| * @param yDot placeholder array where to put the time derivative of the state vector |
| * @throws DerivativeException this user-defined exception should be used if an error is |
| * is triggered by user code |
| */ |
| public void computeDerivatives(final double t, final double[] y, final double[] yDot) |
| throws DerivativeException { |
| if (++evaluations > maxEvaluations) { |
| throw new DerivativeException(new MaxEvaluationsExceededException(maxEvaluations)); |
| } |
| equations.computeDerivatives(t, y, yDot); |
| } |
| |
| /** Set the stateInitialized flag. |
| * <p>This method must be called by integrators with the value |
| * {@code false} before they start integration, so a proper lazy |
| * initialization is done automatically on the first step.</p> |
| * @param stateInitialized new value for the flag |
| * @since 2.2 |
| */ |
| protected void setStateInitialized(final boolean stateInitialized) { |
| this.statesInitialized = stateInitialized; |
| } |
| |
| /** Accept a step, triggering events and step handlers. |
| * @param interpolator step interpolator |
| * @param y state vector at step end time, must be reset if an event |
| * asks for resetting or if an events stops integration during the step |
| * @param yDot placeholder array where to put the time derivative of the state vector |
| * @param tEnd final integration time |
| * @return time at end of step |
| * @throws DerivativeException this exception is propagated to the caller if |
| * the underlying user function triggers one |
| * @exception IntegratorException if the value of one event state cannot be evaluated |
| * @since 2.2 |
| */ |
| protected double acceptStep(final AbstractStepInterpolator interpolator, |
| final double[] y, final double[] yDot, final double tEnd) |
| throws DerivativeException, IntegratorException { |
| |
| try { |
| double previousT = interpolator.getGlobalPreviousTime(); |
| final double currentT = interpolator.getGlobalCurrentTime(); |
| resetOccurred = false; |
| |
| // initialize the events states if needed |
| if (! statesInitialized) { |
| for (EventState state : eventsStates) { |
| state.reinitializeBegin(interpolator); |
| } |
| statesInitialized = true; |
| } |
| |
| // search for next events that may occur during the step |
| final int orderingSign = interpolator.isForward() ? +1 : -1; |
| SortedSet<EventState> occuringEvents = new TreeSet<EventState>(new Comparator<EventState>() { |
| |
| /** {@inheritDoc} */ |
| public int compare(EventState es0, EventState es1) { |
| return orderingSign * Double.compare(es0.getEventTime(), es1.getEventTime()); |
| } |
| |
| }); |
| |
| for (final EventState state : eventsStates) { |
| if (state.evaluateStep(interpolator)) { |
| // the event occurs during the current step |
| occuringEvents.add(state); |
| } |
| } |
| |
| while (!occuringEvents.isEmpty()) { |
| |
| // handle the chronologically first event |
| final Iterator<EventState> iterator = occuringEvents.iterator(); |
| final EventState currentEvent = iterator.next(); |
| iterator.remove(); |
| |
| // restrict the interpolator to the first part of the step, up to the event |
| final double eventT = currentEvent.getEventTime(); |
| interpolator.setSoftPreviousTime(previousT); |
| interpolator.setSoftCurrentTime(eventT); |
| |
| // trigger the event |
| interpolator.setInterpolatedTime(eventT); |
| final double[] eventY = interpolator.getInterpolatedState(); |
| currentEvent.stepAccepted(eventT, eventY); |
| isLastStep = currentEvent.stop(); |
| |
| // handle the first part of the step, up to the event |
| for (final StepHandler handler : stepHandlers) { |
| handler.handleStep(interpolator, isLastStep); |
| } |
| |
| if (isLastStep) { |
| // the event asked to stop integration |
| System.arraycopy(eventY, 0, y, 0, y.length); |
| return eventT; |
| } |
| |
| if (currentEvent.reset(eventT, eventY)) { |
| // some event handler has triggered changes that |
| // invalidate the derivatives, we need to recompute them |
| System.arraycopy(eventY, 0, y, 0, y.length); |
| computeDerivatives(eventT, y, yDot); |
| resetOccurred = true; |
| return eventT; |
| } |
| |
| // prepare handling of the remaining part of the step |
| previousT = eventT; |
| interpolator.setSoftPreviousTime(eventT); |
| interpolator.setSoftCurrentTime(currentT); |
| |
| // check if the same event occurs again in the remaining part of the step |
| if (currentEvent.evaluateStep(interpolator)) { |
| // the event occurs during the current step |
| occuringEvents.add(currentEvent); |
| } |
| |
| } |
| |
| interpolator.setInterpolatedTime(currentT); |
| final double[] currentY = interpolator.getInterpolatedState(); |
| for (final EventState state : eventsStates) { |
| state.stepAccepted(currentT, currentY); |
| isLastStep = isLastStep || state.stop(); |
| } |
| isLastStep = isLastStep || MathUtils.equals(currentT, tEnd, 1); |
| |
| // handle the remaining part of the step, after all events if any |
| for (StepHandler handler : stepHandlers) { |
| handler.handleStep(interpolator, isLastStep); |
| } |
| |
| return currentT; |
| } catch (EventException se) { |
| final Throwable cause = se.getCause(); |
| if ((cause != null) && (cause instanceof DerivativeException)) { |
| throw (DerivativeException) cause; |
| } |
| throw new IntegratorException(se); |
| } catch (ConvergenceException ce) { |
| throw new IntegratorException(ce); |
| } |
| |
| } |
| |
| /** Perform some sanity checks on the integration parameters. |
| * @param ode differential equations set |
| * @param t0 start time |
| * @param y0 state vector at t0 |
| * @param t target time for the integration |
| * @param y placeholder where to put the state vector |
| * @exception IntegratorException if some inconsistency is detected |
| */ |
| protected void sanityChecks(final FirstOrderDifferentialEquations ode, |
| final double t0, final double[] y0, |
| final double t, final double[] y) |
| throws IntegratorException { |
| |
| if (ode.getDimension() != y0.length) { |
| throw new IntegratorException( |
| LocalizedFormats.DIMENSIONS_MISMATCH_SIMPLE, ode.getDimension(), y0.length); |
| } |
| |
| if (ode.getDimension() != y.length) { |
| throw new IntegratorException( |
| LocalizedFormats.DIMENSIONS_MISMATCH_SIMPLE, ode.getDimension(), y.length); |
| } |
| |
| if (FastMath.abs(t - t0) <= 1.0e-12 * FastMath.max(FastMath.abs(t0), FastMath.abs(t))) { |
| throw new IntegratorException( |
| LocalizedFormats.TOO_SMALL_INTEGRATION_INTERVAL, |
| FastMath.abs(t - t0)); |
| } |
| |
| } |
| |
| /** Add an event handler for end time checking. |
| * <p>This method can be used to simplify handling of integration end time. |
| * It leverages the nominal stop condition with the exceptional stop |
| * conditions.</p> |
| * @param startTime integration start time |
| * @param endTime desired end time |
| * @param manager manager containing the user-defined handlers |
| * @return a new manager containing all the user-defined handlers plus a |
| * dedicated manager triggering a stop event at entTime |
| * @deprecated as of 2.2, this method is not used any more |
| */ |
| @Deprecated |
| protected CombinedEventsManager addEndTimeChecker(final double startTime, |
| final double endTime, |
| final CombinedEventsManager manager) { |
| CombinedEventsManager newManager = new CombinedEventsManager(); |
| for (final EventState state : manager.getEventsStates()) { |
| newManager.addEventHandler(state.getEventHandler(), |
| state.getMaxCheckInterval(), |
| state.getConvergence(), |
| state.getMaxIterationCount()); |
| } |
| newManager.addEventHandler(new EndTimeChecker(endTime), |
| Double.POSITIVE_INFINITY, |
| FastMath.ulp(FastMath.max(FastMath.abs(startTime), FastMath.abs(endTime))), |
| 100); |
| return newManager; |
| } |
| |
| /** Specialized event handler to stop integration. |
| * @deprecated as of 2.2, this class is not used anymore |
| */ |
| @Deprecated |
| private static class EndTimeChecker implements EventHandler { |
| |
| /** Desired end time. */ |
| private final double endTime; |
| |
| /** Build an instance. |
| * @param endTime desired time |
| */ |
| public EndTimeChecker(final double endTime) { |
| this.endTime = endTime; |
| } |
| |
| /** {@inheritDoc} */ |
| public int eventOccurred(double t, double[] y, boolean increasing) { |
| return STOP; |
| } |
| |
| /** {@inheritDoc} */ |
| public double g(double t, double[] y) { |
| return t - endTime; |
| } |
| |
| /** {@inheritDoc} */ |
| public void resetState(double t, double[] y) { |
| } |
| |
| } |
| |
| } |