!=================================================================================================
!=================================================================================================
!
! PARIS  Parallel Robust Interface Simulator 
! Main program to solve the 3D NS equations for multiphase flows
!
! Contact: Stephane Zaleski zaleski@dalembert.upmc.fr
! 
! Authors (in alphabetical order):
!         Sadegh Dabiri      
!         Daniel Fuster      
! 	  Yue "Stanley" Ling 
!         Leon Malan
!         Ruben Scardovelli  
!         Gretar Tryggvason  
!         Phil Yecko         
!         Stephane Zaleski   
!
! Extended from or inspired by Codes: 
!      - FTC3D2011 (Front Tracking Code for 3D simulations) by Gretar Tryggvason and Sadegh Dabiri      
!      - Surfer VOF code by Stephane Zaleski, Ruben Scardovelli and others
!      - Gerris Flow Solver by Stephane Popinet
!
! GPL Licence
!
!     This file is part of PARIS.
!
!     PARIS is free software: you can redistribute it and/or modify
!     it under the terms of the GNU General Public License as published by
!     the Free Software Foundation, either version 3 of the License, or
!     (at your option) any later version.
! 
!     PARIS is distributed in the hope that it will be useful,
!     but WITHOUT ANY WARRANTY; without even the implied warranty of
!     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
!     GNU General Public License for more details.
!
!     You should have received a copy of the GNU General Public License
!     along with PARIS.  If not, see <http://www.gnu.org/licenses/>.
!
! 
!=================================================================================================
!=================================================================================================
!=================================================================================================
!-------------------------------------------------------------------------------------------------
Program paris
  use module_flow
  use module_grid
  use module_timer
  use module_BC
  use module_tmpvar
  use module_2phase
  use module_freesurface
  use module_front

  use module_poisson
!  use module_averages
  use module_IO
  use module_solid
  use module_vof
  use module_output_vof
  
  use module_surface_tension
  use module_st_testing
  use module_lag_part
  use module_output_LPP

  implicit none
  include 'mpif.h'
  integer :: ierr, icolor
  integer :: req(48),sta(MPI_STATUS_SIZE,48)
  INTEGER :: irank, ii, i, j, k, out_fs
  real(8) :: residual,cflmax,get_cfl_and_check,residualu,residualv,residualw
  integer :: itu,itv,itw

!---------------------------------------INITIALIZATION--------------------------------------------
  ! Initialize MPI
  call MPI_INIT(ierr)
  if (ierr /= 0) call err_no_out_dir("*** Main: unsuccessful MPI-initialization")
  call MPI_COMM_RANK(MPI_COMM_WORLD, rank, ierr)
  call MPI_COMM_SIZE(MPI_COMM_WORLD, numProcess, ierr)
  start_time = MPI_Wtime(ierr)

  call ReadParameters
  if(rank==0) write(out,*)'Parameters read successfully'

  !Check consistency of options
  if(rank==0) then
     if(TwoPhase.or.FreeSurface) then
        if((.not.DoFront).and.(.not.DoVOF)) call pariserror("need a phase tracking for two-phase")
        if(GetPropertiesFromFront.and.(.not.DoFront)) call pariserror("need Front to get properties from")
        if((.not.GetPropertiesFromFront).and.(.not.DoVOF)) call pariserror("need VOF to get properties from")
     endif
     if(TwoPhase.and.FreeSurface) call pariserror("cannnot be both TwoPhase and FreeSurface")
  endif

  ! check number of processors
  if ((NumProcess < nPdomain+1).and.DoFront) call pariserror("*** Main: Error with number of processes - Front!")
  if (NumProcess < nPdomain) call pariserror("*** Main: Error with number of processes!")

  icolor = 0                                  ! Processes 0 to nPdomain-1 are used to solve domain
  if(rank>=nPdomain) icolor = MPI_UNDEFINED
  call MPI_COMM_SPLIT(MPI_COMM_WORLD, icolor, 0, MPI_Comm_Domain, ierr)
  If (ierr /= 0) call pariserror("*** Main: unsuccessful MPI split")

  icolor = 0                                  ! Process nPdomain is used to solve front
  if(rank>nPdomain) icolor = MPI_UNDEFINED
  if((rank==nPdomain).and.(.not.DoFront)) icolor = MPI_UNDEFINED
  call MPI_COMM_SPLIT(MPI_COMM_WORLD, icolor, 0, MPI_Comm_Active, ierr)
  If (ierr /= 0) call pariserror("*** Main: unsuccessful MPI split")

  if((rank>nPdomain).or.((rank==nPdomain).and.(.not.DoFront)))then
     call pariserror("rank>nPdomain).or.((rank==nPdomain).and.(.not.DoFront))")
  endif

  call initialize
  call check_sanity_in_depth()
!  print *, "warning: no stability check"
   call check_stability

  if(DoVOF.and.rank<nPdomain) call initialize_VOF
  if(DoLPP) call initialize_LPP

  if(rank<nPdomain) call initialize_solids

  if(DoFront) call InitFront
  if(rank==0) write(out,*)'initialized'
  if(rank==0) write(*  ,*)'initialized'

  if(HYPRE .and. rank<nPdomain) call poi_initialize(mpi_comm_Cart, &
                                                    is,ie,js,je,ks,ke,Nx,Ny,Nz,bdry_cond)
  if(HYPRE .and. rank==0) write(out,*)'hypre initialized'
  if(HYPRE .and. rank==0) write(*  ,*)'hypre initialized'

  call InitCondition

    if(rank<nPdomain) then
!-------------------------------------------------------------------------------------------------
!------------------------------------------Begin domain-------------------------------------------
!-------------------------------------------------------------------------------------------------

     ! output initial condition
     ! if(rank==0) start_time = MPI_WTIME()
     if(ICOut .and. rank<nPdomain) then
        if (.not.restart) call output(0,is,ie+1,js,je+1,ks,ke+1)
        if(DoVOF .and. .not.restart) then
           call output_VOF(0,is,ie+1,js,je+1,ks,ke+1)
           call output_ALL(0,is,ie+1,js,je+1,ks,ke+1)
           if (FreeSurface) then
              do out_fs = 1,3
                 if (VTK_OUT(out_fs)) then
                    if (rank==0) call append_visit_fs(out_fs,0)
                    SELECT CASE (out_fs)
                    case (1) 
                       tmp = 0d0
                       do k=ks,ke+1; do j=js,je+1; do i=is,ie+1
                          tmp(i,j,k)=ABS((u(i-1,j,k)-u(i,j,k))/dx(i)+(v(i,j-1,k)-v(i,j,k))/dy(j)+(w(i,j,k-1)-w(i,j,k))/dz(k))
                       enddo; enddo; enddo
                       call VTK_scalar_struct(out_fs,0,tmp)
                    case (2)
                       call VTK_scalar_struct(out_fs,0,kappa_fs)
                    case(3)
                       call VTK_scalar_struct(out_fs,0,P_gx)
                    end SELECT
                 endif
              enddo
           endif
        endif
        if(DoLPP .and. .not.restart) call output_LPP(0,is,ie+1,js,je+1,ks,ke+1)
        if(test_droplet) call output_droplet(w,time)
        call setvelocityBC(u,v,w,umask,vmask,wmask,time)
        call write_vec_gnuplot(u,v,cvof,p,itimestep,DoVOF)
        call calcstats

        if(dtFlag==2)call TimeStepSize(dt)
        cflmax = get_cfl_and_check(dt)

        if(rank==0) then
           end_time =  MPI_WTIME()
           open(unit=121,file='stats',position='append')
           write(121,'(30es14.6e2)')time,stats(1:nstatarray),dpdx,(stats(8)-stats(9))/dt,end_time-start_time
           close(121)
           write(out,'("Step:",I9," Iterations:",I9," cpu(s):",f10.2)')-1,0,end_time-start_time
           write(*,  '("START:", I6," dt=",es16.5e2," time=",es16.5e2," cpu(s):",f11.3   ," cfl=",es16.5e2)') &
                itimestep,dt,time,end_time-start_time,cflmax
!           itimestep=0; ii=0
        endif
     endif
     if(test_HF.or.test_LP) then
        ! Exit MPI gracefully
        close(out)
        call print_st_stats()
        call MPI_BARRIER(MPI_COMM_WORLD, ierr)
        if(rank==0) write(*,'("Paris exits succesfully after HF, curvature or LP test")')
        call MPI_finalize(ierr)
        stop
     endif
 !-----------------------------------------MAIN TIME LOOP------------------------------------------
     call initialize_timer()

     do while(time<EndTime .and. itimestep<nstep)
        if(dtFlag==2)call TimeStepSize(dt)
        time=time+dt
        itimestep=itimestep+1
        if(mod(itimestep,termout)==0) then
           end_time =  MPI_WTIME()
           cflmax = get_cfl_and_check(dt)
           if(rank==0) &
                write(out,'("Step: ",I10," dt=",es16.5e2," time=",es16.5e2," cfl="   ,es16.5e2)                 ') &
                itimestep,dt,time                    ,cflmax
           if(rank==0) &
                write(*,  '("Step: ", I6," dt=",es16.5e2," time=",es16.5e2," cpu(s):",f11.3   ," cfl=",es16.5e2)') &
                itimestep,dt,time,end_time-start_time,cflmax
        endif

        if(itime_scheme==2) then
           uold = u
           vold = v
           wold = w
           rhoo = rho
           muold  = mu
           if(DoVOF) cvofold  = cvof
           if ( DoLPP ) call StoreOldPartSol()
        endif
        !if (FreeSurface .and. RP_test .and. rank==0) call Integrate_RP(dt,time)
 !------------------------------------ADVECTION & DIFFUSION----------------------------------------
        du = 0d0; dv = 0d0; dw = 0d0
        do ii=1, itime_scheme
           if(TwoPhase.and.(.not.GetPropertiesFromFront)) then
             call linfunc(rho,rho1,rho2,DensMean)
             call linfunc(mu,mu1,mu2,ViscMean) ! Note: harmonic mean matches shear stress better
           endif
           call my_timer(2)

           if( DoLPP ) call StoreBeforeConvectionTerms()
           if(.not.ZeroReynolds) call momentumConvection()
           if( DoLPP ) call StoreAfterConvectionTerms()
           call my_timer(3)
           if(DoVOF) then
              if (DoMOMCONS) then
                 call vofandmomsweepsstaggered(itimestep)
                 call vofsweeps(itimestep)
!                 call vofandmomsweepsold(itimestep)
              else
                 call vofsweeps(itimestep)
              endif
              call my_timer(4)
              call get_all_heights()
              call my_timer(5)
              call linfunc(rho,rho1,rho2,DensMean)
              if (.not. FreeSurface) call surfaceForce(du,dv,dw,rho)
              call my_timer(8)
              if (FreeSurface) then
                 call get_normals()
                 call get_all_curvatures(kappa_fs)
                 call set_topology(vof_phase,itimestep) !vof_phase updated in vofsweeps
              endif
           endif
           if (DoLPP) then
                call lppsweeps(itimestep,time,ii)  
                call my_timer(12)
           end if ! DoLPP
!------------------------------------FRONT TRACKING  ---------------------------------------------
           ! Receive front from master of front
           if(DoFront) call GetFront('recv')
           call my_timer(13)
           if(Implicit) then
              if(Twophase) then 
                 call momentumDiffusion(u,v,w,rho,mu,du,dv,dw)  
              endif
           else
              call explicitMomDiff(u,v,w,rho,mu,du,dv,dw)
           endif
           call my_timer(3)

           ! reset the surface tension force on the fixed grid (when surface tension from front)
           fx = 0d0;    dIdx=0d0
           fy = 0d0;    dIdy=0d0
           fz = 0d0;    dIdz=0d0
           ! Wait to finish receiving front
           if(DoFront) then
              call GetFront('wait')
              call Front2GridVector(fx, fy, fz, dIdx, dIdy, dIdz)
              call AdvanceFront2(u, v, w, color, dt)
              
              ! Send the updated front back
              call GetFront('send')
           endif
           call my_timer(13)
           
!------------------------------------END VOF STUFF------------------------------------------------ 
           call volumeForce(rho,rho1,rho2,dpdx,dpdy,dpdz,BuoyancyCase,fx,fy,fz,gx,gy,gz,du,dv,dw, &
                rho_ave)
           if(dosolids) then
              du = du*umask; dv = dv*vmask; dw = dw*wmask
           endif
           call my_timer(2)
  
           if(Implicit) then   
              call SetupUvel(u,du,rho,mu,rho1,mu1,dt,A)
              if(hypre)then
                 call poi_solve(A,u(is:ie,js:je,ks:ke),maxError,maxit,it)
              else
                 call LinearSolver1(A,u,umask,maxError,beta,maxit,itu,ierr)
             endif
             if(mod(itimestep,termout)==0) call calcresidual1(A,u,umask,residualu)
             call SetupVvel(v,dv,rho,mu,rho1,mu1,dt,A)
             if(hypre)then
                call poi_solve(A,v(is:ie,js:je,ks:ke),maxError,maxit,it)
             else
                call LinearSolver1(A,v,vmask,maxError,beta,maxit,itv,ierr)
             endif
             if(mod(itimestep,termout)==0) call calcresidual1(A,v,vmask,residualv)
             call SetupWvel(w,dw,rho,mu,rho1,mu1,dt,A)
             if(hypre)then
                call poi_solve(A,w(is:ie,js:je,ks:ke),maxError,maxit,it)
             else
                call LinearSolver1(A,w,wmask,maxError,beta,maxit,itw,ierr)
              endif
              if(mod(itimestep,termout)==0) call calcresidual1(A,w,wmask,residualw)
           else
              u = u + dt * du
              v = v + dt * dv
              w = w + dt * dw
           endif
           call my_timer(3)
           call SetVelocityBC(u,v,w,umask,vmask,wmask,time)
           call do_ghost_vector(u,v,w)
           call my_timer(1)
!-----------------------------------------PROJECTION STEP-----------------------------------------
           call SetPressureBC(umask,vmask,wmask)
           if (.not.FreeSurface) then
              call SetupPoisson(u,v,w,umask,vmask,wmask,rho,dt,A,tmp,VolumeSource)
           else 
              call Setuppoisson_fs(u,v,w,vof_phase,rho,dt,A,cvof,n1,n2,n3,kappa_fs,itimestep)
           endif
           ! (div u)*dt < epsilon => div u < epsilon/dt => maxresidual : maxerror/dt 
           if(HYPRE)then
              if (FreeSurface) call pariserror("HYPRE not functional for Free Surface")
              call poi_solve(A,p(is:ie,js:je,ks:ke),maxError/dt,maxit,it)
              call ghost_x(p,1,req(1:4 ))
              call ghost_y(p,1,req(5:8 ))
              call ghost_z(p,1,req(9:12)) 
              call MPI_WAITALL(12,req(1:12),sta(:,1:12),ierr)
           else
              if (FreeSurface) then
                 solver_flag = 1
                 if (RP_test) call set_RP_pressure(p)
                 call FreeSolver(A,p,maxError/dt,beta,maxit,it,ierr,itimestep,time,residual)
              else
                 call NewSolver(A,p,maxError/dt,beta,maxit,it,ierr)
              endif
           endif
           if(mod(itimestep,termout)==0) then
              !call calcresidual(A,p,residual)
              if(rank==0) then
                 write(*,'("              pressure residual*dt:   ",e7.1,&
                   &" maxerror: ",e7.1)') residual*dt,maxerror
                 write(*,'("              pressure iterations :",I9)')it
              end if
           endif
           if (.not.FreeSurface) then
              do k=ks,ke;  do j=js,je; do i=is,ieu    ! CORRECT THE u-velocity 
                 u(i,j,k)=u(i,j,k)-dt*(2.0*umask(i,j,k)/dxh(i))*(p(i+1,j,k)-p(i,j,k))/(rho(i+1,j,k)+rho(i,j,k))
              enddo; enddo; enddo

              do k=ks,ke;  do j=js,jev; do i=is,ie    ! CORRECT THE v-velocity
                 v(i,j,k)=v(i,j,k)-dt*(2.0*vmask(i,j,k)/dyh(j))*(p(i,j+1,k)-p(i,j,k))/(rho(i,j+1,k)+rho(i,j,k))
              enddo; enddo; enddo

              do k=ks,kew;  do j=js,je; do i=is,ie   ! CORRECT THE w-velocity
                 w(i,j,k)=w(i,j,k)-dt*(2.0*wmask(i,j,k)/dzh(k))*(p(i,j,k+1)-p(i,j,k))/(rho(i,j,k+1)+rho(i,j,k))
              enddo; enddo; enddo
           else
              do k=ks,ke;  do j=js,je; do i=is,ieu    ! CORRECT THE u-velocity 
                 if (u_cmask(i,j,k)==0) then
                    u(i,j,k)=u(i,j,k)-dt*2d0/(rho(i,j,k)+rho(i+1,j,k))&
                         *(p(i+1,j,k)+P_gx(i+1,j,k)-p(i,j,k)-P_gx(i,j,k))/x_mod(i,j,k)
                    if (u(i,j,k) /= u(i,j,k)) write(*,'("WARNING u NaN :",2e14.5)')u(i,j,k), x_mod(i,j,k)
                 endif
              enddo; enddo; enddo

              do k=ks,ke;  do j=js,jev; do i=is,ie    ! CORRECT THE v-velocity
                 if (v_cmask(i,j,k)==0) then
                    v(i,j,k)=v(i,j,k)-dt*2d0/(rho(i,j,k)+rho(i,j+1,k))&
                         *(p(i,j+1,k)+P_gy(i,j+1,k)-p(i,j,k)-P_gy(i,j,k))/y_mod(i,j,k)
                    if (v(i,j,k) /= v(i,j,k)) write(*,'("WARNING v NaN :",2e14.5)')v(i,j,k), y_mod(i,j,k)
                 endif
              enddo; enddo; enddo

              do k=ks,kew;  do j=js,je; do i=is,ie   ! CORRECT THE w-velocity
                 if (w_cmask(i,j,k)==0) then
                    w(i,j,k)=w(i,j,k)-dt*2d0/(rho(i,j,k)+rho(i,j,k+1))&
                         *(p(i,j,k+1)+P_gz(i,j,k+1)-p(i,j,k)-P_gz(i,j,k))/z_mod(i,j,k)
                    if (w(i,j,k) /= w(i,j,k)) write(*,'("WARNING w NaN :",2e14.5)')w(i,j,k), z_mod(i,j,k)
                 endif
              enddo; enddo; enddo
           endif
   
           if(mod(itimestep,nout)==0) call check_corrected_vel(u,umask,itimestep)
           if( DoLPP ) call ComputeSubDerivativeVel()
           call my_timer(10)
           !--------------------------------------UPDATE COLOR---------------------------------------------
           if (DoFront) then
                 call SetupDensity(dIdx,dIdy,dIdz,A,color)
                 if(hypre)then
                    call poi_solve(A,color(is:ie,js:je,ks:ke),maxError,maxit,it)
                 else
                    call NewSolver(A,color,maxError,beta,maxit,it,ierr)
                    if(mod(itimestep,termout)==0) then
                       if(rank==0.and..not.hypre)write(*  ,'("              density  iterations:",I9)')it
                    endif
                 endif
                 !adjust color function to 0-1 range
                 do k=ks,ke;  do j=js,je; do i=is,ie
                    color(i,j,k)=min(color(i,j,k),1d0)
                    color(i,j,k)=max(color(i,j,k),0d0)
                 enddo; enddo; enddo
           endif
           call my_timer(13)
           call SetVelocityBC(u,v,w,umask,vmask,wmask,time)
           call ghost_x(u  ,2,req( 1: 4));  call ghost_x(v,2,req( 5: 8)); call ghost_x(w,2,req( 9:12)); 
           call ghost_x(color,1,req(13:16));  call MPI_WAITALL(16,req(1:16),sta(:,1:16),ierr)
           call ghost_y(u  ,2,req( 1: 4));  call ghost_y(v,2,req( 5: 8)); call ghost_y(w,2,req( 9:12)); 
           call ghost_y(color,1,req(13:16));  call MPI_WAITALL(16,req(1:16),sta(:,1:16),ierr)
           call ghost_z(u  ,2,req( 1: 4));  call ghost_z(v,2,req( 5: 8)); call ghost_z(w,2,req( 9:12)); 
           call ghost_z(color,1,req(13:16));  call MPI_WAITALL(16,req(1:16),sta(:,1:16),ierr)
           call my_timer(1)        

!----------------------------------EXTRAPOLATION FOR FREE SURFACE---------------------------------
           if (DoVOF .and. FreeSurface) then
              call extrapolate_velocities()
              call setuppoisson_fs2(u,v,w,dt,A,vof_phase,itimestep)
              ! Solve for an intermediate pressure in gas
              solver_flag = 2
              if(HYPRE)then !HYPRE will not work with removed nodes from domain.
                 call pariserror("HYPRE solver not yet available for Free Surfaces")
              else
                 call FreeSolver(A,p_ext,maxError/dt,beta,maxit,it,ierr,itimestep,time,residual)
              endif
              if(mod(itimestep,termout)==0) then
                 !call calcresidual(A,p,residual)
                 if(rank==0) then
                    write(*,'("FS2:          pressure residual:   ",e7.1,&
                         &" maxerror: ",e7.1)') residual*dt,maxerror
                    write(*,'("              pressure iterations :",I9)')it
                 endif
              endif
              ! Correct ONLY masked gas velocities at level 1 and 2
              do k=ks,ke;  do j=js,je; do i=is,ieu    ! CORRECT THE u-velocity 
                 if (u_cmask(i,j,k)==1 .or. u_cmask(i,j,k)==2) then
                    u(i,j,k)=u(i,j,k)-(p_ext(i+1,j,k)-p_ext(i,j,k))/dxh(i)
                 endif
              enddo; enddo; enddo

              do k=ks,ke;  do j=js,jev; do i=is,ie    ! CORRECT THE v-velocity
                 if (v_cmask(i,j,k)==1 .or. v_cmask(i,j,k)==2) then
                    v(i,j,k)=v(i,j,k)-(p_ext(i,j+1,k)-p_ext(i,j,k))/dyh(j)
                 endif
              enddo; enddo; enddo

              do k=ks,kew;  do j=js,je; do i=is,ie   ! CORRECT THE w-velocity
                 if (w_cmask(i,j,k)==1 .or. w_cmask(i,j,k)==2) then
                    w(i,j,k)=w(i,j,k)-(p_ext(i,j,k+1)-p_ext(i,j,k))/dzh(k)
                 endif
              enddo; enddo; enddo
              call SetVelocityBC(u,v,w,umask,vmask,wmask,time) !check this
              call do_ghost_vector(u,v,w)
              if (mod(itimestep,nstats)==0 .and. mod(ii,itime_scheme)==0) call discrete_divergence(u,v,w,itimestep/nstats)
           endif !Extrapolation
!------------------------------------------------------------------------------------------------


!--------------------------------------UPDATE DENSITY/VISCOSITY------------------------------------
           if(TwoPhase) then
              if(GetPropertiesFromFront) then
                 rho = rho2 + (rho1-rho2)*color
                 mu  = mu2  + (mu1 -mu2 )*color
              else
!------------------------------------deduce rho, mu from cvof-------------------------------------
                 call linfunc(rho,rho1,rho2,DensMean)
                 call linfunc(mu,mu1,mu2,ViscMean)
!------------------------------------END VOF STUFF------------------------------------------------
              endif
           endif
           call my_timer(2)

           ! Wait for front to be sent back
           if(DoFront)call GetFront('wait')
           call my_timer(13)
        enddo !itime_scheme
        if (FreeSurface .and. RP_test) call Integrate_RP(dt,time)
        if(itime_scheme==2) then
           u = 0.5*(u+uold)
           v = 0.5*(v+vold)
           w = 0.5*(w+wold)
           rho = 0.5*(rho+rhoo)
           mu  = 0.5*(mu +muold)
           if(DoVOF) cvof  = 0.5*(cvof +cvofold)
           if(DoVOF) then
              call get_flags_and_clip(cvof,vof_flag)
              call get_vof_phase(cvof) !cvof updated above from min to max
           endif
           if ( DoLPP ) call AveragePartSol()
        endif
        if (DoLPP) then
            call PartBCWrapper
            call lppvofsweeps(itimestep,time)  
            call SeedParticles
            call my_timer(14)
        end if ! DoLPP
!--------------------------------------------OUTPUT-----------------------------------------------
        if(mod(itimestep,nstats)==0) then
        	call calcStats
        	if( test_KHI2D .or. test_HF ) call h_of_KHI2D(itimestep,time)
        endif
        call my_timer(2)
        if(mod(itimestep,nbackup)==0) then 
           if ( DoFront ) then 
              call backup_write
           else if ( DoVOF ) then 
              call backup_VOF_write
              if ( DoLPP ) call backup_LPP_write 
           end if ! DoFront, DoVOF
        end if ! itimestep
!        if(mod(itimestep,noutuv)==0) then
        if ( tout > 0.d0 ) then  ! output based on time 
            if ( (tout - (time-timeLastOutput)) < 0.9999*dt ) then 
               nfile = NINT(time/tout)
               call output(nfile,is,ie+1,js,je+1,ks,ke+1)
               if(DoVOF) call output_VOF(nfile,is,ie+1,js,je+1,ks,ke+1)
               if(DoVOF) call output_ALL(nfile,is,ie+1,js,je+1,ks,ke+1)
               if(DoLPP) call output_LPP(nfile,is,ie+1,js,je+1,ks,ke+1)
               if(rank==0)then
                  end_time =  MPI_WTIME()
                  write(out,'("Output data at Step:",I9," and time:",f10.2)')itimestep,time
               endif
               timeLastOutput = time
            end if! timeAfterOuput
        else                     ! output based on timestep
            if(mod(itimestep-itimestepRestart,nout)==0) then 
               nfile = ITIMESTEP/nout
               if ( tout > 0.d0 .and. dtFlag == 1 ) nfile = NINT(time/tout)
               call write_vec_gnuplot(u,v,cvof,p,itimestep,DoVOF)
               call output(nfile,is,ie+1,js,je+1,ks,ke+1)
               if(DoVOF) call output_VOF(nfile,is,ie+1,js,je+1,ks,ke+1)
               if(DoVOF) call output_ALL(nfile,is,ie+1,js,je+1,ks,ke+1)
               if(DoLPP) call output_LPP(nfile,is,ie+1,js,je+1,ks,ke+1)
               if(test_droplet) call output_droplet(w,time)
               if(rank==0)then
                  end_time =  MPI_WTIME()
                  write(out,'("Step:",I9," Iterations:",I9," cpu(s):",f10.2)')itimestep,it,end_time-start_time
               endif
            endif
        end if ! tout
        if(nstats==0) call pariserror(" *** Main: nstats = 0")
        if(mod(itimestep,nstats)==0.and.rank==0)then
              !        open(unit=121,file='track')
              !        write(121,'("Step:",I10," dt=",es16.5e2," time=",es16.5e2)')itimestep,dt,time
              !        write(121,'("            Iterations:",I7," cpu(s):",f10.2)')it,end_time-start_time
              !        close(121)
           open(unit=121,file='stats',position='append')
           write(121,'(30es14.6e2)')time,stats(1:nstatarray),dpdx,(stats(8)-stats(9))/dt,end_time-start_time
           close(121)
        endif
        call my_timer(11)
        !output for scalar variables used in free surface
        if (FreeSurface) then
           if (RP_test .and. (mod(itimestep,nstats)==0) .and. rank==0) call write_RP_test(time)
           do out_fs = 1,3
              if (VTK_OUT(out_fs)) then
                 if (mod(itimestep,NOUT_VTK(out_fs))==0) then
                    if (rank==0) call append_visit_fs(out_fs,itimestep/NOUT_VTK(out_fs))
                    SELECT CASE (out_fs)
                    case (1) 
                       tmp = 0d0
                       do k=ks,ke+1; do j=js,je+1; do i=is,ie+1
                          tmp(i,j,k)=ABS((u(i-1,j,k)-u(i,j,k))/dx(i)+(v(i,j-1,k)-v(i,j,k))/dy(j)+(w(i,j,k-1)-w(i,j,k))/dz(k))
                       enddo; enddo; enddo
                       call VTK_scalar_struct(out_fs,itimestep/NOUT_VTK(out_fs),tmp)
                    case (2)
                       call VTK_scalar_struct(out_fs,itimestep/NOUT_VTK(out_fs),kappa_fs)
                    case(3)
                       call VTK_scalar_struct(out_fs,itimestep/NOUT_VTK(out_fs),P_gx)
                    end SELECT
                 endif
              endif
           enddo
        endif
     enddo
     !-------------------------------------------------------------------------------------------------
     !--------------------------------------------End domain-------------------------------------------
     !-------------------------------------------------------------------------------------------------
     elseif((rank==nPdomain).and.DoFront) then !front tracking process (rank=nPdomain)
     !-------------------------------------------------------------------------------------------------
     !--------------------------------------------Begin front------------------------------------------
     !-------------------------------------------------------------------------------------------------
     if(ICout) call print_fronts(0,time)
     !---------------------------------------MAIN TIME LOOP--------------------------------------------
     do while(time<EndTime .and. iTimeStep<nstep)
        if(dtFlag==2)call TimeStepSize(dt)
        time=time+dt
        itimestep=itimestep+1
        
        if(itime_scheme==2) call StoreOldFront
        
        do ii=1, itime_scheme
           call CalcSurfaceTension
           do irank=0,nPdomain-1
              call DistributeFront(irank,'send') !,request(1:2,irank))
           enddo
           do irank=0,nPdomain-1
              call DistributeFront(irank,'recv') !,request(1:2,irank))
           enddo
        enddo
   
        if(itime_scheme==2) call AverageFront
        
         if(mod(itimestep,nregrid)==0) then
            print*,'Starting regrid'
            call RegridFront
            print*,'Finished regrid'
         endif
        if(smooth.and.mod(itimestep,nsmooth)==0) call smoothFront
        call CalcVolume
        if(mod(itimestep,nsmooth)==0) then 
           call CorrectVolume
           print*,'Finished volume correction'
        endif
!--------------------------------------------OUTPUT-----------------------------------------------
        if(mod(itimestep,nout)==0)call print_fronts(ITIMESTEP/nout,time)
        if(mod(itimestep,nbackup)==0)call backup_front_write(time,iTimeStep)
        if(mod(itimestep,nstats)==0)then
           open(unit=121,file='statsbub',position='append')
           do i=1, NumBubble
              write(121,'(6es16.8e2)')time,FrontProps(1,i),FrontProps(5:7,i), FrontProps(14,i)
           enddo
           close(121)
        endif
     enddo
!-------------------------------------------------------------------------------------------------
!--------------------------------------------End front--------------------------------------------
  endif
!-------------------------------------------------------------------------------------------------
!--------------- END OF MAIN TIME LOOP ----------------------------------------------------------
  call wrap_up_timer(itimestep,iTimeStepRestart)
  if(rank==0) then 
     if(output_format==2) call close_visit_file()
  endif

  if(rank<nPdomain)  call output_at_location()
  if(rank==0)  call final_output(stats(2))
  if(HYPRE) call poi_finalize
  if(rank==0) write(*,'("Paris exits succesfully")')
  call print_st_stats()
  call MPI_BARRIER(MPI_COMM_WORLD, ierr)
  call MPI_FINALIZE(ierr)
  stop
end program paris
!=================================================================================================
!=================================================================================================
! subroutine TimeStepSize
!-------------------------------------------------------------------------------------------------
subroutine TimeStepSize(deltaT)
  use module_grid
  use module_flow
  implicit none
  include "mpif.h"
  real(8) :: deltaT, h, vmax, dtadv, mydt
  integer :: ierr

  if(rank<nPdomain)then
    h  = minval(dx)
    vmax = maxval(sqrt(u(is:ie,js:je,ks:ke)**2+v(is:ie,js:je,ks:ke)**2+w(is:ie,js:je,ks:ke)**2))
    vmax = max(vmax,1d-3)
  !  dtadv  = min(h/vmax,2d0*nu_min/vmax**2)
    dtadv  = h/vmax
    mydt = CFL*dtadv
    mydt = min(mydt,MaxDt)
  else
    mydt=1.0e10
  endif
  call MPI_ALLREDUCE(mydt, deltat, 1, MPI_DOUBLE_PRECISION, MPI_MIN, MPI_COMM_Active, ierr)

end subroutine TimeStepSize
!=================================================================================================
function get_cfl_and_check(deltaT)
  use module_grid
  use module_flow
  implicit none
  include 'mpif.h'
  integer :: ierr
  real(8) :: get_cfl_and_check,vmax,inbox_cfl,deltaT,h,glogcfl
  vmax = maxval(sqrt(u(is:ie,js:je,ks:ke)**2 + v(is:ie,js:je,ks:ke)**2 + w(is:ie,js:je,ks:ke)**2))
  h  = minval(dx)
  inbox_cfl=vmax*deltaT/h
  call MPI_ALLREDUCE(inbox_cfl, glogcfl, 1, MPI_DOUBLE_PRECISION, MPI_MAX, MPI_COMM_Cart,ierr) 
  get_cfl_and_check=glogcfl
  if(get_cfl_and_check.gt.cflmax_allowed) call pariserror("CFL too large")
end function get_cfl_and_check

!=================================================================================================
! subroutine calcStats
!-------------------------------------------------------------------------------------------------
subroutine calcStats
  use module_grid
  use module_flow
  use module_VOF
  implicit none
  include "mpif.h"
  integer :: i,j,k,ierr
  real(8) :: vol,CC=0d0
  real(8) :: kenergy,vort2,enstrophy
  real(8), save :: W_int=-0.02066
  real(8), save :: height4Stats=0.875d0

  nstatarray=24
  if(nstatarray > 100) call pariserror("nstatarray too large")
  mystats(1:nstatarray)=0d0
  do k=ks,ke;  do j=js,je;  do i=is,ie
     vol = dx(i)*dy(j)*dz(k)
     ! Average u component
     mystats(2)=mystats(2)+u(i,j,k)*vol
     mystats(3)=mystats(3)+fx(i,j,k)*dxh(i)*dy(j)*dz(k)
     mystats(4)=mystats(4)+fy(i,j,k)*dx(i)*dyh(j)*dz(k)
     mystats(5)=mystats(5)+fz(i,j,k)*dx(i)*dy(j)*dzh(k)
     mystats(6)=mystats(6)+rho(i,j,k)*vol
     mystats(7)=mystats(7)+p(i,j,k)*vol
     ! average momentum
     mystats(8)=mystats(8)+0.5*(rho(i,j,k)+rho(i+1,j,k))*u(i,j,k)*vol
     mystats(9)=mystats(9)+0.5*(rho(i,j,k)+rho(i+1,j,k))*uold(i,j,k)*vol
     ! Phase C=1 volume
     if(DoVOF) CC=cvof(i,j,k) ;  mystats(10)=mystats(10)+CC*vol
     ! Phase C=1 center of mass
     if(DoVOF) CC=cvof(i,j,k) ;  mystats(11)=mystats(11)+CC*vol*x(i)
     ! kinetic energy
     kenergy = 0.5d0*( (0.5d0*(u(i,j,k)+u(i+1,j,k)))**2.d0 & 
          +(0.5d0*(v(i,j,k)+v(i,j+1,k)))**2.d0 & 
          +(0.5d0*(w(i,j,k)+w(i,j,k+1)))**2.d0)
     if(DoVOF) then
        mystats(12)=mystats(12)+rho(i,j,k)*kenergy*vol*      cvof(i,j,k)
        mystats(13)=mystats(13)+rho(i,j,k)*kenergy*vol*(1.d0-cvof(i,j,k))
     else 
        mystats(13)=mystats(13)+rho(i,j,k)*kenergy*vol
     end if ! DoVOF
     ! y-momentum 
     if(DoVOF) then
        mystats(14)=mystats(14)+rho(i,j,k)*0.5d0*(v(i,j,k)+v(i,j+1,k))*vol*(     cvof(i,j,k))
        mystats(15)=mystats(15)+rho(i,j,k)*0.5d0*(v(i,j,k)+v(i,j+1,k))*vol*(1.d0-cvof(i,j,k))
     end if ! (DoVOF)
     ! interfacial area
     if (DoVOF .and. test_PhaseInversion) then
        if (     max((cvof(i+1,j,k)-0.5d0)/abs(cvof(i+1,j,k)-0.5d0),0.d0) & 
             - max((cvof(i-1,j,k)-0.5d0)/abs(cvof(i-1,j,k)-0.5d0),0.d0) /= 0.d0 &
             .or. max((cvof(i,j+1,k)-0.5d0)/abs(cvof(i,j+1,k)-0.5d0),0.d0) & 
             - max((cvof(i,j-1,k)-0.5d0)/abs(cvof(i,j-1,k)-0.5d0),0.d0) /= 0.d0 &
             .or. max((cvof(i,j,k+1)-0.5d0)/abs(cvof(i,j,k+1)-0.5d0),0.d0) & 
             - max((cvof(i,j,k-1)-0.5d0)/abs(cvof(i,j,k-1)-0.5d0),0.d0) /= 0.d0 & 
             ) then
           if ( vof_flag(i,j,k) == 2 ) & 
                mystats(19) = mystats(19) + dx(i)*dy(j)
        end if ! cvof
     end if ! DoVOF
     ! potential energy (considering gravity in y direction)  
     if(DoVOF .and. test_PhaseInversion) then
        mystats(20)=mystats(20)+rho(i,j,k)*Gy*y(j)*vol*      cvof(i,j,k)
        mystats(21)=mystats(21)+rho(i,j,k)*Gy*y(j)*vol*(1.d0-cvof(i,j,k))
     end if ! DoVOF
     ! enstrophy
     if(DoVOF .and. test_PhaseInversion) then
        vort2 = (0.5d0*(w(i,j+1,k)+w(i,j+1,k+1)-w(i,j-1,k)-w(i,j-1,k+1))/(y(j+1)-y(j-1)) & 
             -0.5d0*(v(i,j,k+1)+v(i,j+1,k+1)-v(i,j,k-1)-v(i,j+1,k-1))/(z(k+1)-z(k-1)))**2.d0 & 
             + (0.5d0*(u(i,j,k+1)+u(i+1,j,k+1)-u(i,j,k-1)-u(i+1,j,k-1))/(z(k+1)-z(k-1)) & 
             -0.5d0*(w(i+1,j,k)+w(i+1,j,k+1)-w(i-1,j,k)-w(i-1,j,k+1))/(x(i+1)-x(i-1)))**2.d0 & 
             + (0.5d0*(v(i+1,j,k)+v(i+1,j+1,k)-v(i-1,j,k)-v(i-1,j+1,k))/(x(i+1)-x(i-1)) & 
             -0.5d0*(u(i,j+1,k)+u(i+1,j+1,k)-u(i,j-1,k)-u(i+1,j-1,k))/(y(j+1)-y(j-1)))**2.d0 
        enstrophy = 0.5*vort2
        mystats(22)=mystats(22)+enstrophy*vol*      cvof(i,j,k)
        mystats(23)=mystats(23)+enstrophy*vol*(1.d0-cvof(i,j,k))
     end if ! DoVOF
     ! volume fraction of top layer
     if(DoVOF .and. test_PhaseInversion) then
        if ( y(j) > height4Stats ) & 
             mystats(24)=mystats(24)+vol*cvof(i,j,k)/(xLength*zLength*(yLength-height4Stats))
     end if ! DoVOF
  enddo;  enddo;  enddo

! Shear stress on y=0,Ly
  if(js==Ng+1)then
    do k=ks,ke;  do i=is,ie
      mystats(1)=mystats(1)+mu(i,js,k)*u(i,js,k)*dx(i)*dz(k)/(dy(js)/2.0)
    enddo; enddo
  endif
  if(je==Ng+Ny)then
    do k=ks,ke;  do i=is,ie
      mystats(1)=mystats(1)+mu(i,je,k)*u(i,je,k)*dx(i)*dz(k)/(dy(je)/2.0)
    enddo; enddo
  endif
! Get pressure at entry and exit
  if (test_point_in(ng+1,ny/2+ng,nz/2+ng)) then 
     mystats(17)=p(ng+1,ny/2+ng,nz/2+ng)
  else
     mystats(17)=0d0
  endif
  if (test_point_in(ng+nx,ny/2+ng,nz/2+ng)) then 
     mystats(18)=p(ng+nx,ny/2+ng,nz/2+ng)
  else
     mystats(18)=0d0
  endif
  mystats(2:11) = mystats(2:11)/(xLength*yLength*zLength)
  mystats(1) = mystats(1)/(xLength*zLength*2.0)
  mystats(11) = mystats(11) ! /mystats(10)
  call MPI_ALLREDUCE(mystats(1), stats(1), nstatarray, MPI_DOUBLE_PRECISION, MPI_SUM, MPI_COMM_Domain, ierr)
  rho_ave = stats(6)
  p_ave = stats(7)
! This stops the code in case the average velocity (stats(2)) becomes NaN.
  if(stats(2).ne.stats(2)) call pariserror("********** Invalid flow rate **********")
  W_int = W_int + dt*(stats(2)-1d0)
  dpdx_stat = 1.0d0*(stats(2)-1d0) + 0.2d0*W_int
  !dpdz_stat = min(max(dpdz_stat,-2),2)
  ! time averages
  averages(1,:)=averages(1,:)+dt
  do k=ks,ke;  do j=js,je;  do i=is,ie
    Vdt = dx(i)*dy(j)*dz(k)*dt
    averages(2,j)=averages(2,j)+Vdt
    averages(3,j)=averages(3,j)+Vdt*(1.0-color(i,j,k))
    averages(4,j)=averages(4,j)+Vdt*u(i,j,k)
    averages(5,j)=averages(5,j)+Vdt*color(i,j,k)*u(i,j,k)
    averages(6,j)=averages(6,j)+Vdt*(u(i,j,k)+u(i-1,j,k))*(v(i,j,k)+v(i,j-1,k))/4.0
    averages(7,j)=averages(7,j)+Vdt*color(i,j,k)*(u(i,j,k)+u(i-1,j,k))*(v(i,j,k)+v(i,j-1,k))/4.0
    averages(8,j)=averages(8,j)+Vdt*(u(i,j,k)+u(i-1,j,k))*(w(i,j,k)+w(i,j,k-1))/4.0
    averages(9,j)=averages(9,j)+Vdt*color(i,j,k)*(u(i,j,k)+u(i-1,j,k))*(w(i,j,k)+w(i,j,k-1))/4.0
  enddo;  enddo;  enddo

  call MPI_REDUCE(averages, allaverages, 10*(Ny+2), MPI_DOUBLE_PRECISION, MPI_SUM, 0, MPI_COMM_Domain, ierr)

end subroutine calcStats
!=================================================================================================
subroutine momentumConvection()
  use module_flow
  use module_grid
  use module_tmpvar
  use module_IO

  if (AdvectionScheme=='QUICK') then
    call momentumConvectionQUICK(u,v,w,du,dv,dw)
  elseif (AdvectionScheme=='ENO') then
    call momentumConvectionENO(u,v,w,du,dv,dw)
  elseif (AdvectionScheme=='UpWind') then
    call momentumConvectionUpWind(u,v,w,du,dv,dw)
  elseif (AdvectionScheme=='Verstappen') then
    call momentumConvectionVerstappen(u,v,w,du,dv,dw)
  elseif (AdvectionScheme=='BCG') then
    call momentumConvectionBCG()
  else
     call pariserror("*** unknown convection scheme")
  endif

  if (out_mom .and. mod(itimestep-itimestepRestart,nout)==0) call write_mom_gnuplot(du,dv,dw,itimestep)

end subroutine momentumConvection
!=================================================================================================
! subroutine momentumConvectionENO
! calculates the convection terms in mumentum equation using ENO scheme
! and returns them in du, dv, dw
!-------------------------------------------------------------------------------------------------
subroutine momentumConvectionENO(u,v,w,du,dv,dw)
  use module_grid
  use module_tmpvar
  implicit none
  real(8), dimension(imin:imax,jmin:jmax,kmin:kmax), intent(in) :: u, v, w
  real(8), dimension(imin:imax,jmin:jmax,kmin:kmax), intent(inout) :: du, dv, dw
  real(8), external :: minabs
  integer :: i,j,k
!-------------------------------------ENO interpolation u-velocity--------------------------------
  do k=ks,ke; do j=js,je; do i=is,ieu+1
    if (u(i-1,j,k)+u(i,j,k)>0.0) then
      work(i,j,k,1)=u(i-1,j,k)+0.5*minabs((u(i,j,k)-u(i-1,j,k)),(u(i-1,j,k)-u(i-2,j,k)))
    else
      work(i,j,k,1)=u(i,j,k)-0.5*minabs((u(i+1,j,k)-u(i,j,k)),(u(i,j,k)-u(i-1,j,k)))
    endif
  enddo; enddo; enddo
  do k=ks,ke; do j=js-1,je; do i=is-1,ie
    if(v(i,j,k)+v(i+1,j,k)>0.0) then
      work(i,j,k,2)=u(i,j,k)+0.5*minabs((u(i,j+1,k)-u(i,j,k)),(u(i,j,k)-u(i,j-1,k)))
    else
      work(i,j,k,2)=u(i,j+1,k)-0.5*minabs((u(i,j+2,k)-u(i,j+1,k)),(u(i,j+1,k)-u(i,j,k)))
    endif
  enddo; enddo; enddo
  do k=ks-1,ke; do j=js,je; do i=is-1,ie
    if(w(i,j,k)+w(i+1,j,k)>0.0) then
      work(i,j,k,3)=u(i,j,k)+0.5*minabs((u(i,j,k+1)-u(i,j,k)),(u(i,j,k)-u(i,j,k-1)))
    else
      work(i,j,k,3)=u(i,j,k+1)-0.5*minabs((u(i,j,k+2)-u(i,j,k+1)),(u(i,j,k+1)-u(i,j,k)))
    endif
  enddo; enddo; enddo
  do k=ks,ke;  do j=js,je; do i=is,ieu
    du(i,j,k)= -0.5*((u(i,j  ,k  )+u(i+1,j  ,k  ))*work(i+1,j  ,k  ,1)- &
                    (u(i,j  ,k  )+u(i-1,j  ,k  ))*work(i  ,j  ,k  ,1))/dxh(i) &
              -0.5*((v(i,j  ,k  )+v(i+1,j  ,k  ))*work(i  ,j  ,k  ,2)-&
                    (v(i,j-1,k  )+v(i+1,j-1,k  ))*work(i  ,j-1,k  ,2))/dy(j)  &
              -0.5*((w(i,j  ,k  )+w(i+1,j  ,k  ))*work(i  ,j  ,k  ,3)-&
                    (w(i,j  ,k-1)+w(i+1,j  ,k-1))*work(i  ,j  ,k-1,3))/dz(k)
  enddo; enddo; enddo

!-------------------------------------ENO interpolation v-velocity--------------------------------
  do k=ks,ke; do j=js,jev+1; do i=is,ie
    if (v(i,j-1,k)+v(i,j,k)>0.0) then
      work(i,j,k,2)=v(i,j-1,k)+0.5*minabs((v(i,j,k)-v(i,j-1,k)),(v(i,j-1,k)-v(i,j-2,k)))
    else
      work(i,j,k,2)=v(i,j,k)-0.5*minabs((v(i,j+1,k)-v(i,j,k)),(v(i,j,k)-v(i,j-1,k)))
    endif
  enddo; enddo; enddo
  do k=ks,ke; do j=js-1,je; do i=is-1,ie
    if(u(i,j,k)+u(i,j+1,k)>0.0) then
      work(i,j,k,1)=v(i,j,k)+0.5*minabs((v(i+1,j,k)-v(i,j,k)),(v(i,j,k)-v(i-1,j,k)))
    else
      work(i,j,k,1)=v(i+1,j,k)-0.5*minabs((v(i+2,j,k)-v(i+1,j,k)),(v(i+1,j,k)-v(i,j,k)))
    endif
  enddo; enddo; enddo
  do k=ks-1,ke; do j=js-1,je; do i=is,ie
    if(w(i,j,k)+w(i,j+1,k)>0.0) then
      work(i,j,k,3)=v(i,j,k)+0.5*minabs((v(i,j,k+1)-v(i,j,k)),(v(i,j,k)-v(i,j,k-1)))
    else
      work(i,j,k,3)=v(i,j,k+1)-0.5*minabs((v(i,j,k+2)-v(i,j,k+1)),(v(i,j,k+1)-v(i,j,k)))
    endif
  enddo; enddo; enddo
  do k=ks,ke;  do j=js,jev; do i=is,ie
    dv(i,j,k)= &
              -0.5*((u(i  ,j,k  )+u(i  ,j+1,k  ))*work(i  ,j  ,k  ,1)-&
                    (u(i-1,j,k  )+u(i-1,j+1,k  ))*work(i-1,j  ,k  ,1))/dx(i)  &
              -0.5*((v(i  ,j,k  )+v(i  ,j+1,k  ))*work(i  ,j+1,k  ,2)-&
                    (v(i  ,j,k  )+v(i  ,j-1,k  ))*work(i  ,j  ,k  ,2))/dyh(j) &
              -0.5*((w(i  ,j,k  )+w(i  ,j+1,k  ))*work(i  ,j  ,k  ,3)-&
                    (w(i  ,j,k-1)+w(i  ,j+1,k-1))*work(i  ,j  ,k-1,3))/dz(k)
  enddo; enddo; enddo
!-------------------------------------ENO interpolation w-velocity--------------------------------
  do k=ks,kew+1; do j=js,je; do i=is,ie
    if (w(i,j,k-1)+w(i,j,k)>0.0) then
      work(i,j,k,3)=w(i,j,k-1)+0.5*minabs((w(i,j,k)-w(i,j,k-1)),(w(i,j,k-1)-w(i,j,k-2)))
    else
      work(i,j,k,3)=w(i,j,k)-0.5*minabs((w(i,j,k+1)-w(i,j,k)),(w(i,j,k)-w(i,j,k-1)))
    endif
  enddo; enddo; enddo
  do k=ks-1,ke; do j=js,je; do i=is-1,ie
    if(u(i,j,k)+u(i,j,k+1)>0.0) then
      work(i,j,k,1)=w(i,j,k)+0.5*minabs((w(i+1,j,k)-w(i,j,k)),(w(i,j,k)-w(i-1,j,k)))
    else
      work(i,j,k,1)=w(i+1,j,k)-0.5*minabs((w(i+2,j,k)-w(i+1,j,k)),(w(i+1,j,k)-w(i,j,k)))
    endif
  enddo; enddo; enddo
  do k=ks-1,ke; do j=js-1,je; do i=is,ie
    if(v(i,j,k)+v(i,j,k+1)>0.0) then
      work(i,j,k,2)=w(i,j,k)+0.5*minabs((w(i,j+1,k)-w(i,j,k)),(w(i,j,k)-w(i,j-1,k)))
    else
      work(i,j,k,2)=w(i,j+1,k)-0.5*minabs((w(i,j+2,k)-w(i,j+1,k)),(w(i,j+1,k)-w(i,j,k)))
    endif
  enddo; enddo; enddo
  do k=ks,kew;  do j=js,je; do i=is,ie
    dw(i,j,k)= & 
              -0.5*((u(i  ,j  ,k)+u(i  ,j  ,k+1))*work(i  ,j  ,k  ,1)-&
                    (u(i-1,j  ,k)+u(i-1,j  ,k+1))*work(i-1,j  ,k  ,1))/dx(i)  &
              -0.5*((v(i  ,j  ,k)+v(i  ,j  ,k+1))*work(i  ,j  ,k  ,2)-&
                    (v(i  ,j-1,k)+v(i  ,j-1,k+1))*work(i  ,j-1,k  ,2))/dy(j)  &
              -0.5*((w(i  ,j  ,k)+w(i  ,j  ,k+1))*work(i  ,j  ,k+1,3)-&
                    (w(i  ,j  ,k)+w(i  ,j  ,k-1))*work(i  ,j  ,k  ,3))/dzh(k) 
  enddo; enddo; enddo

end subroutine momentumConvectionENO
!=================================================================================================
! subroutine momentumConvectionUpWind
! calculates the convection terms in mumentum equation using UpWind scheme
! and returns them in du, dv, dw
!-------------------------------------------------------------------------------------------------
subroutine momentumConvectionUpWind(u,v,w,du,dv,dw)
  use module_grid
  use module_tmpvar
  implicit none
  real(8), dimension(imin:imax,jmin:jmax,kmin:kmax), intent(in) :: u, v, w
  real(8), dimension(imin:imax,jmin:jmax,kmin:kmax), intent(inout) :: du, dv, dw
  real(8), external :: minabs
  integer :: i,j,k
!-------------------------------------UpWind interpolation u-velocity--------------------------------
  do k=ks,ke; do j=js,je; do i=is,ieu+1
    if (u(i-1,j,k)+u(i,j,k)>0.0) then
      work(i,j,k,1)=u(i-1,j,k)
    else
      work(i,j,k,1)=u(i,j,k)
    endif
  enddo; enddo; enddo
  do k=ks,ke; do j=js-1,je; do i=is-1,ie
    if(v(i,j,k)+v(i+1,j,k)>0.0) then
      work(i,j,k,2)=u(i,j,k)
    else
      work(i,j,k,2)=u(i,j+1,k)
    endif
  enddo; enddo; enddo
  do k=ks-1,ke; do j=js,je; do i=is-1,ie
    if(w(i,j,k)+w(i+1,j,k)>0.0) then
      work(i,j,k,3)=u(i,j,k)
    else
      work(i,j,k,3)=u(i,j,k+1)
    endif
  enddo; enddo; enddo
  do k=ks,ke;  do j=js,je; do i=is,ieu
    du(i,j,k)= -0.5*((u(i,j ,k  )+u(i+1,j  ,k  ))*work(i+1,j  ,k  ,1)- &
                    (u(i,j  ,k  )+u(i-1,j  ,k  ))*work(i  ,j  ,k  ,1))/dxh(i) &
              -0.5*((v(i,j  ,k  )+v(i+1,j  ,k  ))*work(i  ,j  ,k  ,2)-&
                    (v(i,j-1,k  )+v(i+1,j-1,k  ))*work(i  ,j-1,k  ,2))/dy(j)  &
              -0.5*((w(i,j  ,k  )+w(i+1,j  ,k  ))*work(i  ,j  ,k  ,3)-&
                    (w(i,j  ,k-1)+w(i+1,j  ,k-1))*work(i  ,j  ,k-1,3))/dz(k)
  enddo; enddo; enddo

!-------------------------------------UpWind interpolation v-velocity--------------------------------
  do k=ks,ke; do j=js,jev+1; do i=is,ie
    if (v(i,j-1,k)+v(i,j,k)>0.0) then
      work(i,j,k,2)=v(i,j-1,k)
    else
      work(i,j,k,2)=v(i,j,k)
    endif
  enddo; enddo; enddo
  do k=ks,ke; do j=js-1,je; do i=is-1,ie
    if(u(i,j,k)+u(i,j+1,k)>0.0) then
      work(i,j,k,1)=v(i,j,k)
    else
      work(i,j,k,1)=v(i+1,j,k)
    endif
  enddo; enddo; enddo
  do k=ks-1,ke; do j=js-1,je; do i=is,ie
    if(w(i,j,k)+w(i,j+1,k)>0.0) then
      work(i,j,k,3)=v(i,j,k)
    else
      work(i,j,k,3)=v(i,j,k+1)
    endif
  enddo; enddo; enddo
  do k=ks,ke;  do j=js,jev; do i=is,ie
    dv(i,j,k)= &
              -0.5*((u(i  ,j,k  )+u(i  ,j+1,k  ))*work(i  ,j  ,k  ,1)-&
                    (u(i-1,j,k  )+u(i-1,j+1,k  ))*work(i-1,j  ,k  ,1))/dx(i)  &
              -0.5*((v(i  ,j,k  )+v(i  ,j+1,k  ))*work(i  ,j+1,k  ,2)-&
                    (v(i  ,j,k  )+v(i  ,j-1,k  ))*work(i  ,j  ,k  ,2))/dyh(j) &
              -0.5*((w(i  ,j,k  )+w(i  ,j+1,k  ))*work(i  ,j  ,k  ,3)-&
                    (w(i  ,j,k-1)+w(i  ,j+1,k-1))*work(i  ,j  ,k-1,3))/dz(k)
  enddo; enddo; enddo
!-------------------------------------UpWind interpolation w-velocity--------------------------------
  do k=ks,kew+1; do j=js,je; do i=is,ie
    if (w(i,j,k-1)+w(i,j,k)>0.0) then
      work(i,j,k,3)=w(i,j,k-1)
    else
      work(i,j,k,3)=w(i,j,k)
    endif
  enddo; enddo; enddo
  do k=ks-1,ke; do j=js,je; do i=is-1,ie
    if(u(i,j,k)+u(i,j,k+1)>0.0) then
      work(i,j,k,1)=w(i,j,k)
    else
      work(i,j,k,1)=w(i+1,j,k)
    endif
  enddo; enddo; enddo
  do k=ks-1,ke; do j=js-1,je; do i=is,ie
    if(v(i,j,k)+v(i,j,k+1)>0.0) then
      work(i,j,k,2)=w(i,j,k)
    else
      work(i,j,k,2)=w(i,j+1,k)
    endif
  enddo; enddo; enddo
  do k=ks,kew;  do j=js,je; do i=is,ie
    dw(i,j,k)= & 
              -0.5*((u(i  ,j  ,k)+u(i  ,j  ,k+1))*work(i  ,j  ,k  ,1)-&
                    (u(i-1,j  ,k)+u(i-1,j  ,k+1))*work(i-1,j  ,k  ,1))/dx(i)  &
              -0.5*((v(i  ,j  ,k)+v(i  ,j  ,k+1))*work(i  ,j  ,k  ,2)-&
                    (v(i  ,j-1,k)+v(i  ,j-1,k+1))*work(i  ,j-1,k  ,2))/dy(j)  &
              -0.5*((w(i  ,j  ,k)+w(i  ,j  ,k+1))*work(i  ,j  ,k+1,3)-&
                    (w(i  ,j  ,k)+w(i  ,j  ,k-1))*work(i  ,j  ,k  ,3))/dzh(k) 
  enddo; enddo; enddo
end subroutine momentumConvectionUpWind
!=================================================================================================
! subroutine momentumConvection
! calculates the convection terms in the momentum equations using a QUICK scheme
! and returns them in du, dv, dw
!-------------------------------------------------------------------------------------------------
subroutine momentumConvectionQUICK(u,v,w,du,dv,dw)
  use module_grid
  use module_tmpvar
  implicit none
  real(8), dimension(imin:imax,jmin:jmax,kmin:kmax), intent(in) :: u, v, w
  real(8), dimension(imin:imax,jmin:jmax,kmin:kmax), intent(inout) :: du, dv, dw
  real(8), external :: minabs
  integer :: i,j,k
!  real(8), external :: inter
!  real(8) :: y00,y11,y22
!-----------------------------------QUICK interpolation u-velocity--------------------------------
  do k=ks,ke; do j=js,je; do i=is,ieu+1
    if (u(i-1,j,k)+u(i,j,k)>0.0) then
      work(i,j,k,1) = inter(xh(i-1),xh(i),xh(i-2),x(i),u(i-1,j,k),u(i,j,k),u(i-2,j,k))  !
    else
      work(i,j,k,1) = inter(xh(i),xh(i-1),xh(i+1),x(i),u(i,j,k),u(i-1,j,k),u(i+1,j,k))  !
    endif
  enddo; enddo; enddo
  do k=ks,ke; do j=js-1,je; do i=is-1,ie
    if(v(i,j,k)+v(i+1,j,k)>0.0) then
      work(i,j,k,2) = inter(y(j),y(j+1),y(j-1),yh(j),u(i,j,k),u(i,j+1,k),u(i,j-1,k))  !
    else
      work(i,j,k,2) = inter(y(j+1),y(j),y(j+2),yh(j),u(i,j+1,k),u(i,j,k),u(i,j+2,k))  !
    endif
  enddo; enddo; enddo
  do k=ks-1,ke; do j=js,je; do i=is-1,ie
    if(w(i,j,k)+w(i+1,j,k)>0.0) then
      work(i,j,k,3) = inter(z(k),z(k+1),z(k-1),zh(k),u(i,j,k),u(i,j,k+1),u(i,j,k-1))  !
    else
      work(i,j,k,3) = inter(z(k+1),z(k),z(k+2),zh(k),u(i,j,k+1),u(i,j,k),u(i,j,k+2))  !
    endif
  enddo; enddo; enddo
  do k=ks,ke;  do j=js,je; do i=is,ieu
      du(i,j,k)= &
      - ( work(i+1,j  ,k  ,1)**2 - work(i  ,j  ,k  ,1)**2 )/dxh(i) &
      -0.5*((v(i,j  ,k  )+v(i+1,j  ,k  ))*work(i  ,j  ,k  ,2)-        &
      (v(i,j-1,k  )+v(i+1,j-1,k  ))*work(i  ,j-1,k  ,2))/dy(j)  &
      -0.5*((w(i,j  ,k  )+w(i+1,j  ,k  ))*work(i  ,j  ,k  ,3)-        &
      (w(i,j  ,k-1)+w(i+1,j  ,k-1))*work(i  ,j  ,k-1,3))/dz(k)
  enddo; enddo; enddo
  !-----------------------------------QUICK interpolation v-velocity--------------------------------
  do k=ks,ke; do j=js,jev+1; do i=is,ie
    if (v(i,j-1,k)+v(i,j,k)>0.0) then
      work(i,j,k,2) = inter(yh(j-1),yh(j),yh(j-2),y(j),v(i,j-1,k),v(i,j,k),v(i,j-2,k))  !
    else
      work(i,j,k,2) = inter(yh(j),yh(j-1),yh(j+1),y(j),v(i,j,k),v(i,j-1,k),v(i,j+1,k))  !
    endif
  enddo; enddo; enddo
  do k=ks,ke; do j=js-1,je; do i=is-1,ie
    if(u(i,j,k)+u(i,j+1,k)>0.0) then
      work(i,j,k,1) = inter(x(i),x(i+1),x(i-1),xh(i),v(i,j,k),v(i+1,j,k),v(i-1,j,k))  !
    else
      work(i,j,k,1) = inter(x(i+1),x(i),x(i+2),xh(i),v(i+1,j,k),v(i,j,k),v(i+2,j,k))  !
    endif
  enddo; enddo; enddo
  do k=ks-1,ke; do j=js-1,je; do i=is,ie
    if(w(i,j,k)+w(i,j+1,k)>0.0) then
      work(i,j,k,3) = inter(z(k),z(k+1),z(k-1),zh(k),v(i,j,k),v(i,j,k+1),v(i,j,k-1))  !
    else
      work(i,j,k,3) = inter(z(k+1),z(k),z(k+2),zh(k),v(i,j,k+1),v(i,j,k),v(i,j,k+2))  !
    endif
  enddo; enddo; enddo
  do k=ks,ke;  do j=js,jev; do i=is,ie
     dv(i,j,k)= &
      -0.5*((u(i  ,j,k  )+u(i  ,j+1,k  ))*work(i  ,j  ,k  ,1)-        &
      (u(i-1,j,k  )+u(i-1,j+1,k  ))*work(i-1,j  ,k  ,1))/dx(i)  &
      -   ( work(i  ,j+1,k  ,2)**2 - work(i  ,j  ,k  ,2)**2 )/dyh(j) &
      -0.5*((w(i  ,j,k  )+w(i  ,j+1,k  ))*work(i  ,j  ,k  ,3)-        &
      (w(i  ,j,k-1)+w(i  ,j+1,k-1))*work(i  ,j  ,k-1,3))/dz(k)
  enddo; enddo; enddo
  !-----------------------------------QUICK interpolation w-velocity--------------------------------
  do k=ks,kew+1; do j=js,je; do i=is,ie
    if (w(i,j,k-1)+w(i,j,k)>0.0) then
      work(i,j,k,3) = inter(zh(k-1),zh(k),zh(k-2),z(k),w(i,j,k-1),w(i,j,k),w(i,j,k-2))  !
    else
      work(i,j,k,3) = inter(zh(k),zh(k-1),zh(k+1),z(k),w(i,j,k),w(i,j,k-1),w(i,j,k+1))  !
    endif
  enddo; enddo; enddo
  do k=ks-1,ke; do j=js,je; do i=is-1,ie
    if(u(i,j,k)+u(i,j,k+1)>0.0) then
      work(i,j,k,1) = inter(x(i),x(i+1),x(i-1),xh(i),w(i,j,k),w(i+1,j,k),w(i-1,j,k))  !
    else
      work(i,j,k,1) = inter(x(i+1),x(i),x(i+2),xh(i),w(i+1,j,k),w(i,j,k),w(i+2,j,k))  !
    endif
  enddo; enddo; enddo
  do k=ks-1,ke; do j=js-1,je; do i=is,ie
    if(v(i,j,k)+v(i,j,k+1)>0.0) then
      work(i,j,k,2) = inter(y(j),y(j+1),y(j-1),yh(j),w(i,j,k),w(i,j+1,k),w(i,j-1,k))  !
    else
      work(i,j,k,2) = inter(y(j+1),y(j),y(j+2),yh(j),w(i,j+1,k),w(i,j,k),w(i,j+2,k))  !
    endif
  enddo; enddo; enddo

  do k=ks,kew;  do j=js,je; do i=is,ie
      dw(i,j,k)= &
      -0.5*((u(i  ,j  ,k)+u(i  ,j  ,k+1))*work(i  ,j  ,k  ,1)-        &
      (u(i-1,j  ,k)+u(i-1,j  ,k+1))*work(i-1,j  ,k  ,1))/dx(i)  &
      -0.5*((v(i  ,j  ,k)+v(i  ,j  ,k+1))*work(i  ,j  ,k  ,2)-        &
      (v(i  ,j-1,k)+v(i  ,j-1,k+1))*work(i  ,j-1,k  ,2))/dy(j)  &
      -( work(i  ,j  ,k+1,3)**2 - work(i  ,j  ,k  ,3)**2 )/dzh(k)
  enddo; enddo; enddo
contains
!-------------------------------------------------------------------------------------------------
real(8) function inter(x0,x1,x2,x,y0,y1,y2)
  implicit none
  real(8) x0,x1,x2,x,y0,y1,y2,k,xi,a,b
  ! Interpolation at the cell face (x)
  !     |       |   u>  |
  !     x2      x0  x   x1

  	!QUICK
    xi = (x-x0)/(x1-x0)
    k = (x2-x0)/(x1-x0)
    a = (y2-k**2*y1+(k**2-1.0)*y0)/k/(1.0-k)
    b = (y2-k   *y1+(k   -1.0)*y0)/k/(k-1.0)
    inter = y0+a*xi+b*xi**2
  !  QUICK uniform
  !  inter = 0.75*y0 +0.375*y1 -0.125*y2

    !CD
  !  inter = 0.5*(y0+y1) !+0.125*(2.0*y0-y1-y2)

    !ENO 2nd order
  !  inter = y0 + 0.5*minabs(y1-y0,y0-y2)
end function inter
!-------------------------------------------------------------------------------------------------
end subroutine momentumConvectionQUICK

!=================================================================================================
! subroutine momentumConvectionVerstappen
! calculates the convection terms in the momentum equations using the Verstappen
! symmetric scheme
!-------------------------------------------------------------------------------------------------
subroutine momentumConvectionVerstappen (u,v,w,du,dv,dw)
  use module_grid
  use module_tmpvar
  implicit none
  real(8), dimension(imin:imax,jmin:jmax,kmin:kmax), intent(in) :: u, v, w
  real(8), dimension(imin:imax,jmin:jmax,kmin:kmax), intent(inout) :: du, dv, dw
  real(8), external :: minabs
  integer :: i,j,k

  !fixme: This only works for regular uniform grids
  do k=ks,ke;  do j=js,je; do i=is,ieu
      work(i,j,k,1) = (u(i,j,k) + u(i+1,j,k))*u(i+1,j,k) &
                     -(u(i,j,k) + u(i-1,j,k))*u(i-1,j,k) &
                     +(v(i,j,k) + v(i+1,j,k))*u(i,j+1,k) &
                     -(v(i,j-1,k) + v(i+1,j-1,k))*u(i,j-1,k) &
                     +(w(i,j,k) + w(i+1,j,k))*u(i,j,k+1) &
                     -(w(i,j,k-1) + w(i+1,j,k-1))*u(i,j,k-1)
      work(i,j,k,1) = work(i,j,k,1)*0.25d0
  enddo; enddo; enddo

  do k=ks,ke;  do j=js,je; do i=is,ieu
      du(i,j,k)= - work(i,j,k,1)/dxh(i)
  enddo; enddo; enddo

  do k=ks,ke;  do j=js,jev; do i=is,ie
       work(i,j,k,1) = (v(i,j,k) + v(i,j+1,k))*v(i,j+1,k) &
                     -(v(i,j,k) + v(i,j-1,k))*v(i,j-1,k) &
                     +(u(i,j,k) + u(i,j+1,k))*v(i+1,j,k) &
                     -(u(i-1,j,k) + u(i-1,j+1,k))*v(i-1,j,k) &
                     +(w(i,j,k) + w(i,j+1,k))*v(i,j,k+1) &
                     -(w(i,j,k-1) + w(i,j+1,k-1))*v(i,j,k-1)
      work(i,j,k,1) = work(i,j,k,1)*0.25d0
  enddo; enddo; enddo

  do k=ks,ke;  do j=js,jev; do i=is,ie
     dv(i,j,k)= - work(i,j,k,1)/dxh(i)
  enddo; enddo; enddo

  do k=ks,kew;  do j=js,je; do i=is,ie
      work(i,j,k,1) = (w(i,j,k) + w(i,j,k+1))*w(i,j,k+1) &
                     -(w(i,j,k) + w(i,j,k-1))*w(i,j,k-1) &
                     +(u(i,j,k) + u(i,j,k+1))*w(i+1,j,k) &
                     -(u(i-1,j,k) + u(i-1,j,k+1))*w(i-1,j,k) &
                     +(v(i,j,k) + v(i,j+1,k))*w(i,j,k+1) &
                     -(v(i,j,k-1) + v(i,j+1,k-1))*w(i,j,k-1)
      work(i,j,k,1) = work(i,j,k,1)*0.25d0
  enddo; enddo; enddo

  do k=ks,kew;  do j=js,je; do i=is,ie
      dw(i,j,k)= - work(i,j,k,1)/dxh(i)
  enddo; enddo; enddo

end subroutine momentumConvectionVerstappen
!=======================================================================================
! subroutine momentumConvectionBCG
! calculates the convection terms in the momentum equations using a QUICK scheme
! and returns them in du, dv, dw
!-------------------------------------------------------------------------------------------------
subroutine momentumConvectionBCG()
  use module_grid
  use module_tmpvar
  use module_BC
  use module_poisson
  use module_flow
  use module_VOF
  use module_2phase
  use module_surface_tension

  implicit none
  include 'mpif.h'
  real(8), external :: minabs
  real(8) :: grad, unorm, uc, fxx, fyy, fzz,usign
  integer :: i,j,k, iaux,ierr
  integer :: req(48),sta(MPI_STATUS_SIZE,48)
 
  !prediction stage
  call predict_velocity(u,du,1,dt,u,v,w,work(:,:,:,1))
  call predict_velocity(v,dv,2,dt,v,u,w,work(:,:,:,2))
  call predict_velocity(w,dw,3,dt,w,u,v,work(:,:,:,3))

  call SetVelocityBC(work(:,:,:,1),work(:,:,:,2),work(:,:,:,3),umask,vmask,wmask,time)
  call do_ghost_vector(work(:,:,:,1),work(:,:,:,2),work(:,:,:,3))
  !-----------------------------------------PROJECTION STEP-----------------------------------------
  tmp = p
  call SetPressureBC(umask,vmask,wmask)
  call SetupPoisson(work(:,:,:,1),work(:,:,:,2),work(:,:,:,3), &
  umask,vmask,wmask,rho,dt,A,tmp,cvof,n1,n2,n3,VolumeSource)
  ! (div u)*dt < epsilon => div u < epsilon/dt => maxresidual : maxerror/dt 
  if(HYPRE)then
    call poi_solve(A,tmp(is:ie,js:je,ks:ke),maxError/dt,maxit,it)
    call ghost_x(tmp,1,req(1:4 ))
    call ghost_y(tmp,1,req(5:8 ))
    call ghost_z(tmp,1,req(9:12)) 
    call MPI_WAITALL(12,req(1:12),sta(:,1:12),ierr)
  else
    call NewSolver(A,tmp,maxError/dt,beta,maxit,it,ierr)
  endif
  if (.not.FreeSurface) then
    do k=ks,ke;  do j=js,je; do i=is,ieu    ! CORRECT THE u-velocity 
      work(i,j,k,1)=work(i,j,k,1)-dt*(2.0*umask(i,j,k)/dxh(i))*(tmp(i+1,j,k)-tmp(i,j,k))/ &
                                     (rho(i+1,j,k)+rho(i,j,k))
    enddo; enddo; enddo

    do k=ks,ke;  do j=js,jev; do i=is,ie    ! CORRECT THE v-velocity
      work(i,j,k,2)=work(i,j,k,2)-dt*(2.0*vmask(i,j,k)/dyh(j))*(tmp(i,j+1,k)-tmp(i,j,k))/ &
                                      (rho(i,j+1,k)+rho(i,j,k))
    enddo; enddo; enddo

    do k=ks,kew;  do j=js,je; do i=is,ie   ! CORRECT THE w-velocity
      work(i,j,k,3)=work(i,j,k,3)-dt*(2.0*wmask(i,j,k)/dzh(k))*(tmp(i,j,k+1)-tmp(i,j,k))/ &
                                     (rho(i,j,k+1)+rho(i,j,k))
    enddo; enddo; enddo
  else
    do k=ks,ke;  do j=js,je; do i=is,ieu    ! CORRECT THE u-velocity 
      work(i,j,k,1)=work(i,j,k,1)-dt*(2.0*umask(i,j,k)/(dxh(i)-x_mod(i,j,k)))*(tmp(i+1,j,k)-tmp(i,j,k))/ &
                                      (rho(i+1,j,k)+rho(i,j,k))
    enddo; enddo; enddo

    do k=ks,ke;  do j=js,jev; do i=is,ie    ! CORRECT THE v-velocity
      work(i,j,k,2)=work(i,j,k,2)-dt*(2.0*vmask(i,j,k)/(dyh(j)-y_mod(i,j,k)))*(tmp(i,j+1,k)-tmp(i,j,k))/ &
                                      (rho(i,j+1,k)+rho(i,j,k))
    enddo; enddo; enddo

    do k=ks,kew;  do j=js,je; do i=is,ie   ! CORRECT THE w-velocity
      work(i,j,k,3)=work(i,j,k,3)-dt*(2.0*wmask(i,j,k)/(dzh(k)-z_mod(i,j,k)))*(tmp(i,j,k+1)-tmp(i,j,k))/ &
                                      (rho(i,j,k+1)+rho(i,j,k))
    enddo; enddo; enddo
  endif 

  call do_ghost_vector(work(:,:,:,1),work(:,:,:,2),work(:,:,:,3))

  !advection step
  call predict_velocity(u,du,1,dt,work(:,:,:,1),work(:,:,:,2),work(:,:,:,3),tmp)
  du = tmp
  call predict_velocity(v,dv,2,dt,work(:,:,:,2),work(:,:,:,1),work(:,:,:,3),tmp)
  dv = tmp
  call predict_velocity(w,dw,3,dt,work(:,:,:,3),work(:,:,:,1),work(:,:,:,2),tmp)
  dw = tmp

  !check: required?
  call do_ghost_vector(du,dv,dw)

  work(:,:,:,1) = du
  work(:,:,:,2) = dv
  work(:,:,:,3) = dw

  !cell centered fluxes
  do k=ks-1,ke+1
    do j=js-1,je+1
      do i=is-1,ie+1
        du(i,j,k) = -0.5d0*(work(i,j,k,1)*work(i,j,k,1) - &
                    work(i-1,j,k,1)*work(i-1,j,k,1))/dxh(i) &
                  - 0.5d0*(work(i,j,k,2)+work(i,j-1,k,2))*  &
                    (work(i,j,k,1)-work(i,j-1,k,1))/dyh(j)  &
                  - 0.5d0*(work(i,j,k,3)+work(i,j,k-1,3))*  &
                    (work(i,j,k,1)-work(i,j,k-1,1))/dzh(j)  
        dv(i,j,k) = -0.5d0*(work(i,j,k,2)*work(i,j,k,2) - &
                    work(i-1,j,k,2)*work(i-1,j,k,2))/dyh(i) &
                  - 0.5d0*(work(i,j,k,1)+work(i,j-1,k,1))*  &
                    (work(i,j,k,2)-work(i,j-1,k,2))/dxh(j)  &
                  - 0.5d0*(work(i,j,k,3)+work(i,j,k-1,3))*  &
                    (work(i,j,k,2)-work(i,j,k-1,2))/dzh(j)  
        dw(i,j,k) = -0.5d0*(work(i,j,k,3)*work(i,j,k,3) - &
                    work(i-1,j,k,3)*work(i-1,j,k,3))/dzh(i) &
                  - 0.5d0*(work(i,j,k,2)+work(i,j-1,k,2))*  &
                    (work(i,j,k,3)-work(i,j-1,k,3))/dyh(j)  &
                  - 0.5d0*(work(i,j,k,1)+work(i,j,k-1,1))*  &
                    (work(i,j,k,3)-work(i,j,k-1,3))/dxh(j)  
      enddo
    enddo
  enddo

  call do_ghost_vector(du,dv,dw)

  !reinterpolating at face centers
  work(:,:,:,1) = du
  work(:,:,:,2) = dv
  work(:,:,:,3) = dw

  do k=ks-1,ke+1
    do j=js-1,je+1
      do i=is-1,ie+1
        du(i,j,k) = 0.5d0*(work(i,j,k,1) + work(i+1,j,k,1)) 
        dv(i,j,k) = 0.5d0*(work(i,j,k,2) + work(i+1,j,k,2)) 
        dw(i,j,k) = 0.5d0*(work(i,j,k,3) + work(i+1,j,k,3)) 
      enddo
    enddo
  enddo

  call do_ghost_vector(du,dv,dw)

end subroutine momentumConvectionBCG

subroutine predict_velocity(u,du,d,dt,upred,vpred,wpred,unew)

  use module_grid
  implicit none
  
  integer i,j,k,d,iaux,jaux,kaux
  integer i0,j0,k0
  integer onex, oney, onez

  REAL (8), DIMENSION(imin:imax,jmin:jmax,kmin:kmax), INTENT(IN) :: u, du,upred, vpred, wpred
  REAL (8), DIMENSION(imin:imax,jmin:jmax,kmin:kmax), INTENT(INOUT) :: unew
  real(8) :: dt, uc, unorm, usign, grad, dxcell,dxcell2
  real(8) :: fyy, fzz
  
  call init_i0j0k0 (d,i0,j0,k0)
  iaux = 0; jaux = 0; kaux = 0
  onex = 0; oney = 0; onez = 0

  do k=ks-1,ke+1
    do j=js-1,je+1
      do i=is-1,ie+1

        uc    = (u(i,j,k) + u(i-i0,j-j0,k-k0))*0.5d0
        usign = sign(1.d0,uc)

        if (d.eq.1) then
          iaux  = -(usign + 1)/2
          onex  = 1
          dxcell = dxh(i)
          dxcell2 = dxh(i+iaux)
        elseif (d.eq.2) then
          jaux  = -(usign + 1)/2
          oney  = 1
          dxcell = dyh(j)
          dxcell2 = dyh(j+jaux)
        else 
          kaux  = -(usign + 1)/2
          onez  = 1
          dxcell = dzh(k)
          dxcell2 = dzh(k+kaux)
        endif
        
        unorm = uc*dt/dxcell

        grad  = (upred(i+iaux,j+jaux,k+kaux) &
                 -upred(i+iaux-onex,j+jaux-oney,k+kaux-onez))/dxcell2

        if (vpred(i+iaux,j+jaux,k+kaux).lt.0.d0) then 
          fyy = upred(i+iaux+onex,j+jaux+oney,k+kaux+onez) &
              - upred(i+iaux,j+jaux,k+kaux)
        else
          fyy = upred(i+iaux,j+jaux,k+kaux) &
              - upred(i+iaux-onex,j+jaux-oney,k+kaux-onez)
        endif

        if (wpred(i+iaux,j+jaux,k+kaux).lt.0.d0) then 
          fzz = upred(i+iaux+onex,j+jaux+oney,k+kaux+onez) &
              - upred(i+iaux,j+jaux,k+kaux)
        else
          fzz = upred(i+iaux,j+jaux,k+kaux) &
              - upred(i+iaux-onex,j+jaux-oney,k+kaux-onez)
        endif
!fixme: check the dxcell values in the traverse terms
        unew(i,j,k) = (u(i+iaux,j+jaux,k+kaux) &
                     + u(i+iaux-onex,j+jaux-oney,k+kaux-onez))*0.5d0 &
        + du(i+iaux,j+jaux,k+kaux)*dt/2.d0 &
        + usign*min(1.d0,1.d0-usign*unorm)*grad*dxcell/2.d0 &
        - dt*vpred(i+iaux,j+jaux,k+kaux)*fyy/(2.d0*dxcell) &
        - dt*wpred(i+iaux,j+kaux,k+kaux)*fzz/(2.d0*dxcell) 

      enddo
    enddo
  enddo

end subroutine predict_velocity

subroutine init_i0j0k0 (d,i0,j0,k0)
  integer d,i0,j0,k0

  i0=0;j0=0;k0=0

  if (d.eq.1) then
    i0=1
  elseif (d.eq.2) then
    j0=1
  elseif (d.eq.3) then
    k0=1
  endif

end subroutine init_i0j0k0
!=================================================================================================
! Calculates the diffusion terms in the momentum equation and adds them to du,dv,dw
!-------------------------------------------------------------------------------------------------
subroutine momentumDiffusion(u,v,w,rho,mu,du,dv,dw)
  use module_grid
  implicit none
  real(8), dimension(imin:imax,jmin:jmax,kmin:kmax), intent(in) :: u, v, w, rho, mu
  real(8), dimension(imin:imax,jmin:jmax,kmin:kmax), intent(out) :: du, dv, dw
  integer :: i,j,k
!-------------------------------------PREDICTED u-velocity-DIFFUSION------------------------------
  do k=ks,ke; do j=js,je; do i=is,ieu
    du(i,j,k)= du(i,j,k)+(   &
     +(1./dy(j))*( 0.25*(mu(i,j,k)+mu(i+1,j,k)+mu(i+1,j+1,k)+mu(i,j+1,k))*               &
         ( (1./dxh(i))*(v(i+1,j,k)-v(i,j,k)) ) -    &
                   0.25*(mu(i,j,k)+mu(i+1,j,k)+mu(i+1,j-1,k)+mu(i,j-1,k))*               &
         ( (1./dxh(i))*(v(i+1,j-1,k)-v(i,j-1,k))))  &
     +(1./dz(k))*( 0.25*(mu(i,j,k)+mu(i+1,j,k)+mu(i+1,j,k+1)+mu(i,j,k+1))*               &
         ( (1./dxh(i))*(w(i+1,j,k)-w(i,j,k)) ) -    &
                   0.25*(mu(i,j,k)+mu(i+1,j,k)+mu(i+1,j,k-1)+mu(i,j,k-1))*               &
         ( (1./dxh(i))*(w(i+1,j,k-1)-w(i,j,k-1))))  &
                          )/(0.5*(rho(i+1,j,k)+rho(i,j,k))   )
  enddo; enddo; enddo
!-------------------------------------PREDICTED v-velocity-DIFFUSION------------------------------
  do k=ks,ke; do j=js,jev; do i=is,ie
    dv(i,j,k)= dv(i,j,k)+(   &
      (1./dx(i))*( 0.25*(mu(i,j,k)+mu(i+1,j,k)+mu(i+1,j+1,k)+mu(i,j+1,k))*               &
         ( (1./dyh(j))*(u(i,j+1,k)-u(i,j,k)) ) -      &
                   0.25*(mu(i,j,k)+mu(i,j+1,k)+mu(i-1,j+1,k)+mu(i-1,j,k))*               &
         ( (1./dyh(j))*(u(i-1,j+1,k)-u(i-1,j,k)) ) )  &
     +(1./dz(k))*( 0.25*(mu(i,j,k)+mu(i,j+1,k)+mu(i,j+1,k+1)+mu(i,j,k+1))*               &
         ( (1./dyh(j))*(w(i,j+1,k)-w(i,j,k)) ) -    &
                   0.25*(mu(i,j,k)+mu(i,j+1,k)+mu(i,j+1,k-1)+mu(i,j,k-1))*               &
         ( (1./dyh(j))*(w(i,j+1,k-1)-w(i,j,k-1)) ) )  &
                           )/(0.5*(rho(i,j+1,k)+rho(i,j,k))   )
  enddo; enddo; enddo
!-------------------------------------PREDICTED w-velocity-DIFFUSION------------------------------
  do k=ks,kew; do j=js,je; do i=is,ie
    dw(i,j,k)= dw(i,j,k)+(   &
      (1./dx(i))*( 0.25*(mu(i,j,k)+mu(i+1,j,k)+mu(i+1,j,k+1)+mu(i,j,k+1))*               &
         ((1./dzh(k))*(u(i,j,k+1)-u(i,j,k)) ) -      &
                   0.25*(mu(i,j,k)+mu(i-1,j,k)+mu(i-1,j,k+1)+mu(i,j,k+1))*               &
         ((1./dzh(k))*(u(i-1,j,k+1)-u(i-1,j,k)) ) )  &
     +(1./dy(j))*( 0.25*(mu(i,j,k)+mu(i,j+1,k)+mu(i,j+1,k+1)+mu(i,j,k+1))*               &
         ((1./dzh(k))*(v(i,j,k+1)-v(i,j,k)) ) -      &
                   0.25*(mu(i,j,k)+mu(i,j-1,k)+mu(i,j-1,k+1)+mu(i,j,k+1))*               &
         ((1./dzh(k))*(v(i,j-1,k+1)-v(i,j-1,k)) ) )  &
                          )/(0.5*(rho(i,j,k+1)+rho(i,j,k))   )
  enddo; enddo; enddo
end subroutine momentumDiffusion
!=================================================================================================
!=================================================================================================
! Calculates the diffusion terms explicitly in the momentum equation and adds them to du,dv,dw
!-------------------------------------------------------------------------------------------------
subroutine explicitMomDiff(u,v,w,rho,mu,du,dv,dw)
  use module_grid
  implicit none
  real(8), dimension(imin:imax,jmin:jmax,kmin:kmax), intent(in) :: u, v, w, rho, mu
  real(8), dimension(imin:imax,jmin:jmax,kmin:kmax), intent(out) :: du, dv, dw
  integer :: i,j,k
!-------------------------------------PREDICTED u-velocity-DIFFUSION------------------------------
  do k=ks,ke; do j=js,je; do i=is,ieu
    du(i,j,k)= du(i,j,k)+(   &
      (1./dxh(i))*2.*(   mu(i+1,j,k)*(1./dx(i+1))*(u(i+1,j,k)-u(i,j,k)) -                &
                         mu(i,j,k)  *(1./dx(i))  *(u(i,j,k)-u(i-1,j,k)) )                &
     +(1./dy(j))*( 0.25*(mu(i,j,k)+mu(i+1,j,k)+mu(i+1,j+1,k)+mu(i,j+1,k))*               &
         ((1./dyh(j))*  (u(i,j+1,k)-u(i,j,k)) + (1./dxh(i))*(v(i+1,j,k)-v(i,j,k)) ) -    &
                   0.25*(mu(i,j,k)+mu(i+1,j,k)+mu(i+1,j-1,k)+mu(i,j-1,k))*               &
         ((1./dyh(j-1))*(u(i,j,k)-u(i,j-1,k)) + (1./dxh(i))*(v(i+1,j-1,k)-v(i,j-1,k))))  &
     +(1./dz(k))*( 0.25*(mu(i,j,k)+mu(i+1,j,k)+mu(i+1,j,k+1)+mu(i,j,k+1))*               &
         ((1./dzh(k))*  (u(i,j,k+1)-u(i,j,k)) + (1./dxh(i))*(w(i+1,j,k)-w(i,j,k)) ) -    &
                   0.25*(mu(i,j,k)+mu(i+1,j,k)+mu(i+1,j,k-1)+mu(i,j,k-1))*               &
         ((1./dzh(k-1))*(u(i,j,k)-u(i,j,k-1)) + (1./dxh(i))*(w(i+1,j,k-1)-w(i,j,k-1))))  &
                          )/(0.5*(rho(i+1,j,k)+rho(i,j,k))   )
  enddo; enddo; enddo
!-------------------------------------PREDICTED v-velocity-DIFFUSION------------------------------
  do k=ks,ke; do j=js,jev; do i=is,ie
    dv(i,j,k)= dv(i,j,k)+(   &
      (1./dx(i))*( 0.25*(mu(i,j,k)+mu(i+1,j,k)+mu(i+1,j+1,k)+mu(i,j+1,k))*               &
         ((1./dyh(j))*(u(i,j+1,k)-u(i,j,k)) + (1./dxh(i))*(v(i+1,j,k)-v(i,j,k)) ) -      &
                   0.25*(mu(i,j,k)+mu(i,j+1,k)+mu(i-1,j+1,k)+mu(i-1,j,k))*               &
         ((1./dyh(j))*(u(i-1,j+1,k)-u(i-1,j,k))+ (1./dxh(i-1))*(v(i,j,k)- v(i-1,j,k))))  &
     +(1./dyh(j))*2.*(   mu(i,j+1,k)*(1./dy(j+1))*(v(i,j+1,k)-v(i,j,k)) -                &
                         mu(i,j,k)  *(1./dy(j))*  (v(i,j,k)-v(i,j-1,k)) )                &
     +(1./dz(k))*( 0.25*(mu(i,j,k)+mu(i,j+1,k)+mu(i,j+1,k+1)+mu(i,j,k+1))*               &
         ((1./dzh(k))*  (v(i,j,k+1)-v(i,j,k)) + (1./dyh(j))*(w(i,j+1,k)-w(i,j,k)) ) -    &
                   0.25*(mu(i,j,k)+mu(i,j+1,k)+mu(i,j+1,k-1)+mu(i,j,k-1))*               &
         ((1./dzh(k-1))*(v(i,j,k)-v(i,j,k-1)) + (1./dyh(j))*(w(i,j+1,k-1)-w(i,j,k-1))))  &
                           )/(0.5*(rho(i,j+1,k)+rho(i,j,k))   )
  enddo; enddo; enddo
!-------------------------------------PREDICTED w-velocity-DIFFUSION------------------------------
  do k=ks,kew; do j=js,je; do i=is,ie
    dw(i,j,k)= dw(i,j,k)+(   &
      (1./dx(i))*( 0.25*(mu(i,j,k)+mu(i+1,j,k)+mu(i+1,j,k+1)+mu(i,j,k+1))*               &
         ((1./dzh(k))*(u(i,j,k+1)-u(i,j,k)) + (1./dxh(i))*(w(i+1,j,k)-w(i,j,k)) ) -      &
                   0.25*(mu(i,j,k)+mu(i-1,j,k)+mu(i-1,j,k+1)+mu(i,j,k+1))*               &
         ((1./dzh(k))*(u(i-1,j,k+1)-u(i-1,j,k))+ (1./dxh(i-1))*(w(i,j,k)-w(i-1,j,k))) )  &
     +(1./dy(j))*( 0.25*(mu(i,j,k)+mu(i,j+1,k)+mu(i,j+1,k+1)+mu(i,j,k+1))*               &
         ((1./dzh(k))*(v(i,j,k+1)-v(i,j,k)) + (1./dyh(j))*(w(i,j+1,k)-w(i,j,k)) ) -      &
                   0.25*(mu(i,j,k)+mu(i,j-1,k)+mu(i,j-1,k+1)+mu(i,j,k+1))*               &
         ((1./dzh(k))*(v(i,j-1,k+1)-v(i,j-1,k))+ (1./dyh(j-1))*(w(i,j,k)-w(i,j-1,k))) )  &
     +(1./dzh(k))*2.*(   mu(i,j,k+1)*(1./dz(k+1))*(w(i,j,k+1)-w(i,j,k)) -                &
                         mu(i,j,k)  *(1./dz(k))*  (w(i,j,k)-w(i,j,k-1)) )                &
                          )/(0.5*(rho(i,j,k+1)+rho(i,j,k))   )
  enddo; enddo; enddo
end subroutine explicitMomDiff
!=================================================================================================
!=================================================================================================
! Calculates the volume force in the momentum equations and adds them to du,dv,dw
!-------------------------------------------------------------------------------------------------
subroutine volumeForce(rho,rho1,rho2,dpdx,dpdy,dpdz,BuoyancyCase,fx,fy,fz,gx,gy,gz,du,dv,dw, &
                       rho_ave)
!  use module_solid
  use module_grid
  implicit none
  integer, intent(in) :: BuoyancyCase
  real(8), intent(in) :: gx,gy,gz,rho1,rho2, dpdx, dpdy, dpdz, rho_ave
  real(8), dimension(imin:imax,jmin:jmax,kmin:kmax), intent(in) :: rho
  real(8), dimension(imin:imax,jmin:jmax,kmin:kmax), intent(inout) :: du, dv, dw, fx,fy,fz
  real(8) :: rro
  integer :: i,j,k
  if(BuoyancyCase==0) then
    rro = 0.0
  elseif(BuoyancyCase==1) then
    rro=rho1
  elseif(BuoyancyCase==2) then
    rro=rho2
  elseif(BuoyancyCase==3) then
    rro=rho_ave
  else
    call pariserror("volumeForce: invalid buoyancy option")
  endif

  do k=ks,ke;  do j=js,je; do i=is,ieu
    fx(i,j,k)=fx(i,j,k)-dpdx+(0.5*(rho(i+1,j,k)+rho(i,j,k))-rro)*gx
    du(i,j,k)=du(i,j,k) + fx(i,j,k)/(0.5*(rho(i+1,j,k)+rho(i,j,k)))
  enddo; enddo; enddo
  
  do k=ks,ke;  do j=js,jev; do i=is,ie
    fy(i,j,k)=fy(i,j,k)-dpdy+(0.5*(rho(i,j+1,k)+rho(i,j,k))-rro)*gy
    dv(i,j,k)=dv(i,j,k) + fy(i,j,k)/(0.5*(rho(i,j+1,k)+rho(i,j,k)))
  enddo; enddo; enddo

  do k=ks,kew;  do j=js,je; do i=is,ie
    fz(i,j,k)=fz(i,j,k)-dpdz+(0.5*(rho(i,j,k+1)+rho(i,j,k))-rro)*gz
    dw(i,j,k)=dw(i,j,k) + fz(i,j,k)/(0.5*(rho(i,j,k+1)+rho(i,j,k)))
  enddo; enddo; enddo

end subroutine volumeForce
!=================================================================================================
!=================================================================================================
!=================================================================================================
! Calculates the surface force in the momentum equations and adds them to du,dv,dw
!-------------------------------------------------------------------------------------------------
subroutine surfaceForce(du,dv,dw,rho)
!  use module_solid
  use module_grid
  use module_vof
  use module_2phase
  use module_surface_tension
  use module_tmpvar
  use module_timer
  
  implicit none
  real(8) :: kappa,deltax
  real(8), dimension(imin:imax,jmin:jmax,kmin:kmax), intent(inout) :: du, dv, dw, rho
  integer :: i,j,k,n
  deltax=dx(nx)
  call get_all_curvatures(tmp)
  call my_timer(7)
  do k=ks,ke;  do j=js,je; do i=is,ie
     if(abs(cvof(i+1,j,k)-cvof(i,j,k))>EPSC/1d1) then  ! there is a non-zero grad H (H Heaviside function) 
        n=0
        kappa=0d0
        if(tmp(i+1,j,k).lt.1e6) then
           kappa=kappa+tmp(i+1,j,k)
           n=n+1
        endif
        if(tmp(i,j,k).lt.1e6) then
           kappa=kappa+tmp(i,j,k)
           n=n+1
        endif
        if(n==0) then 
           geom_case_count(18) = geom_case_count(18) + 1
           ! call print_cvof_3x3x3(i,j,k)
        else
           kappa=kappa/(deltax*n)
           du(i,j,k) = du(i,j,k) - kappa*sigma*(2.0/dxh(i))*(cvof(i+1,j,k)-cvof(i,j,k))/(rho(i+1,j,k)+rho(i,j,k))
        endif
     endif
  enddo; enddo; enddo
  
  do k=ks,ke;  do j=js,jev; do i=is,ie
     if(abs(cvof(i,j+1,k)-cvof(i,j,k))>EPSC/1d1) then  ! there is a non-zero grad H (H Heaviside function) 
        n=0
        kappa=0d0
        if(tmp(i,j+1,k).lt.1e6) then
           kappa=kappa+tmp(i,j+1,k)
           n=n+1
        endif
        if(tmp(i,j,k).lt.1e6) then
           kappa=kappa+tmp(i,j,k)
           n=n+1
        endif
        if(n==0) then 
           geom_case_count(19) = geom_case_count(19) + 1
           ! call print_cvof_3x3x3(i,j,k)
        else
           kappa=kappa/(deltax*n)
           dv(i,j,k)=dv(i,j,k) - kappa*sigma*(2.0/dyh(j))*(cvof(i,j+1,k)-cvof(i,j,k))/(rho(i,j+1,k)+rho(i,j,k))
        endif
     endif
  enddo; enddo; enddo

  do k=ks,kew;  do j=js,je; do i=is,ie
     if(abs(cvof(i,j,k+1) - cvof(i,j,k))>EPSC/1d1) then  ! there is a non-zero grad H (H Heaviside function) 
        n=0
        kappa=0d0
        if(tmp(i,j,k+1).lt.1e6) then
           kappa=kappa+tmp(i,j,k+1)
           n=n+1
        endif
        if(tmp(i,j,k).lt.1e6) then
           kappa=kappa+tmp(i,j,k)
           n=n+1
        endif
        if(n==0) then  
           geom_case_count(20) = geom_case_count(20) + 1
           ! call print_cvof_3x3x3(i,j,k)
        else
           kappa=kappa/(deltax*n)
           dw(i,j,k)=dw(i,j,k) - kappa*sigma*(2.0/dzh(k))*(cvof(i,j,k+1)-cvof(i,j,k))/(rho(i,j,k+1)+rho(i,j,k))
        endif
     endif
  enddo; enddo; enddo

end subroutine surfaceForce
!=================================================================================================
!=================================================================================================
! subroutine initialize
!   Initializes the flow solver
!   called in:    program paris
!-------------------------------------------------------------------------------------------------
subroutine initialize
  use module_grid
  use module_flow
  use module_BC
  use module_IO
  use module_tmpvar
  
  use module_timer
  implicit none
  include 'mpif.h'
  integer :: ierr, i,j,k
  Nxt=Nx+2*Ng; Nyt=Ny+2*Ng; Nzt=Nz+2*Ng ! total number of cells

  if(rank==0) call check_integers()
 
  if(rank<nPdomain)then
    allocate(dims(ndim),periodic(ndim),reorder(ndim),coords(ndim),STAT=ierr)
    dims(1) = nPx; dims(2) = nPy; dims(3) = nPz
    
    reorder = 1

! Initialise boundary condition and MPI_Comm_cart periodicity
    periodic = 0 
    do i=1,ndim 
       if (bdry_cond(i) == 1) then
          periodic(i) = 1
          if (bdry_cond(i+3) /= 1) call pariserror("inconsistent boundary conditions")
       endif
    enddo

! determine if check of bc setup is possible
    do i=1,2*ndim
       if (bdry_cond(i) >= 5) check_setup=.false.
    enddo

    call MPI_Cart_Create(MPI_Comm_Domain,ndim,dims,periodic,reorder,MPI_Comm_Cart,ierr)
    if (ierr /= 0) call pariserror("*** Grid: unsuccessful Cartesian MPI-initialization")

    call MPI_Cart_Coords(MPI_Comm_Cart,rank,ndim,coords,ierr)
    if (ierr /= 0) call pariserror("*** Grid: unsuccessful in getting topology coordinates")

!     print *, "rank",rank,"coords",coords
!     stop

    is=coords(1)*Mx+1+Ng; ie=coords(1)*Mx+Mx+Ng; imin=is-Ng; imax=ie+Ng
    js=coords(2)*My+1+Ng; je=coords(2)*My+My+Ng; jmin=js-Ng; jmax=je+Ng
    ks=coords(3)*Mz+1+Ng; ke=coords(3)*Mz+Mz+Ng; kmin=ks-Ng; kmax=ke+Ng
    ieu=ie; if(bdry_cond(1)/=1 .and. coords(1)==nPx-1) ieu=ie-1
    jev=je; if(bdry_cond(2)/=1 .and. coords(2)==nPy-1) jev=je-1
    kew=ke; if(bdry_cond(3)/=1 .and. coords(3)==nPz-1) kew=ke-1

!     ! For pressure correction with inflow
!     ! dp/dn = 0 for inflow bc on face 1 == x- 
!     ! inflow bc on other faces not implemented yet.  
!     if(bdry_cond(1)==3 .and. coords(1)==0) then
!        istpc=is+1
!     else
!        istpc=is
!     endif

    allocate(  u(imin:imax,jmin:jmax,kmin:kmax), uold(imin:imax,jmin:jmax,kmin:kmax), &
              du(imin:imax,jmin:jmax,kmin:kmax),   fx(imin:imax,jmin:jmax,kmin:kmax), &
               v(imin:imax,jmin:jmax,kmin:kmax), vold(imin:imax,jmin:jmax,kmin:kmax), &
              dv(imin:imax,jmin:jmax,kmin:kmax),   fy(imin:imax,jmin:jmax,kmin:kmax), &
               w(imin:imax,jmin:jmax,kmin:kmax), wold(imin:imax,jmin:jmax,kmin:kmax), &
              dw(imin:imax,jmin:jmax,kmin:kmax),   fz(imin:imax,jmin:jmax,kmin:kmax), &
             rho(imin:imax,jmin:jmax,kmin:kmax), rhoo(imin:imax,jmin:jmax,kmin:kmax), &
            drho(imin:imax,jmin:jmax,kmin:kmax),    p(imin:imax,jmin:jmax,kmin:kmax), &
              mu(imin:imax,jmin:jmax,kmin:kmax),muold(imin:imax,jmin:jmax,kmin:kmax), &
           color(imin:imax,jmin:jmax,kmin:kmax), dIdx(imin:imax,jmin:jmax,kmin:kmax), &
            dIdy(imin:imax,jmin:jmax,kmin:kmax), dIdz(imin:imax,jmin:jmax,kmin:kmax), &
           umask(imin:imax,jmin:jmax,kmin:kmax),vmask(imin:imax,jmin:jmax,kmin:kmax), &
           wmask(imin:imax,jmin:jmax,kmin:kmax))  ! 13.5*2=27

    allocate(tmp(imin:imax,jmin:jmax,kmin:kmax), work(imin:imax,jmin:jmax,kmin:kmax,3), &
               A(is:ie,js:je,ks:ke,1:8), averages(10,Ng:Ny+Ng+1), oldaverages(10,Ng:Ny+Ng+1), &
               allaverages(10,Ng:Ny+Ng+1))  ! 39

    allocate(mask(imin:imax,jmin:jmax,kmin:kmax)) ! 40
    call add_2_my_sizer(40,8)

    du=0.0;dv=0.0;dw=0.0
    u=0.0;v=0.0;w=0.0;p=0.0;tmp=0.0;fx=0.0;fy=0.0;fz=0.0;drho=0.0;rho=0.0;mu=0.0;work=0.0;A=0.0
    averages=0.0; oldaverages=0.0; allaverages=0d0
    uold=0.d0;vold=0.d0;wold=0.d0
    umask = 1d0; vmask = 1d0; wmask = 1d0

  else  !   if(rank<nPdomain)then
    is = Ng+1;  ie = Nx+Ng;  imin = 1;  imax = Nxt
    js = Ng+1;  je = Ny+Ng;  jmin = 1;  jmax = Nyt
    ks = Ng+1;  ke = Nz+Ng;  kmin = 1;  kmax = Nzt
    ieu = ie;  if(bdry_cond(1)/=1) ieu = ie-1      ! only if not periodic
    jev = je;  if(bdry_cond(2)/=1) jev = je-1
    kew = ke;  if(bdry_cond(3)/=1) kew = ke-1
  endif !   if(rank<nPdomain)then

  allocate(x(Nxt), xh(Nxt), dx(Nxt), dxh(Nxt), &
           y(Nyt), yh(Nyt), dy(Nyt), dyh(Nyt), &
           z(Nzt), zh(Nzt), dz(Nzt), dzh(Nzt)  )

! Set the stretched grid
  do i=1,Nxt; s=dfloat(i-Ng)/dfloat(Nx); xh(i)=xLength*(xform*s*(0.5d0-s)*(1d0-s)+s); enddo
  do j=1,Nyt; s=dfloat(j-Ng)/dfloat(Ny); yh(j)=yLength*(yform*s*(0.5d0-s)*(1d0-s)+s); enddo
  do k=1,Nzt; s=dfloat(k-Ng)/dfloat(Nz); zh(k)=zLength*(zform*s*(0.5d0-s)*(1d0-s)+s); enddo

  if(read_x)then
    open(unit=12,file=trim(x_file),status='old',action='read')
    read(12,*) (xh(i), i=Ng,Nx+Ng)
    close(12)
    xLength = xh(Nx+Ng)-xh(Ng)
    i=Nx+Ng+1
    xh(i) = 2d0*xh(i-1)-xh(i-2)
    if(Ng==2)xh(1) = 2d0*xh(2)-xh(3)
    if(Ng==2)xh(i+1) = 2d0*xh(i)-xh(i-1)
  endif
  if(read_y)then
    open(unit=12,file=trim(y_file),status='old',action='read')
    read(12,*) (yh(i), i=Ng,Ny+Ng)
    close(12)
    yLength = yh(Ny+Ng)-yh(Ng)
    i=Ny+Ng+1
    yh(i) = 2d0*yh(i-1)-yh(i-2)
    if(Ng==2)yh(1) = 2d0*yh(2)-yh(3)
    if(Ng==2)yh(i+1) = 2d0*yh(i)-yh(i-1)
  endif
  if(read_z)then
    open(unit=12,file=trim(z_file),status='old',action='read')
    read(12,*) (zh(i), i=Ng,Nz+Ng)
    close(12)
    zLength = zh(Nz+Ng)-zh(Ng)
    i=Nz+Ng+1
    zh(i) = 2d0*zh(i-1)-zh(i-2)
    if(Ng==2)zh(1) = 2d0*zh(2)-zh(3)
    if(Ng==2)zh(i+1) = 2d0*zh(i)-zh(i-1)
  endif

  do i=2,Nxt; x(i)=0.5d0*(xh(i)+xh(i-1)); enddo; x(1)=2d0*xh(1)-x(2)
  do j=2,Nyt; y(j)=0.5d0*(yh(j)+yh(j-1)); enddo; y(1)=2d0*yh(1)-y(2)
  do k=2,Nzt; z(k)=0.5d0*(zh(k)+zh(k-1)); enddo; z(1)=2d0*zh(1)-z(2)

  do i=1,Nxt-1; dxh(i)=x(i+1)-x(i); enddo; dxh(Nxt)=dxh(Nxt-1)
  do j=1,Nyt-1; dyh(j)=y(j+1)-y(j); enddo; dyh(Nyt)=dyh(Nyt-1)
  do k=1,Nzt-1; dzh(k)=z(k+1)-z(k); enddo; dzh(Nzt)=dzh(Nzt-1)
  
  do i=2,Nxt; dx(i)=xh(i)-xh(i-1); enddo; dx(1)=dx(2);
  do j=2,Nyt; dy(j)=yh(j)-yh(j-1); enddo; dy(1)=dy(2);
  do k=2,Nzt; dz(k)=zh(k)-zh(k-1); enddo; dz(1)=dz(2);

end subroutine initialize


!=================================================================================================
! subroutine InitCondition
!   Sets the initial conditions
!   called in:    program paris
!-------------------------------------------------------------------------------------------------
subroutine InitCondition
  
  use module_grid
  use module_flow
  use module_BC
  use module_front
  use module_tmpvar
  use module_poisson
  use module_IO
  use module_vof
  use module_surface_tension
  use module_st_testing
  use module_lag_part
  use module_output_lpp
  use module_output_vof
  use module_freesurface
  implicit none
  include 'mpif.h'
  integer :: i,j,k, ierr, irank, req(12),sta(MPI_STATUS_SIZE,12)
  real(8) :: my_ave
  !---------------------------------------------Domain----------------------------------------------
  if(rank<nPdomain)then
     if(restart)then
        if ( DoFront ) then 
           call backup_read
        else if ( DoVOF ) then 
           call backup_VOF_read
           call ghost_x(cvof,2,req(1:4)); call MPI_WAITALL(4,req(1:4),sta(:,1:4),ierr)
           call ghost_y(cvof,2,req(1:4)); call MPI_WAITALL(4,req(1:4),sta(:,1:4),ierr)
           call ghost_z(cvof,2,req(1:4)); call MPI_WAITALL(4,req(1:4),sta(:,1:4),ierr)
           call ighost_x(vof_flag,2,req(1:4)); call MPI_WAITALL(4,req(1:4),sta(:,1:4),ierr)
           call ighost_y(vof_flag,2,req(1:4)); call MPI_WAITALL(4,req(1:4),sta(:,1:4),ierr)
           call ighost_z(vof_flag,2,req(1:4)); call MPI_WAITALL(4,req(1:4),sta(:,1:4),ierr)
           call setVOFBC(cvof,vof_flag)
        end if ! DoFront, DoVOF
        if ( DoLPP ) then 
           call backup_LPP_read
           call SeedParticles
           if ( DoLPP .and. test_injectdrop ) call backup_LPP_write 
        end if ! DoLPP
        call SetVelocityBC(u,v,w,umask,vmask,wmask,time)
        call ghost_x(u,2,req( 1: 4)); call ghost_x(v,2,req( 5: 8)); call ghost_x(w,2,req( 9:12))
        call MPI_WAITALL(12,req(1:12),sta(:,1:12),ierr)
        call ghost_y(u,2,req( 1: 4)); call ghost_y(v,2,req( 5: 8)); call ghost_y(w,2,req( 9:12))
        call MPI_WAITALL(12,req(1:12),sta(:,1:12),ierr)
        call ghost_z(u,2,req( 1: 4)); call ghost_z(v,2,req( 5: 8)); call ghost_z(w,2,req( 9:12))
        call MPI_WAITALL(12,req(1:12),sta(:,1:12),ierr)
        write(*,*) "Read backup files done!",rank,time,itimestep
     else
        ! Set velocities and the color function. 
        ! The color function is used for density and viscosity in the domain 
        ! when set by Front-Tracking.
        color = 0.;  v = 0;  w = 0.
        u = U_init;

        if(DoLPP) then 
           call SeedParticles
        end if ! DoLPP
        if(DoVOF) then
           call initconditions_VOF()
           call get_all_heights()
           if (FreeSurface) then
              call set_topology(vof_phase,itimestep) !vof_phases are updated in initconditions_VOF called above
              call get_normals()
              call get_all_curvatures(kappa_fs)
              if (RP_test) then
                 call get_ref_volume !!! can rather call this init_RP_test
                 call initialize_P_RP(p)  !initialize P field for RP test
                 call ghost_x(p,1,req( 1: 4))
                 call ghost_y(p,1,req( 5: 8))
                 call ghost_z(p,1,req( 9:12))
                 call MPI_WAITALL(12,req(1:12),sta(:,1:12),ierr) 
              endif
           endif
        endif
        du = 0d0

        ! ====================================================================
        ! Initial conditions for different tests

        ! -------------------------------------------------------------------
        ! Test: inject a drop
         if ( test_injectdrop ) then
            do i=imin,imax-1; do j=jmin,jmax-1; do k=kmin,kmax-1
               if((cvof(i,j,k) + cvof(i+1,j,k)) > 0.0d0) u(i,j,k) = 1.5d-1
               if((cvof(i,j,k) + cvof(i,j+1,k)) > 0.0d0) v(i,j,k) =-1.d-1
               if((cvof(i,j,k) + cvof(i,j,k+1)) > 0.0d0) w(i,j,k) =-5.d-1
            enddo; enddo; enddo
         end if
        
        ! -------------------------------------------------------------------
        ! Test: advect a cylinder
         if ( test_cylinder_advection ) then
            do i=imin,imax-1; do j=jmin,jmax-1; do k=kmin,kmax-1
!            if((cvof(i,j,k) + cvof(i+1,j,k)) > 0.0d0) then
            u(i,j,k) = 1.d-2!*cvof(i,j,k)
            v(i,j,k) = 1.d-2!*cvof(i,j,k)
            w(i,j,k) = 0.d0
!           endif
            enddo; enddo; enddo
         endif
     
         if ( test_shear_multiphase ) then
            do i=imin,imax-1; do j=jmin,jmax-1; do k=kmin,kmax-1
            u(i,j,k) = 1.d0*cvof(i,j,k)+15.d0*(1.-cvof(i,j,k))
            v(i,j,k) = 1.d-2*sin(2.d0*PI*x(i))*exp(-(2.d0*(y(i)-0.5d0)/0.1d0)**2)
            w(i,j,k) = 0.d0
            enddo; enddo; enddo
         endif
     
        ! -------------------------------------------------------------------
        ! Test: Test height function
         if(test_HF) then
            call test_VOF_HF()
         end if

        ! -------------------------------------------------------------------
        ! Test: Test Lagrangian particle module
         if (test_LP) then 
            call test_Lag_part(itimestep)
         end if ! test_LP
        ! -------------------------------------------------------------------
        ! Test: Test 2d (Quasi-2d) Kelvin-Helmoltz Instability
         if ( test_KHI2D ) then 
            do i=imin,imax; do j=jmin,jmax-1; do k=kmin,kmax-1
               !if( y(j) > yLength*0.5d0 + 0.005*yLength*sin(2.d0*PI*x(i)/xLength)) then 
               !   u(i,j,k) = 1.d1 !*erf( (y(j)-0.5d0*yLength)/(0.1d0*yLength*2.d0) )
               !else
               !   u(i,j,k) = 0.5d1
               !endif
               u(i,j,k) = -0.5d1+(0.5d1+0.5d1)*(1+erf((y(j)-0.5d0*yLength+0.002*xLength* &
                    sin(2.d0*PI*xh(i)/xLength))/(0.008d0*xLength*2.d0)))/2
               ! 2D, only perturb x direction 
            enddo; enddo; enddo
            do i=imin,imax-1; do j=jmin,jmax; do k=kmin,kmax-1
               v(i,j,k) = 1.d0*sin(2.d0*PI*x(i)/xLength)& 
                          *exp(-((yh(j)-yLength*0.5d0)/(0.1d0*xLength))**2.d0)
               ! Nz
               ! perturbation thickness = 0.1*xLength, wavenum(x) = 1
            enddo; enddo; enddo
         end if ! test_KHI_2D 
        ! Test: Test height function (TOMAS)
        !if ( test_KHI2D .or. test_HF) then 
        !   call h_of_KHI2D(itimestep,time)
        !endif
        ! -------------------------------------------------------------------
         if (test_jet ) then 
            ! Test: planar or cylindrical jet with finite length nozzle
            !if ( inject_type == 3 .or. inject_type == 4 ) then
            !   do i = is,ie; do j=js,je; do k = ks,ke
            !      if((cvof(i,j,k) + cvof(i+1,j,k)) > 0.0d0) u(i,j,k) = uliq_inject 
            !   end do; end do; end do
            !end if ! inject_type
         end if ! test_jet
     
     endif

     if(DoFront) then
        call GetFront('recv')
        call GetFront('wait')
        call Front2GridVector(fx, fy, fz, dIdx, dIdy, dIdz)
        call SetupDensity(dIdx,dIdy,dIdz,A,color)
        if(hypre) then
           call poi_solve(A,color(is:ie,js:je,ks:ke),maxError,maxit,it)
           call ghost_x(color,1,req( 1: 4))
           call ghost_y(color,1,req( 5: 8))
           call ghost_z(color,1,req( 9:12))
           call MPI_WAITALL(12,req(1:12),sta(:,1:12),ierr)
        else
           call NewSolver(A,color,maxError,beta,maxit,it,ierr)
           if(rank==0)print*,it,'iterations for initial density.'
        endif
        do k=ks,ke;  do j=js,je; do i=is,ie
           color(i,j,k)=min(color(i,j,k),1d0)
           color(i,j,k)=max(color(i,j,k),0d0)
        enddo; enddo; enddo
     endif

     if(TwoPhase) then
        if(GetPropertiesFromFront) then
           rho = rho2 + (rho1-rho2)*color
           mu  = mu2  + (mu1 -mu2 )*color
        else
           call linfunc(rho,rho1,rho2,DensMean)
           call linfunc(mu,mu1,mu2,ViscMean)
        endif
     else
        rho=rho1
        mu=mu1
     endif
     my_ave=0.0
     do k=ks,ke;  do j=js,je;  do i=is,ie
        my_ave=my_ave+rho(i,j,k)*dx(i)*dy(j)*dz(k)
     enddo;  enddo;  enddo
     my_ave = my_ave/(xLength*yLength*zLength)
     call MPI_ALLREDUCE(my_ave, rho_ave, 1, MPI_DOUBLE_PRECISION, MPI_SUM, MPI_COMM_Domain, ierr)
!----------------------------------------------Front----------------------------------------------
  elseif((rank==nPdomain).and.DoFront)then
     if(restartFront)then
        call backup_front_read(time,iTimeStep)
        call RegridFront
     else
        call InitConditionFront
        write(*,'(3f20.10)') FrontProps(:,1)
        call CalcVolume
        write(*,'(3f20.10)') FrontProps(:,1)
        FrontProps(2,1:NumBubble) = FrontProps(1,1:NumBubble)
     endif

     call CalcSurfaceTension
     do irank=0,nPdomain-1
        call DistributeFront(irank,'send') !,request(1:2,irank))
     enddo

  endif
  !---------------------------------------------End Front----------------------------------------------
!  if((.not. restart).or.(.not. restartFront)) then
  if(.not. restart) then
     time = 0d0
     iTimeStep = 0
  endif
  iTimeStepRestart = iTimeStep
  timeLastOutput = DBLE(INT(time/tout))*tout
end subroutine InitCondition

!=================================================================================================
! function minabs
!   used for ENO interpolations
!   called in:    subroutine momentumConvection
!                 subroutine density
!-------------------------------------------------------------------------------------------------
function minabs(a,b)
implicit none
real(8) :: minabs, a, b
  if(abs(a)<abs(b)) then
    minabs=a
  else
    minabs=b
  endif
!  minabs = 0.5*(sign(1.0,abs(b)-abs(a))*(a-b)+a+b)
end function minabs
!=================================================================================================
!=================================================================================================
! function text
!   Returns 'number' as a string with length of 'length'
!   called in:    function output
!-------------------------------------------------------------------------------------------------
function int2text(number,length)
  integer :: number, length, i
  character(len=length) :: int2text
  character, dimension(0:9) :: num = (/'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'/)
  if(number>=10**length)call pariserror("int2text error: string is not large enough")
  do i=1,length
    int2text(length+1-i:length+1-i) = num(mod(number/(10**(i-1)),10))
  enddo
end function
!=================================================================================================
function sphere(x,y,z)
  real(8) :: sphere
  real(8) :: x,y,z
  sphere=0.d0
  if(x*x + y*y + z*z < 0.09) sphere=1.d0
end function
!=================================================================================================
! subroutine ReadParameters
!   Reads the parameters in "input" file for the calculation
!   called in:    program paris
!-------------------------------------------------------------------------------------------------
subroutine ReadParameters
  use module_grid
  use module_flow
  use module_2phase
  use module_BC
  use module_IO
  use module_front
  
  implicit none
  include 'mpif.h'
  integer :: in, ierr
  real(8) :: xyzrad(4,10000)
  namelist /parameters/ out_path,      Nx,            Ny,            Nz,            Ng,          &
                        xLength,       yLength,       zLength,       gx,            gy,          &
                        gz,            bdry_cond,     dPdx,          dPdy,          dPdz,        &
                        itime_scheme,  nstep,         maxit,         maxError,      &
                        beta,          nout,          TwoPhase,      rho1,          mu1,         &
                        rho2,          mu2,           sigma,         BuoyancyCase,  nPx,         &
                        nPy,           nPz,           amin,          amax,          aspmax,      &
                        MaxPoint,      MaxElem,       MaxFront,      xform,         yform,       &
                        zform,         dt,            nregrid,       GetPropertiesFromFront,     &
                        DoVOF,         DoFront,       Implicit,      U_init,        VolumeSource,&
                        CFL,           EndTime,       MaxDt,         smooth,        nsmooth,     &
                        output_format, read_x,        read_y,        read_z,        x_file,      &
                        y_file,        z_file,        restart,       nBackup,       NumBubble,   &
                        xyzrad,        hypre,         dtFlag,        ICOut,         WallVel,     &
                        inject_type,   maxErrorVol,   restartFront,  nstats,        WallShear,   &
                        BoundaryPressure,             ZeroReynolds,  restartAverages,termout,    &  
                        excentricity,  tout,          zip_data,      ugas_inject,   uliq_inject, &  
                        blayer_gas_inject,            tdelay_gas_inject,            padding,     &
                        radius_gas_inject,            radius_liq_inject,     radius_gap_liqgas,  &
                        jetcenter_yc2yLength,         jetcenter_zc2zLength,                      & 
                        cflmax_allowed,               out_P,         AdvectionScheme, out_mom,   &
                        output_fields
 
  Nx = 0; Ny = 4; Nz = 4 ! cause absurd input file that lack nx value to fail. 
  Ng=2;xLength=1d0;yLength=1d0;zLength=1d0
  gx = 0d0; gy=0d0; gz=0d0; bdry_cond = 0
  dPdx = 0d0;  dPdy = 0d0; dPdz = 0d0
  itime_scheme = 1;  nstep = 0; maxit = 50; maxError = 1d-3  
  beta = 1.2; nout = 1; out_P = .false. 
  TwoPhase = .false.; rho1 = 1d0; mu1 = 0d0
  rho2 = 1d0; mu2 = 0d0; sigma = 0d0; BuoyancyCase = 0; nPx = 1
  nPy = 1; nPz = 1; amin = 0.32; amax = 0.96; aspmax = 1.54
  MaxPoint = 1000000; MaxElem  = 2000000; MaxFront = 100; xform=0d0; yform=0d0; zform=0d0
         dt=0.1d0; nregrid=10; GetPropertiesFromFront = .false.
  DoVOF = .true.;   DoFront = .false.;   Implicit=.false.;   U_init=0d0;   VolumeSource=0d0  
  CFL = 0.5;   EndTime = 0.;  MaxDt = 5d-2;   smooth = .true.;   nsmooth = 20
  output_format = 2;   read_x=.false.;   read_y=.false.;   read_z=.false.
  restart = .false.;  nBackup = 2000;  NumBubble=0
  xyzrad = 0.d0;  hypre=.false.;  dtFlag = 2;  ICout=.false.;   WallVel = 0d0
  inject_type=2 ! redundant
  maxErrorVol=1d-4;   restartfront=.false.;  nstats=10;  WallShear=0d0
  BoundaryPressure=0d0;   ZeroReynolds=.false.;   restartAverages=.false.;   termout=0
  excentricity=0d0;   tout = -1.d0;          zip_data=.false.
  ugas_inject=0.d0;   uliq_inject=1.d0
  blayer_gas_inject=8.d-2; tdelay_gas_inject=1.d-2
  radius_gas_inject=0.1d0; radius_liq_inject=0.1d0 ; radius_gap_liqgas=0d0
  jetcenter_yc2yLength=0.5d0;jetcenter_zc2zLength=0.5d0
  padding=5
  cflmax_allowed=0.5d0
  AdvectionScheme = 'QUICK'
  out_mom = .false.
  output_fields = [ .true. , .true. , .true. ]

  in=1
  out=2

  open(unit=in, file='input', status='old', action='read', iostat=ierr)
  if (ierr .ne. 0) call err_no_out_dir("ReadParameters: error opening 'input' file --- perhaps it does not exist ?")
  read(UNIT=in,NML=parameters)
  close(in)
  call check_sanity()
  bdry_read=.true.
  if(MaxFront>10000) call err_no_out_dir("Error: ReadParameters: increase size of xyzrad array")

  if(numBubble>MaxFront) call err_no_out_dir("Error: ReadParameters: increase size of xyzrad array (MaxFront)")

  allocate(xc(MaxFront), yc(MaxFront), zc(MaxFront))
  allocate(FrontProps(1:14,MaxFront),rad(MaxFront))

  xc (1:NumBubble) = xyzrad(1,1:NumBubble)
  yc (1:NumBubble) = xyzrad(2,1:NumBubble)
  zc (1:NumBubble) = xyzrad(3,1:NumBubble)
  rad(1:NumBubble) = xyzrad(4,1:NumBubble)


  FrontProps(5,1:NumBubble) = xyzrad(1,1:NumBubble)
  FrontProps(6,1:NumBubble) = xyzrad(2,1:NumBubble)
  FrontProps(7,1:NumBubble) = xyzrad(3,1:NumBubble)
  rad(1:NumBubble) = xyzrad(4,1:NumBubble)

  if(rank==0)then
     call system('mkdir            '//trim(out_path))
     call system('mkdir            '//trim(out_path)//'/VTK')
     call system('cp input         '//trim(out_path))
     !call system('cp paris.f90 '//trim(out_path))
     open(unit=out, file=trim(out_path)//'/output', action='write', iostat=ierr)
     if (ierr .ne. 0) call pariserror("ReadParameters: error opening output file")
     write(UNIT=out,NML=parameters)
  endif
  call mpi_barrier(MPI_COMM_WORLD, ierr)
  ! Number of grid points in streamwise and transverse directions must
  ! be integral multiples of total number of processors
  if(mod(Nx,nPx) /= 0) call pariserror("ReadParameters: Nx not divisible by nPx!")
  if(mod(Ny,nPy) /= 0) call pariserror("ReadParameters: Ny not divisible by nPy!")
  if(mod(Nz,nPz) /= 0) call pariserror("ReadParameters: Nz not divisible by nPz!")
  Mx = Nx/nPx; My = Ny/nPy; Mz = Nz/nPz
  nPdomain = nPx*nPy*nPz

  ! Check if mesh is uniform in the VOF case
  if(DoVOF.and.rank==0) then
     if ( abs(xLength*dble(Ny)/yLength/dble(Nx) - 1.d0) > 1.d-8 .or. &  
          abs(xLength*dble(Nz)/zLength/dble(Nx) - 1.d0) > 1.d-8 ) & 
          !call pariserror("Mesh is not cubic!")
          write(*,*) "*** WARNING: Mesh is not cubic, and non-uniform VOF is not ready! ***"
  endif

  ! For jet setup
  jetcenter_yc = jetcenter_yc2yLength*yLength
  jetcenter_zc = jetcenter_zc2zLength*zLength

!--- output frequency
  if ( tout > 0.d0 .and. dtFlag == 1) then 
     nout = NINT(tout/dt)
  end if !tout

  if(termout==0) then
     termout=nout
  endif

  call MPI_BARRIER(MPI_COMM_WORLD, ierr)
end subroutine ReadParameters
!=================================================================================================
!=================================================================================================
subroutine parismessage(message) 
  use module_IO
  use module_grid
  implicit none
  include 'mpif.h'
  character(*) :: message
  OPEN(UNIT=88,FILE=TRIM(out_path)//'/message-rank-'//TRIM(int2text(rank,padding))//'.txt')
  write(88,*) message
  if(rank==0) print*,message
  close(88)
end subroutine parismessage
!=================================================================================================
!=================================================================================================
subroutine err_no_out_dir(message) 
! same as pariserror but when out_path directory is not yet created
  use module_IO
  use module_grid
  implicit none
  include 'mpif.h'
  integer :: ierr
  integer :: MPI_errorcode=1
  character(*) :: message
  OPEN(UNIT=89,FILE='error-rank-'//TRIM(int2text(rank,padding))//'.txt')
  write(89,*) message
  if(rank==0) print*,message
  ! Exit MPI gracefully
  close(89)
  close(out)
  call MPI_ABORT(MPI_COMM_WORLD, MPI_errorcode, ierr)
  call MPI_finalize(ierr)
  stop 
end subroutine err_no_out_dir
!=================================================================================================
!=================================================================================================
subroutine pariserror(message) 
  use module_IO
  use module_grid
  implicit none
  include 'mpif.h'
  integer ierr, MPI_errorcode
  character(*) :: message
  OPEN(UNIT=89,FILE=TRIM(out_path)//'/error-rank-'//TRIM(int2text(rank,padding))//'.txt')
  write(89,*) message
  if(rank==0) print*,message
  ! Exit MPI gracefully
  close(89)
  close(out)
  MPI_errorcode=1
  call MPI_ABORT(MPI_COMM_WORLD, MPI_errorcode, ierr)
  call MPI_finalize(ierr)
  stop 
end subroutine pariserror
!=================================================================================================
!=================================================================================================
subroutine check_stability() 
  use module_grid
  use module_flow
  use module_BC
  use module_IO
  use module_front
  
  use module_2phase
  implicit none
  include 'mpif.h'
  real(8) von_neumann

  if(dt*mu1 /= 0) then 
     von_neumann = dx(ng)**2*rho1/(dt*mu1)
     if(rank==0) print *, "dx**2*rho/(dt*mu) = ", von_neumann
     if(von_neumann < 6d0.and..not.Implicit) call pariserror("time step too large for viscous terms")
  endif

  if(dt*sigma /= 0) then 
     von_neumann = dx(ng)**3*rho1/(dt**2*sigma)
     if(rank==0) print *, "dx**3*rho/(dt**2*sigma) = ", von_neumann
     if(von_neumann < 4d0) call pariserror("time step too large for ST terms")
  endif

end subroutine check_stability

subroutine check_integers()
  use module_grid
  implicit none
  integer :: n,big,nbytes,intype
  intype = 4
  print *, "-------------------------"
  print *, "array index integer check"
  print *, "-------------------------"
  nbytes=(mylog2(max(mx,my,mz)))/8
  print *, "log2(box size)", mylog2(max(mx,my,mz))
  nbytes=(3*mylog2(mx)+3)/8
  print *, "nbytes max in box array index", nbytes
  if(nbytes>=intype*8) then
     n=0
     big=1
     print *, "------------"
     print *, "integer check"
     print *, "------------"
     do while (n<=(64/8))
        n=n+1
        big = big*256
        print *,n,big
     end do
     print *, "------------"
     call pariserror("box too large for integer type")
  endif
  contains
    function mylog2(n)
      implicit none
      integer :: mylog2
      integer, intent(in) :: n
      integer :: k
      mylog2=0
      k=n
      if(n<1) call pariserror("mylog2: nonpositive number")
      do while (k>=1) 
         mylog2=mylog2+1
         k=k/2
      end do
      mylog2=mylog2-1
    end function mylog2
end subroutine check_integers

subroutine hello_coucou
  use module_grid
  integer, parameter  :: debug=1
  integer, save :: hello_count=1
  if(debug == 1) then 
  if(rank==0) write(6,*) 'coucou ',hello_count, "Process0"
  if(rank==nPdomain) write(6,*) 'coucou ',hello_count, "Front"
  hello_count = hello_count + 1
  end if
end subroutine hello_coucou

subroutine hello_proc(n)
  use module_grid
  integer, parameter  :: debug=1
  integer, save :: hello_count=1
  integer :: n
  if(debug == 1) then 
  if(rank==n) write(6,*) 'coucou ',hello_count, "Process0"
  if(rank==nPdomain) write(6,*) 'coucou ',hello_count, "Front"
  hello_count = hello_count + 1
  end if
end subroutine hello_proc


subroutine hello_all
  use module_grid
  integer, parameter  :: debug=1
  integer, save :: hello_count=1
  if(debug == 1) then 
  write(6,*) 'coucou ',hello_count, "Process " , rank
  if(rank==nPdomain) write(6,*) 'coucou ',hello_count, "Front"
  hello_count = hello_count + 1
  end if
end subroutine hello_all