!----------------------------------------------------------------------
!   Euler method: dx/dt = 1 - x**2
!
!   coded by Satoshi Sakai for lectures on Geophysical Fluid Dynamics
!   modified by Shigeo Yoden on Sept. 10, 2000
!----------------------------------------------------------------------
program euler01

  use dcl        ! DCL Fortran90 (dcl-5.1/dcl-f90)

  integer, parameter :: mmax=200
  real, parameter :: tmax=2.
  real, dimension(0:mmax) :: t, t0, x, x0
  character :: ct*30

!!!    write(*,*) 'dt=?'
!!!    read (*,*)  dt
    dt = 0.2
 
    nmax = tmax/dt
    if(nmax > mmax) stop 'error: dt must have a larger value'

!-- 初期条件 ----
    t(0) = 0
    x(0) = 0

!-- 時間積分 ----
    do n=0, nmax-1
      dx = (1 - x(n)**2) *dt
      x(n+1) = x(n) + dx
      t(n+1) = t(n) + dt
    end do

!-- 解析解 ----
    do m=0, mmax
      t0(m) = tmax/mmax *m
      x0(m) = tanh(t0(m))
    end do

!-- グラフ化 ----
    call DclOpenGraphics()
    call DclNewFrame
      
    call DclSetTitle('time', 'x')
    call DclDrawScaledGraph(t0, x0)
    call DclDrawLine  (t(0:nmax), x(0:nmax), type=3)
    call DclDrawMarker(t(0:nmax), x(0:nmax), type=3)

      ct = 'dt=xx.xxExxx'
      write(ct(4:12),'(e9.2)') dt
    call DclDrawTitle('T', ct, position=-0.8)
    call DclDrawTitle('T', 'Euler method', position=-1.)

    call DclCloseGraphics
end program euler01
!----------------------------------------------------------------------
!   Improved Euler(Heun) method: dx/dt = 1 - x**2
!
!   coded by Shigeo Yoden on Sept. 12, 2000
!----------------------------------------------------------------------
program heun01

  use dcl        ! DCL Fortran90 (dcl-5.1/dcl-f90)

  integer, parameter :: mmax=200
  real, parameter :: tmax=2.
  real, dimension(0:mmax) :: t, t0, x, x0
  character :: ct*30
  
!!!    write(*,*) 'dt=?'
!!!    read (*,*)  dt
    dt = 0.2

    nmax = tmax/dt
    if(nmax > mmax) stop 'dt must have a larger value'

!-- 初期条件 ----
    t(0) = 0
    x(0) = 0

!-- 時間積分 ----
    do n=0, nmax-1
      call drv(x(n)    , dx1)
      call drv(x(n)+dx1, dx2)
      x(n+1) = x(n) + (dx1+dx2)/2
      t(n+1) = t(n) + dt
    end do

!-- 解析解 ----
    do m=0, mmax
      t0(m) = tmax/mmax *m
      x0(m) = tanh(t0(m))
    end do

!-- グラフ化 ----
    call DclOpenGraphics()
    call graph
    call DclCloseGraphics

!-- 内部手続き ----
  contains
    subroutine drv(x, dx)         ! 時間微分を計算する内部サブルーチン
      real :: x, dx
      dx = (1 - x**2) *dt
    end subroutine drv

    subroutine graph              ! グラフを描く内部サブルーチン
      call DclNewFrame
      
      call DclSetTitle('time', 'x')
      call DclDrawScaledGraph(t0, x0)
      call DclDrawLine  (t(0:nmax), x(0:nmax), type=3)
      call DclDrawMarker(t(0:nmax), x(0:nmax), type=3)

        ct = 'dt=xx.xxExxx'
        write(ct(4:12),'(e9.2)') dt
    call DclDrawTitle('T', ct, position=-0.8)
    call DclDrawTitle('T', 'Modified Euler method', position=-1.)
    end subroutine graph
end program heun01
!----------------------------------------------------------------------
!   4-th Order Runge-Kutta method: dx/dt = 1 - x**2
!
!   coded by Satoshi Sakai for lectures on Geophysical Fluid Dynamics
!   modified by Shigeo Yoden on Sept. 10, 2000
!----------------------------------------------------------------------
program rk01

  use dcl        ! DCL Fortran90 (dcl-5.1/dcl-f90)

  integer, parameter :: mmax=200
  real, parameter :: tmax=2.
  real, dimension(0:mmax) :: t, t0, x, x0
  character :: ct*30
  
!!!    write(*,*) 'dt=?'
!!!    read (*,*)  dt
    dt = 0.2

    nmax = tmax/dt
    if(nmax > mmax) stop 'error: dt must have a larger value'

!-- 初期条件 ----
    t(0) = 0
    x(0) = 0

!-- 時間積分 ----
    do n=0, nmax-1
      call drv(x(n)      , dx1)
      call drv(x(n)+dx1/2, dx2)
      call drv(x(n)+dx2/2, dx3)
      call drv(x(n)+dx3  , dx4)
      x(n+1) = x(n) + (dx1 + 2*(dx2 + dx3) + dx4) / 6
      t(n+1) = t(n) + dt
    end do

!-- 解析解 ----
    do m=0, mmax
      t0(m) = tmax/mmax *m
      x0(m) = tanh(t0(m))
    end do

!-- グラフ化 ----
    call DclOpenGraphics()
    call graph
    call DclCloseGraphics

!-- 内部手続き ----
  contains
    subroutine drv(x, dx)         ! 時間微分を計算する内部サブルーチン
      real :: x, dx
      dx = (1 - x**2) *dt
    end subroutine drv

    subroutine graph              ! グラフを描く内部サブルーチン
      call DclNewFrame
      
      call DclSetTitle('time', 'x')
      call DclDrawScaledGraph(t0, x0)
      call DclDrawLine  (t(0:nmax), x(0:nmax), type=3)
      call DclDrawMarker(t(0:nmax), x(0:nmax), type=3)

        ct = 'dt=xx.xxExxx'
        write(ct(4:12),'(e9.2)') dt
      call DclDrawTitle('T', ct, position=-0.8)
      call DclDrawTitle('T', '4th-Order Runge-Kutta method', position=-1.)
    end subroutine graph
end program rk01
!----------------------------------------------------------------------
!   4-th Order Runge-Kutta method: Satellite Orbit
!
!   coded by Shigeo Yoden on Sept. 12, 2000
!----------------------------------------------------------------------
program rk02

  use dcl        ! DCL Fortran90 (dcl-5.1/dcl-f90)

  integer, parameter :: mmax=2000
  real, parameter :: tmax=16.
  real, dimension(0:mmax) :: t, x, y, u, v 
  real, dimension(4) :: z, dz1, dz2, dz3, dz4
  character :: ct*30

!!!    write(*,*) 'dt=?'
!!!    read (*,*)  dt
    dt = 0.05

    nmax = tmax/dt
    if(nmax > mmax) stop 'error: dt must have a larger value'

!-- 初期条件 ----
    t(0)=0
    x(0)=3  ;  z(1)=x(0)
    y(0)=0  ;  z(2)=y(0)
    u(0)=0.3;  z(3)=u(0)
    v(0)=0.2;  z(4)=v(0)

!-- 時間積分 ----
    do n=0, nmax-1
      call drv(z      , dz1)
      call drv(z+dz1/2, dz2)
      call drv(z+dz2/2, dz3)
      call drv(z+dz3  , dz4)
      z = z + (dz1 + 2*(dz2 + dz3) + dz4) / 6
      t(n+1) = t(n) + dt
      x(n+1) = z(1);  y(n+1) = z(2)
      u(n+1) = z(3);  v(n+1) = z(4)
    end do

!-- グラフ化 ----
    call DclOpenGraphics()
    call graph
    call DclCloseGraphics

!-- 内部手続き ----
  contains
    subroutine drv(z, dz)         ! 時間微分を計算する内部サブルーチン
      real, dimension(4) :: z, dz

      fxy = z(1)**2 + z(2)**2
      fxy = sqrt( fxy**3 )
      dz(1) = z(3)       *dt
      dz(2) = z(4)       *dt
      dz(3) = - z(1)/fxy *dt
      dz(4) = - z(2)/fxy *dt
    end subroutine drv

    subroutine graph              ! グラフを描く内部サブルーチン
      call DclNewFrame
      
      call DclSetTitle('x', 'y')
      call DclScalingPoint   (y(0:nmax), x(0:nmax)) ! 縦軸と横軸の範囲を同一に
      call DclDrawScaledGraph(x(0:nmax), y(0:nmax))
      call DclDrawMarker(x(0:0), y(0:0), type=230)

        ct = 'dt=xx.xxExxx'
        write(ct(4:12),'(e9.2)') dt
      call DclDrawTitle('T', ct, position=-0.8)
      call DclDrawTitle('T', 'Satellite Orbit(RK-4)', position=-1.)
    end subroutine graph
end program rk02
