patternMinor
Linear shooting method to solve a B.V.P
Viewed 0 times
linearshootingsolvemethod
Problem
I have wrote a code to approximate the solution of a boundary value problem:
by using Runge-Kutta method of order N = 4.
Do you have any idea to make it short? I don't know how to call a subroutine as an argument in another subroutine. I had to copy the
```
program test_linsht
!
! Linear Shooting method
! To approximate the solution of boundary value problem
! x'' = p(t)x'(t)+q(t)x(t)+r(t)
!' x(b) = beta in [a,b]
! and using Runge-Kutta method of order N = 4
!
!
!
implicit none
integer,parameter :: n = 2
integer,parameter :: nstep = 20
real(8) :: U(nstep) ! U contains the solutions at each step
real(8) :: V(nstep),out(nstep) ! V just like U for another equation
real(8) :: a,b,h,alph,beta,w
real(8) :: x(0:n)
integer :: i
a = 0.d0
b = 4.0
alph = 125.d-2
beta = -95.d-2
h = (b-a)/nstep
! x = t0 x0 x'0
x = (/0.d0, alph, 0.d0/)
call rks4U(n,h,x,nstep,U)
a = 0.d0
b = 4.0
x = (/0.d0, 0.d0, 1.d0 /)
h = (b-a)/nstep
call rks4V(n,h,x,nstep,V)
! calculate the solution to the boundary value broblem
w = (beta - U(nstep))/V(nstep)
write (,) w,U(nstep),V(nstep)
!w = 0.485884
out = U + w * V
do i = 1,nstep
write(,100) i,ih,U(i), w*V(i), out(i)
enddo
100 format (1x,I5,f10.2,f15.6,f15.6,f15.6)
end program
subroutine xprsysU(n,x,f)
! x prime of system of equations
real(8), dimension(0:n) :: x,f
integer :: n
! time is introduced as a new differential equation
f(0) = 1.d0
f(1) = x(2)
f(2) = 2.x(0)/(1+x(0)x(0))x(2)- 2./(1.+x(0)x(0))*x(1) + 1.0
end subroutine
subroutine xprsysV(n,x,f)
! x prime of system of equations
real(8), dimension(0:n) :: x,f
integer :: n
! time is introduced as a new differential equation
f(0) = 1.d0
f(1) = x(2)
f(2) = 2.x(0)/(1+x(0)x(0))x(2)- 2./(1.+x(0)x(0))*x(1)
end subroutine
subroutine rks4U(n,h,x,nstep,xout)
implicit none
real(8) ::
x'' = p(t)x'(t)+q(t)x(t)+r(t)
x(b) = beta in [a,b]by using Runge-Kutta method of order N = 4.
Do you have any idea to make it short? I don't know how to call a subroutine as an argument in another subroutine. I had to copy the
rk4 subroutine for each equation of U and V.```
program test_linsht
!
! Linear Shooting method
! To approximate the solution of boundary value problem
! x'' = p(t)x'(t)+q(t)x(t)+r(t)
!' x(b) = beta in [a,b]
! and using Runge-Kutta method of order N = 4
!
!
!
implicit none
integer,parameter :: n = 2
integer,parameter :: nstep = 20
real(8) :: U(nstep) ! U contains the solutions at each step
real(8) :: V(nstep),out(nstep) ! V just like U for another equation
real(8) :: a,b,h,alph,beta,w
real(8) :: x(0:n)
integer :: i
a = 0.d0
b = 4.0
alph = 125.d-2
beta = -95.d-2
h = (b-a)/nstep
! x = t0 x0 x'0
x = (/0.d0, alph, 0.d0/)
call rks4U(n,h,x,nstep,U)
a = 0.d0
b = 4.0
x = (/0.d0, 0.d0, 1.d0 /)
h = (b-a)/nstep
call rks4V(n,h,x,nstep,V)
! calculate the solution to the boundary value broblem
w = (beta - U(nstep))/V(nstep)
write (,) w,U(nstep),V(nstep)
!w = 0.485884
out = U + w * V
do i = 1,nstep
write(,100) i,ih,U(i), w*V(i), out(i)
enddo
100 format (1x,I5,f10.2,f15.6,f15.6,f15.6)
end program
subroutine xprsysU(n,x,f)
! x prime of system of equations
real(8), dimension(0:n) :: x,f
integer :: n
! time is introduced as a new differential equation
f(0) = 1.d0
f(1) = x(2)
f(2) = 2.x(0)/(1+x(0)x(0))x(2)- 2./(1.+x(0)x(0))*x(1) + 1.0
end subroutine
subroutine xprsysV(n,x,f)
! x prime of system of equations
real(8), dimension(0:n) :: x,f
integer :: n
! time is introduced as a new differential equation
f(0) = 1.d0
f(1) = x(2)
f(2) = 2.x(0)/(1+x(0)x(0))x(2)- 2./(1.+x(0)x(0))*x(1)
end subroutine
subroutine rks4U(n,h,x,nstep,xout)
implicit none
real(8) ::
Solution
To pass a function or subroutine to another, you need to declare it with an
I'm going to go with the latter since it's easier here due to code size, but the strategy really isn't that different with using a
Note that the
Other odds and ends:
interface block. The compiler also needs to be aware of the function/subroutine you are calling, which means either using a module to hold the RK4 subroutines or, with small enough code, a contains block.I'm going to go with the latter since it's easier here due to code size, but the strategy really isn't that different with using a
module. I'm also going to snip out most of the code, as I don't think much is needed to show how to do it.program test_linsht
implicit none
...
call rk4(n, h, x, nstep, xprsysU, U)
call rk4(n, h, x, nstep, xprsysV, V)
...
contains
subroutine xprsysU(n,x,f)
! x prime of system of equations
real(8), dimension(0:n) :: x,f
integer :: n
! time is introduced as a new differential equation
f(0) = 1.d0
f(1) = x(2)
f(2) = 2.*x(0)/(1+x(0)*x(0))*x(2)- 2./(1.+x(0)*x(0))*x(1) + 1.0
end subroutine
subroutine rk4(n, h, x, nstep, sub, xout)
real(8) :: x(0:n)
real(8) :: y(0:n), f(0:n,4)
interface
subroutine sub(n, x, f)
real(8), dimension(0:n) :: x,f
integer :: n
end subroutine sub
end interface
...
do k=1,nstep
call sub(n, x, f)
end do
...
end subroutine
end program test_linshtNote that the
interface block has the same structure as the declaration part of the subroutine, so it should be obvious that it is necessary for the inputs of the different functions to be the same, otherwise we'd have a compiler error.Other odds and ends:
- Fortran is 1-indexed, so it's more natural in the language to use
dimension(1:n)(or evendimension(n), which is the same thing as the other), instead of defining it asdimension(0:n). Obviously it can handle an index of 0, but it's just not typical of Fortran programs.
- I don't think the labels
in#andouton thedoloops are needed in this case. Typically, I'd only recommend them for the cases where an inner loop requires you to increment the outer loop, but that doesn't happen here, it's just clutter.
- It's possibly due to copy-and-paste, but indentation matters for reading purposes. You've got some parts indented and others not, some indented further than others.
- Fortran allows array math, use it. You can turn those
doloops in the RK4 routine intoy(:) = x(:) + h*f(:,1)etc.
- It is not generally recommended to use
real(8)syntax, it's possibly not what you think it is. The Fortran 90 standard introducedkindnotation to give you portable definition of arbitrary precision numbers.
- With a
containsstatement, theparameters are global, so you would not need to pass them to the subroutines. With amodule, you would have to declare those parameters in the module as global variables.
- I'd recommend declaring variables as
intent(in)andintent(out)whenever and wherever possible, as it helps keep you straight with regards to variables usage (and possibly help optimize the code).
Code Snippets
program test_linsht
implicit none
...
call rk4(n, h, x, nstep, xprsysU, U)
call rk4(n, h, x, nstep, xprsysV, V)
...
contains
subroutine xprsysU(n,x,f)
! x prime of system of equations
real(8), dimension(0:n) :: x,f
integer :: n
! time is introduced as a new differential equation
f(0) = 1.d0
f(1) = x(2)
f(2) = 2.*x(0)/(1+x(0)*x(0))*x(2)- 2./(1.+x(0)*x(0))*x(1) + 1.0
end subroutine
subroutine rk4(n, h, x, nstep, sub, xout)
real(8) :: x(0:n)
real(8) :: y(0:n), f(0:n,4)
interface
subroutine sub(n, x, f)
real(8), dimension(0:n) :: x,f
integer :: n
end subroutine sub
end interface
...
do k=1,nstep
call sub(n, x, f)
end do
...
end subroutine
end program test_linshtContext
StackExchange Code Review Q#119077, answer score: 4
Revisions (0)
No revisions yet.