public void ChangeStep() { dt = dt / 2.0; if (dt < epsilon) { throw new ArgumentException("Cannot generate numerical solution"); } zn = NordsieckState.Rescale(zn, 0.5d); }
/// <summary> /// Implementation of Gear's BDF method with dynamically changed step size and order. Order changes between 1 and 3. /// </summary> /// <param name="t0">Initial time point</param> /// <param name="x0">Initial phase vector</param> /// <param name="f">Right parts of the system</param> /// <param name="opts">Options for accuracy control and initial step size</param> /// <returns>Sequence of infinite number of solution points.</returns> public static IEnumerable <SolPoint> GearBDF(double t0, Vector x0, Func <double, Vector, Vector> f, Options opts) { double t = t0; Vector x = x0.Clone(); int n = x0.Length; double tout = t0; Vector xout = new Vector(); if (opts.OutputStep > 0) // Store previous solution point if OutputStep is specified (non-zero) { xout = x0.Clone(); tout += opts.OutputStep; } // Firstly, return initial point yield return(new SolPoint(t0, x0.Clone())); //Initial step size. Vector dx = f(t0, x0).Clone(); double dt; if (opts.InitialStep != 0) { dt = opts.InitialStep; } else { var tol = opts.RelativeTolerance; var ewt = Vector.Zeros(n); var ywt = Vector.Zeros(n); var sum = 0.0; for (int i = 0; i < n; i++) { ewt[i] = opts.RelativeTolerance * Math.Abs(x[i]) + opts.AbsoluteTolerance; ywt[i] = ewt[i] / tol; sum = sum + (double)dx[i] * dx[i] / (ywt[i] * ywt[i]); } dt = Math.Sqrt(tol / ((double)1.0d / (ywt[0] * ywt[0]) + sum / n)); } dt = Math.Min(dt, opts.MaxStep); var resdt = dt; int qmax = 5; int qcurr = 2; //Compute Nordstieck's history matrix at t=t0; Matrix zn = new Matrix(n, qmax + 1); for (int i = 0; i < n; i++) { zn[i, 0] = x[i]; zn[i, 1] = dt * dx[i]; for (int j = qcurr; j < qmax + 1; j++) { zn[i, j] = 0.0d; } } var eold = Vector.Zeros(n); NordsieckState currstate = new NordsieckState(); currstate.delta = 0.0d; currstate.Dq = 0.0d; currstate.dt = dt; currstate.en = eold; currstate.tn = t; currstate.xn = x0; currstate.qn = qcurr; currstate.qmax = qmax; currstate.nsuccess = 0; currstate.zn = zn; currstate.rFactor = 1.0d; bool isIterationFailed = false; //Can produce any number of solution points while (true) { // Reset fail flag isIterationFailed = false; // Predictor step var z0 = currstate.zn.Clone(); currstate.zn = NordsieckState.ZNew(currstate.zn); currstate.en = Vector.Zeros(n); currstate.xn = currstate.zn.CloneColumn(0); // Corrector step currstate = PredictorCorrectorScheme(currstate, ref isIterationFailed, f, opts); if (isIterationFailed) // If iterations are not finished - bad convergence { currstate.zn = z0; currstate.nsuccess = 0; currstate.ChangeStep(); } else // Iterations finished { var r = Math.Min(1.1d, Math.Max(0.2d, currstate.rFactor)); if (currstate.delta >= 1.0d) { if (opts.MaxStep < Double.MaxValue) { r = Math.Min(r, opts.MaxStep / currstate.dt); } if (opts.MinStep > 0) { r = Math.Max(r, opts.MinStep / currstate.dt); } r = Math.Min(r, opts.MaxScale); r = Math.Max(r, opts.MinScale); currstate.dt = currstate.dt * r; // Decrease step currstate.zn = NordsieckState.Rescale(currstate.zn, r); } else { // Output data if (opts.OutputStep > 0) // Output points with specified step { while (currstate.tn <= tout && tout <= currstate.tn + currstate.dt) { yield return(new SolPoint(tout, Vector.Lerp(tout, currstate.tn, xout, currstate.tn + currstate.dt, currstate.xn))); tout += opts.OutputStep; } Vector.Copy(currstate.xn, xout); } else // Output each point { yield return(new SolPoint(currstate.tn + currstate.dt, currstate.xn)); } currstate.tn = currstate.tn + currstate.dt; if (opts.MaxStep < Double.MaxValue) { r = Math.Min(r, opts.MaxStep / currstate.dt); } if (opts.MinStep > 0) { r = Math.Max(r, opts.MinStep / currstate.dt); } r = Math.Min(r, opts.MaxScale); r = Math.Max(r, opts.MinScale); currstate.dt = currstate.dt * r; currstate.zn = NordsieckState.Rescale(currstate.zn, r); } } } }