R/ode23s.R

Defines functions ode23s

Documented in ode23s

ode23s <- function(f, t0, tfinal, y0, jac = NULL, ...,
            rtol = 1e-03, atol = 1e-06, hmax = 0.0) {
    stopifnot(is.numeric(y0), is.numeric(t0), length(t0) == 1,
              is.numeric(tfinal), length(tfinal) == 1)
    if (t0 >= tfinal)
        warning("'t0 >= tfinal' may lead to incorrect behavior or results.")

    if (is.vector(y0)) {
        y0 <- as.matrix(y0)
    } else if (is.matrix(y0)) {
        if (ncol(y0) != 1)
            stop("Argument 'y0' must be a vector or single column matrix.")
    }

    fun <- match.fun(f)
    f <- function(t, y) fun(t, y, ...)
    if (length(f(t0, y0)) != length(y0))
        stop("Argument 'f' does not describe a system of equations.")

    n <- length(y0); m <- length(f(t, y0))
    # use finite difference Jacobian
    if (is.null(jac)) {
        jac <- function(t, x) {
            jacob <- matrix(NA, m, n)
            hh <- numeric(n); heps <- 5e-06
            for (i in 1:n) {
                hh[i] <- heps
                jacob[, i] <- (f(t, x+hh) - f(t, x-hh)) / (2*heps)
                hh[i] <- 0
            }
            jacob
        }
    }

    # Set initial parameters
    d <- 1/(2 + sqrt(2))
    cc <- 1/2
    e32 <-  6 + sqrt(2)

    t <- t0
    tdir <- sign(tfinal - t)

    h <- tdir * 0.01 * (tfinal - t)
    if (hmax == 0.0)
        hmax <- 0.1 * abs(tfinal - t)
    hmin <- min(16 * eps(tfinal - t), h)

    y <- as.matrix(y0)
    tout <- c(t)
    yout <- c(y)

    # Main loop
    while (abs(t) < abs(tfinal) && hmin < abs(h)) {
        if (abs(t - tfinal) < abs(h))
            h <- tfinal - t

        J <- jac(t, y)

        # approximate time-derivative of f
        T <- (f(t + 0.01*h, y) - f(t, y)) / (0.01*h)

        # Wolfbrandt coefficient
        W <- eye(length(y0)) - h * d * J

        # modified Rosenbrock formula
        F1 <- f(t, y)
        k1 <- qr.solve(W, F1 + h * d * T)
        F2 <- f(t + cc * h, y + cc * h * k1)
        k2 <- qr.solve(W, F2 - k1) + k1

        # 2nd and 3rd order estimates
        ynew <- y + h * k2
        F3 = f(t + h, ynew)
        k3 = qr.solve(W, (F3 - e32*(k2 - F2) - 2*(k1 - F1) + h*d*T))

        # estimate error and acceptable error
        err <- h/6 * Norm(k1 - 2*k2 + k3)
        tau <- max(rtol * max(Norm(y),Norm(ynew)), atol)

        # check if new solution is acceptable
        if (err <= tau) {
            t <- t + h
            tout <- c(tout, t)
            y <- ynew
            yout <- cbind(yout, y)
            if (err == 0) err <- eps()/2
            # h <- min(hmax, 1.25*h)
        } else {
            if (h <= hmin)
                warning("ode23s: Requested step size too small!")
            # h <- max(hmin, 0.5*h)
        }
        # update the step size
        h <- tdir * min(hmax, abs(h)*0.8*(tau/err)^(1/3))
        if (abs(h) > hmax)
            h <- sign(h)*hmax
    }
    return(list(t = c(tout), y = t(yout)))
}

Try the pracma package in your browser

Any scripts or data that you put into this service are public.

pracma documentation built on March 19, 2024, 3:05 a.m.