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