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.math.ode.sampling;
19
20 import org.apache.commons.math.ode.DerivativeException;
21
22 /**
23 * This class wraps an object implementing {@link FixedStepHandler}
24 * into a {@link StepHandler}.
25
26 * <p>This wrapper allows to use fixed step handlers with general
27 * integrators which cannot guaranty their integration steps will
28 * remain constant and therefore only accept general step
29 * handlers.</p>
30 *
31 * <p>The stepsize used is selected at construction time. The {@link
32 * FixedStepHandler#handleStep handleStep} method of the underlying
33 * {@link FixedStepHandler} object is called at the beginning time of
34 * the integration t0 and also at times t0+h, t0+2h, ... If the
35 * integration range is an integer multiple of the stepsize, then the
36 * last point handled will be the endpoint of the integration tend, if
37 * not, the last point will belong to the interval [tend - h ;
38 * tend].</p>
39 *
40 * <p>There is no constraint on the integrator, it can use any
41 * timestep it needs (time steps longer or shorter than the fixed time
42 * step and non-integer ratios are all allowed).</p>
43 *
44 * @see StepHandler
45 * @see FixedStepHandler
46 * @version $Revision: 786881 $ $Date: 2009-06-20 14:53:08 -0400 (Sat, 20 Jun 2009) $
47 * @since 1.2
48 */
49
50 public class StepNormalizer implements StepHandler {
51
52 /** Fixed time step. */
53 private double h;
54
55 /** Underlying step handler. */
56 private final FixedStepHandler handler;
57
58 /** Last step time. */
59 private double lastTime;
60
61 /** Last State vector. */
62 private double[] lastState;
63
64 /** Last Derivatives vector. */
65 private double[] lastDerivatives;
66
67 /** Integration direction indicator. */
68 private boolean forward;
69
70 /** Simple constructor.
71 * @param h fixed time step (sign is not used)
72 * @param handler fixed time step handler to wrap
73 */
74 public StepNormalizer(final double h, final FixedStepHandler handler) {
75 this.h = Math.abs(h);
76 this.handler = handler;
77 reset();
78 }
79
80 /** Determines whether this handler needs dense output.
81 * This handler needs dense output in order to provide data at
82 * regularly spaced steps regardless of the steps the integrator
83 * uses, so this method always returns true.
84 * @return always true
85 */
86 public boolean requiresDenseOutput() {
87 return true;
88 }
89
90 /** Reset the step handler.
91 * Initialize the internal data as required before the first step is
92 * handled.
93 */
94 public void reset() {
95 lastTime = Double.NaN;
96 lastState = null;
97 lastDerivatives = null;
98 forward = true;
99 }
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 }