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
018package org.apache.commons.math3.ode.sampling;
019
020import org.apache.commons.math3.RealFieldElement;
021import org.apache.commons.math3.exception.MaxCountExceededException;
022import org.apache.commons.math3.ode.FieldODEStateAndDerivative;
023import org.apache.commons.math3.util.FastMath;
024import org.apache.commons.math3.util.Precision;
025
026/**
027 * This class wraps an object implementing {@link FieldFixedStepHandler}
028 * into a {@link FieldStepHandler}.
029
030 * <p>This wrapper allows to use fixed step handlers with general
031 * integrators which cannot guaranty their integration steps will
032 * remain constant and therefore only accept general step
033 * handlers.</p>
034 *
035 * <p>The stepsize used is selected at construction time. The {@link
036 * FieldFixedStepHandler#handleStep handleStep} method of the underlying
037 * {@link FieldFixedStepHandler} object is called at normalized times. The
038 * normalized times can be influenced by the {@link StepNormalizerMode} and
039 * {@link StepNormalizerBounds}.</p>
040 *
041 * <p>There is no constraint on the integrator, it can use any time step
042 * it needs (time steps longer or shorter than the fixed time step and
043 * non-integer ratios are all allowed).</p>
044 *
045 * <p>
046 * <table border="1" align="center">
047 * <tr BGCOLOR="#CCCCFF"><td colspan=6><font size="+2">Examples (step size = 0.5)</font></td></tr>
048 * <tr BGCOLOR="#EEEEFF"><font size="+1"><td>Start time</td><td>End time</td>
049 *  <td>Direction</td><td>{@link StepNormalizerMode Mode}</td>
050 *  <td>{@link StepNormalizerBounds Bounds}</td><td>Output</td></font></tr>
051 * <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>
052 * <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>
053 * <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>
054 * <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>
055 * <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>
056 * <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>
057 * <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>
058 * <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>
059 * <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>
060 * <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>
061 * <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>
062 * <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>
063 * <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>
064 * <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>
065 * <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>
066 * <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>
067 * <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>
068 * <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>
069 * <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>
070 * <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>
071 * <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>
072 * <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>
073 * <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>
074 * <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>
075 * <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>
076 * <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>
077 * <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>
078 * <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>
079 * <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>
080 * <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>
081 * <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>
082 * <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>
083 * </table>
084 * </p>
085 *
086 * @param <T> the type of the field elements
087 * @see FieldStepHandler
088 * @see FieldFixedStepHandler
089 * @see StepNormalizerMode
090 * @see StepNormalizerBounds
091 * @since 3.6
092 */
093
094public class FieldStepNormalizer<T extends RealFieldElement<T>> implements FieldStepHandler<T> {
095
096    /** Fixed time step. */
097    private double h;
098
099    /** Underlying step handler. */
100    private final FieldFixedStepHandler<T> handler;
101
102    /** First step state. */
103    private FieldODEStateAndDerivative<T> first;
104
105    /** Last step step. */
106    private FieldODEStateAndDerivative<T> last;
107
108    /** Integration direction indicator. */
109    private boolean forward;
110
111    /** The step normalizer bounds settings to use. */
112    private final StepNormalizerBounds bounds;
113
114    /** The step normalizer mode to use. */
115    private final StepNormalizerMode mode;
116
117    /** Simple constructor. Uses {@link StepNormalizerMode#INCREMENT INCREMENT}
118     * mode, and {@link StepNormalizerBounds#FIRST FIRST} bounds setting, for
119     * backwards compatibility.
120     * @param h fixed time step (sign is not used)
121     * @param handler fixed time step handler to wrap
122     */
123    public FieldStepNormalizer(final double h, final FieldFixedStepHandler<T> handler) {
124        this(h, handler, StepNormalizerMode.INCREMENT,
125             StepNormalizerBounds.FIRST);
126    }
127
128    /** Simple constructor. Uses {@link StepNormalizerBounds#FIRST FIRST}
129     * bounds setting.
130     * @param h fixed time step (sign is not used)
131     * @param handler fixed time step handler to wrap
132     * @param mode step normalizer mode to use
133     * @since 3.0
134     */
135    public FieldStepNormalizer(final double h, final FieldFixedStepHandler<T> handler,
136                               final StepNormalizerMode mode) {
137        this(h, handler, mode, StepNormalizerBounds.FIRST);
138    }
139
140    /** Simple constructor. Uses {@link StepNormalizerMode#INCREMENT INCREMENT}
141     * mode.
142     * @param h fixed time step (sign is not used)
143     * @param handler fixed time step handler to wrap
144     * @param bounds step normalizer bounds setting to use
145     * @since 3.0
146     */
147    public FieldStepNormalizer(final double h, final FieldFixedStepHandler<T> handler,
148                               final StepNormalizerBounds bounds) {
149        this(h, handler, StepNormalizerMode.INCREMENT, bounds);
150    }
151
152    /** Simple constructor.
153     * @param h fixed time step (sign is not used)
154     * @param handler fixed time step handler to wrap
155     * @param mode step normalizer mode to use
156     * @param bounds step normalizer bounds setting to use
157     * @since 3.0
158     */
159    public FieldStepNormalizer(final double h, final FieldFixedStepHandler<T> handler,
160                               final StepNormalizerMode mode, final StepNormalizerBounds bounds) {
161        this.h       = FastMath.abs(h);
162        this.handler = handler;
163        this.mode    = mode;
164        this.bounds  = bounds;
165        first        = null;
166        last         = null;
167        forward      = true;
168    }
169
170    /** {@inheritDoc} */
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    /**
183     * Handle the last accepted step
184     * @param interpolator interpolator for the last accepted step. For
185     * efficiency purposes, the various integrators reuse the same
186     * object on each call, so if the instance wants to keep it across
187     * all calls (for example to provide at the end of the integration a
188     * continuous model valid throughout the integration range), it
189     * should build a local copy using the clone method and store this
190     * copy.
191     * @param isLast true if the step is the last one
192     * @exception MaxCountExceededException if the interpolator throws one because
193     * the number of functions evaluations is exceeded
194     */
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((FastMath.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
273}