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.sampling;
019    
020    import org.apache.commons.math.ode.DerivativeException;
021    
022    /**
023     * This class wraps an object implementing {@link FixedStepHandler}
024     * into a {@link StepHandler}.
025    
026     * <p>This wrapper allows to use fixed step handlers with general
027     * integrators which cannot guaranty their integration steps will
028     * remain constant and therefore only accept general step
029     * handlers.</p>
030     *
031     * <p>The stepsize used is selected at construction time. The {@link
032     * FixedStepHandler#handleStep handleStep} method of the underlying
033     * {@link FixedStepHandler} object is called at the beginning time of
034     * the integration t0 and also at times t0+h, t0+2h, ... If the
035     * integration range is an integer multiple of the stepsize, then the
036     * last point handled will be the endpoint of the integration tend, if
037     * not, the last point will belong to the interval [tend - h ;
038     * tend].</p>
039     *
040     * <p>There is no constraint on the integrator, it can use any
041     * timestep it needs (time steps longer or shorter than the fixed time
042     * step and non-integer ratios are all allowed).</p>
043     *
044     * @see StepHandler
045     * @see FixedStepHandler
046     * @version $Revision: 811833 $ $Date: 2009-09-06 12:27:50 -0400 (Sun, 06 Sep 2009) $
047     * @since 1.2
048     */
049    
050    public class StepNormalizer implements StepHandler {
051    
052        /** Fixed time step. */
053        private double h;
054    
055        /** Underlying step handler. */
056        private final FixedStepHandler handler;
057    
058        /** Last step time. */
059        private double lastTime;
060    
061        /** Last State vector. */
062        private double[] lastState;
063    
064        /** Last Derivatives vector. */
065        private double[] lastDerivatives;
066    
067        /** Integration direction indicator. */
068        private boolean forward;
069    
070        /** Simple constructor.
071         * @param h fixed time step (sign is not used)
072         * @param handler fixed time step handler to wrap
073         */
074        public StepNormalizer(final double h, final FixedStepHandler handler) {
075            this.h       = Math.abs(h);
076            this.handler = handler;
077            reset();
078        }
079    
080        /** Determines whether this handler needs dense output.
081         * This handler needs dense output in order to provide data at
082         * regularly spaced steps regardless of the steps the integrator
083         * uses, so this method always returns true.
084         * @return always true
085         */
086        public boolean requiresDenseOutput() {
087            return true;
088        }
089    
090        /** Reset the step handler.
091         * Initialize the internal data as required before the first step is
092         * handled.
093         */
094        public void reset() {
095            lastTime        = Double.NaN;
096            lastState       = null;
097            lastDerivatives = null;
098            forward         = true;
099        }
100    
101        /**
102         * Handle the last accepted step
103         * @param interpolator interpolator for the last accepted step. For
104         * efficiency purposes, the various integrators reuse the same
105         * object on each call, so if the instance wants to keep it across
106         * all calls (for example to provide at the end of the integration a
107         * continuous model valid throughout the integration range), it
108         * should build a local copy using the clone method and store this
109         * copy.
110         * @param isLast true if the step is the last one
111         * @throws DerivativeException this exception is propagated to the
112         * caller if the underlying user function triggers one
113         */
114        public void handleStep(final StepInterpolator interpolator, final boolean isLast)
115            throws DerivativeException {
116    
117            if (lastState == null) {
118    
119                lastTime = interpolator.getPreviousTime();
120                interpolator.setInterpolatedTime(lastTime);
121                lastState = interpolator.getInterpolatedState().clone();
122                lastDerivatives = interpolator.getInterpolatedDerivatives().clone();
123    
124                // take the integration direction into account
125                forward = interpolator.getCurrentTime() >= lastTime;
126                if (! forward) {
127                    h = -h;
128                }
129    
130            }
131    
132            double nextTime = lastTime + h;
133            boolean nextInStep = forward ^ (nextTime > interpolator.getCurrentTime());
134            while (nextInStep) {
135    
136                // output the stored previous step
137                handler.handleStep(lastTime, lastState, lastDerivatives, false);
138    
139                // store the next step
140                lastTime = nextTime;
141                interpolator.setInterpolatedTime(lastTime);
142                System.arraycopy(interpolator.getInterpolatedState(), 0,
143                                 lastState, 0, lastState.length);
144                System.arraycopy(interpolator.getInterpolatedDerivatives(), 0,
145                                 lastDerivatives, 0, lastDerivatives.length);
146    
147                nextTime  += h;
148                nextInStep = forward ^ (nextTime > interpolator.getCurrentTime());
149    
150            }
151    
152            if (isLast) {
153                // there will be no more steps,
154                // the stored one should be flagged as being the last
155                handler.handleStep(lastTime, lastState, lastDerivatives, true);
156            }
157    
158        }
159    
160    }