    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    }