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.math.ode.nonstiff; 19 20 import org.apache.commons.math.exception.util.LocalizedFormats; 21 import org.apache.commons.math.ode.AbstractIntegrator; 22 import org.apache.commons.math.ode.DerivativeException; 23 import org.apache.commons.math.ode.ExtendedFirstOrderDifferentialEquations; 24 import org.apache.commons.math.ode.FirstOrderDifferentialEquations; 25 import org.apache.commons.math.ode.IntegratorException; 26 import org.apache.commons.math.util.FastMath; 27 28 /** 29 * This abstract class holds the common part of all adaptive 30 * stepsize integrators for Ordinary Differential Equations. 31 * 32 * <p>These algorithms perform integration with stepsize control, which 33 * means the user does not specify the integration step but rather a 34 * tolerance on error. The error threshold is computed as 35 * <pre> 36 * threshold_i = absTol_i + relTol_i * max (abs (ym), abs (ym+1)) 37 * </pre> 38 * where absTol_i is the absolute tolerance for component i of the 39 * state vector and relTol_i is the relative tolerance for the same 40 * component. The user can also use only two scalar values absTol and 41 * relTol which will be used for all components. 42 * </p> 43 * 44 * <p>If the Ordinary Differential Equations is an {@link ExtendedFirstOrderDifferentialEquations 45 * extended ODE} rather than a {@link FirstOrderDifferentialEquations basic ODE}, 46 * then <em>only</em> the {@link ExtendedFirstOrderDifferentialEquations#getMainSetDimension() 47 * main set} part of the state vector is used for stepsize control, not the complete 48 * state vector. 49 * </p> 50 * 51 * <p>If the estimated error for ym+1 is such that 52 * <pre> 53 * sqrt((sum (errEst_i / threshold_i)^2 ) / n) < 1 54 * </pre> 55 * 56 * (where n is the main set dimension) then the step is accepted, 57 * otherwise the step is rejected and a new attempt is made with a new 58 * stepsize.</p> 59 * 60 * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $ 61 * @since 1.2 62 * 63 */ 64 65 public abstract class AdaptiveStepsizeIntegrator 66 extends AbstractIntegrator { 67 68 /** Allowed absolute scalar error. */ 69 protected final double scalAbsoluteTolerance; 70 71 /** Allowed relative scalar error. */ 72 protected final double scalRelativeTolerance; 73 74 /** Allowed absolute vectorial error. */ 75 protected final double[] vecAbsoluteTolerance; 76 77 /** Allowed relative vectorial error. */ 78 protected final double[] vecRelativeTolerance; 79 80 /** Main set dimension. */ 81 protected int mainSetDimension; 82 83 /** User supplied initial step. */ 84 private double initialStep; 85 86 /** Minimal step. */ 87 private final double minStep; 88 89 /** Maximal step. */ 90 private final double maxStep; 91 92 /** Build an integrator with the given stepsize bounds. 93 * The default step handler does nothing. 94 * @param name name of the method 95 * @param minStep minimal step (must be positive even for backward 96 * integration), the last step can be smaller than this 97 * @param maxStep maximal step (must be positive even for backward 98 * integration) 99 * @param scalAbsoluteTolerance allowed absolute error 100 * @param scalRelativeTolerance allowed relative error 101 */ AdaptiveStepsizeIntegrator(final String name, final double minStep, final double maxStep, final double scalAbsoluteTolerance, final double scalRelativeTolerance)102 public AdaptiveStepsizeIntegrator(final String name, 103 final double minStep, final double maxStep, 104 final double scalAbsoluteTolerance, 105 final double scalRelativeTolerance) { 106 107 super(name); 108 109 this.minStep = FastMath.abs(minStep); 110 this.maxStep = FastMath.abs(maxStep); 111 this.initialStep = -1.0; 112 113 this.scalAbsoluteTolerance = scalAbsoluteTolerance; 114 this.scalRelativeTolerance = scalRelativeTolerance; 115 this.vecAbsoluteTolerance = null; 116 this.vecRelativeTolerance = null; 117 118 resetInternalState(); 119 120 } 121 122 /** Build an integrator with the given stepsize bounds. 123 * The default step handler does nothing. 124 * @param name name of the method 125 * @param minStep minimal step (must be positive even for backward 126 * integration), the last step can be smaller than this 127 * @param maxStep maximal step (must be positive even for backward 128 * integration) 129 * @param vecAbsoluteTolerance allowed absolute error 130 * @param vecRelativeTolerance allowed relative error 131 */ AdaptiveStepsizeIntegrator(final String name, final double minStep, final double maxStep, final double[] vecAbsoluteTolerance, final double[] vecRelativeTolerance)132 public AdaptiveStepsizeIntegrator(final String name, 133 final double minStep, final double maxStep, 134 final double[] vecAbsoluteTolerance, 135 final double[] vecRelativeTolerance) { 136 137 super(name); 138 139 this.minStep = minStep; 140 this.maxStep = maxStep; 141 this.initialStep = -1.0; 142 143 this.scalAbsoluteTolerance = 0; 144 this.scalRelativeTolerance = 0; 145 this.vecAbsoluteTolerance = vecAbsoluteTolerance.clone(); 146 this.vecRelativeTolerance = vecRelativeTolerance.clone(); 147 148 resetInternalState(); 149 150 } 151 152 /** Set the initial step size. 153 * <p>This method allows the user to specify an initial positive 154 * step size instead of letting the integrator guess it by 155 * itself. If this method is not called before integration is 156 * started, the initial step size will be estimated by the 157 * integrator.</p> 158 * @param initialStepSize initial step size to use (must be positive even 159 * for backward integration ; providing a negative value or a value 160 * outside of the min/max step interval will lead the integrator to 161 * ignore the value and compute the initial step size by itself) 162 */ setInitialStepSize(final double initialStepSize)163 public void setInitialStepSize(final double initialStepSize) { 164 if ((initialStepSize < minStep) || (initialStepSize > maxStep)) { 165 initialStep = -1.0; 166 } else { 167 initialStep = initialStepSize; 168 } 169 } 170 171 /** Perform some sanity checks on the integration parameters. 172 * @param equations differential equations set 173 * @param t0 start time 174 * @param y0 state vector at t0 175 * @param t target time for the integration 176 * @param y placeholder where to put the state vector 177 * @exception IntegratorException if some inconsistency is detected 178 */ 179 @Override sanityChecks(final FirstOrderDifferentialEquations equations, final double t0, final double[] y0, final double t, final double[] y)180 protected void sanityChecks(final FirstOrderDifferentialEquations equations, 181 final double t0, final double[] y0, 182 final double t, final double[] y) 183 throws IntegratorException { 184 185 super.sanityChecks(equations, t0, y0, t, y); 186 187 if (equations instanceof ExtendedFirstOrderDifferentialEquations) { 188 mainSetDimension = ((ExtendedFirstOrderDifferentialEquations) equations).getMainSetDimension(); 189 } else { 190 mainSetDimension = equations.getDimension(); 191 } 192 193 if ((vecAbsoluteTolerance != null) && (vecAbsoluteTolerance.length != mainSetDimension)) { 194 throw new IntegratorException( 195 LocalizedFormats.DIMENSIONS_MISMATCH_SIMPLE, mainSetDimension, vecAbsoluteTolerance.length); 196 } 197 198 if ((vecRelativeTolerance != null) && (vecRelativeTolerance.length != mainSetDimension)) { 199 throw new IntegratorException( 200 LocalizedFormats.DIMENSIONS_MISMATCH_SIMPLE, mainSetDimension, vecRelativeTolerance.length); 201 } 202 203 } 204 205 /** Initialize the integration step. 206 * @param equations differential equations set 207 * @param forward forward integration indicator 208 * @param order order of the method 209 * @param scale scaling vector for the state vector (can be shorter than state vector) 210 * @param t0 start time 211 * @param y0 state vector at t0 212 * @param yDot0 first time derivative of y0 213 * @param y1 work array for a state vector 214 * @param yDot1 work array for the first time derivative of y1 215 * @return first integration step 216 * @exception DerivativeException this exception is propagated to 217 * the caller if the underlying user function triggers one 218 */ initializeStep(final FirstOrderDifferentialEquations equations, final boolean forward, final int order, final double[] scale, final double t0, final double[] y0, final double[] yDot0, final double[] y1, final double[] yDot1)219 public double initializeStep(final FirstOrderDifferentialEquations equations, 220 final boolean forward, final int order, final double[] scale, 221 final double t0, final double[] y0, final double[] yDot0, 222 final double[] y1, final double[] yDot1) 223 throws DerivativeException { 224 225 if (initialStep > 0) { 226 // use the user provided value 227 return forward ? initialStep : -initialStep; 228 } 229 230 // very rough first guess : h = 0.01 * ||y/scale|| / ||y'/scale|| 231 // this guess will be used to perform an Euler step 232 double ratio; 233 double yOnScale2 = 0; 234 double yDotOnScale2 = 0; 235 for (int j = 0; j < scale.length; ++j) { 236 ratio = y0[j] / scale[j]; 237 yOnScale2 += ratio * ratio; 238 ratio = yDot0[j] / scale[j]; 239 yDotOnScale2 += ratio * ratio; 240 } 241 242 double h = ((yOnScale2 < 1.0e-10) || (yDotOnScale2 < 1.0e-10)) ? 243 1.0e-6 : (0.01 * FastMath.sqrt(yOnScale2 / yDotOnScale2)); 244 if (! forward) { 245 h = -h; 246 } 247 248 // perform an Euler step using the preceding rough guess 249 for (int j = 0; j < y0.length; ++j) { 250 y1[j] = y0[j] + h * yDot0[j]; 251 } 252 computeDerivatives(t0 + h, y1, yDot1); 253 254 // estimate the second derivative of the solution 255 double yDDotOnScale = 0; 256 for (int j = 0; j < scale.length; ++j) { 257 ratio = (yDot1[j] - yDot0[j]) / scale[j]; 258 yDDotOnScale += ratio * ratio; 259 } 260 yDDotOnScale = FastMath.sqrt(yDDotOnScale) / h; 261 262 // step size is computed such that 263 // h^order * max (||y'/tol||, ||y''/tol||) = 0.01 264 final double maxInv2 = FastMath.max(FastMath.sqrt(yDotOnScale2), yDDotOnScale); 265 final double h1 = (maxInv2 < 1.0e-15) ? 266 FastMath.max(1.0e-6, 0.001 * FastMath.abs(h)) : 267 FastMath.pow(0.01 / maxInv2, 1.0 / order); 268 h = FastMath.min(100.0 * FastMath.abs(h), h1); 269 h = FastMath.max(h, 1.0e-12 * FastMath.abs(t0)); // avoids cancellation when computing t1 - t0 270 if (h < getMinStep()) { 271 h = getMinStep(); 272 } 273 if (h > getMaxStep()) { 274 h = getMaxStep(); 275 } 276 if (! forward) { 277 h = -h; 278 } 279 280 return h; 281 282 } 283 284 /** Filter the integration step. 285 * @param h signed step 286 * @param forward forward integration indicator 287 * @param acceptSmall if true, steps smaller than the minimal value 288 * are silently increased up to this value, if false such small 289 * steps generate an exception 290 * @return a bounded integration step (h if no bound is reach, or a bounded value) 291 * @exception IntegratorException if the step is too small and acceptSmall is false 292 */ filterStep(final double h, final boolean forward, final boolean acceptSmall)293 protected double filterStep(final double h, final boolean forward, final boolean acceptSmall) 294 throws IntegratorException { 295 296 double filteredH = h; 297 if (FastMath.abs(h) < minStep) { 298 if (acceptSmall) { 299 filteredH = forward ? minStep : -minStep; 300 } else { 301 throw new IntegratorException( 302 LocalizedFormats.MINIMAL_STEPSIZE_REACHED_DURING_INTEGRATION, 303 minStep, FastMath.abs(h)); 304 } 305 } 306 307 if (filteredH > maxStep) { 308 filteredH = maxStep; 309 } else if (filteredH < -maxStep) { 310 filteredH = -maxStep; 311 } 312 313 return filteredH; 314 315 } 316 317 /** {@inheritDoc} */ integrate(FirstOrderDifferentialEquations equations, double t0, double[] y0, double t, double[] y)318 public abstract double integrate (FirstOrderDifferentialEquations equations, 319 double t0, double[] y0, 320 double t, double[] y) 321 throws DerivativeException, IntegratorException; 322 323 /** {@inheritDoc} */ 324 @Override getCurrentStepStart()325 public double getCurrentStepStart() { 326 return stepStart; 327 } 328 329 /** Reset internal state to dummy values. */ resetInternalState()330 protected void resetInternalState() { 331 stepStart = Double.NaN; 332 stepSize = FastMath.sqrt(minStep * maxStep); 333 } 334 335 /** Get the minimal step. 336 * @return minimal step 337 */ getMinStep()338 public double getMinStep() { 339 return minStep; 340 } 341 342 /** Get the maximal step. 343 * @return maximal step 344 */ getMaxStep()345 public double getMaxStep() { 346 return maxStep; 347 } 348 349 } 350