public static int general_driver( Func <Func <double, vector, vector>, double, vector, double, vector[]> stepper, Func <double, vector, vector> f, double a, ref vector y, double b, double h, // Starting step size double acc, double eps, List <double> ts = null, List <vector> ys = null ) { // The current posistion, set to starting condition: double t = a; vector yt = y; // Not needed? if (ts != null) { ts.Add(t); } if (ys != null) { ys.Add(yt); } vector err = new vector(y.size); vector[] res; // Step untill less than one step is needed: int steps = 0; while (t < b - h) { steps++; //Write($"\nStep nr. {steps}, h = {h}\n"); // Do the step: res = stepper(f, t, y, h); yt = res[0]; err = res[1]; // Calculate the current step tolerance: vector tau_is = (acc + eps * yt.abs()) * Sqrt(h / (b - a)); // Asses if the error of the step is within the tolerance // and calculate the ratio of the tolerance and the error // for each entry. bool errAccepted = true; vector tolRatios = new vector(err.size); for (int i = 0; i < tau_is.size; i++) { if (Abs(err[i]) > Abs(tau_is[i])) { errAccepted = false; } tolRatios[i] = Abs(tau_is[i]) / Abs(err[i]); } // Is error estimate lower than tolerance? // Then step is accepted, t and yt if (errAccepted) { // Update t and y: t += h; y = yt; if (ts != null) { ts.Add(t); } if (ys != null) { ys.Add(yt); } } else { // Don't change t and yt. } // Update h: (vector.min() is a new method I implemented myself) double h_factor = Pow(tolRatios.min(), 0.25) * 0.95; h = (h_factor < 2)?h_factor * h:2 * h; } // Do the final step to reach b: (For now, there is a chance that // the error gets too large in this step... But test this code // first) double h_final = b - t; res = stepper(f, t, y, h_final); yt = res[0]; err = res[1]; y = yt; if (ts != null) { ts.Add(t + h_final); } if (ys != null) { ys.Add(yt); } return(steps); }
public static (List <double>, List <vector>) driver( Func <double, vector, vector> f, /* equation */ double a, /* starting-point */ vector ya, /* initial values y(a) */ double b, /* end-point of integration */ double h, /* Initial stepsize */ double acc, /* Absolute accuracy goal */ double eps, /* Relative accuracy goal */ List <double> xlist, /* Lists to fill results into */ List <vector> ylist, int limit, /* Absolute step limit */ double max_factor, /* limit on step factor increase */ bool slack, /* decides if accuracy should reduce with integration */ Func <Func <double, vector, vector>, double, vector, double, vector[]> stepper /* Stepping function */ ) { int nsteps = 0; if (xlist != null) { // Clears or creates lists to be filled with results, and adds starting point xlist.Clear(); xlist.Add(a); } else { // null-coealescing assignment operator ??= used because comment whined xlist ??= new List <double>(); // List<double> xlist = new List<double>(); xlist.Add(a); } if (ylist != null) { ylist.Clear(); ylist.Add(ya); } else { ylist ??= new List <vector>(); // List<vector> ylist = new List<vector>(); ylist.Add(ya); } double x = a; vector yx = ya; while (x < b) { nsteps++; if (x + h > b) { h = b - x; } // Attempt step vector[] attempt = stepper(f, x, yx, h); vector yh = attempt[0]; vector err = attempt[1]; // Calculate step tolerance vector tau; if (slack) { tau = (eps * yh.abs() + acc) * Sqrt(h / (b - x)); } else { tau = (eps * yh.abs() + acc) * Sqrt(h / (b - a)); } // Slack uses x instead of a. Reduces accuray requirements as integration progresses. // Determine if error is within tolerance, calculate tolerance ratio bool accept = true; vector tol_ratio = new vector(err.size); for (int i = 0; i < tau.size; i++) { tol_ratio[i] = Abs(tau[i]) / Abs(err[i]); if (tol_ratio[i] < 1) { accept = false; } } // Update if step was accepted if (accept) { x += h; yx = yh; xlist.Add(x); ylist.Add(yx); } else { Error.WriteLine("Bad step at x={0}. Step rejected.", x); } // Adjust step size based on empirical formula double tolmin = tol_ratio[0]; for (int i = 1; i < tol_ratio.size; i++) { tolmin = Min(tolmin, tol_ratio[i]); } double adj_factor = Pow(tolmin, 0.25) * 0.95; if (adj_factor > max_factor) { adj_factor = max_factor; } h = h * adj_factor; if (nsteps >= limit) { Error.WriteLine("Step limit exceeded, returning current result"); break; } } // end while loop Error.WriteLine("ODE solved in {0} steps", nsteps); return(xlist, ylist); } // ode driver