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.util.FastMath;
021    import org.apache.commons.math.util.MathUtils;
022    import org.apache.commons.math.util.Precision;
023    
024    /**
025     * This class wraps an object implementing {@link FixedStepHandler}
026     * into a {@link StepHandler}.
027    
028     * <p>This wrapper allows to use fixed step handlers with general
029     * integrators which cannot guaranty their integration steps will
030     * remain constant and therefore only accept general step
031     * handlers.</p>
032     *
033     * <p>The stepsize used is selected at construction time. The {@link
034     * FixedStepHandler#handleStep handleStep} method of the underlying
035     * {@link FixedStepHandler} object is called at normalized times. The
036     * normalized times can be influenced by the {@link StepNormalizerMode} and
037     * {@link StepNormalizerBounds}.</p>
038     *
039     * <p>There is no constraint on the integrator, it can use any time step
040     * it needs (time steps longer or shorter than the fixed time step and
041     * non-integer ratios are all allowed).</p>
042     *
043     * <p>
044     * <table border="1" align="center">
045     * <tr BGCOLOR="#CCCCFF"><td colspan=6><font size="+2">Examples (step size = 0.5)</font></td></tr>
046     * <tr BGCOLOR="#EEEEFF"><font size="+1"><td>Start time</td><td>End time</td>
047     *  <td>Direction</td><td>{@link StepNormalizerMode Mode}</td>
048     *  <td>{@link StepNormalizerBounds Bounds}</td><td>Output</td></font></tr>
049     * <tr><td>0.3</td><td>3.1</td><td>forward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#NEITHER NEITHER}</td><td>0.8, 1.3, 1.8, 2.3, 2.8</td></tr>
050     * <tr><td>0.3</td><td>3.1</td><td>forward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#FIRST FIRST}</td><td>0.3, 0.8, 1.3, 1.8, 2.3, 2.8</td></tr>
051     * <tr><td>0.3</td><td>3.1</td><td>forward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#LAST LAST}</td><td>0.8, 1.3, 1.8, 2.3, 2.8, 3.1</td></tr>
052     * <tr><td>0.3</td><td>3.1</td><td>forward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#BOTH BOTH}</td><td>0.3, 0.8, 1.3, 1.8, 2.3, 2.8, 3.1</td></tr>
053     * <tr><td>0.3</td><td>3.1</td><td>forward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#NEITHER NEITHER}</td><td>0.5, 1.0, 1.5, 2.0, 2.5, 3.0</td></tr>
054     * <tr><td>0.3</td><td>3.1</td><td>forward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#FIRST FIRST}</td><td>0.3, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0</td></tr>
055     * <tr><td>0.3</td><td>3.1</td><td>forward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#LAST LAST}</td><td>0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.1</td></tr>
056     * <tr><td>0.3</td><td>3.1</td><td>forward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#BOTH BOTH}</td><td>0.3, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.1</td></tr>
057     * <tr><td>0.0</td><td>3.0</td><td>forward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#NEITHER NEITHER}</td><td>0.5, 1.0, 1.5, 2.0, 2.5, 3.0</td></tr>
058     * <tr><td>0.0</td><td>3.0</td><td>forward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#FIRST FIRST}</td><td>0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0</td></tr>
059     * <tr><td>0.0</td><td>3.0</td><td>forward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#LAST LAST}</td><td>0.5, 1.0, 1.5, 2.0, 2.5, 3.0</td></tr>
060     * <tr><td>0.0</td><td>3.0</td><td>forward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#BOTH BOTH}</td><td>0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0</td></tr>
061     * <tr><td>0.0</td><td>3.0</td><td>forward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#NEITHER NEITHER}</td><td>0.5, 1.0, 1.5, 2.0, 2.5, 3.0</td></tr>
062     * <tr><td>0.0</td><td>3.0</td><td>forward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#FIRST FIRST}</td><td>0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0</td></tr>
063     * <tr><td>0.0</td><td>3.0</td><td>forward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#LAST LAST}</td><td>0.5, 1.0, 1.5, 2.0, 2.5, 3.0</td></tr>
064     * <tr><td>0.0</td><td>3.0</td><td>forward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#BOTH BOTH}</td><td>0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0</td></tr>
065     * <tr><td>3.1</td><td>0.3</td><td>backward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#NEITHER NEITHER}</td><td>2.6, 2.1, 1.6, 1.1, 0.6</td></tr>
066     * <tr><td>3.1</td><td>0.3</td><td>backward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#FIRST FIRST}</td><td>3.1, 2.6, 2.1, 1.6, 1.1, 0.6</td></tr>
067     * <tr><td>3.1</td><td>0.3</td><td>backward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#LAST LAST}</td><td>2.6, 2.1, 1.6, 1.1, 0.6, 0.3</td></tr>
068     * <tr><td>3.1</td><td>0.3</td><td>backward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#BOTH BOTH}</td><td>3.1, 2.6, 2.1, 1.6, 1.1, 0.6, 0.3</td></tr>
069     * <tr><td>3.1</td><td>0.3</td><td>backward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#NEITHER NEITHER}</td><td>3.0, 2.5, 2.0, 1.5, 1.0, 0.5</td></tr>
070     * <tr><td>3.1</td><td>0.3</td><td>backward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#FIRST FIRST}</td><td>3.1, 3.0, 2.5, 2.0, 1.5, 1.0, 0.5</td></tr>
071     * <tr><td>3.1</td><td>0.3</td><td>backward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#LAST LAST}</td><td>3.0, 2.5, 2.0, 1.5, 1.0, 0.5, 0.3</td></tr>
072     * <tr><td>3.1</td><td>0.3</td><td>backward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#BOTH BOTH}</td><td>3.1, 3.0, 2.5, 2.0, 1.5, 1.0, 0.5, 0.3</td></tr>
073     * <tr><td>3.0</td><td>0.0</td><td>backward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#NEITHER NEITHER}</td><td>2.5, 2.0, 1.5, 1.0, 0.5, 0.0</td></tr>
074     * <tr><td>3.0</td><td>0.0</td><td>backward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#FIRST FIRST}</td><td>3.0, 2.5, 2.0, 1.5, 1.0, 0.5, 0.0</td></tr>
075     * <tr><td>3.0</td><td>0.0</td><td>backward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#LAST LAST}</td><td>2.5, 2.0, 1.5, 1.0, 0.5, 0.0</td></tr>
076     * <tr><td>3.0</td><td>0.0</td><td>backward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#BOTH BOTH}</td><td>3.0, 2.5, 2.0, 1.5, 1.0, 0.5, 0.0</td></tr>
077     * <tr><td>3.0</td><td>0.0</td><td>backward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#NEITHER NEITHER}</td><td>2.5, 2.0, 1.5, 1.0, 0.5, 0.0</td></tr>
078     * <tr><td>3.0</td><td>0.0</td><td>backward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#FIRST FIRST}</td><td>3.0, 2.5, 2.0, 1.5, 1.0, 0.5, 0.0</td></tr>
079     * <tr><td>3.0</td><td>0.0</td><td>backward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#LAST LAST}</td><td>2.5, 2.0, 1.5, 1.0, 0.5, 0.0</td></tr>
080     * <tr><td>3.0</td><td>0.0</td><td>backward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#BOTH BOTH}</td><td>3.0, 2.5, 2.0, 1.5, 1.0, 0.5, 0.0</td></tr>
081     * </table>
082     * </p>
083     *
084     * @see StepHandler
085     * @see FixedStepHandler
086     * @see StepNormalizerMode
087     * @see StepNormalizerBounds
088     * @version $Id: StepNormalizer.java 1181282 2011-10-10 22:35:54Z erans $
089     * @since 1.2
090     */
091    
092    public class StepNormalizer implements StepHandler {
093        /** Fixed time step. */
094        private double h;
095    
096        /** Underlying step handler. */
097        private final FixedStepHandler handler;
098    
099        /** First step time. */
100        private double firstTime;
101    
102        /** Last step time. */
103        private double lastTime;
104    
105        /** Last state vector. */
106        private double[] lastState;
107    
108        /** Last derivatives vector. */
109        private double[] lastDerivatives;
110    
111        /** Integration direction indicator. */
112        private boolean forward;
113    
114        /** The step normalizer bounds settings to use. */
115        private final StepNormalizerBounds bounds;
116    
117        /** The step normalizer mode to use. */
118        private final StepNormalizerMode mode;
119    
120        /** Simple constructor. Uses {@link StepNormalizerMode#INCREMENT INCREMENT}
121         * mode, and {@link StepNormalizerBounds#FIRST FIRST} bounds setting, for
122         * backwards compatibility.
123         * @param h fixed time step (sign is not used)
124         * @param handler fixed time step handler to wrap
125         */
126        public StepNormalizer(final double h, final FixedStepHandler handler) {
127            this(h, handler, StepNormalizerMode.INCREMENT,
128                 StepNormalizerBounds.FIRST);
129        }
130    
131        /** Simple constructor. Uses {@link StepNormalizerBounds#FIRST FIRST}
132         * bounds setting.
133         * @param h fixed time step (sign is not used)
134         * @param handler fixed time step handler to wrap
135         * @param mode step normalizer mode to use
136         * @since 3.0
137         */
138        public StepNormalizer(final double h, final FixedStepHandler handler,
139                              final StepNormalizerMode mode) {
140            this(h, handler, mode, StepNormalizerBounds.FIRST);
141        }
142    
143        /** Simple constructor. Uses {@link StepNormalizerMode#INCREMENT INCREMENT}
144         * mode.
145         * @param h fixed time step (sign is not used)
146         * @param handler fixed time step handler to wrap
147         * @param bounds step normalizer bounds setting to use
148         * @since 3.0
149         */
150        public StepNormalizer(final double h, final FixedStepHandler handler,
151                              final StepNormalizerBounds bounds) {
152            this(h, handler, StepNormalizerMode.INCREMENT, bounds);
153        }
154    
155        /** Simple constructor.
156         * @param h fixed time step (sign is not used)
157         * @param handler fixed time step handler to wrap
158         * @param mode step normalizer mode to use
159         * @param bounds step normalizer bounds setting to use
160         * @since 3.0
161         */
162        public StepNormalizer(final double h, final FixedStepHandler handler,
163                              final StepNormalizerMode mode,
164                              final StepNormalizerBounds bounds) {
165            this.h       = FastMath.abs(h);
166            this.handler = handler;
167            this.mode    = mode;
168            this.bounds  = bounds;
169            reset();
170        }
171    
172        /** Reset the step handler.
173         * Initialize the internal data as required before the first step is
174         * handled.
175         */
176        public void reset() {
177            firstTime       = Double.NaN;
178            lastTime        = Double.NaN;
179            lastState       = null;
180            lastDerivatives = null;
181            forward         = true;
182        }
183    
184        /**
185         * Handle the last accepted step
186         * @param interpolator interpolator for the last accepted step. For
187         * efficiency purposes, the various integrators reuse the same
188         * object on each call, so if the instance wants to keep it across
189         * all calls (for example to provide at the end of the integration a
190         * continuous model valid throughout the integration range), it
191         * should build a local copy using the clone method and store this
192         * copy.
193         * @param isLast true if the step is the last one
194         */
195        public void handleStep(final StepInterpolator interpolator, final boolean isLast) {
196            // The first time, update the last state with the start information.
197            if (lastState == null) {
198                firstTime = interpolator.getPreviousTime();
199                lastTime = interpolator.getPreviousTime();
200                interpolator.setInterpolatedTime(lastTime);
201                lastState = interpolator.getInterpolatedState().clone();
202                lastDerivatives = interpolator.getInterpolatedDerivatives().clone();
203    
204                // Take the integration direction into account.
205                forward = interpolator.getCurrentTime() >= lastTime;
206                if (!forward) {
207                    h = -h;
208                }
209            }
210    
211            // Calculate next normalized step time.
212            double nextTime = (mode == StepNormalizerMode.INCREMENT) ?
213                              lastTime + h :
214                              (FastMath.floor(lastTime / h) + 1) * h;
215            if (mode == StepNormalizerMode.MULTIPLES &&
216                Precision.equals(nextTime, lastTime, 1)) {
217                nextTime += h;
218            }
219    
220            // Process normalized steps as long as they are in the current step.
221            boolean nextInStep = isNextInStep(nextTime, interpolator);
222            while (nextInStep) {
223                // Output the stored previous step.
224                doNormalizedStep(false);
225    
226                // Store the next step as last step.
227                storeStep(interpolator, nextTime);
228    
229                // Move on to the next step.
230                nextTime += h;
231                nextInStep = isNextInStep(nextTime, interpolator);
232            }
233    
234            if (isLast) {
235                // There will be no more steps. The stored one should be given to
236                // the handler. We may have to output one more step. Only the last
237                // one of those should be flagged as being the last.
238                boolean addLast = bounds.lastIncluded() &&
239                                  lastTime != interpolator.getCurrentTime();
240                doNormalizedStep(!addLast);
241                if (addLast) {
242                    storeStep(interpolator, interpolator.getCurrentTime());
243                    doNormalizedStep(true);
244                }
245            }
246        }
247    
248        /**
249         * Returns a value indicating whether the next normalized time is in the
250         * current step.
251         * @param nextTime the next normalized time
252         * @param interpolator interpolator for the last accepted step, to use to
253         * get the end time of the current step
254         * @return value indicating whether the next normalized time is in the
255         * current step
256         */
257        private boolean isNextInStep(double nextTime,
258                                     StepInterpolator interpolator) {
259            return forward ?
260                   nextTime <= interpolator.getCurrentTime() :
261                   nextTime >= interpolator.getCurrentTime();
262        }
263    
264        /**
265         * Invokes the underlying step handler for the current normalized step.
266         * @param isLast true if the step is the last one
267         */
268        private void doNormalizedStep(boolean isLast) {
269            if (!bounds.firstIncluded() && firstTime == lastTime) {
270                return;
271            }
272            handler.handleStep(lastTime, lastState, lastDerivatives, isLast);
273        }
274    
275        /** Stores the interpolated information for the given time in the current
276         * state.
277         * @param interpolator interpolator for the last accepted step, to use to
278         * get the interpolated information
279         * @param t the time for which to store the interpolated information
280         */
281        private void storeStep(StepInterpolator interpolator, double t) {
282            lastTime = t;
283            interpolator.setInterpolatedTime(lastTime);
284            System.arraycopy(interpolator.getInterpolatedState(), 0,
285                             lastState, 0, lastState.length);
286            System.arraycopy(interpolator.getInterpolatedDerivatives(), 0,
287                             lastDerivatives, 0, lastDerivatives.length);
288        }
289    }