! This program simulates quasi 1-D inviscid flow through a 
!divering nozzle.  Depending on the back pressure given,
!the code will output mach number data that shows either
! a smooth progression or a shock wave in the middle of the
!nozzle.  The equations governing flow have been cast in
!conservative form so that the shock wave may be captured,
!and the mesh is clustered around x=0.5 to capture the
!sudden discontinuity. To get the data that shows the 
!shock wave, use a back pressure of 0.88; to get the
!unshocked data, use a back pressure of 0.5 or so.
!The program gives the user a choice of second order scheme (Runge 
!kutta) or first order scheme (Lax).  The Runge Kutta takes
!more iterations, especially in the shock wave case.
!This program makes use of the FORALL statement frequently;
!in fact there are only two DO loops in the entire code.
!This construct is more succint, and because it guarantees
!no dependencies make conversion to parallel implementation
!quite easy.
!
!Summary of data files:    mach.dat--> axial location, mach no.
!                          laxiter.dat --> iteration, RMS of res for Lax
!                          jamiter.dat --> "                   " for R-K
!                          mfr.dat --> axial location, mass flow rate
!                          enth.dat --> axial location, enthalpy
!                          tropy.dat --> axial location, entropy
!  This program must be compiled in fixed format

      program nozzle
      Implicit none
      Integer, Parameter :: nx=140
      Integer, Parameter :: dim=3
      Real V(dim,0:nx)
      Real xloc(0:nx)
      Integer scheme,i

      Write(6,*) 'The following schemes are available:'
      Write(6,*) 'Lax                   scheme=1'
      Write(6,*) 'Jameson 4th order RK  scheme=2'
      
      Write(6,*) 'Enter the scheme you wish to run:'
      Read(5,*) scheme

      Call Mesh(xloc,nx,scheme)
     
      Call Init_Cons(V,xloc,dim,nx)
      If (scheme .eq. 1 ) then
         Call Solver_Lax(V,xloc,dim,nx,scheme)
		Else if(scheme .eq. 2) then
		Call Solver_James(V,xloc,dim,nx,scheme)
      End if

      End Program

      Subroutine Mesh(xloc,nx,scheme)
      Implicit none
      Integer nx,scheme
      Real xloc(0:nx)
      Real zeta,xmin,xmax
      Real pi
      Integer i

      zeta=0.0
      xmin=-0.2
      xmax=1.2
      pi=4.0*atan(1.0)

! Create a mesh clustered at x=0.5, near the shock wave

	 if (scheme .eq. 2) then
      Do i=0,nx/2
         xloc(i)=(1.0-2.0*xmin)/2.0*sin(pi*zeta) + xmin
         zeta=zeta+ 1.0/nx
      End Do

      Do i=nx/2+1,nx
         xloc(i) = xmax-(2.0*xmax-1)/2.0*sin(pi*zeta)
         zeta=zeta+1.0/nx
		End Do
	 End if

	  If (scheme .eq. 1 ) then
	  xloc(0)=xmin
	  Do i=1,nx
	   xloc(i)=xloc(i-1) + (xmax-xmin)/real(nx)
	  End do
	  End if

      End Subroutine

      Subroutine Init_Cons(V,xloc,dim,nx)
      Implicit none
      Integer dim,nx
      Real V(dim,0:nx)
      Real xloc(0:nx)
      Integer j
!Set initial conditions

      Forall(j=0:nx) V(1,j)=1.0
      Forall(j=0:nx) V(2,j)=1.0
      Forall(j=0:nx) V(3,j)=1.976
      End Subroutine



      Subroutine Solver_Lax(V,xloc,dim,nx,scheme)
      Implicit none	
      Integer dim,nx,scheme
      Real V(dim,0:nx)
      Real h(dim,0:nx)
      Real Res(dim,0:nx),ef(dim,0:nx),eb(dim,0:nx)
      Real df(dim,0:nx),db(dim,0:nx)
      Real dt(1:nx-1)
      Real s(0:nx)
      Real xloc(0:nx)
      Real p(0:nx),test(0:nx)
      Real rms
      Real cfl,iter,pback
      Integer i,j

      Write(6,*) 'Please enter P_back (0.1 < p  < 0.99)'
      read(5,*) pback
      Write(6,*) 'Please enter CFL number (0.1 < x < 0.9)'
      read(5,*) cfl
      Forall(i=1:nx-1) p(i)=0.0
      p(0)=0.5903
      p(nx)=0.0
	  Forall(i=1:dim,j=0:nx) h(i,j) =0.0
		test=1.0
		res=0.0	
       Call shape_noz(s,xloc,nx)
		iter=0.0
		open(unit=12,file='laxiter.dat',status='unknown')

!______________Start looping___________
!General form of the loop:
!        -Compute time step at each axial location
!        -Compute flux vectors for each "volume"
!        -Compute dissipation vectors
!        -Compute residual
!        -Update solution
!	 -Output iteration & RMS of residual

       Do While(maxval(abs(test)) .gt. 3.8e-2) 
         Call Time_step(V,dt,p,xloc,cfl,nx,dim)
         Call Flux(V,ef,eb,p,xloc,dim,nx)
         Call Dissp(V,df,db,xloc,dt,p,dim,nx,scheme)
         Forall(i=1:dim,j=1:nx-1) res(i,j)= -1.0*((ef(i,j)
     $   -df(i,j))
     $ *0.5*(s(j+1)+s(j)) - (eb(i,j)-db(i,j))
     $ *0.5*(s(j)+s(j-1)))
     $ /(s(j)*(xloc(j+1)-xloc(j-1))*0.5) 
     $    + h(i,j)
        Forall(i=1:dim,j=1:nx-1) V(i,j)=V(i,j)+dt(j)*Res(i,j)
       
        Forall(i=1:nx-1) p(i)=0.4*(V(3,i)-0.5*V(2,i)*V(2,i)/V(1,i))
        Forall(i=1:nx-1) h(2,i)= (p(i)/s(i))*(s(i+1)-s(i-1))
     $   /(xloc(i+1)-xloc(i-1)) 
         Call Boun_cond(V,p,pback,dim,nx)
		iter=iter+1
		ForalL(i=0:nx)   	test(i)=res(1,i)
		write(6,*) maxval(abs(test)) ,iter, p(nx)
		if (mod(int(iter),10) . eq. 0.0) then
		rms=sqrt(sum(test*test)/141)
		write(12,*) iter,rms
		end if
         End do
         Call Output(V,xloc,p,s,dim,nx)
         End subroutine

     	Subroutine Solver_James(V,xloc,dim,nx,scheme)
        Implicit none
        Integer dim,nx,scheme
        Real V(dim,0:nx)
        Real h(dim,0:nx)
        Real Res(dim,0:nx),ef(dim,0:nx),eb(dim,0:nx)
        Real df(dim,0:nx),db(dim,0:nx),Q(dim,0:nx)
        Real dt(1:nx-1)
        Real s(0:nx)
        Real xloc(0:nx)
        Real p(0:nx)
        Real test(0:nx)
        Real cfl,iter,pback,rms
        Integer i,j

        Write(6,*) 'Please enter P_back (0.1 < p < 0.9)'
        read(5,*) pback
        Write(6,*) 'Please enter CFL number (0.1 < x < 0.9)'
        read(5,*) cfl
        p(0)=0.5903

        Forall(i=1:dim,j=0:nx) h(i,j) =0.0
        Forall(i=1:nx) p(i)=0.1
		res=0.0
          test(5)=1.0
		df=0.0
		db=0.0
       Call Shape_noz(s,xloc,nx)
		Q=V
		open(unit=14,file='jamiter.dat',status='unknown')
		
!-----------Start looping-------------
!General form of the loop:
!        -Compute time step at each axial location
!        -Compute flux vectors for each "volume"
!        -Compute dissipation vectors
!        -Compute residual
!	 -Compute stage of Runge Kutta
!        -Repeat for all 4 stages
!	 -Update solution 
!	 -Output iteration, RMS of residual

	   Do While(maxval(abs(test)) .gt. 2.8e-3)
	   Call Time_step(V,dt,p,xloc,cfl,nx,dim)
        Call Flux(V,ef,eb,p,xloc,dim,nx)
        Call Dissp(V,df,db,xloc,dt,p,dim,nx,scheme)
        Forall(i=1:dim,j=1:nx-1) res(i,j)=-1.0*((ef(i,j)
     $  -df(i,j))
     $ *0.5*(s(j+1)+s(j)) - (eb(i,j)-db(i,j))
     $  *0.5*(s(j)+s(j-1)))
     $ /(s(j)*(xloc(j+1)-xloc(j-1))*0.5)
     $    + h(i,j)
        Forall(i=1:dim,j=1:nx-1) Q(i,j)=V(i,j)+0.25*dt(j)*Res(i,j)
        Forall(i=1:nx-1) p(i)=0.4*(Q(3,i)-0.5*Q(2,i)*Q(2,i)/Q(1,i))
        Forall(i=1:nx-1) h(2,i)= p(i)/s(i)*(s(i+1)-s(i-1))
     $   /(xloc(i+1)-xloc(i-1))
        Call Boun_cond(Q,p,pback,dim,nx)
		Call Time_step(Q,dt,p,xloc,cfl,nx,dim)
         Call Flux(Q,ef,eb,p,xloc,dim,nx)
        Forall(i=1:dim,j=1:nx-1) res(i,j)=-1.0*((ef(i,j)
     $ -df(i,j))
     $ *0.5*(s(j+1)+s(j)) - (eb(i,j)-db(i,j))
     $ *0.5*(s(j)+s(j-1)))
     $ /(s(j)*(xloc(j+1)-xloc(j-1))*0.5)
     $    + h(i,j)
        Forall(i=1:dim,j=1:nx-1) Q(i,j)=V(i,j)+1.0/3.0*dt(j)*Res(i,j)
        Forall(i=1:nx-1) p(i)=0.4*(Q(3,i)-0.5*Q(2,i)*Q(2,i)/Q(1,i))
        Forall(i=1:nx-1) h(2,i)= p(i)/s(i)*(s(i+1)-s(i-1))
     $   /(xloc(i+1)-xloc(i-1))
        Call Boun_cond(Q,p,pback,dim,nx)
		call Time_step(Q,dt,p,xloc,cfl,dim,nx)	
         Call Flux(Q,ef,eb,p,xloc,dim,nx)

         Forall(i=1:dim,j=1:nx-1) res(i,j)=-1.0*((ef(i,j)
     $  -df(i,j))
     $ *0.5*(s(j+1)+s(j)) - (eb(i,j)-db(i,j))
     $  *0.5*(s(j)+s(j-1)))
     $ /(s(j)*(xloc(j+1)-xloc(j-1))*0.5)
     $    + h(i,j)

       Forall(i=1:dim,j=1:nx-1) Q(i,j)=V(i,j) + 0.5*dt(j)*Res(i,j)
       Forall(i=1:nx-1) p(i)=0.4*(Q(3,i)-0.5*Q(2,i)*Q(2,i)/Q(1,i))
       Forall(i=1:nx-1) h(2,i)= p(i)/s(i)*(s(i+1)-s(i-1))
     $   /(xloc(i+1)-xloc(i-1))
       Call Boun_cond(Q,p,pback,dim,nx)
		Call Time_step(Q,dt,p,xloc,cfl,dim,nx)
         Call Flux(Q,ef,eb,p,xloc,dim,nx)
         Forall(i=1:dim,j=1:nx-1) res(i,j)=-1.0*((ef(i,j)-df(i,j))
     $ *0.5*(s(j+1)+s(j)) - (eb(i,j)-db(i,j))*0.5*(s(j)+s(j-1)))
     $ /(s(j)*(xloc(j+1)-xloc(j-1))*0.5)
     $    + h(i,j)

       Forall(i=1:dim,j=1:nx-1) V(i,j)=V(i,j) + (dt(j))*Res(i,j)
       Forall(i=1:nx-1) p(i)=0.4*(V(3,i)-0.5*V(2,i)*V(2,i)/V(1,i))
        Forall(i=1:nx-1) h(2,i)= p(i)/s(i)*(s(i+1)-s(i-1))
     $   /(xloc(i+1)-xloc(i-1))
       Call Boun_cond(V,p,pback,dim,nx)
		iter=iter+1

		 Forall(i=0:nx) test(i)=res(1,i)
             write(6,*) maxval(abs(test)),iter,p(nx)
		if (mod(int(iter),10) .eq. 0) then
		rms=sqrt(sum(test*test)/141)
		write(14,*) iter,rms
		end if
         End do	
		Call Output(V,xloc,p,s,dim,nx)
		End Subroutine


       Subroutine Flux(V,ef,eb,p,xloc,dim,nx)
       Implicit none
       Integer dim,nx
       Real V(dim,0:nx)
       Real ef(dim,0:nx),eb(dim,0:nx)
       Real xloc(0:nx)
       Real p(0:nx)
       Integer i

!Compute mass flux, momentum flux and enthalpy flux into volume
		ef=0.0
		eb=0.0
       Forall(i=1:nx-1) ef(1,i)=0.5*(V(2,i+1)+V(2,i))
       Forall(i=1:nx-1) ef(2,i)=0.5*(V(2,i+1)**2 / V(1,i+1)
     $ +p(i+1) + V(2,i)**2 / V(1,i) + p(i))
       Forall(i=1:nx-1) ef(3,i)=0.5*((V(3,i+1)+p(i+1))*V(2,i+1)
     $ /V(1,i+1)  + (V(3,i)+p(i))*V(2,i)/V(1,i))

  	    Forall(i=1:nx-1) eb(1,i)=0.5*(V(2,i-1)+V(2,i))
       Forall(i=1:nx-1) eb(2,i)=0.5*(V(2,i-1)**2 / V(1,i-1)
     $ +p(i-1) + V(2,i)**2 / V(1,i) + p(i))
       Forall(i=1:nx-1) eb(3,i)=0.5*((V(3,i-1)+p(i-1))*V(2,i-1)
     $  /V(1,i-1) + (V(3,i)+p(i))*V(2,i)/V(1,i)) 
       End Subroutine


       Subroutine Dissp(V,df,db,xloc,dt,p,dim,nx,scheme)
       Implicit none	
       Integer dim,nx,scheme
       Real V(dim,0:nx)
       Real df(dim,0:nx),db(dim,0:nx)
       Real xloc(0:nx),dt(1:nx-1)
       Real eps2(0:nx),eps4(0:nx)	
       Real p(0:nx)
       Real kp2,kp4
       Integer i,j

! Compute artificial dissipation vectors, necessary for stability
!of scheme
        df=0.0
        db=0.0
        eps2=0.0
        eps4=0.0
        kp2=1.0/4.0
        kp4=1.0/256.0
       If (scheme .eq. 1) then
       Forall(i=1:nx-1) df(1,i)=0.5*(xloc(i+1)-xloc(i))
     $  *(V(1,i+1)-V(1,i))/dt(i)
       Forall(i=1:nx-1) df(2,i)=0.5*(xloc(i+1)-xloc(i))
     $  *(V(2,i+1)-V(2,i))/dt(i)
       Forall(i=1:nx-1) df(3,i)=0.5*(xloc(i+1)-xloc(i))
     $  *(V(3,i+1)-V(3,i))/dt(i)
  
        Forall(i=1:nx-1) db(1,i)=0.5*(xloc(i)-xloc(i-1))
     $  *(V(1,i)-V(1,i-1))/dt(i)
       Forall(i=1:nx-1) db(2,i)=0.5*(xloc(i)-xloc(i-1))
     $  *(V(2,i)-V(2,i-1))/dt(i)
       Forall(i=1:nx-1) db(3,i)=0.5*(xloc(i)-xloc(i-1))
     $  *(V(3,i)-V(3,i-1))/dt(i)
		End if

		If (scheme .eq. 2) then
		Forall(i=2:nx-2) eps2(i)=kp2*max(abs((p(i+1)-2.0*p(i)
     $  +p(i-1))/(p(i+1)+2.0*p(i)+p(i-1))),abs((p(i+2)
     $  -2.0*p(i+1) +p(i))/(p(i+2)+2.0*p(i+1)+p(i))))	
		
		Forall(i=2:nx-2) eps4(i)=max(0.0,(kp4-eps2(i)))
        
		Forall(i=1:dim,j=2:nx-2) df(i,j)=eps2(j)*0.5
     $ *(abs(V(2,j+1)/V(1,j+1)+sqrt(1.4*p(j+1)/V(1,j+1)))
     $ +abs(V(2,j)/V(1,j)+sqrt(1.4*p(j)/V(1,j))))*(V(i,j+1)-V(i,j))
     $ -eps4(j)*0.5
     $ * abs(V(2,j+1)/V(1,j+1)+sqrt(1.4*p(j+1)/V(1,j+1))
     $ + V(2,j)/V(1,j)+sqrt(1.4*p(j)/V(1,j)))*(V(i,j+2)-3.0*V(i,j+1)
     $ + 3.0*V(i,j) -V(i,j-1))

	    Forall(i=2:nx-2) eps2(i)=kp2*max(abs((p(i)-2.0*p(i-1)
     $  +p(i-2))/(p(i)+2.0*p(i-1)+p(i-2))),abs((p(i+1)
     $  -2.0*p(i) +p(i-1))/(p(i+1)+2.0*p(i)+p(i-1))))

        Forall(i=2:nx-2) eps4(i)=max(0.0,(kp4-eps2(i)))

        Forall(i=1:dim,j=2:nx-2) db(i,j)=eps2(j)*0.5
     $ * (abs(V(2,j)/V(1,j)+sqrt(1.4*p(j)/V(1,j))
     $ + V(2,j-1)/V(1,j-1)+sqrt(1.4*p(j-1)/V(1,j-1))))
     $ * (V(i,j)-V(i,j-1))
     $ -eps4(j)*0.5
     $ *( abs(V(2,j)/V(1,j)+sqrt(1.4*p(j)/V(1,j))
     $ + V(2,j-1)/V(1,j-1)+sqrt(1.4*p(j-1)/V(1,j-1))))
     $ *(V(i,j+1)-3.0*V(i,j)+3.0*V(i,j-1) -V(i,j-2))
		End if

       End subroutine


       
       Subroutine Boun_cond(V,p,pback,dim,nx)
       Implicit none
       Integer dim,nx
       Real V(dim,0:nx)
       Real p(0:nx)
       Real pback
       Integer i,j
!If the mach number at the exit is supersonic, dont specify back pressure
!If the mach number at the exit is subsonic, specify back pressure as given

       V(1,nx)=V(1,nx-1)
       V(2,nx)=V(2,nx-1)
       V(3,nx)=V(3,nx-1)
		If ((V(2,nx-1)/V(1,nx-1))/(sqrt(1.4*p(nx-1)
     $   /V(1,nx-1))) .gt. 1.0 ) then
			p(nx)=p(nx-1)
		End if
		If ((V(2,nx-1)/V(1,nx-1))
     $   /(sqrt(1.4*p(nx-1)/V(1,nx-1))) .le. 1.0 ) then
			p(nx) = pback
		End if


       end subroutine

      Subroutine Time_step(V,dt,p,xloc,cfl,nx,dim)
      Implicit none	
      Integer nx,dim
      Real V(dim,nx)	
      Real dt(1:nx-1),p(0:nx),xloc(0:nx)
      Real cfl
      Integer i

      Do i=1,nx-1
         dt(i)=0.5*cfl/(V(2,i)/V(1,i)+sqrt(1.4*p(i)/V(1,i)))
     $   *(xloc(i+1)-xloc(i-1))
      End do 

      End subroutine

      Subroutine Shape_noz(s,xloc,nx)
	  Implicit none
      Integer nx
      Real s(0:nx),xloc(0:nx)
      Real sexit,pi
      Integer i
!Get shape of the divering nozzle (cos wave between 0< x < 1
      sexit=2.0
      pi=4.0*atan(1.0)
		open(unit=8,file='noz.dat',status='unknown')
      Do i=0,nx
         If (xloc(i) .le. 0.0) then
            s(i)=1.0
         Else if (xloc(i) .gt. 0.0 .and. xloc(i) .le. 1.0)then
            s(i) = 0.5*(1.0-sexit)*cos(pi*xloc(i))  
     $ + 0.5*(1.0+sexit)

         Else if (xloc(i) .gt. 1.0) then
            s(i)=sexit
         End if
		write(8,*) xloc(i),s(i)
       End do

       End subroutine

       Subroutine Output(V,xloc,p,s,dim,nx)
	   implicit none
       Integer dim,nx
       Real V(dim,0:nx)
       Real xloc(0:nx),p(0:nx),s(0:nx)
       Integer i

       Open(unit=8,file='mach.dat',status='unknown')
		Open(unit=9,file='mfr.dat',status='unknown')
	   Open(unit=10,file='enth.dat',status='unknown')
	   Open(unit=11,file='tropy.dat',status='unknown')
       DO i=0,nx
          Write(8,*) xloc(i),V(2,i)/V(1,i)/sqrt(1.4*p(i)/V(1,i))
		  Write(9,*) xloc(i),V(2,i)*s(i)
	      Write(10,*) xloc(i), v(3,i)/V(1,i) + p(i)/V(1,i)
          Write(11,*) xloc(i),(1.4/0.4)*log(p(i)/V(1,i) )
     $   -log(p(i)) - (1.4/0.4)*log(p(0)/V(1,0)) + log(p(0))
       End do

       End subroutine


