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 }