Exemplo n.º 1
0
    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);
    }
Exemplo n.º 2
0
    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