View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.math4.legacy.ode.sampling;
19  
20  import org.apache.commons.math4.legacy.exception.MaxCountExceededException;
21  import org.apache.commons.math4.core.jdkmath.JdkMath;
22  import org.apache.commons.numbers.core.Precision;
23  
24  /**
25   * This class wraps an object implementing {@link FixedStepHandler}
26   * into a {@link StepHandler}.
27  
28   * <p>This wrapper allows to use fixed step handlers with general
29   * integrators which cannot guaranty their integration steps will
30   * remain constant and therefore only accept general step
31   * handlers.</p>
32   *
33   * <p>The stepsize used is selected at construction time. The {@link
34   * FixedStepHandler#handleStep handleStep} method of the underlying
35   * {@link FixedStepHandler} object is called at normalized times. The
36   * normalized times can be influenced by the {@link StepNormalizerMode} and
37   * {@link StepNormalizerBounds}.</p>
38   *
39   * <p>There is no constraint on the integrator, it can use any time step
40   * it needs (time steps longer or shorter than the fixed time step and
41   * non-integer ratios are all allowed).</p>
42   *
43   * <table border="1" style="text-align: center">
44   * <caption>Examples (step size = 0.5)</caption>
45   * <tr style="background-color: #CCCCFF"><td style="font-size: x-large">Examples (step size = 0.5)</td></tr>
46   * <tr style="background-color: #EEEEFF; font-size: large"><td>Start time</td><td>End time</td>
47   *  <td>Direction</td><td>{@link StepNormalizerMode Mode}</td>
48   *  <td>{@link StepNormalizerBounds Bounds}</td><td>Output</td></tr>
49   * <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>
50   * <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>
51   * <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>
52   * <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>
53   * <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>
54   * <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>
55   * <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>
56   * <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>
57   * <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>
58   * <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>
59   * <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>
60   * <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>
61   * <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>
62   * <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>
63   * <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>
64   * <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>
65   * <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>
66   * <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>
67   * <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>
68   * <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>
69   * <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>
70   * <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>
71   * <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>
72   * <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>
73   * <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>
74   * <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>
75   * <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>
76   * <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>
77   * <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>
78   * <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>
79   * <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>
80   * <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>
81   * </table>
82   *
83   * @see StepHandler
84   * @see FixedStepHandler
85   * @see StepNormalizerMode
86   * @see StepNormalizerBounds
87   * @since 1.2
88   */
89  
90  public class StepNormalizer implements StepHandler {
91      /** Fixed time step. */
92      private double h;
93  
94      /** Underlying step handler. */
95      private final FixedStepHandler handler;
96  
97      /** First step time. */
98      private double firstTime;
99  
100     /** Last step time. */
101     private double lastTime;
102 
103     /** Last state vector. */
104     private double[] lastState;
105 
106     /** Last derivatives vector. */
107     private double[] lastDerivatives;
108 
109     /** Integration direction indicator. */
110     private boolean forward;
111 
112     /** The step normalizer bounds settings to use. */
113     private final StepNormalizerBounds bounds;
114 
115     /** The step normalizer mode to use. */
116     private final StepNormalizerMode mode;
117 
118     /** Simple constructor. Uses {@link StepNormalizerMode#INCREMENT INCREMENT}
119      * mode, and {@link StepNormalizerBounds#FIRST FIRST} bounds setting, for
120      * backwards compatibility.
121      * @param h fixed time step (sign is not used)
122      * @param handler fixed time step handler to wrap
123      */
124     public StepNormalizer(final double h, final FixedStepHandler handler) {
125         this(h, handler, StepNormalizerMode.INCREMENT,
126              StepNormalizerBounds.FIRST);
127     }
128 
129     /** Simple constructor. Uses {@link StepNormalizerBounds#FIRST FIRST}
130      * bounds setting.
131      * @param h fixed time step (sign is not used)
132      * @param handler fixed time step handler to wrap
133      * @param mode step normalizer mode to use
134      * @since 3.0
135      */
136     public StepNormalizer(final double h, final FixedStepHandler handler,
137                           final StepNormalizerMode mode) {
138         this(h, handler, mode, StepNormalizerBounds.FIRST);
139     }
140 
141     /** Simple constructor. Uses {@link StepNormalizerMode#INCREMENT INCREMENT}
142      * mode.
143      * @param h fixed time step (sign is not used)
144      * @param handler fixed time step handler to wrap
145      * @param bounds step normalizer bounds setting to use
146      * @since 3.0
147      */
148     public StepNormalizer(final double h, final FixedStepHandler handler,
149                           final StepNormalizerBounds bounds) {
150         this(h, handler, StepNormalizerMode.INCREMENT, bounds);
151     }
152 
153     /** Simple constructor.
154      * @param h fixed time step (sign is not used)
155      * @param handler fixed time step handler to wrap
156      * @param mode step normalizer mode to use
157      * @param bounds step normalizer bounds setting to use
158      * @since 3.0
159      */
160     public StepNormalizer(final double h, final FixedStepHandler handler,
161                           final StepNormalizerMode mode,
162                           final StepNormalizerBounds bounds) {
163         this.h          = JdkMath.abs(h);
164         this.handler    = handler;
165         this.mode       = mode;
166         this.bounds     = bounds;
167         firstTime       = Double.NaN;
168         lastTime        = Double.NaN;
169         lastState       = null;
170         lastDerivatives = null;
171         forward         = true;
172     }
173 
174     /** {@inheritDoc} */
175     @Override
176     public void init(double t0, double[] y0, double t) {
177 
178         firstTime       = Double.NaN;
179         lastTime        = Double.NaN;
180         lastState       = null;
181         lastDerivatives = null;
182         forward         = true;
183 
184         // initialize the underlying handler
185         handler.init(t0, y0, t);
186     }
187 
188     /**
189      * Handle the last accepted step.
190      * @param interpolator interpolator for the last accepted step. For
191      * efficiency purposes, the various integrators reuse the same
192      * object on each call, so if the instance wants to keep it across
193      * all calls (for example to provide at the end of the integration a
194      * continuous model valid throughout the integration range), it
195      * should build a local copy using the clone method and store this
196      * copy.
197      * @param isLast true if the step is the last one
198      * @exception MaxCountExceededException if the interpolator throws one because
199      * the number of functions evaluations is exceeded
200      */
201     @Override
202     public void handleStep(final StepInterpolator interpolator, final boolean isLast)
203         throws MaxCountExceededException {
204         // The first time, update the last state with the start information.
205         if (lastState == null) {
206             firstTime = interpolator.getPreviousTime();
207             lastTime = interpolator.getPreviousTime();
208             interpolator.setInterpolatedTime(lastTime);
209             lastState = interpolator.getInterpolatedState().clone();
210             lastDerivatives = interpolator.getInterpolatedDerivatives().clone();
211 
212             // Take the integration direction into account.
213             forward = interpolator.getCurrentTime() >= lastTime;
214             if (!forward) {
215                 h = -h;
216             }
217         }
218 
219         // Calculate next normalized step time.
220         double nextTime = (mode == StepNormalizerMode.INCREMENT) ?
221                           lastTime + h :
222                           (JdkMath.floor(lastTime / h) + 1) * h;
223         if (mode == StepNormalizerMode.MULTIPLES &&
224             Precision.equals(nextTime, lastTime, 1)) {
225             nextTime += h;
226         }
227 
228         // Process normalized steps as long as they are in the current step.
229         boolean nextInStep = isNextInStep(nextTime, interpolator);
230         while (nextInStep) {
231             // Output the stored previous step.
232             doNormalizedStep(false);
233 
234             // Store the next step as last step.
235             storeStep(interpolator, nextTime);
236 
237             // Move on to the next step.
238             nextTime += h;
239             nextInStep = isNextInStep(nextTime, interpolator);
240         }
241 
242         if (isLast) {
243             // There will be no more steps. The stored one should be given to
244             // the handler. We may have to output one more step. Only the last
245             // one of those should be flagged as being the last.
246             boolean addLast = bounds.lastIncluded() &&
247                               lastTime != interpolator.getCurrentTime();
248             doNormalizedStep(!addLast);
249             if (addLast) {
250                 storeStep(interpolator, interpolator.getCurrentTime());
251                 doNormalizedStep(true);
252             }
253         }
254     }
255 
256     /**
257      * Returns a value indicating whether the next normalized time is in the
258      * current step.
259      * @param nextTime the next normalized time
260      * @param interpolator interpolator for the last accepted step, to use to
261      * get the end time of the current step
262      * @return value indicating whether the next normalized time is in the
263      * current step
264      */
265     private boolean isNextInStep(double nextTime,
266                                  StepInterpolator interpolator) {
267         return forward ?
268                nextTime <= interpolator.getCurrentTime() :
269                nextTime >= interpolator.getCurrentTime();
270     }
271 
272     /**
273      * Invokes the underlying step handler for the current normalized step.
274      * @param isLast true if the step is the last one
275      */
276     private void doNormalizedStep(boolean isLast) {
277         if (!bounds.firstIncluded() && firstTime == lastTime) {
278             return;
279         }
280         handler.handleStep(lastTime, lastState, lastDerivatives, isLast);
281     }
282 
283     /** Stores the interpolated information for the given time in the current
284      * state.
285      * @param interpolator interpolator for the last accepted step, to use to
286      * get the interpolated information
287      * @param t the time for which to store the interpolated information
288      * @exception MaxCountExceededException if the interpolator throws one because
289      * the number of functions evaluations is exceeded
290      */
291     private void storeStep(StepInterpolator interpolator, double t)
292         throws MaxCountExceededException {
293         lastTime = t;
294         interpolator.setInterpolatedTime(lastTime);
295         System.arraycopy(interpolator.getInterpolatedState(), 0,
296                          lastState, 0, lastState.length);
297         System.arraycopy(interpolator.getInterpolatedDerivatives(), 0,
298                          lastDerivatives, 0, lastDerivatives.length);
299     }
300 }