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 * https://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 /*
19 * This is not the original file distributed by the Apache Software Foundation
20 * It has been modified by the Hipparchus project
21 */
22
23 package org.hipparchus.ode.nonstiff;
24
25 import org.hipparchus.CalculusFieldElement;
26 import org.hipparchus.Field;
27 import org.hipparchus.exception.MathIllegalArgumentException;
28 import org.hipparchus.exception.MathIllegalStateException;
29 import org.hipparchus.ode.AbstractFieldIntegrator;
30 import org.hipparchus.ode.FieldODEState;
31 import org.hipparchus.ode.FieldODEStateAndDerivative;
32 import org.hipparchus.util.FastMath;
33 import org.hipparchus.util.MathArrays;
34
35 /**
36 * This abstract class holds the common part of all adaptive
37 * stepsize integrators for Ordinary Differential Equations.
38 *
39 * <p>These algorithms perform integration with stepsize control, which
40 * means the user does not specify the integration step but rather a
41 * tolerance on error. The error threshold is computed as
42 * </p>
43 * <pre>
44 * threshold_i = absTol_i + relTol_i * max (abs (ym), abs (ym+1))
45 * </pre>
46 * <p>
47 * where absTol_i is the absolute tolerance for component i of the
48 * state vector and relTol_i is the relative tolerance for the same
49 * component. The user can also use only two scalar values absTol and
50 * relTol which will be used for all components.
51 * </p>
52 * <p>
53 * Note that <em>only</em> the {@link FieldODEState#getPrimaryState() main part}
54 * of the state vector is used for stepsize control. The {@link
55 * FieldODEState#getSecondaryState(int) secondary parts} of the state
56 * vector are explicitly ignored for stepsize control.
57 * </p>
58 *
59 * <p>If the estimated error for ym+1 is such that</p>
60 * <pre>
61 * sqrt((sum (errEst_i / threshold_i)^2 ) / n) < 1
62 * </pre>
63 *
64 * <p>(where n is the main set dimension) then the step is accepted,
65 * otherwise the step is rejected and a new attempt is made with a new
66 * stepsize.</p>
67 *
68 * @param <T> the type of the field elements
69 *
70 */
71
72 public abstract class AdaptiveStepsizeFieldIntegrator<T extends CalculusFieldElement<T>>
73 extends AbstractFieldIntegrator<T> {
74
75 /** Helper for step size control. */
76 private StepsizeHelper stepsizeHelper;
77
78 /** Build an integrator with the given stepsize bounds.
79 * The default step handler does nothing.
80 * @param field field to which the time and state vector elements belong
81 * @param name name of the method
82 * @param minStep minimal step (sign is irrelevant, regardless of
83 * integration direction, forward or backward), the last step can
84 * be smaller than this
85 * @param maxStep maximal step (sign is irrelevant, regardless of
86 * integration direction, forward or backward), the last step can
87 * be smaller than this
88 * @param scalAbsoluteTolerance allowed absolute error
89 * @param scalRelativeTolerance allowed relative error
90 */
91 protected AdaptiveStepsizeFieldIntegrator(final Field<T> field, final String name,
92 final double minStep, final double maxStep,
93 final double scalAbsoluteTolerance,
94 final double scalRelativeTolerance) {
95 super(field, name);
96 stepsizeHelper = new StepsizeHelper(minStep, maxStep, scalAbsoluteTolerance, scalRelativeTolerance);
97 resetInternalState();
98 }
99
100 /** Build an integrator with the given stepsize bounds.
101 * The default step handler does nothing.
102 * @param field field to which the time and state vector elements belong
103 * @param name name of the method
104 * @param minStep minimal step (sign is irrelevant, regardless of
105 * integration direction, forward or backward), the last step can
106 * be smaller than this
107 * @param maxStep maximal step (sign is irrelevant, regardless of
108 * integration direction, forward or backward), the last step can
109 * be smaller than this
110 * @param vecAbsoluteTolerance allowed absolute error
111 * @param vecRelativeTolerance allowed relative error
112 */
113 protected AdaptiveStepsizeFieldIntegrator(final Field<T> field, final String name,
114 final double minStep, final double maxStep,
115 final double[] vecAbsoluteTolerance,
116 final double[] vecRelativeTolerance) {
117 super(field, name);
118 stepsizeHelper = new StepsizeHelper(minStep, maxStep, vecAbsoluteTolerance, vecRelativeTolerance);
119 resetInternalState();
120 }
121
122 /** Set the adaptive step size control parameters.
123 * <p>
124 * A side effect of this method is to also reset the initial
125 * step so it will be automatically computed by the integrator
126 * if {@link #setInitialStepSize(double) setInitialStepSize}
127 * is not called by the user.
128 * </p>
129 * @param minimalStep minimal step (must be positive even for backward
130 * integration), the last step can be smaller than this
131 * @param maximalStep maximal step (must be positive even for backward
132 * integration)
133 * @param absoluteTolerance allowed absolute error
134 * @param relativeTolerance allowed relative error
135 */
136 public void setStepSizeControl(final double minimalStep, final double maximalStep,
137 final double absoluteTolerance, final double relativeTolerance) {
138 stepsizeHelper = new StepsizeHelper(minimalStep, maximalStep, absoluteTolerance, relativeTolerance);
139 }
140
141 /** Set the adaptive step size control parameters.
142 * <p>
143 * A side effect of this method is to also reset the initial
144 * step so it will be automatically computed by the integrator
145 * if {@link #setInitialStepSize(double) setInitialStepSize}
146 * is not called by the user.
147 * </p>
148 * @param minimalStep minimal step (must be positive even for backward
149 * integration), the last step can be smaller than this
150 * @param maximalStep maximal step (must be positive even for backward
151 * integration)
152 * @param absoluteTolerance allowed absolute error
153 * @param relativeTolerance allowed relative error
154 */
155 public void setStepSizeControl(final double minimalStep, final double maximalStep,
156 final double[] absoluteTolerance,
157 final double[] relativeTolerance) {
158 stepsizeHelper = new StepsizeHelper(minimalStep, maximalStep, absoluteTolerance, relativeTolerance);
159 }
160
161 /** Get the stepsize helper.
162 * @return stepsize helper
163 * @since 2.0
164 */
165 protected StepsizeHelper getStepSizeHelper() {
166 return stepsizeHelper;
167 }
168
169 /** Set the initial step size.
170 * <p>This method allows the user to specify an initial positive
171 * step size instead of letting the integrator guess it by
172 * itself. If this method is not called before integration is
173 * started, the initial step size will be estimated by the
174 * integrator.</p>
175 * @param initialStepSize initial step size to use (must be positive even
176 * for backward integration ; providing a negative value or a value
177 * outside of the min/max step interval will lead the integrator to
178 * ignore the value and compute the initial step size by itself)
179 */
180 public void setInitialStepSize(final double initialStepSize) {
181 stepsizeHelper.setInitialStepSize(initialStepSize);
182 }
183
184 /** {@inheritDoc} */
185 @Override
186 protected void sanityChecks(final FieldODEState<T> initialState, final T t)
187 throws MathIllegalArgumentException {
188 super.sanityChecks(initialState, t);
189 stepsizeHelper.setMainSetDimension(initialState.getPrimaryStateDimension());
190 }
191
192 /** Initialize the integration step.
193 * @param forward forward integration indicator
194 * @param order order of the method
195 * @param scale scaling vector for the state vector (can be shorter than state vector)
196 * @param state0 state at integration start time
197 * @return first integration step
198 * @exception MathIllegalStateException if the number of functions evaluations is exceeded
199 * @exception MathIllegalArgumentException if arrays dimensions do not match equations settings
200 */
201 public double initializeStep(final boolean forward, final int order, final T[] scale,
202 final FieldODEStateAndDerivative<T> state0)
203 throws MathIllegalArgumentException, MathIllegalStateException {
204
205 if (stepsizeHelper.getInitialStep() > 0) {
206 // use the user provided value
207 return forward ? stepsizeHelper.getInitialStep() : -stepsizeHelper.getInitialStep();
208 }
209
210 // very rough first guess : h = 0.01 * ||y/scale|| / ||y'/scale||
211 // this guess will be used to perform an Euler step
212 final T[] y0 = state0.getCompleteState();
213 final T[] yDot0 = state0.getCompleteDerivative();
214 double yOnScale2 = 0;
215 double yDotOnScale2 = 0;
216 for (int j = 0; j < scale.length; ++j) {
217 final double ratio = y0[j].getReal() / scale[j].getReal();
218 yOnScale2 += ratio * ratio;
219 final double ratioDot = yDot0[j].getReal() / scale[j].getReal();
220 yDotOnScale2 += ratioDot * ratioDot;
221 }
222
223 double h = ((yOnScale2 < 1.0e-10) || (yDotOnScale2 < 1.0e-10)) ?
224 1.0e-6 : (0.01 * FastMath.sqrt(yOnScale2 / yDotOnScale2));
225 if (h > getMaxStep()) {
226 h = getMaxStep();
227 }
228 if (! forward) {
229 h = -h;
230 }
231
232 // perform an Euler step using the preceding rough guess
233 final T[] y1 = MathArrays.buildArray(getField(), y0.length);
234 for (int j = 0; j < y0.length; ++j) {
235 y1[j] = y0[j].add(yDot0[j].multiply(h));
236 }
237 final T[] yDot1 = computeDerivatives(state0.getTime().add(h), y1);
238
239 // estimate the second derivative of the solution
240 double yDDotOnScale = 0;
241 for (int j = 0; j < scale.length; ++j) {
242 final double ratioDotDot = (yDot1[j].getReal() - yDot0[j].getReal()) / scale[j].getReal();
243 yDDotOnScale += ratioDotDot * ratioDotDot;
244 }
245 yDDotOnScale = FastMath.sqrt(yDDotOnScale) / h;
246
247 // step size is computed such that
248 // h^order * max (||y'/tol||, ||y''/tol||) = 0.01
249 final double maxInv2 = FastMath.max(FastMath.sqrt(yDotOnScale2), yDDotOnScale);
250 final double h1 = (maxInv2 < 1.0e-15) ?
251 FastMath.max(1.0e-6, 0.001 * FastMath.abs(h)) :
252 FastMath.pow(0.01 / maxInv2, 1.0 / order);
253 h = FastMath.min(100.0 * FastMath.abs(h), h1);
254 h = FastMath.max(h, 1.0e-12 * FastMath.abs(state0.getTime().getReal())); // avoids cancellation when computing t1 - t0
255 if (h < getMinStep()) {
256 h = getMinStep();
257 }
258 if (h > getMaxStep()) {
259 h = getMaxStep();
260 }
261
262 if (! forward) {
263 h = -h;
264 }
265
266 return h;
267
268 }
269
270 /** Reset internal state to dummy values. */
271 protected void resetInternalState() {
272 setStepStart(null);
273 setStepSize(getField().getZero().add(stepsizeHelper.getDummyStepsize()));
274 }
275
276 /** Get the minimal step.
277 * @return minimal step
278 */
279 public double getMinStep() {
280 return stepsizeHelper.getMinStep();
281 }
282
283 /** Get the maximal step.
284 * @return maximal step
285 */
286 public double getMaxStep() {
287 return stepsizeHelper.getMaxStep();
288 }
289
290 }