public void update() { double t = 0.0; double errRatio; if (step > samplingStep) step = samplingStep; int nSteps = 0; for (; ; ) { if ((samplingStep - t < step) && (samplingStep - t > 0.0)) step = samplingStep - t; rkckStep(step); errRatio = Numerics.max(yerr) / tolerance; // // Update the substance levels and go to the next step if // 1. The target tolerance is reached. // 2. The maximum # of iteractions is exceeded. // 3. The minimum time step is reached. // if (errRatio <= 1.0) { t += step; updateSystem(step); // // grow the step a little bit. but not more than a factor of // five // step *= Math.min(SAFETY * Math.pow(errRatio, PGROW), 5.0); } else if (nSteps > maxIterations) { System.out.println(" WARNING: # of iterations exceeds " + maxIterations); t += step; updateSystem(step); } else if (step < minStep) { System.out.println(" WARNING: Minimum integrateion step size (" + minStep + ") reached"); t += step; updateSystem(step); } else { // step size too large. // Need to reset the values of all the substances // for (int i = 0; i < numberOfSubstances; i++) s[i].setValue(y1[i]); // // reset the Timer back to where it started. // timer.step(0 - step); // // shrink the step size // and recompute. But shrink by a factor smaller than 10. // step *= Math.max(SAFETY * Math.pow(errRatio, PSHRINK), 0.1); } if (t >= samplingStep) break; } // // Store the data if the sampling step is reached. // timer.storeTimePoint(); for (int i = 0; i < numberOfSubstances; i++) { s[i].storeValue(); s[i].storeRate(); } }
// // rkckStep takes a Cash-Karp Runge-Kutta step // to estimate the error and the substance levels // after the attempted step. // private void rkckStep(double h) { double t = timer.getTime(); // step 1 for (int i = 0; i < numberOfSubstances; i++) { // // get the substance values before this step // y1[i] = s[i].getValue(); // // k1 = h * f(t, y); // k1[i] = h * s[i].getRate(); } // step 2 // // k2 = h * f (t + a2 * h, y + b21 * k1) // timer.setTime(t + a2 * h); for (int i = 0; i < numberOfSubstances; i++) { s[i].setValue(y1[i] + k1[i] * b21); k2[i] = h * s[i].getRate(); } // step 3 // // k3 = h * f (t + a3 * h, y + b31 * k1 + b32 * k2) // timer.setTime(t + a3 * h); for (int i = 0; i < numberOfSubstances; i++) { s[i].setValue(y1[i] + b31 * k1[i] + b32 * k2[i]); k3[i] = h * s[i].getRate(); } // step 4 // // k4 = h * f (t + a4 * h, y + b41 * k1 + b42 * k2 + b43 * k3) // timer.setTime(t + a4 * h); for (int i = 0; i < numberOfSubstances; i++) { s[i].setValue(y1[i] + b41 * k1[i] + b42 * k2[i] + b43 * k3[i]); k4[i] = h * s[i].getRate(); } // step 5 // k5 = h * f (t + a5 * h, y + b51 * k1 + b52 * k2 + b53 * k3 + b54 * // k4) // timer.setTime(t + a5 * h); for (int i = 0; i < numberOfSubstances; i++) { s[i].setValue(y1[i] + b51 * k1[i] + b52 * k2[i] + b53 * k3[i] + b54 * k4[i]); k5[i] = h * s[i].getRate(); } // step 6 // k6 = h * f (t + a6 * h, y + b61 * k1 + b62 * k2 + b63 * k3 + b64 * k4 // + k65 * k5) // timer.setTime(t + a5 * h); for (int i = 0; i < numberOfSubstances; i++) { s[i].setValue(y1[i] + b61 * k1[i] + b62 * k2[i] + b63 * k3[i] + b64 * k4[i] + b65 * k5[i]); k6[i] = h * s[i].getRate(); } for (int i = 0; i < numberOfSubstances; i++) { y2[i] = y1[i] + c1 * k1[i] + c3 * k3[i] + c4 * k4[i] + c6 * k6[i]; // fifth // order // estimate // // error between 4th and 5th // // note that the errors are scaled with respect to estimated new // values // if the new values are smaller than 1e-8, scale to 1e-8. This // number is // used to avoid scaling with respect to a number that is too small. // yerr[i] = Math.abs( (dc1 * k1[i] + dc3 * k3[i] + dc4 * k4[i] + dc5 * k5[i] + dc6 * k6[i]) / Math.max(Math.abs(y2[i]), 1e-8)); } }