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