library(knitr)
library(kableExtra)
library(ggplot2)
library(ggpubr)
## Loading required package: magrittr
7.1
The Heur method is given as:
\[
y(x+h)=y(x)+\frac{h}{2}\{f(x,y(x))+f[x+h,y(x)+hf(x,y(x))]\}
\] \[
=y(x)+\frac{h}{2}f(x,y(x))+\frac{h}{2}f[x+h,y(x)+hf(x,y(x))]
\]
Given that the third term contains an Euler step, we can use a Taylor expansion to estimate this term…
\[
\frac{h}{2}f[x+h,y(x)+hf(x,y(x))]=\frac{h}{2}[f(x,y(x))+h\frac{d}{dh}f(x+h,y(x)+hf(x,y(x)))|_{h=0}+\mathcal{O}[h^2]]
\] \[
\frac{h}{2}[f(x,y(x))+h[1\frac{df}{dh}+hf(x,y(x))\frac{df}{dy}]+\mathcal{O}[h^2]]
\]
Plugging back in…
\[
y(x+h)=y(x)+\frac{h}{2}f(x,y(x))+\frac{h}{2}[f(x,y(x))+h[1\frac{df}{dh}+hf(x,y(x))\frac{df}{dy}]+\mathcal{O}[h^2]]
\]
\[
=y(x)+hf(x,y(x))+\frac{h^2}{2}(\frac{df}{dx}+f(x,y(x))\frac{df}{dy})+\mathcal{O}[h^3]
\]
Therefore, the error is \(\mathcal{O}[h^3]\), in spite of the fact that we only do a 2nd order Euler approximation.
7.2
Euler Method
#Trying multiple different step sizes...
h<-c(.0001,.001,.01,.1,.25)
num_steps<-round(100*pi/h)
Y1<-list()
Y2<-list()
for(j in 1:length(h)){
num_steps<-round((100*pi)/h[j])
y1<-c()
y2<-c()
y1[1]<-1
y2[1]<-0
for(i in 1:num_steps){
y1[i+1]<-y1[i]+h[j]*y2[i]
y2[i+1]<-y2[i]-h[j]*y1[i]
}
Y1[[j]]<-data.frame("y1"=y1[2:length(y1)],"exact"=cos((0:(num_steps-1))*h[j]))
Y2[[j]]<-data.frame("y2"=y2[2:length(y1)],"exact"=-sin((0:(num_steps-1))*h[j]))
}
Y1<-lapply(Y1,function(x) cbind(x,x$exact-x$y1))
Y2<-lapply(Y2,function(x) cbind(x,x$exact-x$y2))
mean_error_y1<-unlist(lapply(Y1,function(x) mean(abs(x[,3]))))
mean_error_y2<-unlist(lapply(Y2,function(x) mean(abs(x[,3]))))
mean_y1_error<-data.frame("h"=h,"mean_error_y1"=mean_error_y1)
kable(mean_y1_error,col.names = c("Step Size","Mean Error, Y"),title="Y1,Euler")%>%
kable_styling(full_width = F)
Step Size
|
Mean Error, Y
|
0.0001
|
5.027700e-03
|
0.0010
|
5.273770e-02
|
0.0100
|
9.077604e-01
|
0.1000
|
2.469614e+05
|
0.2500
|
6.083680e+14
|
mean_y2_error<-data.frame("h"=h,"mean_error_y2"=mean_error_y2)
kable(mean_y2_error,col.names = c("Step Size","Mean Error, Y''"),title="Y2, Euler")%>%
kable_styling(full_width = F)
Step Size
|
Mean Error, Y’’
|
0.0001
|
5.027300e-03
|
0.0010
|
5.273470e-02
|
0.0100
|
9.078058e-01
|
0.1000
|
2.547900e+05
|
0.2500
|
5.849678e+14
|
final_error_y1<-unlist(lapply(Y1,function(x) x[nrow(x),3]))
final_error_y2<-unlist(lapply(Y2,function(x) x[nrow(x),3]))
final_y1_error<-data.frame("h"=h,"final_error_y1"=final_error_y1)
kable(final_y1_error,col.names = c("Step Size","Final Error, Y"),title="Y1,Euler, Final Error")%>%
kable_styling(full_width = F)
Step Size
|
Final Error, Y
|
0.0001
|
-1.583200e-02
|
0.0010
|
-1.700893e-01
|
0.0100
|
-3.809932e+00
|
0.1000
|
-3.321023e+06
|
0.2500
|
-3.522898e+16
|
final_y2_error<-data.frame("h"=h,"final_error_y2"=final_error_y2)
kable(final_y2_error,col.names = c("Step Size","Final Error, Y''"),title="Y2, Euler, Final Error")%>%
kable_styling(full_width = F)
Step Size
|
Final Error, Y’’
|
0.0001
|
9.950000e-05
|
0.0010
|
8.323000e-04
|
0.0100
|
-3.756890e-02
|
0.1000
|
-5.176277e+06
|
0.2500
|
2.190518e+15
|
Runge-Kutta
#Define the Runge-Kutta Function
RK<-function(Y1,Y2,H){
k1.y1<-H*Y2
k1.y2<-(-H)*Y1
k2.y1<-H*(Y2+k1.y2/2)
k2.y2<-(-H)*(Y1+k1.y1/2)
k3.y1<-H*(Y2+k2.y2/2)
k3.y2<-(-H)*(Y1+k2.y1/2)
k4.y1<-H*(Y2+k3.y2)
k4.y2<-(-H)*(Y1+k3.y1)
Y1.RK<-(Y1+k1.y1/6+k2.y1/3+k3.y1/3+k4.y1/6)
Y2.RK<-(Y2+k1.y2/6+k2.y2/3+k3.y2/3+k4.y2/6)
temp<-c(Y1.RK,Y2.RK)
names(temp)<-c("y1","y2")
return(temp)
}
Y1RK<-list()
Y2RK<-list()
h<-c(.0001,.001,.01,.1,.25,.5,1)
for(j in 1:length(h)){
num_steps<-round((100*pi)/h[j])
y1<-c()
y2<-c()
y1[1]<-1
y2[1]<-0
for(i in 1:num_steps){
temp<-RK(y1[i],y2[i],h[j])
y1[i+1]<-temp["y1"]
y2[i+1]<-temp["y2"]
}
Y1RK[[j]]<-data.frame("y1"=y1[1:length(y1)],"exact"=cos((0:(num_steps))*h[j]))
Y2RK[[j]]<-data.frame("y2"=y2[1:length(y1)],"exact"=-sin((0:(num_steps))*h[j]))
}
Y1RK<-lapply(Y1RK,function(x) cbind(x,x$exact-x$y1))
Y2RK<-lapply(Y2RK,function(x) cbind(x,x$exact-x$y2))
mean_error_y1rk<-unlist(lapply(Y1RK,function(x) mean(abs(x[,3]))))
mean_error_y2rk<-unlist(lapply(Y2RK,function(x) mean(abs(x[,3]))))
mean_y1_errorrk<-data.frame("h"=h,"mean_error_y1"=mean_error_y1rk)
kable(mean_y1_errorrk,col.names = c("Step Size","Mean Error, Y1"),title="Y1, Mean Error, RK")%>%
kable_styling(full_width = F)
Step Size
|
Mean Error, Y1
|
0.0001
|
0.0000000
|
0.0010
|
0.0000000
|
0.0100
|
0.0000000
|
0.1000
|
0.0000833
|
0.2500
|
0.0032518
|
0.5000
|
0.0508693
|
1.0000
|
0.4550929
|
mean_y2_errorrk<-data.frame("h"=h,"mean_error_y2"=mean_error_y2rk)
kable(mean_y2_errorrk,col.names = c("Step Size","Mean Error, Y"),title="Y2, Mean Error, RK")%>%
kable_styling(full_width = F)
Step Size
|
Mean Error, Y
|
0.0001
|
0.0000000
|
0.0010
|
0.0000000
|
0.0100
|
0.0000000
|
0.1000
|
0.0000833
|
0.2500
|
0.0032519
|
0.5000
|
0.0507112
|
1.0000
|
0.4533847
|
final_error_y1rk<-unlist(lapply(Y1RK,function(x) x[nrow(x),3]))
final_error_y2rk<-unlist(lapply(Y2RK,function(x) x[nrow(x),3]))
final_y1_errorrk<-data.frame("h"=h,"final_error_y1"=final_error_y1rk)
kable(final_y1_errorrk,col.names = c("Step Size","Final Error, Y1"),title="Y1, Final Error, RK")%>%
kable_styling(full_width = F)
Step Size
|
Final Error, Y1
|
0.0001
|
0.0000000
|
0.0010
|
0.0000000
|
0.0100
|
0.0000000
|
0.1000
|
0.0000112
|
0.2500
|
0.0012489
|
0.5000
|
0.0954130
|
1.0000
|
1.0362849
|
final_y2_errorrk<-data.frame("h"=h,"final_error_y2"=final_error_y2rk)
kable(final_y2_errorrk,col.names = c("Step Size","Final Error, Y1"),title="Y1, Final Error, RK")%>%
kable_styling(full_width = F)
Step Size
|
Final Error, Y1
|
0.0001
|
0.0000000
|
0.0010
|
0.0000000
|
0.0100
|
0.0000000
|
0.1000
|
-0.0002616
|
0.2500
|
-0.0101357
|
0.5000
|
-0.1255992
|
1.0000
|
0.0203453
|
7.3
end<-100*pi
adaptiveRK.y1<-list()
adaptiveRK.y2<-list()
desired_error<-c(.000001,.00001,.0001,.001,.01,.1,1)
for(j in 1:length(desired_error)){
i<-1
t<-0
time<-c()
time[1]<-0
actual_h<-c()
h<-.5
actual_h[1]<-h
y1<-c()
y2<-c()
y1[1]<-1
y2[1]<-0
while(t<end){
temp<-RK(y1[i],y2[i],h)
#Take two half steps
temp.5<-RK(y1[i],y2[i],(h/2))
temp.5<-RK(temp.5[1],temp.5[2],(h/2))
#Adapt step size based only on position...
if(abs(temp[1]-temp.5[1]) > desired_error[j]){
h<-h*.9
} else{
h<-h*1.1
y1[i+1]<-temp.5["y1"]+((temp.5["y1"]-temp["y1"])/15)
y2[i+1]<-temp.5["y2"]+((temp.5["y2"]-temp["y2"])/15)
i<-i+1
t<-t+(i*h)
time[i]<-t
actual_h[i]<-h
}
}
adaptiveRK.y1[[j]]<-cbind(y1,actual_h)
adaptiveRK.y2[[j]]<-cbind(y2,actual_h)
}
mean_h<-unlist(lapply(adaptiveRK.y1,function(x) mean(x[,2],na.rm=T)))
kable(data.frame(desired_error,mean_h),
col.names = c("Desired Errors","Mean Step Size"),
title="Relationship Between Step Size & Desired Error, Adaptive RK")%>%
kable_styling(full_width = F)
Desired Errors
|
Mean Step Size
|
1e-06
|
0.2023960
|
1e-05
|
0.3104956
|
1e-04
|
0.4801989
|
1e-03
|
0.7299420
|
1e-02
|
1.0101682
|
1e-01
|
1.2720473
|
1e+00
|
1.3462919
|
7.4
RK.pendulum<-function(Y1,Y2,H,A,w,t,l,g){
k1.y1<-H*Y2
k1.y2<-(-H)*((g+A*sin(w*t))*sin(Y1)/l)
k2.y1<-H*(Y2+k1.y2/2)
k2.y2<-(-H)*((g+A*sin(w*(t+h/2)))*sin(Y1+k1.y1/2)/l)
k3.y1<-H*(Y2+k2.y2/2)
k3.y2<-(-H)*((g+A*sin(w*(t+h/2)))*sin(Y1+k2.y1/2)/l)
k4.y1<-H*(Y2+k3.y2)
k4.y2<-(-H)*((g+A*sin(w*(t+h)))*sin(Y1+k3.y1)/l)
Y1.RK<-(Y1+k1.y1/6+k2.y1/3+k3.y1/3+k4.y1/6)
Y2.RK<-(Y2+k1.y2/6+k2.y2/3+k3.y2/3+k4.y2/6)
temp<-c(Y1.RK,Y2.RK)
names(temp)<-c("y1","y2")
return(temp)
}
time<-0
time<-c()
time[1]<-0
actual_h<-c()
pendulum.y1<-list()
pendulum.y2<-list()
desired_error<-.00001
h<-.5
actual_h[1]<-h
i<-1
pendulums<-data.frame("A"=c(.5,1,2.5,5),"W"=c(2,4,6,8))
end<-pi*1000
for(j in 1:nrow(pendulums)){
i<-1
t<-0
time<-c()
time[1]<-0
actual_h<-c()
h<-.5
actual_h[1]<-h
y1<-c()
y2<-c()
y1[1]<-pi/4
y2[1]<-0
while(t<end){
temp<-RK.pendulum(y1[i],y2[i],h,A=pendulums[j,1],w=pendulums[j,2],t,l=1,g=(9.8))
#Take two half steps
temp.5<-RK.pendulum(y1[i],y2[i],(h/2),A=pendulums[j,1],w=pendulums[j,2],t,l=1,g=(9.8))
temp.5<-RK.pendulum(temp.5[1],temp.5[2],(h/2),A=pendulums[j,1],w=pendulums[j,2],t,l=1,g=(9.8))
#Adapt step size based only on position...
if(abs(temp[1]-temp.5[1]) > desired_error){
h<-h*.9
} else{
h<-h*1.1
y1[i+1]<-temp.5["y1"]+((temp.5["y1"]-temp["y1"])/15)
y2[i+1]<-temp.5["y2"]+((temp.5["y2"]-temp["y2"])/15)
i<-i+1
t<-t+(i*h)
time[i]<-t
actual_h[i]<-h
}
}
pendulum.y1[[j]]<-cbind(y1,actual_h,time)
pendulum.y2[[j]]<-cbind(y2,actual_h,time)
}
pendulum.y1<-lapply(pendulum.y1,function(x) cbind(x,1:nrow(x)))
pendulum.y2<-lapply(pendulum.y2,function(x) cbind(x,1:nrow(x)))
p1<-ggplot(as.data.frame(pendulum.y1[[1]]),aes(x=time,y=y1),group=1)+geom_line()+ggtitle("A=.5,W=2")+xlim(0,3000)
p2<-ggplot(as.data.frame(pendulum.y1[[2]]),aes(x=time,y=y1),group=1)+geom_line()+ggtitle("A=1,W=4")+xlim(0,3000)
p3<-ggplot(as.data.frame(pendulum.y1[[3]]),aes(x=time,y=y1),group=1)+geom_line()+ggtitle("A=2.5,W=6")+xlim(0,3000)
p4<-ggplot(as.data.frame(pendulum.y1[[4]]),aes(x=time,y=y1),group=1)+geom_line()+ggtitle("A=5,W=8")+xlim(0,3000)
ggarrange(p1,p2,p3,p4,nrow=2,ncol=2)
## Warning: Removed 7 rows containing missing values (geom_path).
## Warning: Removed 7 rows containing missing values (geom_path).
## Warning: Removed 9 rows containing missing values (geom_path).
## Warning: Removed 12 rows containing missing values (geom_path).