--- title: "Comparison of ODE solvers" author: "Alfonso R. Reyes" date: "`r Sys.Date()`" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Comparison of ODE solvers} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ## Build the ODE class (without class accumulator) ### Comparison of solutions: RK45 vs analytical solution For the differential equation: $$\dfrac{dy}{dt} = -5 \, e^{-t}$$ the analytical solution is: $$y(t) = 5 \, e^{-t}$$ ```{r message=FALSE, results="hold"} library(rODE) # ODETest.R setClass("ODETest", slots = c( n = "numeric" # counts the number of getRate evaluations ), contains = c("ODE") ) setMethod("initialize", "ODETest", function(.Object, ...) { .Object@n <- 0 .Object@state <- c(5.0, 0.0) return(.Object) }) setMethod("getExactSolution", "ODETest", function(object, t, ...) { # analytical solution return(5.0 * exp(-t)) }) setMethod("getState", "ODETest", function(object, ...) { object@state }) setMethod("getRate", "ODETest", function(object, state, ...) { object@rate[1] <- -state[1] object@rate[2] <- 1 # rate of change of time, dt/dt object@n <- object@n + 1 object@rate }) # constructor ODETest <- function() { odetest <- new("ODETest") odetest } ``` ## Build and run the application ComparisonRK45App ```{r} # This script can also be found under ./demo # ComparisonRK45App.R # # Compares the solution by the RK45 ODE solver versus the analytical solution ComparisonRK45App <- function(verbose = FALSE) { ode <- new("ODETest") ode_solver <- RK45(ode) ode_solver <- setStepSize(ode_solver, 1) ode_solver <- setTolerance(ode_solver, 1e-8) time <- 0 while (time < 50) { ode_solver <- step(ode_solver) stepSize <- ode_solver@stepSize # update the step size time <- time + stepSize # ode <- ode_solver@ode state <- getState(ode_solver@ode) if (verbose) cat(sprintf("time=%10f xl=%14e error=%14e n=%5d \n", time, state[1], (state[1] - getExactSolution(ode_solver@ode, time)), ode_solver@ode@n)) } cat("rate steps evaluated #", ode_solver@ode@n) } ComparisonRK45App(verbose = TRUE) ``` > Notes. In this example, the number of iterations does not return from `ode_solver@ode@n` that is part of the class `ODETest`. We will try to fix this in another example. ## Storing the number of counts in a class environment object In this example, we create the environment object `stack` that will allow us to store temporary values or accumulators inside an S4 class. ```{r message=FALSE, results="hold"} library(rODE) setClass("ODETest", slots = c( stack = "environment" # environment object inside the class ), contains = c("ODE") ) setMethod("initialize", "ODETest", function(.Object, ...) { .Object@stack$n <- 0 # "n" belongs to the class environment .Object@state <- c(5.0, 0.0) return(.Object) }) setMethod("getExactSolution", "ODETest", function(object, t, ...) { # analytical solution return(5.0 * exp(-t)) }) setMethod("getState", "ODETest", function(object, ...) { object@state }) setMethod("getRate", "ODETest", function(object, state, ...) { object@rate[1] <- -state[1] object@rate[2] <- 1 # rate of change of time, dt/dt object@stack$n <- object@stack$n + 1 # add 1 to the rate count object@rate }) # constructor ODETest <- function() { odetest <- new("ODETest") odetest } # class implementation ComparisonRK45App <- function(verbose = FALSE) { ode <- new("ODETest") ode_solver <- RK45(ode) ode_solver <- setStepSize(ode_solver, 1) ode_solver <- setTolerance(ode_solver, 1e-8) cat(sprintf("%10s %14s %14s %5s \n", "time", "xl", "error", "n")) # header time <- 0 while (time < 50) { ode_solver <- step(ode_solver) stepSize <- ode_solver@stepSize # update the step size time <- time + stepSize state <- getState(ode_solver@ode) if (verbose) cat(sprintf("%10f %14e %14e %5d \n", time, state[1], (state[1] - getExactSolution(ode_solver@ode, time)), ode_solver@ode@stack$n)) } cat("rate steps evaluated #", ode_solver@ode@stack$n) } ComparisonRK45App(verbose = TRUE) ```