Exemplo n.º 1
0
 /// <summary>
 /// Solves the differential equation $y'=f(t,y)$ from $t=s_0$ to $t=s$ with initial value $y(s_0)=y_0$. Used by
 /// <see cref="SolveCashKarpAdaptive" /> and <see cref="SolveVernerAdaptive" />.
 /// </summary>
 public static Vector SolveAdaptive(Func<double, Vector, Vector> f, Vector y0, double s0, double s, double eps, double h0, double hmin, StepperAdaptive stepper, out double[] t)
 {
     List<double> tsteps = new List<double>();
     Vector y = SolveAdaptive(f, y0, s0, s, eps, h0, hmin, stepper, tsteps);
     t = tsteps.ToArray();
     return y;
 }
Exemplo n.º 2
0
        private static Vector SolveAdaptive(Func<double, Vector, Vector> f, Vector y0, double s0, double s, double eps, double h0, double hmin, StepperAdaptive stepper, List<double> tsteps)
        {
            if (f == null || y0 == null || stepper == null)
            {
                throw new ArgumentNullException();
            }

            if (eps <= 0.0)
            {
                throw new ArgumentOutOfRangeException("Invalid target accuracy.");
            }

            if (hmin < 0.0 || h0 < hmin || h0 == 0.0)
            {
                throw new ArgumentOutOfRangeException("Invalid step size specification.");
            }

            // Use initial value to determine the dimension of the problem.
            int n = y0.Length;

            //  Use this step size for the first step (unless determined too large).
            double h = h0;

            // Negative step size if integrating backwards through time.
            if (s < s0)
            {
                h = -h;
                hmin = -hmin;
            }

            double t = s0;
            Vector y = y0;

            while (t != s)
            {
                // Store intermediate steps if allocated.
                if (tsteps != null)
                {
                    tsteps.Add(t);
                }

                // Try this step. And maintain a minimum step size.
                double u = t + h;
                double umin = t + hmin;

                // Don't allow the current step to overshoot.
                if (s > s0 && u > s || s < s0 && u < s)
                {
                    u = s;
                }

                // May take a smaller step than the minimum step size at the very last step to avoid overshooting.
                if (s > s0 && umin > s || s < s0 && umin < s)
                {
                    umin = s;
                }

                // Evaluate derivative at the initial point for this step. This may be reused even if guessed step size is too large.
                Vector dy = f(t, y);
                Vector z;

                while (true)
                {
                    // Don't go below the minimum step size.
                    if (s > s0 && u < umin || s < s0 && u > umin)
                    {
                        u = umin;
                        h = hmin;
                    }

                    if (u == t)
                    {
                        throw new ArithmeticException("Adaptive step size underflow.");
                    }

                    // Take a step.
                    Vector yerr;
                    z = stepper(f, y, t, u, dy, out yerr);

                    // Evaluate accuracy. And scale relative to required tolerance.
                    double errmax = 0.0;
                    for (int i = 0; i < n; i++)
                    {
                        // Scaling to monitor accuracy. This is a general-purpose choice.
                        double yscal = Math.Abs(y[i]) + Math.Abs(dy[i] * h);

                        // Need this to be non-zero. Otherwise the entry can't be that important.
                        if (yscal != 0.0)
                        {
                            errmax = Math.Max(errmax, Math.Abs(yerr[i] / yscal));
                        }
                    }
                    errmax /= eps;

                    if (errmax <= 1.0)
                    {
                        // Step succeeded. Increase the step size for the next step, but no more than a factor of 5.
                        h *= Math.Min(5.0, 0.9 * Math.Pow(errmax, -0.2));
                        break;
                    }

                    if (u == umin)
                    {
                        // Already at minimum step size. Not allowed to improve the step size below this.
                        break;
                    }

                    // Not succeeded: Truncation error too large, reduce stepsize, but no more than a factor of 10.
                    h *= Math.Max(0.1, 0.9 * Math.Pow(errmax, -0.25));
                    u = t + h;

                    // Restart loop with the reduced step size.
                }

                // Finish the step.
                t = u;
                y = z;
            }

            return y;
        }
Exemplo n.º 3
0
 /// <summary>
 /// Solves the differential equation $y'=f(t,y)$ from $t=s_0$ to $t=s$ with initial value $y(s_0)=y_0$. Used by
 /// <see cref="SolveCashKarpAdaptive" /> and <see cref="SolveVernerAdaptive" />.
 /// </summary>
 public static Vector SolveAdaptive(Func<double, Vector, Vector> f, Vector y0, double s0, double s, double eps, double h0, double hmin, StepperAdaptive stepper)
 {
     return SolveAdaptive(f, y0, s0, s, eps, h0, hmin, stepper, null);
 }