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.core.RealFieldElement;
21 import org.apache.commons.math4.legacy.exception.MaxCountExceededException;
22 import org.apache.commons.math4.legacy.ode.FieldODEStateAndDerivative;
23 import org.apache.commons.math4.core.jdkmath.JdkMath;
24 import org.apache.commons.numbers.core.Precision;
25
26 /**
27 * This class wraps an object implementing {@link FieldFixedStepHandler}
28 * into a {@link FieldStepHandler}.
29
30 * <p>This wrapper allows to use fixed step handlers with general
31 * integrators which cannot guaranty their integration steps will
32 * remain constant and therefore only accept general step
33 * handlers.</p>
34 *
35 * <p>The stepsize used is selected at construction time. The {@link
36 * FieldFixedStepHandler#handleStep handleStep} method of the underlying
37 * {@link FieldFixedStepHandler} object is called at normalized times. The
38 * normalized times can be influenced by the {@link StepNormalizerMode} and
39 * {@link StepNormalizerBounds}.</p>
40 *
41 * <p>There is no constraint on the integrator, it can use any time step
42 * it needs (time steps longer or shorter than the fixed time step and
43 * non-integer ratios are all allowed).</p>
44 *
45 * <table border="1" style="text-align: center">
46 * <caption>Examples (step size = 0.5)</caption>
47 * <tr style="background-color: #CCCCFF"><td colspan=6 style="font-size: x-large">Examples (step size = 0.5)</td></tr>
48 * <tr style="background-color: #EEEEFF; font-size: large"><td>Start time</td><td>End time</td>
49 * <td>Direction</td><td>{@link StepNormalizerMode Mode}</td>
50 * <td>{@link StepNormalizerBounds Bounds}</td><td>Output</td></tr>
51 * <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>
52 * <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>
53 * <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>
54 * <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>
55 * <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>
56 * <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>
57 * <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>
58 * <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>
59 * <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>
60 * <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>
61 * <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>
62 * <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>
63 * <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>
64 * <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>
65 * <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>
66 * <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>
67 * <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>
68 * <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>
69 * <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>
70 * <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>
71 * <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>
72 * <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>
73 * <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>
74 * <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>
75 * <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>
76 * <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>
77 * <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>
78 * <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>
79 * <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>
80 * <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>
81 * <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>
82 * <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>
83 * </table>
84 *
85 * @param <T> the type of the field elements
86 * @see FieldStepHandler
87 * @see FieldFixedStepHandler
88 * @see StepNormalizerMode
89 * @see StepNormalizerBounds
90 * @since 3.6
91 */
92
93 public class FieldStepNormalizer<T extends RealFieldElement<T>> implements FieldStepHandler<T> {
94
95 /** Fixed time step. */
96 private double h;
97
98 /** Underlying step handler. */
99 private final FieldFixedStepHandler<T> handler;
100
101 /** First step state. */
102 private FieldODEStateAndDerivative<T> first;
103
104 /** Last step step. */
105 private FieldODEStateAndDerivative<T> last;
106
107 /** Integration direction indicator. */
108 private boolean forward;
109
110 /** The step normalizer bounds settings to use. */
111 private final StepNormalizerBounds bounds;
112
113 /** The step normalizer mode to use. */
114 private final StepNormalizerMode mode;
115
116 /** Simple constructor. Uses {@link StepNormalizerMode#INCREMENT INCREMENT}
117 * mode, and {@link StepNormalizerBounds#FIRST FIRST} bounds setting, for
118 * backwards compatibility.
119 * @param h fixed time step (sign is not used)
120 * @param handler fixed time step handler to wrap
121 */
122 public FieldStepNormalizer(final double h, final FieldFixedStepHandler<T> handler) {
123 this(h, handler, StepNormalizerMode.INCREMENT,
124 StepNormalizerBounds.FIRST);
125 }
126
127 /** Simple constructor. Uses {@link StepNormalizerBounds#FIRST FIRST}
128 * bounds setting.
129 * @param h fixed time step (sign is not used)
130 * @param handler fixed time step handler to wrap
131 * @param mode step normalizer mode to use
132 * @since 3.0
133 */
134 public FieldStepNormalizer(final double h, final FieldFixedStepHandler<T> handler,
135 final StepNormalizerMode mode) {
136 this(h, handler, mode, StepNormalizerBounds.FIRST);
137 }
138
139 /** Simple constructor. Uses {@link StepNormalizerMode#INCREMENT INCREMENT}
140 * mode.
141 * @param h fixed time step (sign is not used)
142 * @param handler fixed time step handler to wrap
143 * @param bounds step normalizer bounds setting to use
144 * @since 3.0
145 */
146 public FieldStepNormalizer(final double h, final FieldFixedStepHandler<T> handler,
147 final StepNormalizerBounds bounds) {
148 this(h, handler, StepNormalizerMode.INCREMENT, bounds);
149 }
150
151 /** Simple constructor.
152 * @param h fixed time step (sign is not used)
153 * @param handler fixed time step handler to wrap
154 * @param mode step normalizer mode to use
155 * @param bounds step normalizer bounds setting to use
156 * @since 3.0
157 */
158 public FieldStepNormalizer(final double h, final FieldFixedStepHandler<T> handler,
159 final StepNormalizerMode mode, final StepNormalizerBounds bounds) {
160 this.h = JdkMath.abs(h);
161 this.handler = handler;
162 this.mode = mode;
163 this.bounds = bounds;
164 first = null;
165 last = null;
166 forward = true;
167 }
168
169 /** {@inheritDoc} */
170 @Override
171 public void init(final FieldODEStateAndDerivative<T> initialState, final T finalTime) {
172
173 first = null;
174 last = null;
175 forward = true;
176
177 // initialize the underlying handler
178 handler.init(initialState, finalTime);
179 }
180
181 /**
182 * Handle the last accepted step.
183 * @param interpolator interpolator for the last accepted step. For
184 * efficiency purposes, the various integrators reuse the same
185 * object on each call, so if the instance wants to keep it across
186 * all calls (for example to provide at the end of the integration a
187 * continuous model valid throughout the integration range), it
188 * should build a local copy using the clone method and store this
189 * copy.
190 * @param isLast true if the step is the last one
191 * @exception MaxCountExceededException if the interpolator throws one because
192 * the number of functions evaluations is exceeded
193 */
194 @Override
195 public void handleStep(final FieldStepInterpolator<T> interpolator, final boolean isLast)
196 throws MaxCountExceededException {
197 // The first time, update the last state with the start information.
198 if (last == null) {
199
200 first = interpolator.getPreviousState();
201 last = first;
202
203 // Take the integration direction into account.
204 forward = interpolator.isForward();
205 if (!forward) {
206 h = -h;
207 }
208 }
209
210 // Calculate next normalized step time.
211 T nextTime = (mode == StepNormalizerMode.INCREMENT) ?
212 last.getTime().add(h) :
213 last.getTime().getField().getZero().add((JdkMath.floor(last.getTime().getReal() / h) + 1) * h);
214 if (mode == StepNormalizerMode.MULTIPLES &&
215 Precision.equals(nextTime.getReal(), last.getTime().getReal(), 1)) {
216 nextTime = nextTime.add(h);
217 }
218
219 // Process normalized steps as long as they are in the current step.
220 boolean nextInStep = isNextInStep(nextTime, interpolator);
221 while (nextInStep) {
222 // Output the stored previous step.
223 doNormalizedStep(false);
224
225 // Store the next step as last step.
226 last = interpolator.getInterpolatedState(nextTime);
227
228 // Move on to the next step.
229 nextTime = nextTime.add(h);
230 nextInStep = isNextInStep(nextTime, interpolator);
231 }
232
233 if (isLast) {
234 // There will be no more steps. The stored one should be given to
235 // the handler. We may have to output one more step. Only the last
236 // one of those should be flagged as being the last.
237 final boolean addLast = bounds.lastIncluded() &&
238 last.getTime().getReal() != interpolator.getCurrentState().getTime().getReal();
239 doNormalizedStep(!addLast);
240 if (addLast) {
241 last = interpolator.getCurrentState();
242 doNormalizedStep(true);
243 }
244 }
245 }
246
247 /**
248 * Returns a value indicating whether the next normalized time is in the
249 * current step.
250 * @param nextTime the next normalized time
251 * @param interpolator interpolator for the last accepted step, to use to
252 * get the end time of the current step
253 * @return value indicating whether the next normalized time is in the
254 * current step
255 */
256 private boolean isNextInStep(final T nextTime, final FieldStepInterpolator<T> interpolator) {
257 return forward ?
258 nextTime.getReal() <= interpolator.getCurrentState().getTime().getReal() :
259 nextTime.getReal() >= interpolator.getCurrentState().getTime().getReal();
260 }
261
262 /**
263 * Invokes the underlying step handler for the current normalized step.
264 * @param isLast true if the step is the last one
265 */
266 private void doNormalizedStep(final boolean isLast) {
267 if (!bounds.firstIncluded() && first.getTime().getReal() == last.getTime().getReal()) {
268 return;
269 }
270 handler.handleStep(last, isLast);
271 }
272 }