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;
019    
020    import java.util.ArrayList;
021    import java.util.Collection;
022    import java.util.Collections;
023    
024    import org.apache.commons.math.MaxEvaluationsExceededException;
025    import org.apache.commons.math.ode.events.CombinedEventsManager;
026    import org.apache.commons.math.ode.events.EventHandler;
027    import org.apache.commons.math.ode.events.EventState;
028    import org.apache.commons.math.ode.sampling.StepHandler;
029    
030    /**
031     * Base class managing common boilerplate for all integrators.
032     * @version $Revision: 799857 $ $Date: 2009-08-01 09:07:12 -0400 (Sat, 01 Aug 2009) $
033     * @since 2.0
034     */
035    public abstract class AbstractIntegrator implements FirstOrderIntegrator {
036    
037        
038        /** Name of the method. */
039        private final String name;
040    
041        /** Maximal number of evaluations allowed. */
042        private int maxEvaluations;
043    
044        /** Number of evaluations already performed. */
045        private int evaluations;
046    
047        /** Differential equations to integrate. */
048        private transient FirstOrderDifferentialEquations equations;
049    
050        /** Step handler. */
051        protected Collection<StepHandler> stepHandlers;
052    
053        /** Current step start time. */
054        protected double stepStart;
055    
056        /** Current stepsize. */
057        protected double stepSize;
058    
059        /** Events handlers manager. */
060        protected CombinedEventsManager eventsHandlersManager;
061    
062        /** Build an instance.
063         * @param name name of the method
064         */
065        public AbstractIntegrator(final String name) {
066            this.name = name;
067            stepHandlers = new ArrayList<StepHandler>();
068            stepStart = Double.NaN;
069            stepSize  = Double.NaN;
070            eventsHandlersManager = new CombinedEventsManager();
071            setMaxEvaluations(-1);
072            resetEvaluations();
073        }
074    
075        /** Build an instance with a null name.
076         */
077        protected AbstractIntegrator() {
078            this(null);
079        }
080        
081        /** {@inheritDoc} */
082        public String getName() {
083            return name;
084        }
085    
086        /** {@inheritDoc} */
087        public void addStepHandler(final StepHandler handler) {
088            stepHandlers.add(handler);
089        }
090    
091        /** {@inheritDoc} */
092        public Collection<StepHandler> getStepHandlers() {
093            return Collections.unmodifiableCollection(stepHandlers);
094        }
095    
096        /** {@inheritDoc} */
097        public void clearStepHandlers() {
098            stepHandlers.clear();
099        }
100    
101        /** {@inheritDoc} */
102        public void addEventHandler(final EventHandler function,
103                                    final double maxCheckInterval,
104                                    final double convergence,
105                                    final int maxIterationCount) {
106            eventsHandlersManager.addEventHandler(function, maxCheckInterval,
107                                                  convergence, maxIterationCount);
108        }
109    
110        /** {@inheritDoc} */
111        public Collection<EventHandler> getEventHandlers() {
112            return eventsHandlersManager.getEventsHandlers();
113        }
114    
115        /** {@inheritDoc} */
116        public void clearEventHandlers() {
117            eventsHandlersManager.clearEventsHandlers();
118        }
119    
120        /** Check if one of the step handlers requires dense output.
121         * @return true if one of the step handlers requires dense output
122         */
123        protected boolean requiresDenseOutput() {
124            for (StepHandler handler : stepHandlers) {
125                if (handler.requiresDenseOutput()) {
126                    return true;
127                }
128            }
129            return false;
130        }
131    
132        /** {@inheritDoc} */
133        public double getCurrentStepStart() {
134            return stepStart;
135        }
136    
137        /** {@inheritDoc} */
138        public double getCurrentSignedStepsize() {
139            return stepSize;
140        }
141    
142        /** {@inheritDoc} */
143        public void setMaxEvaluations(int maxEvaluations) {
144            this.maxEvaluations = (maxEvaluations < 0) ? Integer.MAX_VALUE : maxEvaluations;
145        }
146    
147        /** {@inheritDoc} */
148        public int getMaxEvaluations() {
149            return maxEvaluations;
150        }
151    
152        /** {@inheritDoc} */
153        public int getEvaluations() {
154            return evaluations;
155        }
156    
157        /** Reset the number of evaluations to zero.
158         */
159        protected void resetEvaluations() {
160            evaluations = 0;
161        }
162    
163        /** Set the differential equations.
164         * @param equations differential equations to integrate
165         * @see #computeDerivatives(double, double[], double[])
166         */
167        protected void setEquations(final FirstOrderDifferentialEquations equations) {
168            this.equations = equations;
169        }
170    
171        /** Compute the derivatives and check the number of evaluations.
172         * @param t current value of the independent <I>time</I> variable
173         * @param y array containing the current value of the state vector
174         * @param yDot placeholder array where to put the time derivative of the state vector
175         * @throws DerivativeException this exception is propagated to the caller if the
176         * underlying user function triggers one
177         */
178        public void computeDerivatives(final double t, final double[] y, final double[] yDot)
179            throws DerivativeException {
180            if (++evaluations > maxEvaluations) {
181                throw new DerivativeException(new MaxEvaluationsExceededException(maxEvaluations));
182            }
183            equations.computeDerivatives(t, y, yDot);
184        }
185    
186        /** Perform some sanity checks on the integration parameters.
187         * @param equations differential equations set
188         * @param t0 start time
189         * @param y0 state vector at t0
190         * @param t target time for the integration
191         * @param y placeholder where to put the state vector
192         * @exception IntegratorException if some inconsistency is detected
193         */
194        protected void sanityChecks(final FirstOrderDifferentialEquations equations,
195                                    final double t0, final double[] y0,
196                                    final double t, final double[] y)
197            throws IntegratorException {
198    
199            if (equations.getDimension() != y0.length) {
200                throw new IntegratorException(
201                        "dimensions mismatch: ODE problem has dimension {0}," +
202                        " initial state vector has dimension {1}",
203                        equations.getDimension(), y0.length);
204            }
205    
206            if (equations.getDimension() != y.length) {
207                throw new IntegratorException(
208                        "dimensions mismatch: ODE problem has dimension {0}," +
209                        " final state vector has dimension {1}",
210                        equations.getDimension(), y.length);
211            }
212    
213            if (Math.abs(t - t0) <= 1.0e-12 * Math.max(Math.abs(t0), Math.abs(t))) {
214                throw new IntegratorException(
215                        "too small integration interval: length = {0}",
216                        Math.abs(t - t0));
217            }
218    
219        }
220    
221        /** Add an event handler for end time checking.
222         * <p>This method can be used to simplify handling of integration end time.
223         * It leverages the nominal stop condition with the exceptional stop
224         * conditions.</p>
225         * @param startTime integration start time
226         * @param endTime desired end time
227         * @param manager manager containing the user-defined handlers
228         * @return a new manager containing all the user-defined handlers plus a
229         * dedicated manager triggering a stop event at entTime
230         */
231        protected CombinedEventsManager addEndTimeChecker(final double startTime,
232                                                          final double endTime,
233                                                          final CombinedEventsManager manager) {
234            CombinedEventsManager newManager = new CombinedEventsManager();
235            for (final EventState state : manager.getEventsStates()) {
236                newManager.addEventHandler(state.getEventHandler(),
237                                           state.getMaxCheckInterval(),
238                                           state.getConvergence(),
239                                           state.getMaxIterationCount());
240            }
241            newManager.addEventHandler(new EndTimeChecker(endTime),
242                                       Double.POSITIVE_INFINITY,
243                                       Math.ulp(Math.max(Math.abs(startTime), Math.abs(endTime))),
244                                       100);
245            return newManager;
246        }
247    
248        /** Specialized event handler to stop integration. */
249        private static class EndTimeChecker implements EventHandler {
250    
251            /** Desired end time. */
252            private final double endTime;
253    
254            /** Build an instance.
255             * @param endTime desired time
256             */
257            public EndTimeChecker(final double endTime) {
258                this.endTime = endTime;
259            }
260    
261            /** {@inheritDoc} */
262            public int eventOccurred(double t, double[] y, boolean increasing) {
263                return STOP;
264            }
265    
266            /** {@inheritDoc} */
267            public double g(double t, double[] y) {
268                return t - endTime;
269            }
270    
271            /** {@inheritDoc} */
272            public void resetState(double t, double[] y) {
273            }
274            
275        }
276    
277    }