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.math4.legacy.ode.sampling;
019
020import org.apache.commons.math4.legacy.core.RealFieldElement;
021import org.apache.commons.math4.legacy.exception.MaxCountExceededException;
022import org.apache.commons.math4.legacy.ode.FieldODEStateAndDerivative;
023import org.apache.commons.math4.core.jdkmath.JdkMath;
024import org.apache.commons.numbers.core.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 * <table border="1" style="text-align: center">
046 * <caption>Examples (step size = 0.5)</caption>
047 * <tr style="background-color: #CCCCFF"><td colspan=6 style="font-size: x-large">Examples (step size = 0.5)</td></tr>
048 * <tr style="background-color: #EEEEFF; font-size: large"><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></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 *
085 * @param <T> the type of the field elements
086 * @see FieldStepHandler
087 * @see FieldFixedStepHandler
088 * @see StepNormalizerMode
089 * @see StepNormalizerBounds
090 * @since 3.6
091 */
092
093public class FieldStepNormalizer<T extends RealFieldElement<T>> implements FieldStepHandler<T> {
094
095    /** Fixed time step. */
096    private double h;
097
098    /** Underlying step handler. */
099    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}