View Javadoc

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.analysis.solvers.BracketingNthOrderBrentSolver;
30  import org.apache.commons.math.analysis.solvers.UnivariateRealSolver;
31  import org.apache.commons.math.exception.DimensionMismatchException;
32  import org.apache.commons.math.exception.MathIllegalArgumentException;
33  import org.apache.commons.math.exception.MathIllegalStateException;
34  import org.apache.commons.math.exception.MaxCountExceededException;
35  import org.apache.commons.math.exception.NumberIsTooSmallException;
36  import org.apache.commons.math.exception.util.LocalizedFormats;
37  import org.apache.commons.math.ode.events.EventHandler;
38  import org.apache.commons.math.ode.events.EventState;
39  import org.apache.commons.math.ode.sampling.AbstractStepInterpolator;
40  import org.apache.commons.math.ode.sampling.StepHandler;
41  import org.apache.commons.math.util.FastMath;
42  import org.apache.commons.math.util.Incrementor;
43  import org.apache.commons.math.util.MathUtils;
44  import org.apache.commons.math.util.Precision;
45  
46  /**
47   * Base class managing common boilerplate for all integrators.
48   * @version $Id: AbstractIntegrator.java 1181282 2011-10-10 22:35:54Z erans $
49   * @since 2.0
50   */
51  public abstract class AbstractIntegrator implements FirstOrderIntegrator {
52  
53      /** Step handler. */
54      protected Collection<StepHandler> stepHandlers;
55  
56      /** Current step start time. */
57      protected double stepStart;
58  
59      /** Current stepsize. */
60      protected double stepSize;
61  
62      /** Indicator for last step. */
63      protected boolean isLastStep;
64  
65      /** Indicator that a state or derivative reset was triggered by some event. */
66      protected boolean resetOccurred;
67  
68      /** Events states. */
69      private Collection<EventState> eventsStates;
70  
71      /** Initialization indicator of events states. */
72      private boolean statesInitialized;
73  
74      /** Name of the method. */
75      private final String name;
76  
77      /** Counter for number of evaluations. */
78      private Incrementor evaluations;
79  
80      /** Differential equations to integrate. */
81      private transient ExpandableStatefulODE expandable;
82  
83      /** Build an instance.
84       * @param name name of the method
85       */
86      public AbstractIntegrator(final String name) {
87          this.name = name;
88          stepHandlers = new ArrayList<StepHandler>();
89          stepStart = Double.NaN;
90          stepSize  = Double.NaN;
91          eventsStates = new ArrayList<EventState>();
92          statesInitialized = false;
93          evaluations = new Incrementor();
94          setMaxEvaluations(-1);
95          resetEvaluations();
96      }
97  
98      /** Build an instance with a null name.
99       */
100     protected AbstractIntegrator() {
101         this(null);
102     }
103 
104     /** {@inheritDoc} */
105     public String getName() {
106         return name;
107     }
108 
109     /** {@inheritDoc} */
110     public void addStepHandler(final StepHandler handler) {
111         stepHandlers.add(handler);
112     }
113 
114     /** {@inheritDoc} */
115     public Collection<StepHandler> getStepHandlers() {
116         return Collections.unmodifiableCollection(stepHandlers);
117     }
118 
119     /** {@inheritDoc} */
120     public void clearStepHandlers() {
121         stepHandlers.clear();
122     }
123 
124     /** {@inheritDoc} */
125     public void addEventHandler(final EventHandler handler,
126                                 final double maxCheckInterval,
127                                 final double convergence,
128                                 final int maxIterationCount) {
129         addEventHandler(handler, maxCheckInterval, convergence,
130                         maxIterationCount,
131                         new BracketingNthOrderBrentSolver(convergence, 5));
132     }
133 
134     /** {@inheritDoc} */
135     public void addEventHandler(final EventHandler handler,
136                                 final double maxCheckInterval,
137                                 final double convergence,
138                                 final int maxIterationCount,
139                                 final UnivariateRealSolver solver) {
140         eventsStates.add(new EventState(handler, maxCheckInterval, convergence,
141                                         maxIterationCount, solver));
142     }
143 
144     /** {@inheritDoc} */
145     public Collection<EventHandler> getEventHandlers() {
146         final List<EventHandler> list = new ArrayList<EventHandler>();
147         for (EventState state : eventsStates) {
148             list.add(state.getEventHandler());
149         }
150         return Collections.unmodifiableCollection(list);
151     }
152 
153     /** {@inheritDoc} */
154     public void clearEventHandlers() {
155         eventsStates.clear();
156     }
157 
158     /** {@inheritDoc} */
159     public double getCurrentStepStart() {
160         return stepStart;
161     }
162 
163     /** {@inheritDoc} */
164     public double getCurrentSignedStepsize() {
165         return stepSize;
166     }
167 
168     /** {@inheritDoc} */
169     public void setMaxEvaluations(int maxEvaluations) {
170         evaluations.setMaximalCount((maxEvaluations < 0) ? Integer.MAX_VALUE : maxEvaluations);
171     }
172 
173     /** {@inheritDoc} */
174     public int getMaxEvaluations() {
175         return evaluations.getMaximalCount();
176     }
177 
178     /** {@inheritDoc} */
179     public int getEvaluations() {
180         return evaluations.getCount();
181     }
182 
183     /** Reset the number of evaluations to zero.
184      */
185     protected void resetEvaluations() {
186         evaluations.resetCount();
187     }
188 
189     /** Set the equations.
190      * @param equations equations to set
191      */
192     protected void setEquations(final ExpandableStatefulODE equations) {
193         this.expandable = equations;
194     }
195 
196     /** {@inheritDoc} */
197     public double integrate(final FirstOrderDifferentialEquations equations,
198                             final double t0, final double[] y0, final double t, final double[] y)
199         throws MathIllegalStateException, MathIllegalArgumentException {
200 
201         if (y0.length != equations.getDimension()) {
202             throw new DimensionMismatchException(y0.length, equations.getDimension());
203         }
204         if (y.length != equations.getDimension()) {
205             throw new DimensionMismatchException(y.length, equations.getDimension());
206         }
207 
208         // prepare expandable stateful equations
209         final ExpandableStatefulODE expandableODE = new ExpandableStatefulODE(equations);
210         expandableODE.setTime(t0);
211         expandableODE.setPrimaryState(y0);
212 
213         // perform integration
214         integrate(expandableODE, t);
215 
216         // extract results back from the stateful equations
217         System.arraycopy(expandableODE.getPrimaryState(), 0, y, 0, y.length);
218         return expandableODE.getTime();
219 
220     }
221 
222     /** Integrate a set of differential equations up to the given time.
223      * <p>This method solves an Initial Value Problem (IVP).</p>
224      * <p>The set of differential equations is composed of a main set, which
225      * can be extended by some sets of secondary equations. The set of
226      * equations must be already set up with initial time and partial states.
227      * At integration completion, the final time and partial states will be
228      * available in the same object.</p>
229      * <p>Since this method stores some internal state variables made
230      * available in its public interface during integration ({@link
231      * #getCurrentSignedStepsize()}), it is <em>not</em> thread-safe.</p>
232      * @param equations complete set of differential equations to integrate
233      * @param t target time for the integration
234      * (can be set to a value smaller than <code>t0</code> for backward integration)
235      * @throws MathIllegalStateException if the integrator cannot perform integration
236      * @throws MathIllegalArgumentException if integration parameters are wrong (typically
237      * too small integration span)
238      */
239     public abstract void integrate(ExpandableStatefulODE equations, double t)
240         throws MathIllegalStateException, MathIllegalArgumentException;
241 
242     /** Compute the derivatives and check the number of evaluations.
243      * @param t current value of the independent <I>time</I> variable
244      * @param y array containing the current value of the state vector
245      * @param yDot placeholder array where to put the time derivative of the state vector
246      * @exception MaxCountExceededException if the number of functions evaluations is exceeded
247      */
248     public void computeDerivatives(final double t, final double[] y, final double[] yDot)
249         throws MaxCountExceededException {
250         evaluations.incrementCount();
251         expandable.computeDerivatives(t, y, yDot);
252     }
253 
254     /** Set the stateInitialized flag.
255      * <p>This method must be called by integrators with the value
256      * {@code false} before they start integration, so a proper lazy
257      * initialization is done automatically on the first step.</p>
258      * @param stateInitialized new value for the flag
259      * @since 2.2
260      */
261     protected void setStateInitialized(final boolean stateInitialized) {
262         this.statesInitialized = stateInitialized;
263     }
264 
265     /** Accept a step, triggering events and step handlers.
266      * @param interpolator step interpolator
267      * @param y state vector at step end time, must be reset if an event
268      * asks for resetting or if an events stops integration during the step
269      * @param yDot placeholder array where to put the time derivative of the state vector
270      * @param tEnd final integration time
271      * @return time at end of step
272      * @exception MathIllegalStateException if the value of one event state cannot be evaluated
273      * @since 2.2
274      */
275     protected double acceptStep(final AbstractStepInterpolator interpolator,
276                                 final double[] y, final double[] yDot, final double tEnd)
277         throws MathIllegalStateException {
278 
279             double previousT = interpolator.getGlobalPreviousTime();
280             final double currentT = interpolator.getGlobalCurrentTime();
281             resetOccurred = false;
282 
283             // initialize the events states if needed
284             if (! statesInitialized) {
285                 for (EventState state : eventsStates) {
286                     state.reinitializeBegin(interpolator);
287                 }
288                 statesInitialized = true;
289             }
290 
291             // search for next events that may occur during the step
292             final int orderingSign = interpolator.isForward() ? +1 : -1;
293             SortedSet<EventState> occuringEvents = new TreeSet<EventState>(new Comparator<EventState>() {
294 
295                 /** {@inheritDoc} */
296                 public int compare(EventState es0, EventState es1) {
297                     return orderingSign * Double.compare(es0.getEventTime(), es1.getEventTime());
298                 }
299 
300             });
301 
302             for (final EventState state : eventsStates) {
303                 if (state.evaluateStep(interpolator)) {
304                     // the event occurs during the current step
305                     occuringEvents.add(state);
306                 }
307             }
308 
309             while (!occuringEvents.isEmpty()) {
310 
311                 // handle the chronologically first event
312                 final Iterator<EventState> iterator = occuringEvents.iterator();
313                 final EventState currentEvent = iterator.next();
314                 iterator.remove();
315 
316                 // restrict the interpolator to the first part of the step, up to the event
317                 final double eventT = currentEvent.getEventTime();
318                 interpolator.setSoftPreviousTime(previousT);
319                 interpolator.setSoftCurrentTime(eventT);
320 
321                 // trigger the event
322                 interpolator.setInterpolatedTime(eventT);
323                 final double[] eventY = interpolator.getInterpolatedState();
324                 currentEvent.stepAccepted(eventT, eventY);
325                 isLastStep = currentEvent.stop();
326 
327                 // handle the first part of the step, up to the event
328                 for (final StepHandler handler : stepHandlers) {
329                     handler.handleStep(interpolator, isLastStep);
330                 }
331 
332                 if (isLastStep) {
333                     // the event asked to stop integration
334                     System.arraycopy(eventY, 0, y, 0, y.length);
335                     return eventT;
336                 }
337 
338                 if (currentEvent.reset(eventT, eventY)) {
339                     // some event handler has triggered changes that
340                     // invalidate the derivatives, we need to recompute them
341                     System.arraycopy(eventY, 0, y, 0, y.length);
342                     computeDerivatives(eventT, y, yDot);
343                     resetOccurred = true;
344                     return eventT;
345                 }
346 
347                 // prepare handling of the remaining part of the step
348                 previousT = eventT;
349                 interpolator.setSoftPreviousTime(eventT);
350                 interpolator.setSoftCurrentTime(currentT);
351 
352                 // check if the same event occurs again in the remaining part of the step
353                 if (currentEvent.evaluateStep(interpolator)) {
354                     // the event occurs during the current step
355                     occuringEvents.add(currentEvent);
356                 }
357 
358             }
359 
360             interpolator.setInterpolatedTime(currentT);
361             final double[] currentY = interpolator.getInterpolatedState();
362             for (final EventState state : eventsStates) {
363                 state.stepAccepted(currentT, currentY);
364                 isLastStep = isLastStep || state.stop();
365             }
366             isLastStep = isLastStep || Precision.equals(currentT, tEnd, 1);
367 
368             // handle the remaining part of the step, after all events if any
369             for (StepHandler handler : stepHandlers) {
370                 handler.handleStep(interpolator, isLastStep);
371             }
372 
373             return currentT;
374 
375     }
376 
377     /** Check the integration span.
378      * @param equations set of differential equations
379      * @param t target time for the integration
380      * @exception NumberIsTooSmallException if integration span is too small
381      */
382     protected void sanityChecks(final ExpandableStatefulODE equations, final double t)
383         throws NumberIsTooSmallException {
384 
385         final double threshold = 1000 * FastMath.ulp(FastMath.max(FastMath.abs(equations.getTime()),
386                                                                   FastMath.abs(t)));
387         final double dt = FastMath.abs(equations.getTime() - t);
388         if (dt <= threshold) {
389             throw new NumberIsTooSmallException(LocalizedFormats.TOO_SMALL_INTEGRATION_INTERVAL,
390                                                 dt, threshold, false);
391         }
392 
393     }
394 
395 }