6.2 Source code

!*******************************************

!* 
!* Solve the advection PDE using Explicit FTCS, 
!* Explicit Lax, Implicit FTCS, and implicit Crank-Nicolson 
!* methods for constant and varying speed. 
!* 
!*  Solve  dc/dt = -u dc/dx 
!*  u = t/20 ft/minute 
!*  and 
!*  u constant 
!* 
!* Compiler used: gnu 95 (g95) on Cygwin. Gcc 3.4.4 
!* Date: June 20 2006 
!* 
!* by Nasser Abbasi 
!******************************************* 
 
PROGRAM advection 
  IMPLICIT NONE 
 
  REAL     :: DT,DX,max_run_time,length,snapshot_delta, & 
              first_limit,second_limit 
  INTEGER  :: N,SNAPSHOTS 
  character(10) :: cmd_arg ! to read time step from command line 
 
  INTEGER :: method  ! 1=FTCS, 2=LAX, 3=Implicit FTCS, 4=C-R 
  INTEGER :: mode    ! 1=Fixed wind speed, 2=speed function of time 
 
  REAL    :: t_start, t_end, cpu_time_used,end_line(1002) 
  INTEGER :: ALL_DATA_FILE_ID 
  PARAMETER(ALL_DATA_FILE_ID=900) 
 
  ! Initialize data. All methods will use the same 
  ! parameters to make comparing them easier 
 
  ! read delta t from command line. 
  CALL getarg(1,cmd_arg) 
  cmd_arg=TRIM(cmd_arg) 
  print *,'= ', cmd_arg 
  read(cmd_arg,*)dt   !delta in time,  in minutes 
 
  print *,'Dt=',DT 
 
  N      = 1000      ! number of grid points in space 
  length = 100       ! length of space solution in feet 
 
  first_limit  = 0.25*length 
  second_limit = 0.35*length 
 
  DX = length/N     ! delta in space, in feets 
 
  max_run_time = 30.0  ! how long to run for in minutes 
  SNAPSHOTS    = 200   ! number of snapshots per run. Used for animation 
 
  snapshot_delta = max_run_time / SNAPSHOTS   ! time between each snap shot 
 
  print *,'DT=',DT,' minutes, DX=',DX,' feets' 
  print *,'taking snapshots every ', snapshot_delta ,' minutes' 
 
  DO mode=1,2 
     print*,'=======> processing mode ',mode 
     DO method=1,4   ! No enumeration data types in Fotran 90 
 
        CALL CPU_TIME(t_start)  ! get current CPU time 
        CALL process(mode,method,N,DT,DX,max_run_time,snapshot_delta,& 
             first_limit,second_limit) 
        CALL CPU_TIME(t_end)    ! get current CPU time 
 
        cpu_time_used = t_end - t_start 
 
        WRITE(*,FMT='(A,I2,A,F12.5)') 'CPU TIME used for method', method, ' = ', cpu_time_used 
        ! Now record test case parameters in last line 
        end_line=0 
        end_line(1)=cpu_time_used 
        end_line(2)=DT 
        end_line(3)=DX 
        end_line(4)=mode 
        end_line(5)=method 
 
        WRITE(UNIT=ALL_DATA_FILE_ID,FMT=*) end_line 
        CLOSE(ALL_DATA_FILE_ID) 
 
     END DO 
  END DO 
 
END PROGRAM advection 
!************************************ 
!* 
!* 
!************************************ 
SUBROUTINE process(mode,method,N,DT,DX,max_run_time,snapshot_delta,& 
                   first_limit,second_limit) 
  IMPLICIT NONE 
 
  INTEGER, INTENT(IN) :: mode,method,N 
  REAL,    INTENT(IN) :: DT,DX,max_run_time,snapshot_delta,& 
                         first_limit,second_limit 
 
  INTEGER :: I 
  LOGICAL :: snap_shot_at_15_taken 
  INTEGER :: ALL_DATA_FILE_ID 
  PARAMETER(ALL_DATA_FILE_ID=900) 
  REAL    :: snap_current_time 
  REAL    :: current_time 
  REAL    :: C(N)       ! current solution 
  REAL    :: CNEW(N)    ! future solution 
  REAL    :: CEXACT(N)  ! current exact solution 
  REAL    :: current_first_limit 
  REAL    :: A(N,N),aa(N),b(2:N),cc(N-1),CTEMP(N)  ! for C-R and implicit FTCS 
  REAL    :: K,speed 
  REAL    :: error,RMS   ! root mean square error between current and initial sol. 
 
  current_time      = 0. 
  snap_current_time = 0. 
 
  CALL initialize_solution(C,N,DX,first_limit,second_limit) 
  CEXACT = C 
  current_first_limit = first_limit 
 
  CALL pre_loop_initialization(mode,method,current_time,K, & 
                               DT,DX,N,C,ALL_DATA_FILE_ID, & 
                               A,aa,b,cc ) 
 
  snap_shot_at_15_taken=.FALSE. 
 
  DO WHILE(current_time < max_run_time) 
 
     IF( snap_current_time >= snapshot_delta ) THEN 
        snap_current_time = 0. 
        WRITE( UNIT=ALL_DATA_FILE_ID, FMT=*) current_time, error, C 
     END IF 
 
     SELECT CASE(method) 
 
     CASE( 1:2 ) 
 
        IF(method==1) THEN  ! ftcs 
           IF(mode==2)THEN 
              K  = speed(mode,current_time)*DT/(2.*DX) 
           ENDIF 
 
           DO I = 2,N-1 
              CNEW(I) = C(I) - K * ( C(I+1) - C(I-1) ) 
           END DO 
        ELSE    !lax 
           IF(mode == 2) THEN 
              K = speed(mode,current_time)*DT/(DX) 
           ENDIF 
 
           DO I = 2,N-1 
              CNEW(I) = C(I) - K/2. * ( C(I+1) - C(I-1) ) + & 
                   (K**2.)/2 * ( C(I+1) +C(I-1)-2.*C(I) ) 
           END DO 
        END IF 
 
        CNEW(1) = C(1) 
        CNEW(N) = C(N)  ! Boundary conditions 
        C=CNEW 
 
     CASE( 3 )  ! implicit ftcs 
 
        IF( mode == 2) THEN ! only need to update Matrix for varying U 
           K  = speed(mode,current_time)*DT/(2.*DX) 
 
           CALL init_A_matrix(A,K,N) 
           CALL init_diagonal_vectors(N,A,cc,aa,b) 
        END IF 
 
        CALL solve_thomas_algorithm(N,aa,b,cc,C,CNEW) 
        C = CNEW 
 
     CASE( 4 )   ! C-R 
 
        IF(mode == 2) THEN  !only need to update A if U changes 
           K  = speed(mode,current_time)*DT/(4*DX)   ! C-R 
           CALL init_A_matrix(A,K,N) 
           CALL init_diagonal_vectors(N,A,cc,aa,b) 
        END IF 
 
        CTEMP(1) = C(1) 
        CTEMP(N) = C(N) 
 
        DO I=2,N-1 
           CTEMP(I)=C(I)+K*C(I-1)-K*C(I+1) 
        END DO 
 
        CALL solve_thomas_algorithm(N,aa,b,cc,CTEMP,C) 
 
     END SELECT 
 
     IF( current_time>=15.0 .AND. (.NOT. snap_shot_at_15_taken)) THEN 
        snap_shot_at_15_taken = .TRUE. 
        CALL take_one_snap_shot(mode,method,15,N,C,DX) 
     END IF 
 
     current_time = current_time + DT 
     current_first_limit = current_first_limit + speed(mode,current_time)*DT 
     CALL get_current_exact_solution(CEXACT,N,current_first_limit,DX) 
     error = RMS(CEXACT,C,N) 
 
     snap_current_time = snap_current_time + DT 
 
  END DO 
 
  CALL take_one_snap_shot(mode,method,30,N,C,DX) 
 
END SUBROUTINE process 
!************************************ 
!* 
!* 
!************************************ 
SUBROUTINE pre_loop_initialization(mode,method,current_time,K,& 
                                   DT,DX,N,C,ALL_DATA_FILE_ID,& 
                                   A,aa,b,cc) 
  IMPLICIT NONE 
 
  INTEGER, INTENT(IN)  :: mode,method,N,ALL_DATA_FILE_ID 
  REAL,    INTENT(IN)  :: C(N),DT,DX,current_time 
  REAL,    INTENT(OUT) :: K,A(N,N),aa(N),b(2:N),cc(N-1) 
  REAL                 :: speed 
 
  SELECT CASE(method) 
  CASE( 1 )   ! FTCS 
 
     K  = speed(mode,current_time)*DT/(2.*DX) 
 
     IF(mode==1) THEN 
        OPEN(UNIT=ALL_DATA_FILE_ID, file='expAll.txt') ! all time shots 
        CALL print_to_file(C,'exp0.txt',N,DX) 
     ELSE 
        OPEN(UNIT=ALL_DATA_FILE_ID, file='exp_extraAll.txt') ! all time shots 
        CALL print_to_file(C,'exp_extra0.txt',N,DX) 
     END IF 
 
  CASE( 2 )    ! Lax 
 
     K = speed(mode,current_time)*DT/(DX) 
 
     IF(mode==1) THEN 
        OPEN(UNIT=ALL_DATA_FILE_ID, file='laxAll.txt') ! all time shots 
        CALL print_to_file(C,'lax0.txt',N,DX) 
     ELSE 
        OPEN(UNIT=ALL_DATA_FILE_ID, file='lax_extraAll.txt') ! all time shots 
        CALL print_to_file(C,'lax_extra0.txt',N,DX) 
     END IF 
 
  CASE( 3 )    ! Implicit FTCS 
 
     K  = speed(mode,current_time)*DT/(2.*DX) 
 
     CALL init_A_matrix(A,K,N) 
     CALL init_diagonal_vectors(N,A,cc,aa,b) 
 
     IF(mode==1) THEN 
        OPEN(UNIT=ALL_DATA_FILE_ID, file='impAll.txt') ! all time shots 
        CALL print_to_file(C,'imp0.txt',N,DX) 
     ELSE 
        OPEN(UNIT=ALL_DATA_FILE_ID, file='imp_extraAll.txt') ! all time shots 
        CALL print_to_file(C,'imp_extra0.txt',N,DX) 
     END IF 
 
  CASE( 4 )     ! C-R 
 
     K  = speed(mode,current_time)*DT/(4*DX)   ! C-R 
 
     CALL init_A_matrix(A,K,N) 
     CALL init_diagonal_vectors(N,A,cc,aa,b) 
 
     IF(mode==1) THEN 
        OPEN(UNIT=ALL_DATA_FILE_ID, file='crAll.txt') ! all time shots 
        CALL print_to_file(C,'cr0.txt',N,DX) 
     ELSE 
        OPEN(UNIT=ALL_DATA_FILE_ID, file='cr_extraAll.txt') ! all time shots 
        CALL print_to_file(C,'cr_extra0.txt',N,DX) 
     END IF 
  END SELECT 
 
  WRITE( UNIT=ALL_DATA_FILE_ID, FMT=*) current_time,0, C 
 
END SUBROUTINE pre_loop_initialization 
!************************************ 
!* 
!* 
!************************************ 
SUBROUTINE init_diagonal_vectors(N,A,cc,aa,b) 
  IMPLICIT NONE 
 
  INTEGER, INTENT(IN) ::N 
  REAL, INTENT(IN)    ::A(N,N) 
  REAL, INTENT(OUT)   ::aa(N),b(2:N),cc(N-1) 
 
  INTEGER ::I,J 
 
  J=2 
  DO I=1,N-1 
     cc(I)=A(I,J) 
     J=J+1 
  END DO 
  cc(1)=0 
 
  DO I=1,N 
     aa(I)=A(I,I) 
  END DO 
 
  J=1 
  DO I=2,N 
     b(I)=A(I,J) 
     J=J+1 
  END DO 
 
END SUBROUTINE init_diagonal_vectors 
!************************************ 
!* 
!* 
!************************************ 
SUBROUTINE initialize_solution(C,N,DX,first_limit,second_limit) 
  IMPLICIT NONE 
 
  INTEGER, INTENT(IN)    :: N 
  REAL,    INTENT(IN)    :: DX,first_limit,second_limit 
  REAL,    INTENT(INOUT) :: C(0:N-1) 
 
  INTEGER :: I 
  REAL    :: x, PI,av,R 
 
  PARAMETER( PI = ACOS(-1.) ) 
 
  x = 0 
  av = (second_limit+first_limit)/2.0 
  R  = av - first_limit 
 
  C = 0.0 
 
  DO I=0,N-1 
 
     IF( x >= first_limit .AND. x <= second_limit ) THEN 
        C(I) = 1 + COS( PI * (x-av)/R ) 
     END IF 
 
     x = x + DX 
  END DO 
 
END SUBROUTINE initialize_solution 
!************************************ 
!* 
!* 
!************************************ 
SUBROUTINE print_to_file(C,file_name,N,DX) 
  IMPLICIT NONE 
 
 
  REAL,    INTENT(IN) :: C(N),DX 
  INTEGER, INTENT(IN) :: N 
 
  CHARACTER* (*), INTENT(IN) :: file_name 
 
  INTEGER :: I 
  INTEGER :: FILE_ID 
  PARAMETER(FILE_ID=999) 
  REAL :: current_position 
 
  OPEN(UNIT=FILE_ID, file=file_name) 
 
  current_position = 0; 
  DO I=1,N 
 
     WRITE( UNIT=FILE_ID, FMT=* ) current_position ,'\t', C(I) 
     current_position = current_position + DX 
 
  END DO 
 
  CLOSE(FILE_ID) 
 
END SUBROUTINE print_to_file 
!************************************ 
!* 
!* 
!************************************ 
SUBROUTINE init_A_matrix(A,K,N) 
  IMPLICIT NONE 
 
  INTEGER, INTENT(IN) ::N 
  REAL, INTENT(IN)    ::K 
  REAL, INTENT(OUT)   ::A(N,N) 
 
  INTEGER ::I 
 
  DO I = 2,N-1 
     A(I,I-1)   = -K 
     A(I,I) = 1 
     A(I,I+1) = K 
  END DO 
 
  A(1,1) = 1 
  A(N,N) = 1 
 
END SUBROUTINE init_A_matrix 
!************************************ 
!* 
!* 
!************************************ 
SUBROUTINE solve_thomas_algorithm(N,aa,b,c,old_c,new_c) 
  IMPLICIT NONE 
 
  REAL, INTENT(IN)    :: aa(N),b(2:N),c(N-1),old_c(N) 
  INTEGER, INTENT(IN) :: N 
  REAL, INTENT(INOUT) :: new_c(N) 
 
  INTEGER  :: I 
  REAL :: alpha(N),beta(2:N),g(N) 
 
  alpha(1) = aa(1) 
  DO I=2,N 
     beta(I)=b(I)/alpha(I-1) 
     alpha(I)=aa(I)-beta(I)*c(I-1) 
  END DO 
 
  g(1)=old_c(1) 
  DO I=2,N 
     g(I)=old_c(I)-beta(I)*g(I-1) 
  END DO 
 
  new_c(N)=g(N)/alpha(N) 
  DO I=N-1,1,-1 
     new_c(I)=(g(I)-c(I)*new_c(I+1))/alpha(I) 
  END DO 
 
END SUBROUTINE solve_thomas_algorithm 
!************************************ 
!* 
!* 
!************************************ 
REAL FUNCTION speed(MODE,time) 
  IMPLICIT NONE 
 
  INTEGER, INTENT(IN) :: MODE 
  REAL,    INTENT(IN) :: time 
 
  IF( MODE == 1 ) THEN 
     speed=2.0 
  ELSE 
     speed=time/20.0 
  END IF 
 
END FUNCTION speed 
!************************************ 
!* 
!* 
!************************************ 
SUBROUTINE take_one_snap_shot(mode,method,TIME,N,C,DX) 
  IMPLICIT NONE 
 
  INTEGER, INTENT(IN) ::TIME,mode,method,N 
  REAL,    INTENT(IN) ::C(N),DX 
 
  IF(TIME==15) THEN 
     SELECT CASE(method) 
     CASE(1) 
        IF(mode==1) THEN 
           CALL print_to_file(C,'exp15.txt',N,DX) 
        ELSE 
           CALL print_to_file(C,'exp_extra15.txt',N,DX) 
        END IF 
     CASE(2) 
        IF(mode==1) THEN 
           CALL print_to_file(C,'lax15.txt',N,DX) 
        ELSE 
           CALL print_to_file(C,'lax_extra15.txt',N,DX) 
        ENDIF 
     CASE(3) 
        IF(mode==1) THEN 
           CALL print_to_file(C,'imp15.txt',N,DX) 
        ELSE 
           CALL print_to_file(C,'imp_extra15.txt',N,DX) 
        END IF 
     CASE(4) 
        IF(mode==1) THEN 
           CALL print_to_file(C,'cr15.txt',N,DX) 
        ELSE 
           CALL print_to_file(C,'cr_extra15.txt',N,DX) 
        END IF 
     END SELECT 
  ELSE 
     SELECT CASE(method) 
     CASE(1) 
        IF(mode==1) THEN 
           CALL print_to_file(C,'exp30.txt',N,DX) 
        ELSE 
           CALL print_to_file(C,'exp_extra30.txt',N,DX) 
        END IF 
     CASE(2) 
        IF(mode==1) THEN 
           CALL print_to_file(C,'lax30.txt',N,DX) 
        ELSE 
           CALL print_to_file(C,'lax_extra30.txt',N,DX) 
        ENDIF 
     CASE(3) 
        IF(mode==1) THEN 
           CALL print_to_file(C,'imp30.txt',N,DX) 
        ELSE 
           CALL print_to_file(C,'imp_extra30.txt',N,DX) 
        END IF 
     CASE(4) 
        IF(mode==1) THEN 
           CALL print_to_file(C,'cr30.txt',N,DX) 
        ELSE 
           CALL print_to_file(C,'cr_extra30.txt',N,DX) 
        END IF 
     END SELECT 
  END IF 
END SUBROUTINE    take_one_snap_shot 
!************************************ 
!* 
!* 
!************************************ 
REAL FUNCTION RMS(CEXACT,C,N) 
  IMPLICIT NONE 
 
  REAL, INTENT(IN)    :: CEXACT(N),C(N) 
  INTEGER, INTENT(IN) :: N 
 
  INTEGER  :: I 
 
  RMS=0. 
  DO I=1,N 
     RMS = RMS+(CEXACT(I)-C(I))**2 
  END DO 
 
  RMS = RMS/N 
  RMS = SQRT(RMS) 
END FUNCTION RMS 
!************************************ 
!* 
!* 
!************************************ 
SUBROUTINE get_current_exact_solution(CEXACT,N,current_first_limit,DX) 
  IMPLICIT NONE 
  REAL, INTENT(IN)    :: current_first_limit,DX 
  REAL, INTENT(OUT)   :: CEXACT(0:N-1) 
  INTEGER, INTENT(IN) :: N 
 
  INTEGER :: I 
  REAL :: first_limit 
  REAL :: second_limit 
  REAL :: av,R,shift,x,PI 
 
  PARAMETER( PI = ACOS(-1.) ) 
 
  first_limit  = 25.0 
  second_limit = 35.0 
 
  shift = current_first_limit - FIRST_LIMIT 
  first_limit = current_first_limit 
  second_limit = second_limit + shift 
 
  av = (second_limit+first_limit)/2.0 
  R  = av - first_limit 
 
  CEXACT = 0. 
  x = 0. 
  DO I = 0,N-1 
 
     IF( x >= first_limit .AND. x <= second_limit ) THEN 
        CEXACT(I) = 1 + COS( PI * (x -av)/R ) 
     END IF 
 
     x = x + DX 
  END DO 
END SUBROUTINE get_current_exact_solution