For the differential equation:
\[\dfrac{dy}{dt} = y + 2t\] the general analytical solution is: \[y(t) = C_1e^{t} - 2t - 2\]
Find the errors given this initial condition: \[y(0) = 1\] which allows to find: \[C_1 = 3\]
library(rODE)
setClass("MuskatTest", slots = c(
stack = "environment" # environment object inside the class
),
contains = c("ODE")
)
setMethod("initialize", "MuskatTest", function(.Object, ...) {
.Object@stack$n <- 0 # "n" belongs to the class environment
.Object@state <- c(1.0, 0.0) # initial value
return(.Object)
})
setMethod("getExactSolution", "MuskatTest", function(object, t, ...) {
# analytical solution
return(3 * exp(t) - 2 *t - 2) # constant C1 = 3
})
setMethod("getState", "MuskatTest", function(object, ...) {
object@state
})
setMethod("getRate", "MuskatTest", function(object, state, ...) {
object@rate[1] <- state[1] + 2 * state[2]
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
MuskatTest <- function() {
odetest <- new("MuskatTest")
odetest
}
## [1] "initialize"
## [1] "getExactSolution"
## [1] "getState"
## [1] "getRate"
ComparisonEulerApp <- function(stepSize) {
ode <- new("MuskatTest")
ode_solver <- Euler(ode)
ode_solver <- setStepSize(ode_solver, stepSize)
time <- 0
rowVector <- vector("list")
i <- 1
while (time < 5) {
state <- getState(ode_solver@ode)
time <- state[2]
error <- getExactSolution(ode_solver@ode, time) - state[1]
rowVector[[i]] <- list(step_size = stepSize,
t = time,
y_t = state[1],
exact = getExactSolution(ode_solver@ode, time),
error = error,
rel_err = error / getExactSolution(ode_solver@ode, time),
steps = ode_solver@ode@stack$n
)
ode_solver <- step(ode_solver)
i <- i + 1
}
data.table::rbindlist(rowVector)
}
# get a summary table for different step sizes
get_table <- function(stepSize) {
dt <- ComparisonEulerApp(stepSize)
dt[round(t,1) %in% c(1.0, 2.0, 3.0, 4.0, 5.0)]
}
step_sizes <- c(0.2, 0.1, 0.05)
dt_li <- lapply(step_sizes, get_table)
data.table::rbindlist(dt_li)
## step_size t y_t exact error rel_err steps
## 1: 0.20 1.00 3.464960 4.154845 0.6898855 0.16604360 5
## 2: 0.20 2.00 12.575209 16.167168 3.5919590 0.22217614 10
## 3: 0.20 3.00 38.221065 52.256611 14.0355460 0.26858891 15
## 4: 0.20 4.00 105.012800 153.794450 48.7816503 0.31718733 20
## 5: 0.20 5.00 274.188650 433.239477 159.0508274 0.36711989 25
## 6: 0.10 1.00 3.781227 4.154845 0.3736181 0.08992347 10
## 7: 0.10 2.00 14.182500 16.167168 1.9846684 0.12275919 20
## 8: 0.10 3.00 44.348207 52.256611 7.9084040 0.15133787 30
## 9: 0.10 4.00 125.777767 153.794450 28.0166834 0.18216966 40
## 10: 0.10 5.00 340.172559 433.239477 93.0669187 0.21481634 50
## 11: 0.05 0.95 3.680851 3.857129 0.1762784 0.04570197 19
## 12: 0.05 1.00 3.959893 4.154845 0.1949524 0.04692169 20
## 13: 0.05 1.95 14.214253 15.186063 0.9718093 0.06399350 39
## 14: 0.05 2.00 15.119966 16.167168 1.0472022 0.06477338 40
## 15: 0.05 3.00 48.037558 52.256611 4.2190531 0.08073721 60
## 16: 0.05 3.05 50.739436 55.246033 4.5065977 0.08157324 61
## 17: 0.05 4.00 138.684323 153.794450 15.1101269 0.09824884 80
## 18: 0.05 4.05 146.018539 162.092371 16.0738318 0.09916464 81
## 19: 0.05 5.00 382.503774 433.239477 50.7357038 0.11710776 100
## 20: 0.05 5.05 402.128962 455.967393 53.8384312 0.11807518 101
ComparisonRK4App <- function(stepSize) {
ode <- new("MuskatTest")
ode_solver <- RK4(ode)
ode_solver <- setStepSize(ode_solver, stepSize)
time <- 0
rowVector <- vector("list")
i <- 1
while (time < 5) {
state <- getState(ode_solver@ode)
time <- state[2]
error <- getExactSolution(ode_solver@ode, time) - state[1]
rowVector[[i]] <- list(step_size = stepSize,
t = time,
y_t = state[1],
exact = getExactSolution(ode_solver@ode, time),
error = error,
rel_err = error / getExactSolution(ode_solver@ode, time),
steps = ode_solver@ode@stack$n
)
ode_solver <- step(ode_solver)
i <- i + 1
}
data.table::rbindlist(rowVector)
}
# get a summary table for different step sizes
get_table <- function(stepSize) {
dt <- ComparisonRK4App(stepSize)
dt[round(t, 1) %in% c(1.0, 2.0, 3.0, 4.0, 5.0)]
}
step_sizes <- c(0.2, 0.1, 0.05)
dt_li <- lapply(step_sizes, get_table)
data.table::rbindlist(dt_li)
## step_size t y_t exact error rel_err steps
## 1: 0.20 1.00 4.154753 4.154845 9.207556e-05 2.216101e-05 20
## 2: 0.20 2.00 16.166668 16.167168 5.005718e-04 3.096224e-05 40
## 3: 0.20 3.00 52.254570 52.256611 2.041031e-03 3.905786e-05 60
## 4: 0.20 4.00 153.787053 153.794450 7.397423e-03 4.809941e-05 80
## 5: 0.20 5.00 433.214342 433.239477 2.513521e-02 5.801689e-05 100
## 6: 0.10 1.00 4.154839 4.154845 6.252972e-06 1.504983e-06 40
## 7: 0.10 2.00 16.167134 16.167168 3.399467e-05 2.102698e-06 80
## 8: 0.10 3.00 52.256472 52.256611 1.386106e-04 2.652498e-06 120
## 9: 0.10 4.00 153.793948 153.794450 5.023766e-04 3.266546e-06 160
## 10: 0.10 5.00 433.237770 433.239477 1.707001e-03 3.940086e-06 200
## 11: 0.05 0.95 3.857129 3.857129 3.681617e-07 9.544967e-08 76
## 12: 0.05 1.00 4.154845 4.154845 4.074081e-07 9.805615e-08 80
## 13: 0.05 1.95 15.186061 15.186063 2.054206e-06 1.352692e-07 156
## 14: 0.05 2.00 16.167166 16.167168 2.214900e-06 1.369999e-07 160
## 15: 0.05 3.00 52.256602 52.256611 9.031084e-06 1.728218e-07 240
## 16: 0.05 3.05 55.246024 55.246033 9.652353e-06 1.747158e-07 244
## 17: 0.05 4.00 153.794417 153.794450 3.273204e-05 2.128298e-07 320
## 18: 0.05 4.05 162.092336 162.092371 3.484038e-05 2.149415e-07 324
## 19: 0.05 5.00 433.239366 433.239477 1.112186e-04 2.567140e-07 400
## 20: 0.05 5.05 455.967275 455.967393 1.180901e-04 2.589881e-07 404
We see above that we are repeating code when the only parameter that is being changed is the ODE solver. In these cases is more convenient to use the function ODESolverFactory
.
ComparisonApp <- function(solver, stepSize) {
ode <- new("MuskatTest")
solver_factory <- ODESolverFactory()
ode_solver <- createODESolver(solver_factory, ode, solver)
ode_solver <- setStepSize(ode_solver, stepSize)
rowVector <- vector("list")
time <- 0
i <- 1
while (time < 5.001) {
state <- getState(ode_solver@ode)
time <- state[2]
error <- getExactSolution(ode_solver@ode, time) - state[1]
rowVector[[i]] <- list(step_size = stepSize,
t = time,
y_t = state[1],
exact = getExactSolution(ode_solver@ode, time),
error = error,
rel_err = error / getExactSolution(ode_solver@ode, time),
steps = ode_solver@ode@stack$n
)
ode_solver <- step(ode_solver)
time <- time + getStepSize(ode_solver) # step size retrievd from ODE solver
i <- i + 1
}
data.table::rbindlist(rowVector)
}
# get a summary table for different step sizes
create_table <- function(stepSize, solver) {
dt <- ComparisonApp(solver, stepSize)
# dt
dt[round(t, 1) %in% c(1.0, 2.0, 3.0, 4.0, 5.0)]
}
# Create summary table for ODE solver Euler
step_sizes <- c(0.2, 0.1, 0.05)
dt_li <- lapply(step_sizes, create_table, solver = "Euler")
data.table::rbindlist(dt_li)
## step_size t y_t exact error rel_err steps
## 1: 0.20 1.00 3.464960 4.154845 0.6898855 0.16604360 5
## 2: 0.20 2.00 12.575209 16.167168 3.5919590 0.22217614 10
## 3: 0.20 3.00 38.221065 52.256611 14.0355460 0.26858891 15
## 4: 0.20 4.00 105.012800 153.794450 48.7816503 0.31718733 20
## 5: 0.20 5.00 274.188650 433.239477 159.0508274 0.36711989 25
## 6: 0.10 1.00 3.781227 4.154845 0.3736181 0.08992347 10
## 7: 0.10 2.00 14.182500 16.167168 1.9846684 0.12275919 20
## 8: 0.10 3.00 44.348207 52.256611 7.9084040 0.15133787 30
## 9: 0.10 4.00 125.777767 153.794450 28.0166834 0.18216966 40
## 10: 0.10 5.00 340.172559 433.239477 93.0669187 0.21481634 50
## 11: 0.05 0.95 3.680851 3.857129 0.1762784 0.04570197 19
## 12: 0.05 1.00 3.959893 4.154845 0.1949524 0.04692169 20
## 13: 0.05 1.95 14.214253 15.186063 0.9718093 0.06399350 39
## 14: 0.05 2.00 15.119966 16.167168 1.0472022 0.06477338 40
## 15: 0.05 3.00 48.037558 52.256611 4.2190531 0.08073721 60
## 16: 0.05 3.05 50.739436 55.246033 4.5065977 0.08157324 61
## 17: 0.05 4.00 138.684323 153.794450 15.1101269 0.09824884 80
## 18: 0.05 4.05 146.018539 162.092371 16.0738318 0.09916464 81
## 19: 0.05 5.00 382.503774 433.239477 50.7357038 0.11710776 100
# Create summary table for ODE solver Verlet
step_sizes <- c(0.2, 0.1, 0.05)
dt_li <- lapply(step_sizes, create_table, solver = "Verlet")
data.table::rbindlist(dt_li)
## step_size t y_t exact error rel_err steps
## 1: 0.20 1.00 3.613792 4.154845 0.5410535 0.13022229 10
## 2: 0.20 2.00 13.094383 16.167168 3.0727854 0.19006330 20
## 3: 0.20 3.00 39.661767 52.256611 12.5948439 0.24101915 30
## 4: 0.20 4.00 108.746560 153.794450 45.0478903 0.29290973 40
## 5: 0.20 5.00 283.628272 433.239477 149.6112057 0.34533142 50
## 6: 0.10 1.00 3.860915 4.154845 0.2939310 0.07074414 20
## 7: 0.10 2.00 14.468875 16.167168 1.6982935 0.10504582 40
## 8: 0.10 3.00 45.170677 52.256611 7.0859338 0.13559880 60
## 9: 0.10 4.00 127.990729 153.794450 25.8037206 0.16778057 80
## 10: 0.10 5.00 345.992101 433.239477 87.2473760 0.20138372 100
## 11: 0.05 0.95 3.719024 3.857129 0.1381046 0.03580503 38
## 12: 0.05 1.00 4.001226 4.154845 0.1536199 0.03697368 40
## 13: 0.05 1.95 14.356872 15.186063 0.8291905 0.05460207 78
## 14: 0.05 2.00 15.270966 16.167168 0.8962024 0.05543348 80
## 15: 0.05 3.00 48.479537 52.256611 3.7770734 0.07227934 120
## 16: 0.05 3.05 51.204764 55.246033 4.0412691 0.07315039 122
## 17: 0.05 4.00 139.898359 153.794450 13.8960909 0.09035496 160
## 18: 0.05 4.05 147.294527 162.092371 14.7978439 0.09129266 162
## 19: 0.05 5.00 385.766305 433.239477 47.4731723 0.10957721 200
# Create summary table for ODE solver EulerRichardson
step_sizes <- c(0.2, 0.1, 0.05)
dt_li <- lapply(step_sizes, create_table, solver = "EulerRichardson")
data.table::rbindlist(dt_li)
## step_size t y_t exact error rel_err steps
## 1: 0.20 1.00 4.108124 4.154845 0.046720996 0.0112449418 10
## 2: 0.20 2.00 15.913894 16.167168 0.253274051 0.0156659500 20
## 3: 0.20 3.00 51.226861 52.256611 1.029749903 0.0197056389 30
## 4: 0.20 4.00 150.072920 153.794450 3.721529754 0.0241980758 40
## 5: 0.20 5.00 420.630389 433.239477 12.609088782 0.0291042009 50
## 6: 0.10 1.00 4.142243 4.154845 0.012602946 0.0030333127 20
## 7: 0.10 2.00 16.098705 16.167168 0.068463771 0.0042347410 40
## 8: 0.10 3.00 51.977671 52.256611 0.278940081 0.0053378908 60
## 9: 0.10 4.00 152.784247 153.794450 1.010202860 0.0065685261 80
## 10: 0.10 5.00 429.809608 433.239477 3.429869746 0.0079167987 100
## 11: 0.05 0.95 3.854172 3.857129 0.002957122 0.0007666641 38
## 12: 0.05 1.00 4.151573 4.154845 0.003272322 0.0007875918 40
## 13: 0.05 1.95 15.169566 15.186063 0.016496342 0.0010862817 78
## 14: 0.05 2.00 16.149382 16.167168 0.017786619 0.0011001691 80
## 15: 0.05 3.00 52.184102 52.256611 0.072509016 0.0013875568 120
## 16: 0.05 3.05 55.168537 55.246033 0.077496299 0.0014027487 122
## 17: 0.05 4.00 153.531703 153.794450 0.262747199 0.0017084310 160
## 18: 0.05 4.05 161.812703 162.092371 0.279668463 0.0017253647 162
## 19: 0.05 5.00 432.346880 433.239477 0.892597084 0.0020602857 200
# Create summary table for ODE solver RK4
step_sizes <- c(0.2, 0.1, 0.05)
dt_li <- lapply(step_sizes, create_table, solver = "RK4")
data.table::rbindlist(dt_li)
## step_size t y_t exact error rel_err steps
## 1: 0.20 1.00 4.154753 4.154845 9.207556e-05 2.216101e-05 20
## 2: 0.20 2.00 16.166668 16.167168 5.005718e-04 3.096224e-05 40
## 3: 0.20 3.00 52.254570 52.256611 2.041031e-03 3.905786e-05 60
## 4: 0.20 4.00 153.787053 153.794450 7.397423e-03 4.809941e-05 80
## 5: 0.20 5.00 433.214342 433.239477 2.513521e-02 5.801689e-05 100
## 6: 0.10 1.00 4.154839 4.154845 6.252972e-06 1.504983e-06 40
## 7: 0.10 2.00 16.167134 16.167168 3.399467e-05 2.102698e-06 80
## 8: 0.10 3.00 52.256472 52.256611 1.386106e-04 2.652498e-06 120
## 9: 0.10 4.00 153.793948 153.794450 5.023766e-04 3.266546e-06 160
## 10: 0.10 5.00 433.237770 433.239477 1.707001e-03 3.940086e-06 200
## 11: 0.05 0.95 3.857129 3.857129 3.681617e-07 9.544967e-08 76
## 12: 0.05 1.00 4.154845 4.154845 4.074081e-07 9.805615e-08 80
## 13: 0.05 1.95 15.186061 15.186063 2.054206e-06 1.352692e-07 156
## 14: 0.05 2.00 16.167166 16.167168 2.214900e-06 1.369999e-07 160
## 15: 0.05 3.00 52.256602 52.256611 9.031084e-06 1.728218e-07 240
## 16: 0.05 3.05 55.246024 55.246033 9.652353e-06 1.747158e-07 244
## 17: 0.05 4.00 153.794417 153.794450 3.273204e-05 2.128298e-07 320
## 18: 0.05 4.05 162.092336 162.092371 3.484038e-05 2.149415e-07 324
## 19: 0.05 5.00 433.239366 433.239477 1.112186e-04 2.567140e-07 400
# Create summary table for ODE solver RK45
step_sizes <- c(0.2, 0.1, 0.05)
dt_li <- lapply(step_sizes, create_table, solver = "RK45")
data.table::rbindlist(dt_li)
## step_size t y_t exact error rel_err
## 1: 0.20 0.9522982 3.870380 3.870381 2.875062e-07 7.428370e-08
## 2: 0.20 2.0391724 16.974395 16.974396 1.213611e-06 7.149655e-08
## 3: 0.20 3.0453665 54.962460 54.962464 3.629974e-06 6.604459e-08
## 4: 0.20 3.9703209 149.063962 149.063972 9.407265e-06 6.310891e-08
## 5: 0.20 4.9806881 424.762134 424.762160 2.612840e-05 6.151302e-08
## 6: 0.10 1.0343657 4.371232 4.371232 2.791563e-07 6.386215e-08
## 7: 0.10 1.9779529 15.727889 15.727890 1.027767e-06 6.534679e-08
## 8: 0.10 2.9865304 51.477359 51.477362 3.106420e-06 6.034536e-08
## 9: 0.10 4.0151469 156.264003 156.264012 8.999481e-06 5.759151e-08
## 10: 0.05 0.9956097 4.127902 4.127903 2.849226e-07 6.902357e-08
## 11: 0.05 2.9589687 49.916298 49.916301 3.151893e-06 6.314357e-08
## 12: 0.05 3.9975217 153.393964 153.393973 9.221170e-06 6.011429e-08
## steps
## 1: 35
## 2: 87
## 3: 145
## 4: 204
## 5: 286
## 6: 41
## 7: 82
## 8: 140
## 9: 210
## 10: 41
## 11: 140
## 12: 210