diff --git a/src/advec_2nd.f90 b/src/advec_2nd.f90 index b2457bba..836e0655 100644 --- a/src/advec_2nd.f90 +++ b/src/advec_2nd.f90 @@ -81,7 +81,7 @@ subroutine advecc_2nd(a_in,a_out) end do do j=2,j1 - do k=2,kmax + do k=2,kmax do i=2,i1 a_out(i,j,k) = a_out(i,j,k)- (1./rhobf(k))*( & w0(i,j,k+1) * (rhobf(k+1) * a_in(i,j,k+1) + rhobf(k) * a_in(i,j,k)) & @@ -118,12 +118,12 @@ end subroutine advecc_2nd !> Advection at the u point. -subroutine advecu_2nd(a_in, a_out) - +subroutine advecu_2nd(a_in, a_out, sx) use modglobal, only : i1,ih,j1,jh,k1,kmax,dxiq,dyiq,dziq,dzf,dzh,leq use modfields, only : u0, v0, w0, rhobf implicit none + integer, intent(in) :: sx real(field_r), dimension(2-ih:i1+ih,2-jh:j1+jh,k1), intent(in) :: a_in real(field_r), dimension(2-ih:i1+ih,2-jh:j1+jh,k1), intent(inout) :: a_out ! real, dimension(2-ih:i1+ih,2-jh:j1+jh,k1) :: rho_a_in @@ -144,7 +144,7 @@ subroutine advecu_2nd(a_in, a_out) do j=2,j1 jm=j-1 jp=j+1 - do i=2,i1 + do i=sx,i1 im=i-1 ip=i+1 a_out(i,j,k) = a_out(i,j,k)- ( & @@ -166,7 +166,7 @@ subroutine advecu_2nd(a_in, a_out) do j=2,j1 jm=j-1 jp=j+1 - do i=2,i1 + do i=sx,i1 im=i-1 ip=i+1 a_out(i,j,1) = a_out(i,j,1)-(1./rhobf(1))*( & @@ -181,7 +181,7 @@ subroutine advecu_2nd(a_in, a_out) do k=2,kmax km=k-1 kp=k+1 - do i=2,i1 + do i=sx,i1 im=i-1 ip=i+1 a_out(i,j,k) = a_out(i,j,k)- (1./rhobf(k))*( & @@ -197,7 +197,7 @@ subroutine advecu_2nd(a_in, a_out) do j=2,j1 jm=j-1 jp=j+1 - do i=2,i1 + do i=sx,i1 im=i-1 ip=i+1 a_out(i,j,1) = a_out(i,j,1)- (1./rhobf(1))*( & @@ -212,7 +212,7 @@ subroutine advecu_2nd(a_in, a_out) do k=2,kmax km=k-1 kp=k+1 - do i=2,i1 + do i=sx,i1 im=i-1 ip=i+1 a_out(i,j,k) = a_out(i,j,k)- (1./rhobf(k))*( & @@ -234,12 +234,13 @@ end subroutine advecu_2nd !> Advection at the v point. -subroutine advecv_2nd(a_in, a_out) - +subroutine advecv_2nd(a_in, a_out, sy) use modglobal, only : i1,ih,j1,jh,k1,kmax,dxiq,dyiq,dziq,dzf,dzh,leq use modfields, only : u0, v0, w0, rhobf implicit none + + integer,intent(in) :: sy real(field_r), dimension(2-ih:i1+ih,2-jh:j1+jh,k1), intent(in) :: a_in !< Input: the v-field real(field_r), dimension(2-ih:i1+ih,2-jh:j1+jh,k1), intent(inout) :: a_out !< Output: the tendency ! real, dimension(2-ih:i1+ih,2-jh:j1+jh,k1) :: rho_a_in @@ -257,7 +258,7 @@ subroutine advecv_2nd(a_in, a_out) do k=1,kmax km=k-1 kp=k+1 - do j=2,j1 + do j=sy,j1 jm=j-1 jp=j+1 do i=2,i1 @@ -279,7 +280,7 @@ subroutine advecv_2nd(a_in, a_out) if (leq) then - do j=2,j1 + do j=sy,j1 jm=j-1 jp=j+1 do i=2,i1 @@ -291,7 +292,7 @@ subroutine advecv_2nd(a_in, a_out) end do end do - do j=2,j1 + do j=sy,j1 jm=j-1 jp=j+1 do k=2,kmax @@ -309,7 +310,7 @@ subroutine advecv_2nd(a_in, a_out) end do else - do j=2,j1 + do j=sy,j1 jm=j-1 jp=j+1 do i=2,i1 @@ -322,7 +323,7 @@ subroutine advecv_2nd(a_in, a_out) end do end do - do j=2,j1 + do j=sy,j1 jm=j-1 jp=j+1 do k=2,kmax diff --git a/src/modadvection.f90 b/src/modadvection.f90 index 4bb644a8..ade47bd8 100644 --- a/src/modadvection.f90 +++ b/src/modadvection.f90 @@ -30,7 +30,9 @@ module modadvection subroutine advection use modglobal, only : lmoist, nsv, iadv_mom,iadv_tke,iadv_thl,iadv_qt,iadv_sv, & - iadv_cd2,iadv_5th,iadv_52,iadv_cd6,iadv_62,iadv_kappa,iadv_upw,iadv_hybrid,iadv_hybrid_f,iadv_null,leq + iadv_cd2,iadv_5th,iadv_52,iadv_cd6,iadv_62,iadv_kappa,& + iadv_upw,iadv_hybrid,iadv_hybrid_f,iadv_null,leq,& + lopenbc,lboundary,lperiodic use modfields, only : u0,up,v0,vp,w0,wp,e120,e12p,thl0,thlp,qt0,qtp,sv0,svp use modsubgrid, only : lsmagorinsky use advec_2nd, only : advecu_2nd, advecv_2nd, advecw_2nd, advecc_2nd @@ -43,15 +45,20 @@ subroutine advection use advec_kappa, only : advecc_kappa use advec_upw, only : advecc_upw implicit none - integer :: n + integer :: n,sx = 2,sy = 2 ! leq = .false. ! for testing that the non-uniform advection routines agree with the uniform ones ! when the grid is uniform - + + if(lopenbc) then ! Calculate tendencies only for non-domain boundary cells if openboundaries are used + if(lboundary(1).and. .not. lperiodic(1)) sx = 3 + if(lboundary(3).and. .not. lperiodic(3)) sy = 3 + endif + select case(iadv_mom) case(iadv_cd2) - call advecu_2nd(u0,up) - call advecv_2nd(v0,vp) + call advecu_2nd(u0,up,sx) + call advecv_2nd(v0,vp,sy) call advecw_2nd(w0,wp) case(iadv_5th) !if (.not. leq) stop "advec_5th does not support a non-uniform vertical grid." @@ -82,7 +89,7 @@ subroutine advection call advecv_5th(v0,vp) call advecw_5th(w0,wp) case(iadv_null) - ! null advection scheme + ! null advection scheme stop "Null advection scheme selected for iadv_mom - probably a bad idea." case default stop "Unknown advection scheme " @@ -109,9 +116,9 @@ subroutine advection call advecc_hybrid(e120,e12p) case(iadv_hybrid_f) !if (.not. leq) stop "advec_hybrid_f does not support a non-uniform vertical grid." - call advecc_hybrid_f(e120,e12p) + call advecc_hybrid_f(e120,e12p) case(iadv_null) - ! null advection scheme + ! null advection scheme stop "Null advection scheme selected for iadv_tke - probably a bad idea." case default stop "Unknown advection scheme " @@ -143,8 +150,8 @@ subroutine advection !if (.not. leq) stop "advec_hybrid_f does not support a non-uniform vertical grid." call advecc_hybrid_f(thl0,thlp,1.0) case(iadv_null) - ! null advection scheme - stop "Null advection scheme selected for iadv_thl - probably a bad idea." + ! null advection scheme + stop "Null advection scheme selected for iadv_thl - probably a bad idea." case default stop "Unknown advection scheme " end select @@ -174,7 +181,7 @@ subroutine advection !if (.not. leq) stop "advec_hybrid_f does not support a non-uniform vertical grid." call advecc_hybrid_f(qt0,qtp,1e-3) case(iadv_null) - ! null advection scheme + ! null advection scheme stop "Null advection scheme selected for iadv_qt - probably a bad idea." case default stop "Unknown advection scheme " @@ -204,7 +211,7 @@ subroutine advection call advecc_hybrid(sv0(:,:,:,n),svp(:,:,:,n)) case(iadv_hybrid_f) !if (.not. leq) stop "advec_hybrid_f does not support a non-uniform vertical grid." - call advecc_hybrid_f(sv0(:,:,:,n),svp(:,:,:,n)) + call advecc_hybrid_f(sv0(:,:,:,n),svp(:,:,:,n)) case(iadv_null) ! null advection scheme - do nothing case default diff --git a/src/modboundary.f90 b/src/modboundary.f90 index 7d5e73c6..9a60fc5f 100644 --- a/src/modboundary.f90 +++ b/src/modboundary.f90 @@ -140,7 +140,7 @@ end subroutine cyclicm !! to infinity at the bottom of the sponge layer. !! \endlatexonly subroutine grwdamp - use modglobal, only : i1,j1,kmax,cu,cv,lcoriol,igrw_damp,geodamptime,nsv,rdt,unudge,dzf + use modglobal, only : i1,j1,kmax,cu,cv,lcoriol,igrw_damp,geodamptime,nsv,rdt,unudge,dzf,lopenbc use modfields, only : up,vp,wp,thlp,qtp,u0,v0,w0,thl0,qt0,sv0,ug,vg & ,thl0av,qt0av,sv0av,u0av,v0av implicit none @@ -189,12 +189,13 @@ subroutine grwdamp ! Additional to gravity wave damping, set qt, thl and sv0(:) equal to slabaverage ! at level kmax. ! Originally done in subroutine tqaver, now using averages from modthermodynamics - - thl0(2:i1,2:j1,kmax) = thl0av(kmax) - qt0 (2:i1,2:j1,kmax) = qt0av(kmax) - do n=1,nsv - sv0(2:i1,2:j1,kmax,n) = sv0av(kmax,n) - end do + if(.not.lopenbc) then + thl0(2:i1,2:j1,kmax) = thl0av(kmax) + qt0 (2:i1,2:j1,kmax) = qt0av(kmax) + do n=1,nsv + sv0(2:i1,2:j1,kmax,n) = sv0av(kmax,n) + end do + endif return end subroutine grwdamp diff --git a/src/modchecksim.f90 b/src/modchecksim.f90 index c9ee78b8..f7510727 100644 --- a/src/modchecksim.f90 +++ b/src/modchecksim.f90 @@ -31,7 +31,7 @@ module modchecksim implicit none private - public initchecksim,checksim + public initchecksim,checksim,chkdiv real :: tcheck = 0. integer(kind=longint) :: tnext = 3600.,itcheck @@ -39,7 +39,7 @@ module modchecksim ! explanations for dt_limit, determined in tstep_update() character (len=15) :: dt_reasons(0:5) = [character(len=15):: "initial step", "timee", "dt_lim" , "idtmax", "velocity", "diffusion"] - + save contains !> Initializing Checksim. Read out the namelist, initializing the variables @@ -200,9 +200,8 @@ subroutine chkdiv write(6 ,'(A,2ES11.2,A,A)')'divmax, divtot = ', divmax, divtot, ' dt limited by ', dt_reasons(dt_reason) end if - return - + return + end subroutine chkdiv end module modchecksim - diff --git a/src/modfftw.f90 b/src/modfftw.f90 index 91fd950d..ebb38343 100644 --- a/src/modfftw.f90 +++ b/src/modfftw.f90 @@ -28,7 +28,7 @@ module modfftw use, intrinsic :: iso_c_binding use modprecision, only : pois_r use modglobal, only : itot, jtot, imax, jmax, i1, j1, ih, jh, kmax, ijtot & - , dxi,dyi,pi + , dxi,dyi,pi, lperiodic use modmpi, only : commcol, commrow, mpierr, nprocx, D_MPI_ALLTOALL, nprocy & , myidx, myidy implicit none @@ -170,7 +170,12 @@ subroutine fftwinit(p, Fp, d, xyrt, ps,pe,qs,qe) ! Prepare 1d FFT transforms ! TODO: in plan_many, skip part where k > kmax embed(1) = itot - kinds(1) = FFTW_R2HC + if (lperiodic(1)) then + kinds(1) = FFTW_R2HC + else + kinds(1) = FFTW_REDFT10 ! DCT for Neumann BC for pressure + end if + planx = fftw_plan_many_r2r_if( & 1, & ! rank embed, & ! n (size) [array] @@ -188,7 +193,12 @@ subroutine fftwinit(p, Fp, d, xyrt, ps,pe,qs,qe) ) embed(1) = itot - kinds(1) = FFTW_HC2R + if (lperiodic(1)) then + kinds(1) = FFTW_HC2R + else + kinds(1) = FFTW_REDFT01 ! Inverse DCT + end if + planxi = fftw_plan_many_r2r_if( & 1, & ! rank embed, & ! n (size) [array] @@ -206,7 +216,12 @@ subroutine fftwinit(p, Fp, d, xyrt, ps,pe,qs,qe) ) embed(1) = jtot - kinds(1) = FFTW_R2HC + if (lperiodic(3)) then + kinds(1) = FFTW_R2HC + else + kinds(1) = FFTW_REDFT10 + end if + plany = fftw_plan_many_r2r_if( & 1, & ! rank embed, & ! n (size) [array] @@ -222,9 +237,13 @@ subroutine fftwinit(p, Fp, d, xyrt, ps,pe,qs,qe) kinds, & ! kind FFTW_MEASURE & ! flags (FFTW_MEASURE or FFTW_ESTIMATE) ) - + embed(1) = jtot - kinds(1) = FFTW_HC2R + if (lperiodic(3)) then + kinds(1) = FFTW_HC2R + else + kinds(1) = FFTW_REDFT01 ! Inverse DCT + end if planyi = fftw_plan_many_r2r_if( & 1, & ! rank embed, & ! n (size) [array] @@ -276,6 +295,9 @@ subroutine fftwinit(p, Fp, d, xyrt, ps,pe,qs,qe) kinds(1) = FFTW_R2HC kinds(2) = FFTW_R2HC + if (.not. lperiodic(1)) kinds(1) = FFTW_REDFT10 + if (.not. lperiodic(3)) kinds(2) = FFTW_REDFT10 + planxy = fftw_plan_guru_r2r_if(& 2, & ! rank dimij, & ! dims @@ -289,6 +311,9 @@ subroutine fftwinit(p, Fp, d, xyrt, ps,pe,qs,qe) kinds(1) = FFTW_HC2R kinds(2) = FFTW_HC2R + if (.not. lperiodic(1)) kinds(1) = FFTW_REDFT01 + if (.not. lperiodic(3)) kinds(2) = FFTW_REDFT01 + planxyi = fftw_plan_guru_r2r_if(& 2, & ! rank dimij, & ! dims @@ -656,7 +681,7 @@ subroutine fftwf(p, Fp) stop 'Illegal method in fftwsolver.' endif - Fp(:,:,:) = Fp(:,:,:) / sqrt(ijtot) + !Fp(:,:,:) = Fp(:,:,:) / sqrt(ijtot) ! moving normalization to fftwb end subroutine subroutine fftwb(p, Fp) @@ -664,8 +689,9 @@ subroutine fftwb(p, Fp) real(pois_r), pointer :: p(:,:,:) real(pois_r), pointer :: Fp(:,:,:) - - Fp(:,:,:) = Fp(:,:,:) / sqrt(ijtot) + real :: norm + + !Fp(:,:,:) = Fp(:,:,:) / sqrt(ijtot) if (method == 1) then call transpose_a3inv(p201, Fp) @@ -680,19 +706,26 @@ subroutine fftwb(p, Fp) call fftw_execute_r2r_if(planxyi, p_nohalo, p_nohalo) else stop 'Illegal method in fftwsolver.' - endif - end subroutine + endif + norm = 1.0/ijtot + if (.not. lperiodic(1)) norm = norm / 2 ! different normalization for the DCT + if (.not. lperiodic(3)) norm = norm / 2 + p(:,:,:) = p(:,:,:) * norm ! do all normalization at once + end subroutine ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! subroutine fftwinit_factors(xyrt) + use modglobal, only : i1,j1,kmax,imax,jmax,itot,jtot,dxi,dyi,pi,ih,jh,lperiodic + use modmpi, only : myidx, myidy + implicit none real(pois_r), allocatable :: xyrt(:,:) - integer :: i,j,iv,jv - real(pois_r) :: fac - real(pois_r) :: xrt(itot), yrt(jtot) + integer :: i,j,iv,jv + real(pois_r) :: fac + real(pois_r) :: xrt(itot), yrt(jtot) ! Generate Eigenvalues xrt and yrt resulting from d**2/dx**2 F = a**2 F @@ -712,27 +745,19 @@ subroutine fftwinit_factors(xyrt) ! I --> direction fac = 1./(2.*itot) - do i=2,(itot/2) + if (.not. lperiodic(1)) fac = 1./(4.*itot) + do i=2,itot xrt(i)=-4.*dxi*dxi*(sin(float(2*(i-1))*pi*fac))**2 - xrt(itot - i + 2) = xrt(i) end do xrt(1) = 0. - if (mod(itot,2) == 0) then - ! Nyquist frequency - xrt(1 + itot/2) = -4.*dxi*dxi - endif ! J --> direction fac = 1./(2.*jtot) - do j=2,(jtot/2) + if (.not. lperiodic(3)) fac = 1./(4.*jtot) + do j=2,jtot yrt(j)=-4.*dyi*dyi*(sin(float(2*(j-1))*pi*fac))**2 - yrt(jtot - j + 2) = yrt(j) end do yrt(1) = 0. - if (mod(jtot,2) == 0) then - ! Nyquist frequency - yrt(1 + jtot/2) = -4.*dyi*dyi - endif ! Combine I and J directions ! Note that: diff --git a/src/modforces.f90 b/src/modforces.f90 index 09bbe322..01915ec5 100644 --- a/src/modforces.f90 +++ b/src/modforces.f90 @@ -112,63 +112,56 @@ subroutine coriolis ! | !-----------------------------------------------------------------| - use modglobal, only : i1,j1,kmax,dzh,dzf,cu,cv,om22,om23,lcoriol + use modglobal, only : i1,j1,kmax,dzh,dzf,cu,cv,om22,om23,lcoriol,lopenbc,lboundary,lperiodic use modfields, only : u0,v0,w0,up,vp,wp implicit none - integer i, j, k, jm, jp, km, kp + integer :: i, j, k,im,ip, jm, jp, km, kp,sx=2,sy=2 if (lcoriol .eqv. .false.) return - do k=2,kmax - kp=k+1 - km=k-1 - do j=2,j1 - jp=j+1 - jm=j-1 - do i=2,i1 - - up(i,j,k) = up(i,j,k)+ cv*om23 & - +(v0(i,j,k)+v0(i,jp,k)+v0(i-1,j,k)+v0(i-1,jp,k))*om23*0.25 & - -(w0(i,j,k)+w0(i,j,kp)+w0(i-1,j,kp)+w0(i-1,j,k))*om22*0.25 - - vp(i,j,k) = vp(i,j,k) - cu*om23 & - -(u0(i,j,k)+u0(i,jm,k)+u0(i+1,jm,k)+u0(i+1,j,k))*om23*0.25 - - - wp(i,j,k) = wp(i,j,k) + cu*om22 +( (dzf(km) * (u0(i,j,k) + u0(i+1,j,k) ) & - + dzf(k) * (u0(i,j,km) + u0(i+1,j,km)) ) / dzh(k) ) & - * om22*0.25 - end do - end do -! -------------------------------------------end i&j-loop + ! Only calculate interior tendencies when open boundaries are used + if(lopenbc) then + if(lboundary(1).and..not.lperiodic(1)) sx = 3 + if(lboundary(3).and..not.lperiodic(3)) sy = 3 + endif + + ! u tendency + do k = 1,kmax + kp = k+1 + do j = 2,j1 + jp = j+1 + do i = sx,i1 + im = i-1 + up(i,j,k) = up(i,j,k)+ cv*om23 & + +(v0(i,j,k)+v0(i,jp,k)+v0(im,j,k)+v0(im,jp,k))*om23*0.25 & + -(w0(i,j,k)+w0(i,j,kp)+w0(im,j,kp)+w0(im,j,k))*om22*0.25 + end do + end do end do -! -------------------------------------------end k-loop - -! -------------------------------------------- -! special treatment for lowest full level: k=1 -! -------------------------------------------- - - do j=2,j1 - jp = j+1 - jm = j-1 - do i=2,i1 - - up(i,j,1) = up(i,j,1) + cv*om23 & - +(v0(i,j,1)+v0(i,jp,1)+v0(i-1,j,1)+v0(i-1,jp,1))*om23*0.25 & - -(w0(i,j,1)+w0(i,j ,2)+w0(i-1,j,2)+w0(i-1,j ,1))*om22*0.25 - - vp(i,j,1) = vp(i,j,1) - cu*om23 & - -(u0(i,j,1)+u0(i,jm,1)+u0(i+1,jm,1)+u0(i+1,j,1))*om23*0.25 - - wp(i,j,1) = 0.0 - + ! v tendency + do k = 1,kmax + do j = sy,j1 + jm = j -1 + do i = 2,i1 + ip = i+1 + vp(i,j,k) = vp(i,j,k) - cu*om23 & + -(u0(i,j,k)+u0(i,jm,k)+u0(ip,jm,k)+u0(ip,j,k))*om23*0.25 + end do + end do end do + ! w tendency + do k=2,kmax ! wp(:,:,1)=0 + km=k-1 + do j=2,j1 + do i=2,i1 + ip = i+1 + wp(i,j,k) = wp(i,j,k) + cu*om22 +( (dzf(km) * (u0(i,j,k) + u0(ip,j,k) ) & + + dzf(k) * (u0(i,j,km) + u0(ip,j,km)) ) / dzh(k) ) & + * om22*0.25 + end do + end do end do -! ----------------------------------------------end i,j-loop - - - return end subroutine coriolis subroutine lstend @@ -211,17 +204,16 @@ subroutine lstend kp=k+1 km=k-1 - if (whls(kp).lt.0) then !downwind scheme for subsidence + if (whls(kp).lt.0) then !upwind scheme for subsidence thlp(2:i1,2:j1,k) = thlp(2:i1,2:j1,k) - whls(kp) * (thl0(2:i1,2:j1,kp) - thl0(2:i1,2:j1,k))/dzh(kp) qtp (2:i1,2:j1,k) = qtp (2:i1,2:j1,k) - whls(kp) * (qt0 (2:i1,2:j1,kp) - qt0 (2:i1,2:j1,k))/dzh(kp) if (lmomsubs) then up(2:i1,2:j1,k) = up(2:i1,2:j1,k) - whls(kp) * (u0(2:i1,2:j1,kp) - u0(2:i1,2:j1,k))/dzh(kp) vp(2:i1,2:j1,k) = vp(2:i1,2:j1,k) - whls(kp) * (v0(2:i1,2:j1,kp) - v0(2:i1,2:j1,k))/dzh(kp) endif - svp(2:i1,2:j1,k,:) = svp(2:i1,2:j1,k,:) - whls(kp) * (sv0(2:i1,2:j1,kp,:) - sv0(2:i1,2:j1,k,:))/dzh(kp) - else !downwind scheme for mean upward motions + else !upwind scheme for mean upward motions if (k > 1) then !neglect effect of mean ascending on tendencies at the lowest full level thlp(2:i1,2:j1,k) = thlp(2:i1,2:j1,k) - whls(k) * (thl0(2:i1,2:j1,k) - thl0(2:i1,2:j1,km))/dzh(k) qtp (2:i1,2:j1,k) = qtp (2:i1,2:j1,k) - whls(k) * (qt0 (2:i1,2:j1,k) - qt0 (2:i1,2:j1,km))/dzh(k) @@ -237,10 +229,7 @@ subroutine lstend qtp (2:i1,2:j1,k) = qtp (2:i1,2:j1,k)-u0av(k)*dqtdxls (k)-v0av(k)*dqtdyls (k) + dqtdtls(k) up (2:i1,2:j1,k) = up (2:i1,2:j1,k)-u0av(k)*dudxls (k)-v0av(k)*dudyls (k) + dudtls(k) vp (2:i1,2:j1,k) = vp (2:i1,2:j1,k)-u0av(k)*dvdxls (k)-v0av(k)*dvdyls (k) + dvdtls(k) - enddo - - return end subroutine lstend end module modforces diff --git a/src/modglobal.f90 b/src/modglobal.f90 index 518e2e44..f61a4ccc 100644 --- a/src/modglobal.f90 +++ b/src/modglobal.f90 @@ -22,6 +22,7 @@ ! Copyright 1993-2009 Delft University of Technology, Wageningen University, Utrecht University, KNMI ! module modglobal +use iso_c_binding use modprecision implicit none save @@ -148,6 +149,7 @@ module modglobal logical :: lnoclouds = .false. !< switch to enable/disable thl calculations logical :: lfast_thermo = .false. !< switch to enable faster icethermo scheme logical :: lsgbucorr= .false. !< switch to enable subgrid buoyancy flux + logical :: lconstexner = .false. !< switch to use the initial pressure profile in the exner function ! Poisson solver: modpois / modhypre integer :: solver_id = 0 ! Identifier for nummerical solver: 0 1 2 3 4 @@ -156,7 +158,16 @@ module modglobal real(real64):: tolerance = 1E-8! Convergence threshold . X X X X integer :: n_pre = 1 ! Number of pre and post relaxations . X X X X integer :: n_post =1 ! Number of pre and post relaxations . X X X X - integer :: precond = 1 ! Preconditioner ID . . 12 0189 0189 + integer :: precond_id = 1 ! Preconditioner ID . . 12 0189 0189 + integer :: maxiter_precond = 1 ! Number of iterations for precondition per iteration + integer :: hypre_logging = 1 ! HYPRE logging and print level - set higher value for more messages + type solver_type + !integer*8 solver,precond + type(c_ptr) solver, precond + integer solver_id, precond_id, maxiter, n_post, n_pre, maxiter_precond + real tolerance + end type + type(solver_type) :: psolver ! Global variables (modvar.f90) integer :: xyear = 0 !< * year, only for time units in netcdf @@ -197,9 +208,29 @@ module modglobal integer :: iexpnr = 0 !< * number of the experiment character(3) cexpnr + logical :: loutdirs = .false. !< if true, create output directories using myidy character(20) :: output_prefix = '' !< prefix for output files e.g. for an output directory + ! Variables for modopenboundary.f90 + logical :: lopenbc = .false., lsynturb = .false.,linithetero = .false. + type boundary_type + integer :: nx1,nx2,nx1patch,nx2patch,nx1u,nx2u,nx1v,nx2v,nx1w,nx2w + real(field_r), allocatable, dimension(:,:,:) :: u,v,w,thl,qt,e12, & + & u2,v2,w2,uv,uw,vw,thl2,qt2,wthl,wqt,ci,svturb + real(field_r), allocatable, dimension(:,:,:,:) :: sv + real(field_r), allocatable, dimension(:,:) :: radcorr,uphase,uphasesingle, & + radcorrsingle,uturb,vturb,wturb,thlturb,qtturb,e12turb!,randqt,randthl + real(field_r), allocatable, dimension(:,:,:,:) :: eigvec + character (len=:), allocatable :: name + end type + type(boundary_type), dimension(5) :: boundary + logical, dimension(5) :: lboundary = .false. + logical, dimension(5) :: lperiodic = (/.true., .true., .true., .true., .false./) + real :: dxint=-1.,dyint=-1.,dzint=-1.,tauh=60.,taum=0.,tau=60.,lambda,lambdas=-1.,lambdas_x=-1.,lambdas_y=-1.,lambdas_z=-1. + integer :: nmodes=100,ntboundary=1,pbc = 3,iturb=0 + real,dimension(:),allocatable :: tboundary + ! modphsgrd.f90 real :: dx !< grid spacing in x-direction diff --git a/src/modhypre.f90 b/src/modhypre.f90 index 366c6249..ba37b136 100644 --- a/src/modhypre.f90 +++ b/src/modhypre.f90 @@ -26,22 +26,22 @@ module modhypre use modmpi, only : myid, myidx, myidy, nprocx, nprocy, MPI_COMM_WORLD & , MPI_Wtime use modglobal, only : i1, j1, ih, jh, imax, jmax, kmax, solver_id, maxiter & - , n_pre, n_post, tolerance, precond, dzf, dzh, dx, dy & - , itot, jtot + , n_pre, n_post, tolerance, dzf, dzh, dx, dy & + , itot, jtot, solver_type, hypre_logging use modfields, only : rhobf, rhobh - implicit none #ifdef USE_HYPRE private -public :: inithypre, solve_hypre, exithypre, set_initial_guess +public :: inithypre_grid, inithypre_solver, solve_hypre, exithypre_grid, exithypre_solver, set_initial_guess, set_zero_guess save - integer(c_int) mpi_comm_hypre + integer(c_int) mpi_comm_hypre integer(c_int) ierr - type(c_ptr) grid, stencil, solver, precond_solver + + type(c_ptr) grid, stencil !, solver, precond_solver type(c_ptr) matrixA, vectorX, vectorB ! Solve Ax = b integer ilower(3), iupper(3), periodic(3) @@ -345,6 +345,14 @@ subroutine HYPRE_StructLGMRESSetup ( solver, matrixA, vectorB, vectorX, ierr) & type(c_ptr) :: solver, matrixA, vectorB, vectorX integer(c_int) ierr end subroutine + subroutine HYPRE_StructPCGSetRelChange ( solver, tol, ierr) & + bind(c, name="hypre_structpcgsetrelchange_") + use iso_c_binding + implicit none + type(c_ptr) :: solver + real(c_double) :: tol + integer(c_int) ierr + end subroutine HYPRE_StructPCGSetRelChange subroutine HYPRE_StructPCGSetTwoNorm ( solver, two_norm, ierr ) & bind(c, name="hypre_structpcgsettwonorm_") use iso_c_binding @@ -543,6 +551,16 @@ subroutine HYPRE_StructMatrixSetBoxValues ( matrix, ilower, iupper, & real(c_double) :: values(*) integer(c_int) num_stencil_indices, ierr end subroutine + subroutine HYPRE_StructMatrixGetBoxValues ( matrix, ilower, iupper, & + num_stencil_indices, stencil_indices, values, ierr ) & + bind(c, name="hypre_structmatrixgetboxvalues_") + use iso_c_binding + implicit none + type(c_ptr) :: matrix + integer(c_int) :: ilower(*), iupper(*), stencil_indices(*) + real(c_double) :: values(*) + integer(c_int) num_stencil_indices, ierr + end subroutine subroutine HYPRE_StructMatrixInitialize ( matrix, ierr ) & bind(c, name="hypre_structmatrixinitialize_") use iso_c_binding @@ -725,9 +743,10 @@ subroutine HYPRE_StructPCGDestroy ( obj , ierr ) & end interface contains - subroutine initprecond + subroutine initprecond(solver) ! Setup a preconditioner for the BiCGSTAB or GMRES solvers implicit none + type(solver_type), intent(inout) :: solver ! The precond_id flags mean : ! 0 - setup a smg preconditioner @@ -735,56 +754,61 @@ subroutine initprecond ! 7 - setup a jacobi preconditioner ! 8 - setup a ds preconditioner ! 9 - dont setup a preconditioner - if (precond == 0) then - call HYPRE_StructSMGCreate(mpi_comm_hypre, precond_solver, ierr) - call HYPRE_StructSMGSetMemoryUse(precond_solver, zero, ierr) - call HYPRE_StructSMGSetMaxIter(precond_solver, maxiter, ierr) - call HYPRE_StructSMGSetNumPreRelax(precond_solver, n_pre, ierr) - call HYPRE_StructSMGSetNumPostRelax(precond_solver, n_post, ierr) - call HYPRE_StructSMGSetTol(precond_solver, 0.0d0, ierr) - else if (precond == 1) then - call HYPRE_StructPFMGCreate(mpi_comm_hypre, precond_solver, ierr) - call HYPRE_StructPFMGSetMaxIter(precond_solver, maxiter, ierr) - ! weighted Jacobi = 1; red-black GS = 2 - call HYPRE_StructPFMGSetRelaxType(precond_solver, 2, ierr) - call HYPRE_StructPFMGSetNumPreRelax(precond_solver, n_pre, ierr) - call HYPRE_StructPFMGSetNumPostRelax(precond_solver, n_post, ierr) - call HYPRE_StructPFMGSetTol(precond_solver, 0.0d0, ierr) - ! call HYPRE_StructPFMGSetDxyz(precond_solver, dxyz, ierr) - ! call HYPRE_StructPFMGSetSkipRelax(precond, 1, ierr) - ! call HYPRE_StructPFMGSetRAPType(precond, 1, ierr) - else if (precond == 7 .and. solver_id == 5) then - call HYPRE_StructJacobiCreate(mpi_comm_hypre, precond_solver, ierr) - call HYPRE_StructJacobiSetMaxIter(precond_solver, maxiter, ierr) - else if (precond == 8) then - precond_solver = c_null_ptr - else if (precond == 9) then - precond_solver = c_null_ptr + if (solver%precond_id == 0) then + call HYPRE_StructSMGCreate(mpi_comm_hypre, solver%precond, ierr) + call HYPRE_StructSMGSetMemoryUse(solver%precond, zero, ierr) + call HYPRE_StructSMGSetMaxIter(solver%precond, solver%maxiter_precond, ierr) + call HYPRE_StructSMGSetNumPreRelax(solver%precond, solver%n_pre, ierr) + call HYPRE_StructSMGSetNumPostRelax(solver%precond, solver%n_post, ierr) + call HYPRE_StructSMGSetTol(solver%precond, 0.0d0, ierr) + else if (solver%precond_id == 1) then + call HYPRE_StructPFMGCreate(mpi_comm_hypre, solver%precond, ierr) + call HYPRE_StructPFMGSetMaxIter(solver%precond, solver%maxiter_precond, ierr) + ! weighted Jacobi = 1; red-black GS = 2 2 seems faster + call HYPRE_StructPFMGSetRelaxType(solver%precond, 2, ierr) + call HYPRE_StructPFMGSetNumPreRelax(solver%precond, solver%n_pre, ierr) + call HYPRE_StructPFMGSetNumPostRelax(solver%precond, solver%n_post, ierr) + call HYPRE_StructPFMGSetTol(solver%precond, 0.0d0, ierr) + ! call HYPRE_StructPFMGSetDxyz(precond, dxyz, ierr) + ! call HYPRE_StructPFMGSetSkipRelax(precond_id, 1, ierr) + ! call HYPRE_StructPFMGSetRAPType(precond_id, 1, ierr) + else if (solver%precond_id == 7 .and. solver%solver_id == 5) then + call HYPRE_StructJacobiCreate(mpi_comm_hypre, solver%precond, ierr) + call HYPRE_StructJacobiSetMaxIter(solver%precond, solver%maxiter_precond, ierr) + else if (solver%precond_id == 8) then + solver%precond = c_null_ptr + else if (solver%precond_id == 9) then + solver%precond = c_null_ptr else - write (*,*) 'Invalid preconditioner in inithypre', precond + write (*,*) 'Invalid preconditioner in inithypre', solver%precond_id write (*,*) 'Possbile values are (0) SMG (1) PFMG (8) DS (9) None' call exit(-1) endif end subroutine - subroutine exitprecond + subroutine exitprecond(solver) implicit none + type(solver_type), intent(inout) :: solver - if (precond == 0) then - call HYPRE_StructSMGDestroy(precond_solver, ierr) - else if (precond == 1) then - call HYPRE_StructPFMGDestroy(precond_solver, ierr) - else if (precond == 7) then - call HYPRE_StructJacobiDestroy(precond_solver, ierr) + if (solver%precond_id == 0) then + call HYPRE_StructSMGDestroy(solver%precond, ierr) + else if (solver%precond_id == 1) then + call HYPRE_StructPFMGDestroy(solver%precond, ierr) + else if (solver%precond_id == 7) then + call HYPRE_StructJacobiDestroy(solver%precond, ierr) endif end subroutine - subroutine inithypre + subroutine inithypre_grid + use modmpi, only : myid, myidx, myidy, nprocx, nprocy + use modglobal, only : imax, jmax, kmax, dzf, dzh, dx, dy, itot, jtot, lopenbc,lperiodic,lboundary + + use modfields, only : rhobf, rhobh implicit none ! NOTE: we assume kmax is larger than 7, ! when we make the stencil entries - real(real64), allocatable :: values(:) + real(real64), allocatable :: values(:),temp(:) real cx, cy, cz_down, cz_up, cc ! integer num_ghost(6) @@ -795,7 +819,7 @@ subroutine inithypre ! data num_ghost / 3, 3, 3, 3, 0, 0 / - allocate(values(imax*jmax*kmax)) + allocate(values(imax*jmax*7),temp(max(imax,jmax))) ! Have hypre reuse the comm world mpi_comm_hypre = MPI_COMM_WORLD%MPI_VAL @@ -820,6 +844,9 @@ subroutine inithypre periodic(1) = itot periodic(2) = jtot periodic(3) = 0 + ! Remove periodicity if openboundaries are used + if(lopenbc.and..not.lperiodic(1)) periodic(1)=0 + if(lopenbc.and..not.lperiodic(3)) periodic(2)=0 call HYPRE_StructGridSetPeriodic(grid, periodic, ierr) ! This is a collective call finalizing the grid assembly @@ -931,8 +958,85 @@ subroutine inithypre values(i+6) = cc ! center enddo + ! Modify the matrix at a single point to fix the over-all p + ! makes the matrix non-singular which may improve convergence + ! matrix is made symmetric by not coupling to the special element + ! if that element is 0, the couplings of other elements can be set to 0 + ! note: assumes kmax > 10, just to put the special point well inside the domain + if (k == kmax-10 .and. myidx == 0 .and. myidy == 0) then + i = imax/2 + j = jmax/2 + values( (i+j*imax)*7 + 1) = 0 + values( (i+j*imax)*7 + 2) = 0 + values( (i+j*imax)*7 + 3) = 0 + values( (i+j*imax)*7 + 4) = 0 + values( (i+j*imax)*7 + 5) = 0 + values( (i+j*imax)*7 + 6) = 0 + values( (i+j*imax)*7 + 7) = cc ! value here doesn't matter in theory, probably better conditioning if + ! similar to other diagonal values + + !! from here on, it's the neighbors of the special element + values( (i+1+j*imax)*7 + 1) = 0 ! west link of east neighbor + values( (i-1+j*imax)*7 + 2) = 0 ! east link of west neighbor + values( (i+(j+1)*imax)*7 + 3) = 0 ! south link of north neighbor + values( (i+(j-1)*imax)*7 + 4) = 0 ! north link of south neighbor + end if + if (k == kmax-9 .and. myidx == 0 .and. myidy == 0) then + i = imax/2 + j = jmax/2 + values( (i+j*imax)*7 + 5) = 0 ! down link of above neighbor + end if + if (k == kmax-11 .and. myidx == 0 .and. myidy == 0) then + i = imax/2 + j = jmax/2 + values( (i+j*imax)*7 + 6) = 0 ! up link of below neighbor + end if + + call HYPRE_StructMatrixSetBoxValues(matrixA, & ilower, iupper, 7, stencil_indices, values, ierr) + if(lopenbc) then ! Set potential neuman lateral boundary conditions + if(lboundary(1).and. .not. lperiodic(1)) then + call HYPRE_StructMatrixGetBoxValues(matrixA, & + (/0,myidy*jmax,k-1/),(/0,(myidy+1)*jmax-1,k-1/),1,(/6/),temp,ierr) + do i = 1, jmax*2,2 + values(i+0) = 0. + values(i+1) = temp((i+1)/2)+cx + end do + call HYPRE_StructMatrixSetBoxValues(matrixA, & + (/0,myidy*jmax,k-1/),(/0,(myidy+1)*jmax-1,k-1/),2,(/0,6/),values,ierr) + endif + if(lboundary(2).and. .not. lperiodic(2)) then + call HYPRE_StructMatrixGetBoxValues(matrixA, & + (/itot-1,myidy*jmax,k-1/),(/itot-1,(myidy+1)*jmax-1,k-1/),1,(/6/),temp,ierr) + do i = 1, jmax*2,2 + values(i+0) = 0. + values(i+1) = temp((i+1)/2)+cx + end do + call HYPRE_StructMatrixSetBoxValues(matrixA, & + (/itot-1,myidy*jmax,k-1/),(/itot-1,(myidy+1)*jmax-1,k-1/),2,(/1,6/),values,ierr) + endif + if(lboundary(3).and. .not. lperiodic(3)) then + call HYPRE_StructMatrixGetBoxValues(matrixA, & + (/myidx*imax,0,k-1/),(/(myidx+1)*imax-1,0,k-1/),1,(/6/),temp,ierr) + do i = 1,imax*2,2 + values(i+0) = 0. + values(i+1) = temp((i+1)/2)+cy + end do + call HYPRE_StructMatrixSetBoxValues(matrixA, & + (/myidx*imax,0,k-1/),(/(myidx+1)*imax-1,0,k-1/),2,(/2,6/),values,ierr) + endif + if(lboundary(4).and. .not. lperiodic(4)) then + call HYPRE_StructMatrixGetBoxValues(matrixA, & + (/myidx*imax,jtot-1,k-1/),(/(myidx+1)*imax-1,jtot-1,k-1/),1,(/6/),temp,ierr) + do i = 1,imax*2,2 + values(i+0) = 0. + values(i+1) = temp((i+1)/2)+cy + end do + call HYPRE_StructMatrixSetBoxValues(matrixA, & + (/myidx*imax,jtot-1,k-1/),(/(myidx+1)*imax-1,jtot-1,k-1/),2,(/3,6/),values,ierr) + endif + endif enddo call HYPRE_StructMatrixAssemble(matrixA, ierr) @@ -956,7 +1060,7 @@ subroutine inithypre ! initialize some values as starting point for the iterative solver do i=1,imax*jmax - values(i) = 1e-5 + values(i) = 0 !1e-5 enddo do k=1,kmax ilower(3) = k - 1 @@ -965,120 +1069,133 @@ subroutine inithypre enddo call HYPRE_StructVectorAssemble(vectorX, ierr) + deallocate(values,temp) + + end subroutine inithypre_grid + + subroutine inithypre_solver(solver,solver_id,maxiter,tolerance,precond_id,n_pre,n_post,maxiter_precond) + use modmpi, only : myid + implicit none + type(solver_type), intent(inout) :: solver + integer, intent(in) :: solver_id, maxiter, precond_id, n_pre, n_post, maxiter_precond + real, intent(in) :: tolerance !----------------------------------------------------------------------- ! 5. Choose a solver and initialize it !----------------------------------------------------------------------- - - if (solver_id == 1) then + solver%solver_id = solver_id + solver%maxiter = maxiter + solver%tolerance = tolerance + solver%precond_id = precond_id + solver%n_pre = n_pre + solver%n_post = n_post + solver%maxiter_precond = maxiter_precond + if (solver%solver_id == 1) then ! Solve the system using SMG if (myid == 0) then - write (*,*) 'Selected solver 1 (SMG) with parameters:', maxiter, tolerance, n_pre, n_post + write (*,*) 'Selected solver 1 (SMG) with parameters:', solver%maxiter, solver%tolerance, solver%n_pre, solver%n_post endif - call HYPRE_StructSMGCreate(mpi_comm_hypre, solver, ierr) - call HYPRE_StructSMGSetMemoryUse(solver, zero, ierr) - call HYPRE_StructSMGSetMaxIter(solver, maxiter, ierr) - call HYPRE_StructSMGSetTol(solver, tolerance, ierr) - call HYPRE_StructSMGSetRelChange(solver, zero, ierr) - call HYPRE_StructSMGSetNumPreRelax(solver, n_pre, ierr) - call HYPRE_StructSMGSetNumPostRelax(solver, n_post, ierr) - call HYPRE_StructSMGSetLogging(solver, 1, ierr) - call HYPRE_StructSMGSetup(solver, matrixA, vectorB, vectorX, ierr) - else if (solver_id == 2) then + call HYPRE_StructSMGCreate(mpi_comm_hypre, solver%solver, ierr) + call HYPRE_StructSMGSetMemoryUse(solver%solver, zero, ierr) + call HYPRE_StructSMGSetMaxIter(solver%solver, solver%maxiter, ierr) + call HYPRE_StructSMGSetTol(solver%solver, solver%tolerance, ierr) + call HYPRE_StructSMGSetRelChange(solver%solver, zero, ierr) + call HYPRE_StructSMGSetNumPreRelax(solver%solver, solver%n_pre, ierr) + call HYPRE_StructSMGSetNumPostRelax(solver%solver, solver%n_post, ierr) + call HYPRE_StructSMGSetLogging(solver%solver, hypre_logging, ierr) + call HYPRE_StructSMGSetup(solver%solver, matrixA, vectorB, vectorX, ierr) + else if (solver%solver_id == 2) then ! Solve the system using PFMG if (myid == 0) then - write (*,*) 'Selected solver 2 (PFMG) with parameters:', maxiter, tolerance, n_pre, n_post, precond + write (*,*) 'Selected solver 2 (PFMG) with parameters:', solver%maxiter, solver%tolerance, solver%n_pre, solver%n_post, solver%precond_id endif - call HYPRE_StructPFMGCreate(mpi_comm_hypre, solver, ierr) - call HYPRE_StructPFMGSetMaxIter(solver, maxiter, ierr) - call HYPRE_StructPFMGSetTol(solver, tolerance, ierr) - call HYPRE_StructPFMGSetRelChange(solver, zero, ierr) + call HYPRE_StructPFMGCreate(mpi_comm_hypre, solver%solver, ierr) + call HYPRE_StructPFMGSetMaxIter(solver%solver, solver%maxiter, ierr) + call HYPRE_StructPFMGSetTol(solver%solver, solver%tolerance, ierr) + call HYPRE_StructPFMGSetRelChange(solver%solver, zero, ierr) ! weighted Jacobi = 1; red-black GS = 2 - if (precond == 1 .or. precond == 2) then - call HYPRE_StructPFMGSetRelaxType(solver, precond, ierr) + if (solver%precond_id == 1 .or. solver%precond_id == 2) then + call HYPRE_StructPFMGSetRelaxType(solver%solver, solver%precond_id, ierr) else - write (*,*) 'Invalid preconditioner in inithypre', precond + write (*,*) 'Invalid preconditioner in inithypre', solver%precond_id write (*,*) 'Possbile values are (1) weighted jacobi (2) red-black GS' call exit(-1) endif - call HYPRE_StructPFMGSetNumPreRelax(solver, n_pre, ierr) - call HYPRE_StructPFMGSetNumPostRelax(solver, n_post, ierr) + call HYPRE_StructPFMGSetNumPreRelax(solver%solver, solver%n_pre, ierr) + call HYPRE_StructPFMGSetNumPostRelax(solver%solver, solver%n_post, ierr) ! call HYPRE_StructPFMGSetSkipRelax(solver, one) ! call HYPRE_StructPFMGSetDxyz(solver, dxyz, ierr) - call HYPRE_StructPFMGSetLogging(solver, 2, ierr) - call HYPRE_StructPFMGSetup(solver, matrixA, vectorB, vectorX, ierr) + call HYPRE_StructPFMGSetLogging(solver%solver, hypre_logging, ierr) + call HYPRE_StructPFMGSetup(solver%solver, matrixA, vectorB, vectorX, ierr) - else if (solver_id == 3) then + else if (solver%solver_id == 3) then ! Solve the system using BiCGSTAB if (myid == 0) then - write (*,*) 'Selected solver 3 (BiCGSTAB) with parameters:', maxiter, tolerance, n_pre, n_post, precond + write (*,*) 'Selected solver 3 (BiCGSTAB) with parameters:', solver%maxiter, solver%tolerance, solver%n_pre, solver%n_post, solver%precond_id endif - call HYPRE_StructBiCGSTABCreate(mpi_comm_hypre, solver, ierr) - call HYPRE_StructBiCGSTABSetMaxIter(solver, maxiter, ierr) - call HYPRE_StructBiCGSTABSetTol(solver, tolerance, ierr) - call initprecond - call HYPRE_StructBiCGSTABSetPrecond(solver, precond, precond_solver, ierr) + call HYPRE_StructBiCGSTABCreate(mpi_comm_hypre, solver%solver, ierr) + call HYPRE_StructBiCGSTABSetMaxIter(solver%solver, solver%maxiter, ierr) + call HYPRE_StructBiCGSTABSetTol(solver%solver, solver%tolerance, ierr) + call initprecond(solver) + call HYPRE_StructBiCGSTABSetPrecond(solver%solver, solver%precond_id, solver%precond, ierr) - call HYPRE_StructBiCGSTABSetLogging(solver, 2, ierr) - call HYPRE_StructBiCGSTABSetup(solver, matrixA, vectorB, vectorX, ierr) + call HYPRE_StructBiCGSTABSetLogging(solver%solver, hypre_logging, ierr) + call HYPRE_StructBiCGSTABSetup(solver%solver, matrixA, vectorB, vectorX, ierr) - else if (solver_id == 4) then + else if (solver%solver_id == 4) then ! Solve the system using GMRES if (myid == 0) then - write (*,*) 'Selected solver 4 (GMRES) with parameters:', maxiter, tolerance, n_pre, n_post, precond + write (*,*) 'Selected solver 4 (GMRES) with parameters:', solver%maxiter, solver%tolerance, solver%n_pre, solver%n_post, solver%precond_id endif - call HYPRE_StructGMRESCreate(mpi_comm_hypre, solver, ierr) - call HYPRE_StructGMRESSetTol(solver, tolerance, ierr) - call HYPRE_StructGMRESSetMaxIter(solver, maxiter, ierr) - call initprecond - call HYPRE_StructGMRESSetPrecond(solver, precond, precond_solver, ierr) + call HYPRE_StructGMRESCreate(mpi_comm_hypre, solver%solver, ierr) + call HYPRE_StructGMRESSetTol(solver%solver, solver%tolerance, ierr) + call HYPRE_StructGMRESSetMaxIter(solver%solver, solver%maxiter, ierr) + call initprecond(solver) + call HYPRE_StructGMRESSetPrecond(solver%solver, solver%precond_id, solver%precond, ierr) - call HYPRE_StructGMRESSetLogging(solver, 2, ierr) - call HYPRE_StructGMRESSetup(solver, matrixA, vectorB, vectorX, ierr) + call HYPRE_StructGMRESSetLogging(solver%solver, hypre_logging, ierr) + call HYPRE_StructGMRESSetup(solver%solver, matrixA, vectorB, vectorX, ierr) - else if (solver_id == 5) then + else if (solver%solver_id == 5) then ! Solve the system using PCG if (myid == 0) then - write (*,*) 'Selected solver 5 (PCG) with parameters:', maxiter, tolerance, n_pre, n_post, precond + write (*,*) 'Selected solver 5 (PCG) with parameters:', solver%maxiter, solver%tolerance, solver%n_pre, solver%n_post, solver%precond_id endif - call HYPRE_StructPCGCreate(mpi_comm_hypre, solver, ierr) - call HYPRE_StructPCGSetTol(solver, tolerance, ierr) - call HYPRE_StructPCGSetTwoNorm(solver, 1, ierr) - call HYPRE_StructPCGSetMaxIter(solver, maxiter, ierr) - call initprecond - call HYPRE_StructPCGSetPrecond(solver, precond, precond_solver, ierr) - - call HYPRE_StructPCGSetLogging(solver, 2, ierr) - call HYPRE_StructPCGSetPrintLevel(solver, 0, ierr) - call HYPRE_StructPCGSetup(solver, matrixA, vectorB, vectorX, ierr) - - else if (solver_id == 6) then + call HYPRE_StructPCGCreate(mpi_comm_hypre, solver%solver, ierr) + call HYPRE_StructPCGSetRelChange(solver%solver, solver%tolerance, ierr) + call HYPRE_StructPCGSetTwoNorm(solver%solver, 1, ierr) + call HYPRE_StructPCGSetMaxIter(solver%solver, solver%maxiter, ierr) + call initprecond(solver) + call HYPRE_StructPCGSetPrecond(solver%solver, solver%precond_id, solver%precond, ierr) + + call HYPRE_StructPCGSetLogging(solver%solver, hypre_logging, ierr) + call HYPRE_StructPCGSetPrintLevel(solver%solver, hypre_logging, ierr) + call HYPRE_StructPCGSetup(solver%solver, matrixA, vectorB, vectorX, ierr) + + else if (solver%solver_id == 6) then ! Solve the system using LGMRES if (myid == 0) then - write (*,*) 'Selected solver 6 (LGMRES) with parameters:', maxiter, tolerance, n_pre, n_post, precond + write (*,*) 'Selected solver 6 (LGMRES) with parameters:', solver%maxiter, solver%tolerance, solver%n_pre, solver%n_post, solver%precond_id endif - call HYPRE_StructLGMRESCreate(mpi_comm_hypre, solver, ierr) - call HYPRE_StructLGMRESSetTol(solver, tolerance, ierr) - call HYPRE_StructLGMRESSetMaxIter(solver, maxiter, ierr) - call initprecond - call HYPRE_StructLGMRESSetPrecond(solver, precond, precond_solver, ierr) + call HYPRE_StructLGMRESCreate(mpi_comm_hypre, solver%solver, ierr) + call HYPRE_StructLGMRESSetTol(solver%solver, solver%tolerance, ierr) + call HYPRE_StructLGMRESSetMaxIter(solver%solver, solver%maxiter, ierr) + call initprecond(solver) + call HYPRE_StructLGMRESSetPrecond(solver%solver, solver%precond_id, solver%precond, ierr) - call HYPRE_StructLGMRESSetLogging(solver, 2, ierr) - call HYPRE_StructLGMRESSetPrintLevel(solver, 0, ierr) - call HYPRE_StructLGMRESSetup(solver, matrixA, vectorB, vectorX, ierr) + call HYPRE_StructLGMRESSetLogging(solver%solver, hypre_logging, ierr) + call HYPRE_StructLGMRESSetPrintLevel(solver%solver, hypre_logging, ierr) + call HYPRE_StructLGMRESSetup(solver%solver, matrixA, vectorB, vectorX, ierr) else if (myid == 0) then - write (*,*) 'Invalid solver in inithypre', solver_id + write (*,*) 'Invalid solver in inithypre', solver%solver_id endif call exit(-1) endif - - deallocate(values) - - end subroutine + end subroutine inithypre_solver subroutine set_initial_guess(p) @@ -1101,13 +1218,35 @@ subroutine set_initial_guess(p) call HYPRE_StructVectorAssemble(vectorX, ierr) end subroutine - subroutine solve_hypre(p, converged) + subroutine set_zero_guess() + use modglobal, only : i1, j1, ih, jh, imax, jmax, kmax + implicit none + real values(imax,jmax) + + integer i,j,k + do k=1,kmax + do j=1,jmax + do i=1,imax + values(i,j) = 0. + enddo + enddo + ilower(3) = k - 1 + iupper(3) = k - 1 + call HYPRE_StructVectorSetBoxValues(vectorX, ilower, iupper, values, ierr) + enddo + call HYPRE_StructVectorAssemble(vectorX, ierr) + end subroutine + + subroutine solve_hypre(solver, p, converged) + use modmpi, only : myid, myidx, myidy + use modglobal, only : i1, j1, ih, jh, imax, jmax, kmax, rk3step implicit none real(pois_r), intent(inout) :: p(2-ih:i1+ih,2-jh:j1+jh,kmax) logical, intent(out) :: converged - real(real64) values(imax,jmax) + type(solver_type), intent(inout) :: solver + real values(imax,jmax) real(real64) final_res_norm integer i,j,k, num_iterations, stat @@ -1123,6 +1262,14 @@ subroutine solve_hypre(p, converged) enddo enddo + ! special point anchoring P + ! note should match the special point in the matrix + if (k == kmax-10 .and. myidx == 0 .and. myidy == 0) then + i = imax/2 + j = jmax/2 + values(i+1,j+1) = 0 ! values indexed from 1 here + end if + ilower(3) = k - 1 iupper(3) = k - 1 call HYPRE_StructVectorSetBoxValues(vectorB, ilower, iupper, values, ierr) @@ -1131,49 +1278,57 @@ subroutine solve_hypre(p, converged) ! use current values (ie. the solution to the previous call) as starting point + ! ...except in the beginning of a full RK step + !if(rk3step == 1) then + ! if (myid == 0) then + ! write(*,*) "Poisson solver: initial guess 0" + ! end if + ! call set_zero_guess() + !end if + !----------------------------------------------------------------------- ! 2. Call a solver !----------------------------------------------------------------------- - - if (solver_id == 1) then + stat = 0 + if (solver%solver_id == 1) then ! Solve the system using SMG - call HYPRE_StructSMGSolve(solver, matrixA, vectorB, vectorX, stat) - call HYPRE_StructSMGGetNumIterations(solver, num_iterations, ierr) - call HYPRE_StructSMGGetFinalRelative(solver, final_res_norm, ierr) + call HYPRE_StructSMGSolve(solver%solver, matrixA, vectorB, vectorX, stat) + call HYPRE_StructSMGGetNumIterations(solver%solver, num_iterations, ierr) + call HYPRE_StructSMGGetFinalRelative(solver%solver, final_res_norm, ierr) - else if (solver_id == 2) then + else if (solver%solver_id == 2) then ! Solve the system using PFMG - call HYPRE_StructPFMGSolve(solver, matrixA, vectorB, vectorX, stat) - call HYPRE_StructPFMGGetNumIteration(solver, num_iterations, ierr) - call HYPRE_StructPFMGGetFinalRelativ(solver, final_res_norm, ierr) + call HYPRE_StructPFMGSolve(solver%solver, matrixA, vectorB, vectorX, stat) + call HYPRE_StructPFMGGetNumIteration(solver%solver, num_iterations, ierr) + call HYPRE_StructPFMGGetFinalRelativ(solver%solver, final_res_norm, ierr) - else if (solver_id == 3) then + else if (solver%solver_id == 3) then ! Solve the system using BiCGSTAB - call HYPRE_StructBiCGSTABSolve(solver, matrixA, vectorB, vectorX, stat) - call HYPRE_StructBiCGSTABGetNumItera(solver, num_iterations, ierr) - call HYPRE_StructBiCGSTABGetFinalRel(solver, final_res_norm, ierr) + call HYPRE_StructBiCGSTABSolve(solver%solver, matrixA, vectorB, vectorX, stat) + call HYPRE_StructBiCGSTABGetNumItera(solver%solver, num_iterations, ierr) + call HYPRE_StructBiCGSTABGetFinalRel(solver%solver, final_res_norm, ierr) - else if (solver_id == 4) then + else if (solver%solver_id == 4) then ! Solve the system using GMRES - call HYPRE_StructGMRESSolve(solver, matrixA, vectorB, vectorX, stat) - call HYPRE_StructGMRESGetNumIteratio(solver, num_iterations, ierr) - call HYPRE_StructGMRESGetFinalRelati(solver, final_res_norm, ierr) + call HYPRE_StructGMRESSolve(solver%solver, matrixA, vectorB, vectorX, stat) + call HYPRE_StructGMRESGetNumIteratio(solver%solver, num_iterations, ierr) + call HYPRE_StructGMRESGetFinalRelati(solver%solver, final_res_norm, ierr) - else if (solver_id == 5) then + else if (solver%solver_id == 5) then ! Solve the system using PCG - call HYPRE_StructPCGSolve(solver, matrixA, vectorB, vectorX, stat) - call HYPRE_StructPCGGetNumIterations(solver, num_iterations, ierr) - call HYPRE_StructPCGGetFinalRelative(solver, final_res_norm, ierr) + call HYPRE_StructPCGSolve(solver%solver, matrixA, vectorB, vectorX, stat) + call HYPRE_StructPCGGetNumIterations(solver%solver, num_iterations, ierr) + call HYPRE_StructPCGGetFinalRelative(solver%solver, final_res_norm, ierr) - else if (solver_id == 6) then + else if (solver%solver_id == 6) then ! Solve the system using LGMRES - call HYPRE_StructLGMRESSolve(solver, matrixA, vectorB, vectorX, stat) - call HYPRE_StructLGMRESGetNumIter(solver, num_iterations, ierr) - call HYPRE_StructLGMRESGetFinalRel(solver, final_res_norm, ierr) + call HYPRE_StructLGMRESSolve(solver%solver, matrixA, vectorB, vectorX, stat) + call HYPRE_StructLGMRESGetNumIter(solver%solver, num_iterations, ierr) + call HYPRE_StructLGMRESGetFinalRel(solver%solver, final_res_norm, ierr) else - write (*,*) 'Invalid solver in solve_hypre', solver_id + write (*,*) 'Invalid solver in solve_hypre', solver%solver_id call exit(-1) endif @@ -1195,7 +1350,7 @@ subroutine solve_hypre(p, converged) call exit(-1) endif - if (num_iterations >= maxiter) then + if (num_iterations >= solver%maxiter) then converged = .false. return else @@ -1219,35 +1374,38 @@ subroutine solve_hypre(p, converged) enddo end subroutine - subroutine exithypre - + subroutine exithypre_solver(solver) implicit none - - if (solver_id == 1) then - call HYPRE_StructSMGDestroy(solver, ierr) - else if (solver_id == 2) then - call HYPRE_StructPFMGDestroy(solver, ierr) - else if (solver_id == 3) then - if (precond == 0) then - call HYPRE_StructSMGDestroy(precond_solver, ierr) - else if (precond == 1) then - call HYPRE_StructPFMGDestroy(precond_solver, ierr) + type(solver_type), intent(inout) :: solver + if (solver%solver_id == 1) then + call HYPRE_StructSMGDestroy(solver%solver, ierr) + else if (solver%solver_id == 2) then + call HYPRE_StructPFMGDestroy(solver%solver, ierr) + else if (solver%solver_id == 3) then + if (solver%precond_id == 0) then + call HYPRE_StructSMGDestroy(solver%precond, ierr) + else if (solver%precond_id == 1) then + call HYPRE_StructPFMGDestroy(solver%precond, ierr) endif - call HYPRE_StructBiCGSTABDestroy(solver, ierr) - else if (solver_id == 4) then - call exitprecond - call HYPRE_StructGMRESDestroy(solver, ierr) - else if (solver_id == 5) then - call exitprecond - call HYPRE_StructPCGDestroy(solver, ierr) - else if (solver_id == 6) then - call exitprecond - call HYPRE_StructLGMRESDestroy(solver, ierr) + call HYPRE_StructBiCGSTABDestroy(solver%solver, ierr) + else if (solver%solver_id == 4) then + call exitprecond(solver) + call HYPRE_StructGMRESDestroy(solver%solver, ierr) + else if (solver%solver_id == 5) then + call exitprecond(solver) + call HYPRE_StructPCGDestroy(solver%solver, ierr) + else if (solver%solver_id == 6) then + call exitprecond(solver) + call HYPRE_StructLGMRESDestroy(solver%solver, ierr) else - write (*,*) 'Invalid solver in exit_hypre', solver_id + write (*,*) 'Invalid solver in exit_hypre', solver%solver_id call exit(-1) endif + end subroutine exithypre_solver + + subroutine exithypre_grid + implicit none call HYPRE_StructGridDestroy(grid, ierr) call HYPRE_StructStencilDestroy(stencil, ierr) call HYPRE_StructMatrixDestroy(matrixA, ierr) @@ -1258,35 +1416,53 @@ subroutine exithypre #else private -public :: inithypre, solve_hypre, exithypre, set_initial_guess - +public :: inithypre_grid, inithypre_solver, solve_hypre, exithypre_grid, exithypre_solver, set_initial_guess, set_zero_guess contains - subroutine inithypre + subroutine inithypre_grid implicit none call error_and_exit() - end subroutine + end subroutine inithypre_grid - subroutine exithypre + subroutine inithypre_solver(solver,solver_id,maxiter,tolerance,precond_id,n_pre,n_post,maxiter_precond) implicit none + type(solver_type), intent(inout) :: solver + integer, intent(in) :: solver_id, maxiter, precond_id, n_pre, n_post, maxiter_precond + real, intent(in) :: tolerance call error_and_exit() - end subroutine + end subroutine inithypre_solver - subroutine set_initial_guess(p) + subroutine exithypre_grid + implicit none + + call error_and_exit() + end subroutine exithypre_grid + + subroutine exithypre_solver(solver) implicit none + type(solver_type), intent(inout) :: solver + call error_and_exit() + end subroutine exithypre_solver + subroutine set_initial_guess(p) + implicit none real(pois_r), intent(inout) :: p(2-ih:i1+ih,2-jh:j1+jh,kmax) call error_and_exit() end subroutine - subroutine solve_hypre(p, converged) - implicit none + subroutine set_zero_guess() + call error_and_exit() + end subroutine set_zero_guess + + subroutine solve_hypre(solver, p, converged) + implicit none real(pois_r), intent(inout) :: p(2-ih:i1+ih,2-jh:j1+jh,kmax) logical, intent(out) :: converged + type(solver_type), intent(inout) :: solver call error_and_exit() converged = .false. ! suppress warnings about intent(out) variable diff --git a/src/modlsm.f90 b/src/modlsm.f90 index a788eef2..f4e56d89 100644 --- a/src/modlsm.f90 +++ b/src/modlsm.f90 @@ -831,10 +831,11 @@ end subroutine calc_water_bcs ! the diffusion scheme, thermodynamics, ... ! subroutine calc_bulk_bcs - use modglobal, only : i1, j1, cp, rlv, fkar, zf, cu, cv, grav, rv, rd + use modglobal, only : i1, j1, i2, j2, cp, rlv, fkar, zf, cu, cv, grav, rv, rd, lopenbc,lboundary,lperiodic use modfields, only : rhof, thl0, qt0, u0, v0, thvh use modsurface, only : phim, phih use modmpi, only : excjs + use modopenboundary, only : openboundary_excjs use modsurfdata, only : & H, LE, G0, tskin, qskin, thlflux, qtflux, dthldz, dqtdz, & dudz, dvdz, ustar, obl, cliq, ra, rsveg, rssoil @@ -915,9 +916,15 @@ subroutine calc_bulk_bcs dvdz(i,j) = ustar(i,j) / (fkar * zf(1)) * phim(zf(1)/obl(i,j)) * (vcv/du_tot(i,j)) ! Cyclic BCs where needed. - !call excjs(ustar,2,i1,2,j1,1,1,1,1) - ustar_3D(1:size(ustar,1),1:size(ustar,2),1:1) => ustar - call excjs(ustar_3D,2,i1,2,j1,1,1,1,1) + ustar_3D(1:i2,1:j2,1:1) => ustar + + if(lopenbc) then ! Only use periodicity for non-domain boundaries when openboundaries are used + call openboundary_excjs(ustar_3D, 2,i1,2,j1,1,1,1,1, & + & (.not.lboundary(1:4)).or.lperiodic(1:4)) + else + !call excjs(ustar,2,i1,2,j1,1,1,1,1) + call excjs(ustar_3D,2,i1,2,j1,1,1,1,1) + endif ! Just for diagnostics (modlsmcrosssection) cliq(i,j) = tile_ws%frac(i,j) / land_frac(i,j) diff --git a/src/modmpi.f90 b/src/modmpi.f90 index 504ba9ac..2760fe6c 100644 --- a/src/modmpi.f90 +++ b/src/modmpi.f90 @@ -49,6 +49,7 @@ module modmpi integer :: nprocx = 1 integer :: nprocy = 0 integer :: mpierr + logical :: periods(2) = .true. real :: CPU_program !end time real :: CPU_program0 !start time @@ -141,7 +142,13 @@ module modmpi procedure :: excjs_real32 procedure :: excjs_real64 procedure :: excjs_logical - end interface + end interface excjs + + interface openboundary_excjs + procedure :: openboundary_excjs_real32 + procedure :: openboundary_excjs_real64 + end interface openboundary_excjs + interface slabsum procedure :: slabsum_real32 procedure :: slabsum_real64 @@ -444,6 +451,234 @@ subroutine excjs_real32(a,sx,ex,sy,ey,sz,ez,ih,jh) endif end subroutine excjs_real32 + subroutine openboundary_excjs_real32(a,sx,ex,sy,ey,sz,ez,ih,jh,switch) + implicit none + integer sx, ex, sy, ey, sz, ez, ih, jh + real(real32) a(sx-ih:ex+ih, sy-jh:ey+jh, sz:ez) + logical, dimension(4), intent(in) :: switch ! flags for which boundaries need exchange + type(MPI_STATUS) :: status + integer :: xl, yl, zl + type(MPI_REQUEST) :: reqn, reqs, reqe, reqw + type(MPI_REQUEST) :: reqrn, reqrs, reqre, reqrw + integer nssize, ewsize + real(real32),allocatable, dimension(:) :: sendn,recvn & + , sends,recvs & + , sende,recve & + , sendw,recvw + +! Calulate buffer lengths + xl = size(a,1) + yl = size(a,2) + zl = size(a,3) + +! Calculate buffer size + nssize = xl*jh*zl + ewsize = ih*yl*zl + + + if(nprocy .gt. 1)then + + ! Allocate send / receive buffers + allocate(sendn(nssize),sends(nssize),recvn(nssize),recvs(nssize)) + + if(switch(4)) sendn = reshape(a(:,ey-jh+1:ey,:),(/nssize/)) + if(switch(3)) sends = reshape(a(:,sy:sy+jh-1,:),(/nssize/)) + + ! Send north/south + if(switch(4)) call D_MPI_ISEND(sendn, nssize, nbrnorth, 4, comm3d, reqn, mpierr) + if(switch(3)) call D_MPI_ISEND(sends, nssize, nbrsouth, 5, comm3d, reqs, mpierr) + + ! Receive south/north + if(switch(3)) call D_MPI_IRECV(recvs, nssize, nbrsouth, 4, comm3d, reqrs, mpierr) + if(switch(4)) call D_MPI_IRECV(recvn, nssize, nbrnorth, 5, comm3d, reqrn, mpierr) + + ! Wait until data is received + if(switch(3)) call MPI_WAIT(reqrs, status, mpierr) + if(switch(4)) call MPI_WAIT(reqrn, status, mpierr) + + ! Write back buffers + if(switch(3)) a(:,sy-jh:sy-1,:) = reshape(recvs,(/xl,jh,zl/)) + if(switch(4)) a(:,ey+1:ey+jh,:) = reshape(recvn,(/xl,jh,zl/)) + + else + + ! Single processor, make sure the field is periodic + if(switch(3)) a(:,sy-jh:sy-1,:) = a(:,ey-jh+1:ey,:) + if(switch(3)) a(:,ey+1:ey+jh,:) = a(:,sy:sy+jh-1,:) + + endif + + if(nprocx .gt. 1)then + + ! Allocate send / receive buffers + allocate(sende(ewsize),sendw(ewsize),recve(ewsize),recvw(ewsize)) + + if(switch(2)) sende = reshape(a(ex-ih+1:ex,:,:),(/ewsize/)) + if(switch(1)) sendw = reshape(a(sx:sx+ih-1,:,:),(/ewsize/)) + + ! Send east/west + if(switch(2)) call D_MPI_ISEND(sende, ewsize, nbreast, 6, comm3d, reqe, mpierr) + if(switch(1)) call D_MPI_ISEND(sendw, ewsize, nbrwest, 7, comm3d, reqw, mpierr) + + ! Receive west/east + if(switch(1)) call D_MPI_IRECV(recvw, ewsize, nbrwest, 6, comm3d, reqrw, mpierr) + if(switch(2)) call D_MPI_IRECV(recve, ewsize, nbreast, 7, comm3d, reqre, mpierr) + + ! Wait until data is received + if(switch(1)) call MPI_WAIT(reqrw, status, mpierr) + if(switch(2)) call MPI_WAIT(reqre, status, mpierr) + + ! Write back buffers + if(switch(1)) a(sx-ih:sx-1,:,:) = reshape(recvw,(/ih,yl,zl/)) + if(switch(2)) a(ex+1:ex+ih,:,:) = reshape(recve,(/ih,yl,zl/)) + + else + + ! Single processor, make sure the field is periodic + if(switch(1)) a(sx-ih:sx-1,:,:) = a(ex-ih+1:ex,:,:) + if(switch(1)) a(ex+1:ex+ih,:,:) = a(sx:sx+ih-1,:,:) + + endif + + if(nprocy.gt.1)then + + ! Make sure data is sent + if(switch(4)) call MPI_WAIT(reqn, status, mpierr) + if(switch(3)) call MPI_WAIT(reqs, status, mpierr) + + deallocate (sendn, sends) + deallocate (recvn, recvs) + + endif + + if(nprocx.gt.1)then + + ! Make sure data is sent + if(switch(2)) call MPI_WAIT(reqe, status, mpierr) + if(switch(1)) call MPI_WAIT(reqw, status, mpierr) + + ! Deallocate buffers + deallocate (sende, sendw) + deallocate (recve, recvw) + + endif + end subroutine openboundary_excjs_real32 + + + subroutine openboundary_excjs_real64(a,sx,ex,sy,ey,sz,ez,ih,jh,switch) + implicit none + integer sx, ex, sy, ey, sz, ez, ih, jh + real(real64) a(sx-ih:ex+ih, sy-jh:ey+jh, sz:ez) + logical, dimension(4), intent(in) :: switch ! flags for which boundaries need exchange + type(MPI_STATUS) :: status + integer :: xl, yl, zl + type(MPI_REQUEST) :: reqn, reqs, reqe, reqw + type(MPI_REQUEST) :: reqrn, reqrs, reqre, reqrw + integer nssize, ewsize + real(real32),allocatable, dimension(:) :: sendn,recvn & + , sends,recvs & + , sende,recve & + , sendw,recvw + +! Calulate buffer lengths + xl = size(a,1) + yl = size(a,2) + zl = size(a,3) + +! Calculate buffer size + nssize = xl*jh*zl + ewsize = ih*yl*zl + + + if(nprocy .gt. 1)then + + ! Allocate send / receive buffers + allocate(sendn(nssize),sends(nssize),recvn(nssize),recvs(nssize)) + + if(switch(4)) sendn = reshape(a(:,ey-jh+1:ey,:),(/nssize/)) + if(switch(3)) sends = reshape(a(:,sy:sy+jh-1,:),(/nssize/)) + + ! Send north/south + if(switch(4)) call D_MPI_ISEND(sendn, nssize, nbrnorth, 4, comm3d, reqn, mpierr) + if(switch(3)) call D_MPI_ISEND(sends, nssize, nbrsouth, 5, comm3d, reqs, mpierr) + + ! Receive south/north + if(switch(3)) call D_MPI_IRECV(recvs, nssize, nbrsouth, 4, comm3d, reqrs, mpierr) + if(switch(4)) call D_MPI_IRECV(recvn, nssize, nbrnorth, 5, comm3d, reqrn, mpierr) + + ! Wait until data is received + if(switch(3)) call MPI_WAIT(reqrs, status, mpierr) + if(switch(4)) call MPI_WAIT(reqrn, status, mpierr) + + ! Write back buffers + if(switch(3)) a(:,sy-jh:sy-1,:) = reshape(recvs,(/xl,jh,zl/)) + if(switch(4)) a(:,ey+1:ey+jh,:) = reshape(recvn,(/xl,jh,zl/)) + + else + + ! Single processor, make sure the field is periodic + if(switch(3)) a(:,sy-jh:sy-1,:) = a(:,ey-jh+1:ey,:) + if(switch(3)) a(:,ey+1:ey+jh,:) = a(:,sy:sy+jh-1,:) + + endif + + if(nprocx .gt. 1)then + + ! Allocate send / receive buffers + allocate(sende(ewsize),sendw(ewsize),recve(ewsize),recvw(ewsize)) + + if(switch(2)) sende = reshape(a(ex-ih+1:ex,:,:),(/ewsize/)) + if(switch(1)) sendw = reshape(a(sx:sx+ih-1,:,:),(/ewsize/)) + + ! Send east/west + if(switch(2)) call D_MPI_ISEND(sende, ewsize, nbreast, 6, comm3d, reqe, mpierr) + if(switch(1)) call D_MPI_ISEND(sendw, ewsize, nbrwest, 7, comm3d, reqw, mpierr) + + ! Receive west/east + if(switch(1)) call D_MPI_IRECV(recvw, ewsize, nbrwest, 6, comm3d, reqrw, mpierr) + if(switch(2)) call D_MPI_IRECV(recve, ewsize, nbreast, 7, comm3d, reqre, mpierr) + + ! Wait until data is received + if(switch(1)) call MPI_WAIT(reqrw, status, mpierr) + if(switch(2)) call MPI_WAIT(reqre, status, mpierr) + + ! Write back buffers + if(switch(1)) a(sx-ih:sx-1,:,:) = reshape(recvw,(/ih,yl,zl/)) + if(switch(2)) a(ex+1:ex+ih,:,:) = reshape(recve,(/ih,yl,zl/)) + + else + + ! Single processor, make sure the field is periodic + if(switch(1)) a(sx-ih:sx-1,:,:) = a(ex-ih+1:ex,:,:) + if(switch(1)) a(ex+1:ex+ih,:,:) = a(sx:sx+ih-1,:,:) + + endif + + if(nprocy.gt.1)then + + ! Make sure data is sent + if(switch(4)) call MPI_WAIT(reqn, status, mpierr) + if(switch(3)) call MPI_WAIT(reqs, status, mpierr) + + deallocate (sendn, sends) + deallocate (recvn, recvs) + + endif + + if(nprocx.gt.1)then + + ! Make sure data is sent + if(switch(2)) call MPI_WAIT(reqe, status, mpierr) + if(switch(1)) call MPI_WAIT(reqw, status, mpierr) + + ! Deallocate buffers + deallocate (sende, sendw) + deallocate (recve, recvw) + + endif + end subroutine openboundary_excjs_real64 + + subroutine excjs_real64(a,sx,ex,sy,ey,sz,ez,ih,jh) implicit none integer sx, ex, sy, ey, sz, ez, ih, jh diff --git a/src/modopenboundary.f90 b/src/modopenboundary.f90 new file mode 100644 index 00000000..2eaf5b93 --- /dev/null +++ b/src/modopenboundary.f90 @@ -0,0 +1,1192 @@ +!> \file modopenboundary.f90 +!! Sets ghost cells at domain boundary and implements radiation +!! conditions for the boundary normal velocity components +!> +!! Sets ghost cells at domain boundary and implements radiation +!! conditions for the boundary normal velocity components +!> +!! \author Frans Liqui Lung +! This file is part of DALES. +! To do: +! - Allow for different zint and adjust rhointi accordingly +! - Correct non divergence free input more elegently +! - Change definition uphase for division by 0 +! - When to use nextval and currentval for nudging and check rtimee +! - How to handle vertical derivative in top boundary condition (full levels) now obtained from profile (horizontal average) +! - Use correct velocity level to determine in or outflow in full levels, half levels are used now +! - Check rtimee and half level nudgin and correction term +! - Use um or u0 in half levels +! - Add possibility for higher order integration schemes +! - Extent turbulent pertubation generation options +! +! DALES 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. +! +! DALES 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 this program. If not, see . +! +! Copyright 1993-2009 Delft University of Technology, Wageningen University, Utrecht University, KNMI +! +module modopenboundary +use modglobal, only : boundary_type,boundary,lopenbc,linithetero,lboundary,lperiodic,lsynturb,dzint,dxint,dyint,ntboundary,tboundary +use modsynturb, only : synturb,initsynturb,exitsynturb +use netcdf +use modprecision, only : field_r +use modmpi, only : openboundary_excjs + +implicit none +integer :: nxpatch, nypatch, nzpatch +real, dimension(:,:), allocatable :: uturbtemp,vturbtemp,wturbtemp +real, dimension(:), allocatable :: rhointi +real, dimension(:,:,:), allocatable :: thls_hetero,ps_hetero +integer :: nx1max,nx2max + +contains + subroutine initopenboundary + ! Initialisation routine for openboundaries + use modmpi, only : myidx, myidy, nprocx, nprocy,myid + use modglobal, only : imax,jmax,kmax,i1,j1,k1,dx,dy,dzf,itot,jtot,zf,zh,solver_id, & + & iadv_mom,iadv_thl,iadv_qt,iadv_tke,iadv_sv,nsv,cu,cv + use modsurfdata, only : isurf + implicit none + integer :: i,j + + if(.not.lopenbc) return + ! Check for conflicting options + if(solver_id == 0) stop 'Openboundaries only possible with HYPRE or FFTW pressure solver, change solver_id' + if(iadv_mom /=2) stop 'Only second order advection scheme supported with openboundaries, change iadv_mom to 2' + if(iadv_thl /=2) stop 'Only second order advection scheme supported with openboundaries, change iadv_thl to 2' + if(iadv_qt /=2) stop 'Only second order advection scheme supported with openboundaries, change iadv_qt to 2' + if(iadv_tke /=2) stop 'Only second order advection scheme supported with openboundaries, change iadv_tke to 2' + if(any(iadv_sv(1:nsv)/=2)) stop 'Only second order advection scheme supported with openboundaries, change iadv_sv to 2' + if(cu/=0.) stop 'Translation velocity not allowed in combination with open boundaries, set cu to 0' + if(cv/=0.) stop 'Translation velocity not allowed in combination with open boundaries, set cv to 0' + ! Check if boundary is present on process + if(myidx==0) lboundary(1) = .true. + if(myidx==nprocx-1) lboundary(2) = .true. + if(myidy==0) lboundary(3) = .true. + if(myidy==nprocy-1) lboundary(4) = .true. + lboundary(5) = .true. + ! Set boundary names + boundary(1)%name = "west" + boundary(2)%name = "east" + boundary(3)%name = "south" + boundary(4)%name = "north" + boundary(5)%name = "top" + ! Set dimensions for each boundary + boundary(1)%nx1 = jmax; boundary(1)%nx2 = kmax + boundary(2)%nx1 = jmax; boundary(2)%nx2 = kmax + boundary(3)%nx1 = imax; boundary(3)%nx2 = kmax + boundary(4)%nx1 = imax; boundary(4)%nx2 = kmax + boundary(5)%nx1 = imax; boundary(5)%nx2 = jmax + boundary(1)%nx1u = jmax; boundary(1)%nx2u = kmax + boundary(2)%nx1u = jmax; boundary(2)%nx2u = kmax + boundary(3)%nx1u = i1; boundary(3)%nx2u = kmax + boundary(4)%nx1u = i1; boundary(4)%nx2u = kmax + boundary(5)%nx1u = i1; boundary(5)%nx2u = jmax + boundary(1)%nx1v = j1; boundary(1)%nx2v = kmax + boundary(2)%nx1v = j1; boundary(2)%nx2v = kmax + boundary(3)%nx1v = imax; boundary(3)%nx2v = kmax + boundary(4)%nx1v = imax; boundary(4)%nx2v = kmax + boundary(5)%nx1v = imax; boundary(5)%nx2v = j1 + boundary(1)%nx1w = jmax; boundary(1)%nx2w = k1 + boundary(2)%nx1w = jmax; boundary(2)%nx2w = k1 + boundary(3)%nx1w = imax; boundary(3)%nx2w = k1 + boundary(4)%nx1w = imax; boundary(4)%nx2w = k1 + boundary(5)%nx1w = imax; boundary(5)%nx2w = jmax + ! Set number of patches for correction factor for radiation boundary conditions + if(dxint == -1.) dxint = real(itot)*dx ! Set dxint to entire width as default + if(dyint == -1.) dxint = real(jtot)*dy ! Set dyint to entire width as default + nxpatch = int(dx/dxint*real(itot)); + nypatch = int(dy/dyint*real(jtot)); + nzpatch = kmax ! For now vertical integration scale is set equal to dz + if(mod(dxint,dx)/=0 .or. mod(dyint,dy)/=0) stop 'dxint and dyint should be multiples of dx and dy respectively.' + boundary(1)%nx1patch = nypatch; boundary(1)%nx2patch = nzpatch + boundary(2)%nx1patch = nypatch; boundary(2)%nx2patch = nzpatch + boundary(3)%nx1patch = nxpatch; boundary(3)%nx2patch = nzpatch + boundary(4)%nx1patch = nxpatch; boundary(4)%nx2patch = nzpatch + boundary(5)%nx1patch = nxpatch; boundary(5)%nx2patch = nypatch + if(myid==0) print *,"dxint/dx,dyint/dy,nxpatch,nypatch",int(dxint/dx),int(dyint/dy),nxpatch,nypatch + ! Allocate phase velocity, correction term radiation boundaries and pertubation fields + do i = 1,5 + if(.not.lboundary(i) .or. lperiodic(i)) cycle ! Open boundary not present + allocate(boundary(i)%radcorr(boundary(i)%nx1patch,boundary(i)%nx2patch), & + boundary(i)%radcorrsingle(boundary(i)%nx1patch,boundary(i)%nx2patch), & + boundary(i)%uphase(boundary(i)%nx1patch,boundary(i)%nx2patch), & + boundary(i)%uphasesingle(boundary(i)%nx1patch,boundary(i)%nx2patch), & + boundary(i)%uturb(boundary(i)%nx1u,boundary(i)%nx2u), & + boundary(i)%vturb(boundary(i)%nx1v,boundary(i)%nx2v), & + boundary(i)%wturb(boundary(i)%nx1w,boundary(i)%nx2w), & + boundary(i)%thlturb(boundary(i)%nx1,boundary(i)%nx2), & + boundary(i)%qtturb(boundary(i)%nx1,boundary(i)%nx2), & + boundary(i)%e12turb(boundary(i)%nx1,boundary(i)%nx2)) + boundary(i)%uturb = 0.; boundary(i)%vturb = 0.; boundary(i)%wturb = 0. + boundary(i)%thlturb = 0.; boundary(i)%qtturb = 0.; boundary(i)%e12turb = 0. + if(nsv>0) then + allocate(boundary(i)%svturb(boundary(i)%nx1,boundary(i)%nx2,nsv)) + boundary(i)%svturb = 0. + endif + end do + call initsynturb + end subroutine initopenboundary + + subroutine exitopenboundary + ! Exit routine for openboundaries + use modglobal, only : nsv + implicit none + integer :: i + if(.not.lopenbc) return + deallocate(tboundary) + do i = 1,5 + if(.not.lboundary(i) .or. lperiodic(i)) cycle + deallocate(boundary(i)%thl,boundary(i)%qt,boundary(i)%e12, & + boundary(i)%u,boundary(i)%v,boundary(i)%w,boundary(i)%uphasesingle,boundary(i)%uphase, & + boundary(i)%radcorr,boundary(i)%radcorrsingle,boundary(i)%uturb,boundary(i)%vturb, & + boundary(i)%wturb,boundary(i)%thlturb,boundary(i)%qtturb,boundary(i)%name,boundary(i)%e12turb) + if(nsv>0) deallocate(boundary(i)%sv,boundary(i)%svturb) + end do + deallocate(rhointi) + call exitsynturb + end subroutine exitopenboundary + + subroutine openboundary_initfields + ! Routine that reads the fields for a heterogneous initialisation + ! Variables not present in the input file are initialised to their profiles + use modglobal, only : imax,jmax,kmax,i1,j1,cexpnr,nsv,i2,j2,k1 + use modfields, only : u0,um,v0,vm,w0,wm,thl0,thlm,qt0,qtm,e120,e12m, sv0, svm, & + uprof,vprof,thlprof,qtprof,e12prof,svprof + use modmpi, only : myidx,myidy,myid + implicit none + integer :: VARID,STATUS,NCID,mpierr,timeID,n + integer, dimension(3) :: istart + if(.not.lopenbc) return + if(.not.linithetero) return + u0 = 0.; um = 0.; v0 = 0.; vm = 0.; w0 = 0.; wm = 0.; + thl0 = 0.; thlm = 0.; qt0 = 0; qtm = 0; e120 = 0.; e12m = 0. + !--- open initfields.input.xxx.nc --- + STATUS = NF90_OPEN('initfields.inp.'//cexpnr//'.nc', nf90_nowrite, NCID) + if (STATUS .ne. nf90_noerr) call handle_err(STATUS) + istart = (/myidx*imax+1,myidy*jmax+1,1/) + STATUS = NF90_INQ_VARID(NCID,'u0', VARID) + if(STATUS == NF90_ENOTVAR) then ! If not available initialise with profile + call take_prof(u0,um,uprof) + if(myid==0) print *, "u initialized with prof.inp" + else + if (STATUS .ne. nf90_noerr) call handle_err(STATUS) + STATUS = NF90_GET_VAR (NCID, VARID,u0(2:i2,2:j1,1:kmax),start=istart,count=(/i1,jmax,kmax/)) + if (STATUS .ne. nf90_noerr) call handle_err(STATUS) + um = u0 + endif + STATUS = NF90_INQ_VARID(NCID,'v0', VARID) + if(STATUS == NF90_ENOTVAR) then ! If not available initialise with profile + call take_prof(v0,vm,vprof) + if(myid==0) print *, "v initialized with prof.inp" + else + if (STATUS .ne. nf90_noerr) call handle_err(STATUS) + STATUS = NF90_GET_VAR (NCID, VARID,v0(2:i1,2:j2,1:kmax),start=istart,count=(/imax,j1,kmax/)) + if (STATUS .ne. nf90_noerr) call handle_err(STATUS) + vm = v0 + endif + STATUS = NF90_INQ_VARID(NCID,'w0', VARID) + if(STATUS == NF90_ENOTVAR) then ! If not available initialise with 0. + wm = 0. + w0 = 0. + if(myid==0) print *, "w initilised with 0." + else + if (STATUS .ne. nf90_noerr) call handle_err(STATUS) + STATUS = NF90_GET_VAR (NCID, VARID,w0(2:i1,2:j1,1:k1),start=istart,count=(/imax,jmax,k1/)) + if (STATUS .ne. nf90_noerr) call handle_err(STATUS) + wm = w0 + endif + STATUS = NF90_INQ_VARID(NCID,'thl0', VARID) + if(STATUS == NF90_ENOTVAR) then ! If not available initialise with profile + call take_prof(thl0,thlm,thlprof) + if(myid==0) print *, "thl initialized with prof.inp" + else + if (STATUS .ne. nf90_noerr) call handle_err(STATUS) + STATUS = NF90_GET_VAR (NCID, VARID,thl0(2:i1,2:j1,1:kmax),start=istart,count=(/imax,jmax,kmax/)) + if (STATUS .ne. nf90_noerr) call handle_err(STATUS) + thlm = thl0 + endif + STATUS = NF90_INQ_VARID(NCID,'qt0', VARID) + if(STATUS == NF90_ENOTVAR) then ! If not available initialise with profile + call take_prof(qt0,qtm,qtprof) + if(myid==0) print *, "qt initialized with prof.inp" + else + if (STATUS .ne. nf90_noerr) call handle_err(STATUS) + STATUS = NF90_GET_VAR (NCID, VARID,qt0(2:i1,2:j1,1:kmax),start=istart,count=(/imax,jmax,kmax/)) + if (STATUS .ne. nf90_noerr) call handle_err(STATUS) + qtm = qt0 + endif + STATUS = NF90_INQ_VARID(NCID,'e120', VARID) + if(STATUS == NF90_ENOTVAR) then ! If not available initialise with profile + call take_prof(e120,e12m,e12prof) + if(myid==0) print *, "e12 initialized with prof.inp" + else + if (STATUS .ne. nf90_noerr) call handle_err(STATUS) + STATUS = NF90_GET_VAR (NCID, VARID,e120(2:i1,2:j1,1:kmax),start=istart,count=(/imax,jmax,kmax/)) + if (STATUS .ne. nf90_noerr) call handle_err(STATUS) + e12m = e120 + endif + if(nsv>0) then + STATUS = NF90_INQ_VARID(NCID,'sv0', VARID) + if(STATUS == NF90_ENOTVAR) then ! If not available initialise with profile + do n = 1,nsv + call take_prof(sv0(:,:,:,n),svm(:,:,:,n),svprof(:,n)) + end do + if(myid==0) print *, "sv initialized with scalar.inp" + else ! Initial field taken from initfields.inp + if (STATUS .ne. nf90_noerr) call handle_err(STATUS) + STATUS = NF90_GET_VAR (NCID, VARID,sv0(2:i1,2:j1,1:kmax,1),start=istart,count=(/imax,jmax,kmax,nsv/)) + if (STATUS .ne. nf90_noerr) call handle_err(STATUS) + svm = sv0 + endif + endif + status = nf90_close(ncid) + if (status /= nf90_noerr) call handle_err(status) + end subroutine openboundary_initfields + + subroutine openboundary_readboundary + ! Routine reads the boundary input for all time steps + use modglobal, only : kmax,cexpnr,imax,jmax,itot,jtot,k1,ntboundary, & + tboundary,i1,j1,i2,j2,kmax,nsv,iturb + use modmpi, only : myidx,myidy + implicit none + integer :: it,i,j,k,ib + character(len = nf90_max_name) :: RecordDimName + integer :: VARID,STATUS,NCID,mpierr,timeID + integer, dimension(3) :: istart + + if(.not.lopenbc) return + !--- open openboundaries.inp.xxx.nc --- + STATUS = NF90_OPEN('openboundaries.inp.'//cexpnr//'.nc', nf90_nowrite, NCID) + if (STATUS .ne. nf90_noerr) call handle_err(STATUS) + !--- get time dimensions + status = nf90_inq_dimid(ncid, "time", timeID) + if (status /= nf90_noerr) call handle_err(status) + status = nf90_inquire_dimension(NCID, timeID, len=ntboundary, name=RecordDimName) + if (STATUS .ne. nf90_noerr) call handle_err(STATUS) + !--- read time + allocate(tboundary(ntboundary)) + STATUS = NF90_INQ_VARID(NCID, 'time', VARID) + if (STATUS .ne. nf90_noerr) call handle_err(STATUS) + STATUS = NF90_GET_VAR (NCID, VARID, tboundary, start=(/1/), count=(/ntboundary/) ) + if (STATUS .ne. nf90_noerr) call handle_err(STATUS) + do ib = 1,5 ! loop over boundaries + ! Allocate input fields + if(.not.lboundary(ib) .or. lperiodic(ib)) cycle ! Open boundary not present + allocate(boundary(ib)%thl(boundary(ib)%nx1,boundary(ib)%nx2,ntboundary), & + boundary(ib)%qt(boundary(ib)%nx1,boundary(ib)%nx2,ntboundary), & + boundary(ib)%e12(boundary(ib)%nx1,boundary(ib)%nx2,ntboundary), & + boundary(ib)%u(boundary(ib)%nx1u,boundary(ib)%nx2u,ntboundary), & + boundary(ib)%v(boundary(ib)%nx1v,boundary(ib)%nx2v,ntboundary), & + boundary(ib)%w(boundary(ib)%nx1w,boundary(ib)%nx2w,ntboundary)) + if(nsv>0) then + allocate(boundary(ib)%sv(boundary(ib)%nx1,boundary(ib)%nx2,ntboundary,nsv)) + endif + if(lsynturb .and. iturb<10) then ! Allocate turbulent input fields + allocate(boundary(ib)%u2(boundary(ib)%nx1patch,boundary(ib)%nx2patch,ntboundary), & + & boundary(ib)%v2(boundary(ib)%nx1patch,boundary(ib)%nx2patch,ntboundary), & + & boundary(ib)%w2(boundary(ib)%nx1patch,boundary(ib)%nx2patch,ntboundary), & + & boundary(ib)%uv(boundary(ib)%nx1patch,boundary(ib)%nx2patch,ntboundary), & + & boundary(ib)%uw(boundary(ib)%nx1patch,boundary(ib)%nx2patch,ntboundary), & + & boundary(ib)%vw(boundary(ib)%nx1patch,boundary(ib)%nx2patch,ntboundary), & + & boundary(ib)%thl2(boundary(ib)%nx1patch,boundary(ib)%nx2patch,ntboundary),& + & boundary(ib)%qt2(boundary(ib)%nx1patch,boundary(ib)%nx2patch,ntboundary), & + & boundary(ib)%wthl(boundary(ib)%nx1patch,boundary(ib)%nx2patch,ntboundary), & + & boundary(ib)%wqt(boundary(ib)%nx1patch,boundary(ib)%nx2patch,ntboundary)) + endif + ! Read fields + select case(ib) + case(1,2) + istart = (/myidy*jmax+1,1,1/) + case(3,4) + istart = (/myidx*imax+1,1,1/) + case(5) + istart = (/myidx*imax+1,myidy*jmax+1,1/) + end select + ! Read u + STATUS = NF90_INQ_VARID(NCID,'u'//boundary(ib)%name, VARID) + if (STATUS .ne. nf90_noerr) call handle_err(STATUS) + STATUS = NF90_GET_VAR (NCID, VARID, boundary(ib)%u, start=istart, & + & count=(/boundary(ib)%nx1u,boundary(ib)%nx2u,ntboundary/)) + ! Read v + STATUS = NF90_INQ_VARID(NCID, 'v'//boundary(ib)%name, VARID) + if (STATUS .ne. nf90_noerr) call handle_err(STATUS) + STATUS = NF90_GET_VAR (NCID, VARID, boundary(ib)%v, start=istart, & + & count=(/boundary(ib)%nx1v,boundary(ib)%nx2v,ntboundary/)) + if (STATUS .ne. nf90_noerr) call handle_err(STATUS) + ! Read w + STATUS = NF90_INQ_VARID(NCID, 'w'//boundary(ib)%name, VARID) + if (STATUS .ne. nf90_noerr) call handle_err(STATUS) + STATUS = NF90_GET_VAR (NCID, VARID, boundary(ib)%w, start=istart, & + & count=(/boundary(ib)%nx1w,boundary(ib)%nx2w,ntboundary/)) + if (STATUS .ne. nf90_noerr) call handle_err(STATUS) + ! Read thl + STATUS = NF90_INQ_VARID(NCID, 'thl'//boundary(ib)%name, VARID) + if (STATUS .ne. nf90_noerr) call handle_err(STATUS) + STATUS = NF90_GET_VAR (NCID, VARID, boundary(ib)%thl, start=istart, & + & count=(/boundary(ib)%nx1,boundary(ib)%nx2,ntboundary/)) + if (STATUS .ne. nf90_noerr) call handle_err(STATUS) + ! Read qt + STATUS = NF90_INQ_VARID(NCID, 'qt'//boundary(ib)%name, VARID) + if (STATUS .ne. nf90_noerr) call handle_err(STATUS) + STATUS = NF90_GET_VAR (NCID, VARID, boundary(ib)%qt, start=istart, & + & count=(/boundary(ib)%nx1,boundary(ib)%nx2,ntboundary/)) + if (STATUS .ne. nf90_noerr) call handle_err(STATUS) + ! Read e12 + STATUS = NF90_INQ_VARID(NCID, 'e12'//boundary(ib)%name, VARID) + if (STATUS .ne. nf90_noerr) call handle_err(STATUS) + STATUS = NF90_GET_VAR (NCID, VARID, boundary(ib)%e12, start=istart, & + & count=(/boundary(ib)%nx1,boundary(ib)%nx2,ntboundary/)) + if (STATUS .ne. nf90_noerr) call handle_err(STATUS) + ! Read sv + if(nsv>0) then + STATUS = NF90_INQ_VARID(NCID, 'sv'//boundary(ib)%name, VARID) + if(STATUS == NF90_ENOTVAR) then + boundary(ib)%sv = 0. + if(myidx==0 .and. myidy==0) print *, "No boundary information for sv at boundary",ib,"Values set to 0" + else + if (STATUS .ne. nf90_noerr) call handle_err(STATUS) + STATUS = NF90_GET_VAR (NCID, VARID, boundary(ib)%sv, start=(/istart,1/), & + & count=(/boundary(ib)%nx1,boundary(ib)%nx2,ntboundary,nsv/)) + if (STATUS .ne. nf90_noerr) call handle_err(STATUS) + endif + endif + ! Read input for turbulent pertubations + if(lsynturb .and. iturb <10) then + ! Read u2 + STATUS = NF90_INQ_VARID(NCID, 'u2'//boundary(ib)%name, VARID) + if (STATUS .ne. nf90_noerr) call handle_err(STATUS) + STATUS = NF90_GET_VAR (NCID, VARID, boundary(ib)%u2, start=(/1,1,1/), & + & count=(/boundary(ib)%nx1patch,boundary(ib)%nx2patch,ntboundary/)) + if (STATUS .ne. nf90_noerr) call handle_err(STATUS) + ! Read v2 + STATUS = NF90_INQ_VARID(NCID, 'v2'//boundary(ib)%name, VARID) + if (STATUS .ne. nf90_noerr) call handle_err(STATUS) + STATUS = NF90_GET_VAR (NCID, VARID, boundary(ib)%v2, start=(/1,1,1/), & + & count=(/boundary(ib)%nx1patch,boundary(ib)%nx2patch,ntboundary/)) + if (STATUS .ne. nf90_noerr) call handle_err(STATUS) + ! Read w2 + STATUS = NF90_INQ_VARID(NCID, 'w2'//boundary(ib)%name, VARID) + if (STATUS .ne. nf90_noerr) call handle_err(STATUS) + STATUS = NF90_GET_VAR (NCID, VARID, boundary(ib)%w2, start=(/1,1,1/), & + & count=(/boundary(ib)%nx1patch,boundary(ib)%nx2patch,ntboundary/)) + if (STATUS .ne. nf90_noerr) call handle_err(STATUS) + ! Read uv + STATUS = NF90_INQ_VARID(NCID, 'uv'//boundary(ib)%name, VARID) + if (STATUS .ne. nf90_noerr) call handle_err(STATUS) + STATUS = NF90_GET_VAR (NCID, VARID, boundary(ib)%uv, start=(/1,1,1/), & + & count=(/boundary(ib)%nx1patch,boundary(ib)%nx2patch,ntboundary/)) + if (STATUS .ne. nf90_noerr) call handle_err(STATUS) + ! Read uw + STATUS = NF90_INQ_VARID(NCID, 'uw'//boundary(ib)%name, VARID) + if (STATUS .ne. nf90_noerr) call handle_err(STATUS) + STATUS = NF90_GET_VAR (NCID, VARID, boundary(ib)%uw, start=(/1,1,1/), & + & count=(/boundary(ib)%nx1patch,boundary(ib)%nx2patch,ntboundary/)) + if (STATUS .ne. nf90_noerr) call handle_err(STATUS) + ! Read vw + STATUS = NF90_INQ_VARID(NCID, 'vw'//boundary(ib)%name, VARID) + if (STATUS .ne. nf90_noerr) call handle_err(STATUS) + STATUS = NF90_GET_VAR (NCID, VARID, boundary(ib)%vw, start=(/1,1,1/), & + & count=(/boundary(ib)%nx1patch,boundary(ib)%nx2patch,ntboundary/)) + if (STATUS .ne. nf90_noerr) call handle_err(STATUS) + ! Read thl2 + STATUS = NF90_INQ_VARID(NCID, 'thl2'//boundary(ib)%name, VARID) + if (STATUS .ne. nf90_noerr) call handle_err(STATUS) + STATUS = NF90_GET_VAR (NCID, VARID, boundary(ib)%thl2, start=(/1,1,1/), & + & count=(/boundary(ib)%nx1patch,boundary(ib)%nx2patch,ntboundary/)) + if (STATUS .ne. nf90_noerr) call handle_err(STATUS) + ! Read qt2 + STATUS = NF90_INQ_VARID(NCID, 'qt2'//boundary(ib)%name, VARID) + if (STATUS .ne. nf90_noerr) call handle_err(STATUS) + STATUS = NF90_GET_VAR (NCID, VARID, boundary(ib)%qt2, start=(/1,1,1/), & + & count=(/boundary(ib)%nx1patch,boundary(ib)%nx2patch,ntboundary/)) + if (STATUS .ne. nf90_noerr) call handle_err(STATUS) + ! Read wthl + STATUS = NF90_INQ_VARID(NCID, 'wthl'//boundary(ib)%name, VARID) + if (STATUS .ne. nf90_noerr) call handle_err(STATUS) + STATUS = NF90_GET_VAR (NCID, VARID, boundary(ib)%wthl, start=(/1,1,1/), & + & count=(/boundary(ib)%nx1patch,boundary(ib)%nx2patch,ntboundary/)) + if (STATUS .ne. nf90_noerr) call handle_err(STATUS) + ! Read wqt + STATUS = NF90_INQ_VARID(NCID, 'wqt'//boundary(ib)%name, VARID) + if (STATUS .ne. nf90_noerr) call handle_err(STATUS) + STATUS = NF90_GET_VAR (NCID, VARID, boundary(ib)%wqt, start=(/1,1,1/), & + & count=(/boundary(ib)%nx1patch,boundary(ib)%nx2patch,ntboundary/)) + if (STATUS .ne. nf90_noerr) call handle_err(STATUS) + endif + end do + status = nf90_close(ncid) + if (status /= nf90_noerr) call handle_err(status) + end subroutine openboundary_readboundary + + subroutine openboundary_divcorr() + ! Correct for any integrated divergence present in the boundary input + use modmpi, only : myid,comm3d,mpierr,d_mpi_allreduce,MPI_SUM + use modglobal, only : imax,jmax,kmax,dzf,dy,dx,xsize,ysize,zh,k1,lwarmstart, & + i1,i2,j1,j2,k1,dzh + use modfields, only : u0,um,v0,vm,w0,wm,rhobf,rhobh + implicit none + real(field_r) :: sumdiv,div,divpart,divnew,divold + integer :: i,j,k,it,icalc + ! Create 1/int(rho) + allocate(rhointi(k1)) + rhointi = 1./(rhobf*dzf) + ! Divergence correction + if(myid==0) print *, "Start divergence correction boundaries" + do it = 1,ntboundary + do icalc=1,2 + ! Calculate divergence + div = 0. + if(lboundary(1)) then + do j = 1,jmax + do k = 1,kmax + div = div - rhobf(k)*boundary(1)%u(j,k,it)*dzf(k)*dy + end do + end do + endif + if(lboundary(2)) then + do j = 1,jmax + do k = 1,kmax + div = div + rhobf(k)*boundary(2)%u(j,k,it)*dzf(k)*dy + end do + end do + endif + if(lboundary(3)) then + do i = 1,imax + do k = 1,kmax + div = div - rhobf(k)*boundary(3)%v(i,k,it)*dzf(k)*dx + end do + end do + endif + if(lboundary(4)) then + do i = 1,imax + do k = 1,kmax + div = div + rhobf(k)*boundary(4)%v(i,k,it)*dzf(k)*dx + end do + end do + endif + if(lboundary(5)) then + do i = 1,imax + do j = 1,jmax + div = div + rhobh(k1)*boundary(5)%w(i,j,it)*dx*dy + end do + end do + endif + call D_MPI_ALLREDUCE(div,sumdiv,1,MPI_SUM,comm3d,mpierr) + if(icalc==1) then + divold=sumdiv + else + divnew=sumdiv + if(myid==0) print *, 't,input,corrected;',tboundary(it),divold,divnew + exit + endif + ! Apply correction, spread divergence over lateral boundaries + if(lboundary(1)) then + do k = 1,kmax + divpart = sumdiv*ysize*dzf(k)/(2*xsize*zh(k1)+2*ysize*zh(k1)) + boundary(1)%u(:,k,it)=boundary(1)%u(:,k,it)+divpart/(rhobf(k)*ysize*dzf(k)) + end do + endif + if(lboundary(2)) then + do k = 1,kmax + divpart = sumdiv*ysize*dzf(k)/(2*xsize*zh(k1)+2*ysize*zh(k1)) + boundary(2)%u(:,k,it)=boundary(2)%u(:,k,it)-divpart/(rhobf(k)*ysize*dzf(k)) + end do + endif + if(lboundary(3)) then + do k = 1,kmax + divpart = sumdiv*xsize*dzf(k)/(2*xsize*zh(k1)+2*ysize*zh(k1)) + boundary(3)%v(:,k,it)=boundary(3)%v(:,k,it)+divpart/(rhobf(k)*dzf(k)*xsize) + end do + endif + if(lboundary(4)) then + do k = 1,kmax + divpart = sumdiv*xsize*dzf(k)/(2*xsize*zh(k1)+2*ysize*zh(k1)) + boundary(4)%v(:,k,it)=boundary(4)%v(:,k,it)-divpart/(rhobf(k)*xsize*dzf(k)) + enddo + endif + end do + end do + if(myid==0) print *, "Finished divergence correction boundaries" + ! Copy data to boundary information + if(.not.lwarmstart) then + if(lboundary(1).and..not.lperiodic(1)) then + do j = 2,j1 + do k = 1,kmax + u0(2,j,k) = boundary(1)%u(j-1,k,1) + um(2,j,k) = boundary(1)%u(j-1,k,1) + end do + end do + endif + if(lboundary(2).and..not.lperiodic(2)) then + do j = 2,j1 + do k = 1,kmax + u0(i2,j,k) = boundary(2)%u(j-1,k,1) + um(i2,j,k) = boundary(2)%u(j-1,k,1) + end do + end do + endif + if(lboundary(3).and..not.lperiodic(3)) then + do i = 2,i1 + do k = 1,kmax + v0(i,2,k) = boundary(3)%v(i-1,k,1) + vm(i,2,k) = boundary(3)%v(i-1,k,1) + end do + end do + endif + if(lboundary(4).and..not.lperiodic(4)) then + do i = 2,i1 + do k = 1,kmax + v0(i,j2,k) = boundary(4)%v(i-1,k,1) + vm(i,j2,k) = boundary(4)%v(i-1,k,1) + end do + end do + endif + if(lboundary(5).and..not.lperiodic(5)) then + do i = 2,i1 + do j = 2,j1 + w0(i,j,k1) = boundary(5)%w(i-1,j-1,1) + wm(i,j,k1) = boundary(5)%w(i-1,j-1,1) + end do + end do + endif + endif + end subroutine openboundary_divcorr + + subroutine openboundary_ghost + ! Subroutine that fills the ghost cells for the cell centred variables at the boundary + use modglobal, only : i1,j1,k1,ih,jh,nsv + use modfields, only : um,u0,vm,v0,wm,w0,e12m,e120,thlm,thl0,qtm,qt0,svm,sv0,thl0av,qt0av,u0av,v0av + implicit none + integer :: i,n + if(.not.lopenbc) return + ! Apply non domain boundaries and periodic boundaries + call openboundary_excjs(um , 2,i1,2,j1,1,k1,ih,jh,.not.lboundary(1:4).or.lperiodic(1:4)) + call openboundary_excjs(u0 , 2,i1,2,j1,1,k1,ih,jh,.not.lboundary(1:4).or.lperiodic(1:4)) + call openboundary_excjs(vm , 2,i1,2,j1,1,k1,ih,jh,.not.lboundary(1:4).or.lperiodic(1:4)) + call openboundary_excjs(v0 , 2,i1,2,j1,1,k1,ih,jh,.not.lboundary(1:4).or.lperiodic(1:4)) + call openboundary_excjs(wm , 2,i1,2,j1,1,k1,ih,jh,.not.lboundary(1:4).or.lperiodic(1:4)) + call openboundary_excjs(w0 , 2,i1,2,j1,1,k1,ih,jh,.not.lboundary(1:4).or.lperiodic(1:4)) + call openboundary_excjs(e12m , 2,i1,2,j1,1,k1,ih,jh,.not.lboundary(1:4).or.lperiodic(1:4)) + call openboundary_excjs(e120 , 2,i1,2,j1,1,k1,ih,jh,.not.lboundary(1:4).or.lperiodic(1:4)) + call openboundary_excjs(thlm , 2,i1,2,j1,1,k1,ih,jh,.not.lboundary(1:4).or.lperiodic(1:4)) + call openboundary_excjs(thl0 , 2,i1,2,j1,1,k1,ih,jh,.not.lboundary(1:4).or.lperiodic(1:4)) + call openboundary_excjs(qtm , 2,i1,2,j1,1,k1,ih,jh,.not.lboundary(1:4).or.lperiodic(1:4)) + call openboundary_excjs(qt0 , 2,i1,2,j1,1,k1,ih,jh,.not.lboundary(1:4).or.lperiodic(1:4)) + do n = 1,nsv + call openboundary_excjs(svm(:,:,:,n), 2,i1,2,j1,1,k1,ih,jh,.not.lboundary(1:4).or.lperiodic(1:4)) + call openboundary_excjs(sv0(:,:,:,n), 2,i1,2,j1,1,k1,ih,jh,.not.lboundary(1:4).or.lperiodic(1:4)) + end do + ! Apply open boundaries for domain boundaries for full levels (ghost cells) + do i = 1,5 ! Loop over boundaries + if(.not.lboundary(i).or.lperiodic(i)) cycle + call applyboundaryf(thlm ,2,i1,2,j1,1,k1,ih,jh,i,boundary(i)%thl,boundary(i)%nx1,boundary(i)%nx2,0,boundary(i)%thlturb,profile=thl0av) + call applyboundaryf(thl0 ,2,i1,2,j1,1,k1,ih,jh,i,boundary(i)%thl,boundary(i)%nx1,boundary(i)%nx2,0,boundary(i)%thlturb,profile=thl0av) + call applyboundaryf(qtm ,2,i1,2,j1,1,k1,ih,jh,i,boundary(i)%qt,boundary(i)%nx1,boundary(i)%nx2,1,boundary(i)%qtturb,profile=qt0av) + call applyboundaryf(qt0 ,2,i1,2,j1,1,k1,ih,jh,i,boundary(i)%qt,boundary(i)%nx1,boundary(i)%nx2,1,boundary(i)%qtturb,profile=qt0av) + call applyboundaryf(e12m ,2,i1,2,j1,1,k1,ih,jh,i,boundary(i)%e12,boundary(i)%nx1,boundary(i)%nx2,1,boundary(i)%e12turb) + call applyboundaryf(e120 ,2,i1,2,j1,1,k1,ih,jh,i,boundary(i)%e12,boundary(i)%nx1,boundary(i)%nx2,1,boundary(i)%e12turb) + do n = 1,nsv + call applyboundaryf(svm(:,:,:,n) ,2,i1,2,j1,1,k1,ih,jh,i,boundary(i)%sv(:,:,:,n),boundary(i)%nx1,boundary(i)%nx2,1,boundary(i)%svturb(:,:,n)) + call applyboundaryf(sv0(:,:,:,n) ,2,i1,2,j1,1,k1,ih,jh,i,boundary(i)%sv(:,:,:,n),boundary(i)%nx1,boundary(i)%nx2,1,boundary(i)%svturb(:,:,n)) + end do + if(i/=1.and.i/=2) call applyboundaryf(um,2,i1,2,j1,1,k1,ih,jh,i,boundary(i)%u,boundary(i)%nx1u,boundary(i)%nx2u,0,boundary(i)%uturb,profile=u0av) + if(i/=1.and.i/=2) call applyboundaryf(u0,2,i1,2,j1,1,k1,ih,jh,i,boundary(i)%u,boundary(i)%nx1u,boundary(i)%nx2u,0,boundary(i)%uturb,profile=u0av) + if(i/=3.and.i/=4) call applyboundaryf(vm,2,i1,2,j1,1,k1,ih,jh,i,boundary(i)%v,boundary(i)%nx1v,boundary(i)%nx2v,0,boundary(i)%vturb,profile=v0av) + if(i/=3.and.i/=4) call applyboundaryf(v0,2,i1,2,j1,1,k1,ih,jh,i,boundary(i)%v,boundary(i)%nx1v,boundary(i)%nx2v,0,boundary(i)%vturb,profile=v0av) + if(i/=5) call applyboundaryf(wm,2,i1,2,j1,1,k1,ih,jh,i,boundary(i)%w,boundary(i)%nx1w,boundary(i)%nx2w,0,boundary(i)%wturb) + if(i/=5) call applyboundaryf(w0,2,i1,2,j1,1,k1,ih,jh,i,boundary(i)%w,boundary(i)%nx1w,boundary(i)%nx2w,0,boundary(i)%wturb) + end do + end subroutine openboundary_ghost + + subroutine openboundary_tend + ! Subroutine that handles the tendencies of the boundary normal velocity components + ! Radiation boundary conditions are used for outflow boundaries and nudging + ! boundary conditions for the inflow boundaries. + ! Outflow: + ! du/dt = -uphase*du/dx + ! Inflow: + ! du/dt = (u-uboundary)/tau + implicit none + integer :: ib + + if(.not.lopenbc) return + if(lboundary(1).and..not.lperiodic(1)) call applyboundaryh(1,boundary(1)%nx1,boundary(1)%nx2,boundary(1)%uturb) + if(lboundary(2).and..not.lperiodic(2)) call applyboundaryh(2,boundary(2)%nx1,boundary(2)%nx2,boundary(2)%uturb) + if(lboundary(3).and..not.lperiodic(3)) call applyboundaryh(3,boundary(3)%nx1,boundary(3)%nx2,boundary(3)%vturb) + if(lboundary(4).and..not.lperiodic(4)) call applyboundaryh(4,boundary(4)%nx1,boundary(4)%nx2,boundary(4)%vturb) + if(lboundary(5).and..not.lperiodic(5)) call applyboundaryh(5,boundary(5)%nx1,boundary(5)%nx2,boundary(5)%wturb) + ! Calculate and add correction term to guarantee conservation of mass + do ib = 1,5 + if(.not. lboundary(ib).or.lperiodic(ib)) cycle + call radcorrection(ib) + end do + end subroutine openboundary_tend + + subroutine openboundary_phasevelocity + ! Subroutine that calculates the phase velocity that is required for the + ! radiation outflow boundary. The phase velocity is calculated from the phase + ! velocity of one gridcell to the interior at the prior time and is averaged + ! over the integration length scales. Lower limit is given by input boundary + ! velocity and upper limit by cfd criterium. + use modmpi, only : comm3d,commrow,commcol,myidx,myidy,mpierr,D_MPI_ALLREDUCE,MPI_SUM + use modglobal, only : imax,jmax,kmax,i1,j1,dx,dy,dzf + use modfields, only : u0,up,v0,vp,w0,wp,rhobh,rhobf + implicit none + integer :: ib,i,j,k,ipatch,jpatch,kpatch + real :: ipos,jpos + + if(.not.lopenbc) return + do ib = 1,5 ! Loop over boundaries + if(.not.lboundary(ib).or.lperiodic(ib)) cycle + select case(ib) ! Select boundary + case(1) ! West + boundary(1)%uphasesingle=0. + do j = 1,jmax + jpos = j + (myidy * jmax) - 1 + jpatch = int((jpos-0.5)*dy/dyint)+1 + do k = 1,kmax + kpatch = k + boundary(1)%uphasesingle(jpatch,kpatch) = boundary(1)%uphasesingle(jpatch,kpatch) + & + (-up(3,j+1,k)*dx/sign(max(abs(u0(4,j+1,k)-u0(3,j+1,k)),real(1e-10, field_r)),u0(4,j+1,k)-u0(3,j+1,k))) & + *dy*rhobf(k)*dzf(k)/dyint*rhointi(kpatch) + end do + end do + ! Integrate over processes + call D_MPI_ALLREDUCE(boundary(1)%uphasesingle,boundary(1)%uphase,nypatch*nzpatch, & + MPI_SUM, commcol,mpierr) + case(2) ! East + boundary(2)%uphasesingle=0. + do j = 1,jmax + jpos = j + (myidy * jmax) - 1 + jpatch = int((jpos-0.5)*dy/dyint)+1 + do k = 1,kmax + kpatch = k + boundary(2)%uphasesingle(jpatch,kpatch) = boundary(2)%uphasesingle(jpatch,kpatch) + & + (-up(i1,j+1,k)*dx/sign(max(abs(u0(i1,j+1,k)-u0(i1-1,j+1,k)),real(1e-10, field_r)),u0(i1,j+1,k)-u0(i1-1,j+1,k))) & + *dy*rhobf(k)*dzf(k)/dyint*rhointi(kpatch) + end do + end do + ! Integrate over processes + call D_MPI_ALLREDUCE(boundary(2)%uphasesingle,boundary(2)%uphase,nypatch*nzpatch, & + MPI_SUM, commcol,mpierr) + case(3) ! South + boundary(3)%uphasesingle=0. + do i = 1,imax + ipos = i + (myidx * imax) - 1 + ipatch = int((ipos-0.5)*dx/dxint)+1 + do k = 1,kmax + kpatch = k + boundary(3)%uphasesingle(ipatch,kpatch) = boundary(3)%uphasesingle(ipatch,kpatch) + & + (-vp(i+1,3,k)*dy/sign(max(abs(v0(i+1,4,k)-v0(i+1,3,k)),real(1e-10, field_r)),v0(i+1,4,k)-v0(i+1,3,k))) & + *dx*rhobf(k)*dzf(k)/dxint*rhointi(kpatch) + end do + end do + ! Integrate over processes + call D_MPI_ALLREDUCE(boundary(3)%uphasesingle,boundary(3)%uphase,nxpatch*nzpatch, & + MPI_SUM, commrow,mpierr) + case(4) ! North + boundary(4)%uphasesingle=0. + do i = 1,imax + ipos = i + (myidx * imax) - 1 + ipatch = int((ipos-0.5)*dx/dxint)+1 + do k = 1,kmax + kpatch = k + boundary(4)%uphasesingle(ipatch,kpatch) = boundary(4)%uphasesingle(ipatch,kpatch) + & + (-vp(i+1,j1,k)*dy/sign(max(abs(v0(i+1,j1,k)-v0(i+1,j1-1,k)),real(1e-10, field_r)),v0(i+1,j1,k)-v0(i+1,j1-1,k))) & + *dx*rhobf(k)/dxint*rhointi(kpatch) + end do + end do + ! Integrate over processes + call D_MPI_ALLREDUCE(boundary(4)%uphasesingle,boundary(4)%uphase,nxpatch*nzpatch, & + MPI_SUM, commrow,mpierr) + case(5) ! Top + boundary(5)%uphasesingle=0. + do i = 1,imax + ipos = i + (myidx * imax) - 1 + ipatch = int((ipos-0.5)*dx/dxint)+1 + do j = 1,jmax + jpos = j + (myidy * jmax) - 1 + jpatch = int((jpos-0.5)*dy/dyint)+1 + boundary(5)%uphasesingle(ipatch,jpatch) = boundary(5)%uphasesingle(ipatch,jpatch) + & + (-rhobh(kmax)*wp(i+1,j+1,kmax)*dzf(kmax-1)/sign(max(abs(rhobh(kmax)*w0(i+1,j+1,kmax)-rhobh(kmax-1)*w0(i+1,j+1,kmax-1)),real(1e-10, field_r)), & + & rhobh(kmax)*w0(i+1,j+1,kmax)-rhobh(kmax-1)*w0(i+1,j+1,kmax-1)))*dx*dy/(dxint*dyint) + end do + end do + ! Integrate over processes + call D_MPI_ALLREDUCE(boundary(5)%uphasesingle,boundary(5)%uphase,nxpatch*nypatch, & + MPI_SUM, comm3d,mpierr) + end select + end do + end subroutine openboundary_phasevelocity + + subroutine openboundary_turb + ! Subroutine that calls the synthetic turbulence routine for the generation + ! of synthetic turbulence at the dirichlet inflow boundaries. + use modglobal, only : rk3step + implicit none + if(rk3step == 1) call synturb() + end subroutine openboundary_turb + + subroutine applyboundaryf(a,sx,ex,sy,ey,sz,ez,ih,jh,ib,val,nx1,nx2,lmax0,turb,profile) + ! Routine fills ghost cells based on robin (inflow) or + ! homogeneous neumann (outflow) boundary conditions. Adds turbulent + ! pertubations to inflow condition if lsynturb=true. + use modglobal, only : dzh,dx,dy,imax,jmax,kmax,rtimee,rdt,i2,j2,k1,i1,j1,tauh,pbc + use modfields, only : u0,v0,w0,e120 + use modmpi, only : myid + implicit none + integer, intent(in) :: sx,ex,sy,ey,sz,ez,ih,jh,ib,nx1,nx2,lmax0 + real(field_r), intent(in), dimension(nx1,nx2,ntboundary) :: val + real(field_r), intent(in), dimension(nx1,nx2) :: turb + real(field_r), intent(in), dimension(k1), optional :: profile ! optional for top boundary to take gradient into account + real(field_r), intent(inout), dimension(sx-ih:ex+ih,sy-jh:ey+jh,sz:ez) :: a + integer :: i,j,k,itp,itm,kav=5,itpn,itmn + real :: coefdir,coefneu,tp,tm,fp,fm,fpn,fmn,ddz,valtarget,un,e + + ! Get interpolation coefficients for boundary input + itm=1 + if(ntboundary>1) then + do while(rtimee-rdt>tboundary(itm)) + itm=itm+1 + end do + if (rtimee-rdt>tboundary(1)) then + itm=itm-1 + end if + itp = itm+1 + tm = tboundary(itm) + tp = tboundary(itp) + fm = (tp-rtimee+rdt)/(tp-tm) + fp = (rtimee-rdt-tm)/(tp-tm) + else + itp = 1 + fp = 0. + fm = 1. + endif + select case(ib) ! Select domain boundary + case(1) ! West + do k = 1,nx2 + do j = 1,nx1 + un = u0(sx,min(j+1,j1),min(k,kmax)) + if(un<=0) then ! Homogeneous Neumann outflow + a(sx-1,j+1,k)=a(sx,j+1,k) + else ! Robin inflow conditions + e = e120(sx,min(j+1,j1),min(k,kmax)) + coefdir = abs(un)**pbc + coefneu = -tauh*un*(abs(un)**pbc+e**pbc) + valtarget = (fp*val(j,k,itp)+fm*val(j,k,itm)+turb(j,k))*coefdir + a(sx-1,j+1,k) = ( 2.*dx*valtarget - & + a(sx,j+1,k)*(coefdir*dx+2.*coefneu) ) / (coefdir*dx-2.*coefneu) + if(lmax0==1) a(sx-1,j+1,k) = max(0.,a(sx-1,j+1,k)) + endif + end do + end do + case(2) ! East + do k = 1,nx2 + do j = 1,nx1 + un = u0(ex+1,min(j+1,j1),min(k,kmax)) + if(un>=0) then ! Homogeneous Neumann outflow + a(ex+1,j+1,k)=a(ex,j+1,k) + else ! Robin inflow conditions + e = e120(ex,min(j+1,j1),min(k,kmax)) + coefdir = abs(un)**pbc + coefneu = -tauh*un*(abs(un)**pbc+e**pbc) + valtarget = (fp*val(j,k,itp)+fm*val(j,k,itm)+turb(j,k))*coefdir + a(ex+1,j+1,k) = ( 2.*dx*valtarget - & + a(ex,j+1,k)*(coefdir*dx-2.*coefneu) ) / (coefdir*dx+2.*coefneu) + if(lmax0==1) a(ex+1,j+1,k) = max(a(ex+1,j+1,k),0.) + endif + end do + end do + case(3) ! South + do k = 1,nx2 + do i = 1,nx1 + un = v0(min(i+1,i1),sy,min(k,kmax)) + if(un<=0) then ! Homogeneous Neumann outflow + a(i+1,sy-1,k)=a(i+1,sy,k) + else ! Robin inflow conditions + e = e120(min(i+1,i1),sy,min(k,kmax)) + coefdir = abs(un)**pbc + coefneu = -tauh*un*(abs(un)**pbc+e**pbc) + valtarget = (fp*val(i,k,itp)+fm*val(i,k,itm)+turb(i,k))*coefdir + a(i+1,sy-1,k) = ( 2.*dy*valtarget - & + a(i+1,sy,k)*(coefdir*dy+2.*coefneu) ) / (coefdir*dy-2.*coefneu) + if(lmax0==1) a(i+1,sy-1,k) = max(a(i+1,sy-1,k),0.) + endif + end do + end do + case(4) ! North + do k = 1,nx2 + do i = 1,nx1 + un = v0(min(i+1,i1),ey+1,min(k,kmax)) + if(un>=0) then ! Homogeneous Neumann outflow + a(i+1,ey+1,k)=a(i+1,ey,k) + else ! Robin inflow conditions + e = e120(min(i+1,i1),ey,min(k,kmax)) + coefdir = abs(un)**pbc + coefneu = -tauh*un*(abs(un)**pbc+e**pbc) + valtarget = (fp*val(i,k,itp)+fm*val(i,k,itm)+turb(i,k))*coefdir + a(i+1,ey+1,k) = ( 2.*dy*valtarget - & + a(i+1,ey,k)*(coefdir*dy-2.*coefneu) ) / (coefdir*dy+2.*coefneu) + if(lmax0==1) a(i+1,ey+1,k) = max(a(i+1,ey+1,k),0.) + endif + end do + end do + case(5) ! Top + ! Obtain verticle gradient if slab averaged profile is given + if(present(profile)) then + ddz = sum((profile(kmax-kav+1:kmax)-profile(kmax-kav:kmax-1))/ & + dzh(kmax-kav+1:kmax))/kav + else + ddz = 0. + endif + do i = 1,nx1 + do j = 1,nx2 + un = w0(min(i+1,i1),min(j+1,j1),ez) + if(un>=0) then ! Neumann outflow + a(i+1,j+1,ez)=ddz*dzh(ez)+a(i+1,j+1,ez-1) + else ! Robin inflow conditions + e = e120(min(i+1,i1),min(j+1,j1),ez-1) + coefdir = abs(un)**pbc + coefneu = -tauh*un*(abs(un)**pbc+e**pbc) + valtarget = (fp*val(i,j,itp)+fm*val(i,j,itm)+turb(i,j))*coefdir+ddz*coefneu + a(i+1,j+1,ez) = ( 2.*dzh(ez)*valtarget - & + a(i+1,j+1,ez-1)*(coefdir*dzh(ez)-2.*coefneu) ) / (coefdir*dzh(ez)+2.*coefneu) + if(lmax0==1) a(i+1,j+1,ez) = max(a(i+1,j+1,ez),0.) + endif + end do + end do + end select + end subroutine applyboundaryf + + subroutine applyboundaryh(ib,nx1,nx2,turb) + ! Subroutine that applies the radiation and dirichlet boundary conditions + ! for the boundary-normal velocity components. Adds turbulence to + ! the inflow dirichlet boundaries if lsynturb=.true. + use modmpi, only : myidx,myidy,myid + use modglobal, only : dx,dy,dzf,dxi,dyi,rdt,i2,j2,k1,i1,j1,kmax,rtimee,rdt,itot,jtot,imax,jmax,grav,taum + use modfields, only : um,u0,up,vm,v0,vp,wm,w0,wp,rhobf,rhobh,thvh,thv0h + implicit none + integer, intent(in) :: nx1,nx2,ib + real(field_r), intent(in), dimension(nx1,nx2) :: turb + integer :: i,j,k,itmc,itmn,itpc,itpn,ipatch,jpatch,kpatch + real :: tm,tp,fpc,fmc,fpn,fmn,unext,uwallcurrent,ipos,jpos,tau + itmc=1 + itmn=1 + if(ntboundary>1) then + do while(rtimee-rdt>tboundary(itmc)) + itmc=itmc+1 + end do + if (rtimee-rdt>tboundary(1)) then + itmc=itmc-1 + end if + do while(tboundary(itmn)tboundary(1)) then + itmn=itmn-1 + end if + itpc = itmc+1 + itpn = itmn+1 + tm = tboundary(itmc) + tp = tboundary(itpc) + fmc = (tp-rtimee+rdt)/(tp-tm) + fpc = (rtimee-rdt-tm)/(tp-tm) + tm = tboundary(itmn) + tp = tboundary(itpn) + fmn = (tp-rtimee)/(tp-tm) + fpn = (rtimee-tm)/(tp-tm) + else + itpc = 1 + itpn = 1 + fpc = 0. + fmc = 1. + fpn = 0. + fmn = 1. + endif + ! Apply domain boundaries + select case(ib) ! Select boundary + case(1) ! West + tau = max(taum,rdt) + do j = 1,nx1 + jpos = j + (myidy * jmax) - 1 + jpatch = int((jpos-0.5)*dy/dyint)+1 + do k = 1,nx2 + kpatch = k + uwallcurrent = fpc*boundary(1)%u(j,k,itpc)+fmc*boundary(1)%u(j,k,itmc) + if(uwallcurrent<=0.) then ! Outflow (Radiation) + up(2,j+1,k) = -max(min(boundary(1)%uphase(jpatch,kpatch),uwallcurrent),-dx/rdt) * & + (u0(3,j+1,k)-u0(2,j+1,k))*dxi + else ! Inflow nudging + unext = fpn*boundary(1)%u(j,k,itpn)+fmn*boundary(1)%u(j,k,itmn) + up(2,j+1,k) = ((unext+turb(j,k)) - u0(2,j+1,k))/tau + endif + end do + end do + case(2) ! East + tau = max(taum,rdt) + do j = 1,nx1 + jpos = j + (myidy * jmax) - 1 + jpatch = int((jpos-0.5)*dy/dyint)+1 + do k = 1,nx2 + kpatch = k + uwallcurrent = fpc*boundary(2)%u(j,k,itpc)+fmc*boundary(2)%u(j,k,itmc) + if(uwallcurrent>=0.) then ! Outflow (Radiation) + up(i2,j+1,k) = -min(max(boundary(2)%uphase(jpatch,kpatch),uwallcurrent),dx/rdt) * & + (u0(i2,j+1,k)-u0(i1,j+1,k))*dxi + else ! Inflow (Dirichlet) + unext = fpn*boundary(2)%u(j,k,itpn)+fmn*boundary(2)%u(j,k,itmn) + up(i2,j+1,k) = ((unext+turb(j,k)) - u0(i2,j+1,k))/tau + endif + end do + end do + case(3) ! South + tau = max(taum,rdt) + do i = 1,nx1 + ipos = i + (myidx * imax) - 1 + ipatch = int((ipos-0.5)*dx/dxint)+1 + do k = 1,nx2 + kpatch = k + uwallcurrent = fpc*boundary(3)%v(i,k,itpc)+fmc*boundary(3)%v(i,k,itmc) + if(uwallcurrent<=0.) then ! Outflow (Radiation) + vp(i+1,2,k) = -max(min(boundary(3)%uphase(ipatch,kpatch),uwallcurrent),-dy/rdt) * & + (v0(i+1,3,k)-v0(i+1,2,k))*dyi + else ! Inflow (Dirichlet) + unext = fpn*boundary(3)%v(i,k,itpn)+fmn*boundary(3)%v(i,k,itmn) + vp(i+1,2,k) = ((unext+turb(i,k)) - v0(i+1,2,k))/tau + endif + end do + end do + case(4) ! North + tau = max(taum,rdt) + do i = 1,nx1 + ipos = i + (myidx * imax) - 1 + ipatch = int((ipos-0.5)*dx/dxint)+1 + do k = 1,nx2 + kpatch = k + uwallcurrent = fpc*boundary(4)%v(i,k,itpc)+fmc*boundary(4)%v(i,k,itmc) + if(uwallcurrent>=0.) then ! Outflow (Radiation) + vp(i+1,j2,k) = -min(max(boundary(4)%uphase(ipatch,kpatch),uwallcurrent),dy/rdt) * & + (v0(i+1,j2,k)-v0(i+1,j1,k))*dyi + else ! Inflow (Dirichlet) + unext = fpn*boundary(4)%v(i,k,itpn)+fmn*boundary(4)%v(i,k,itmn) + vp(i+1,j2,k) = ((unext+turb(i,k)) - v0(i+1,j2,k))/tau + endif + end do + end do + case(5) ! Top + tau = max(taum,rdt) + do i = 1,nx1 + ipos = i + (myidx * imax) - 1 + ipatch = int((ipos-0.5)*dx/dxint)+1 + do j = 1,nx2 + jpos = j + (myidy * jmax) - 1 + jpatch = int((jpos-0.5)*dy/dyint)+1 + uwallcurrent = fpc*boundary(5)%w(i,j,itpc)+fmc*boundary(5)%w(i,j,itmc) + if(uwallcurrent>=0.) then ! Outflow (Radiation) + wp(i+1,j+1,k1) = -min(max(boundary(5)%uphase(ipatch,jpatch),uwallcurrent),dzf(kmax)/rdt) * & + (rhobh(k1)*w0(i+1,j+1,k1)-rhobh(kmax)*w0(i+1,j+1,kmax))/(dzf(kmax)*rhobh(k1)) + & + grav*(thv0h(i+1,j+1,k1)-thvh(k1))/thvh(k1) + else ! Inflow (Dirichlet) + unext = fpn*boundary(5)%w(i,j,itpn)+fmn*boundary(5)%w(i,j,itmn) + wp(i+1,j+1,k1) = ((unext+turb(i,j)) - w0(i+1,j+1,k1))/tau + endif + end do + end do + end select + end subroutine applyboundaryh + + subroutine radcorrection(ib) + ! Calculates the integrated mass correction term for the boundary normal + ! velocity components + use modmpi, only : comm3d,commrow,commcol,myidx,myidy,mpierr, D_MPI_ALLREDUCE, MPI_SUM + use modglobal, only : jmax,imax,kmax,i1,j1,dx,dy,dzf,i2,j2,k1,dxi,dyi,rtimee,rdt + use modfields, only : rhobf, up, vp, wp + implicit none + integer, intent(in) :: ib + integer :: ipos,jpos,kpos,ipatch,jpatch,kpatch,i,j,k,itp,itm + real :: sum,tp,tm,idtb,dubdt + + itm = 1 + if(ntboundary>1) then + do while(rtimee-rdt>tboundary(itm)) + itm=itm+1 + end do + if (rtimee-rdt>tboundary(1)) then + itm=itm-1 + end if + itp = itm+1 + else + itp = 1 + endif + tm = tboundary(itm) + tp = tboundary(itp) + idtb = 1./max(1e-6,tp-tm) + select case(ib) ! Select boundary + case(1) ! West + ! Calculate correction term for each patch + boundary(1)%radcorrsingle = 0. + do j = 2,j1 + jpos = j + (myidy * jmax) - 1 + jpatch = int((jpos-0.5)*dy/dyint)+1 + do k = 1,kmax + kpatch = k + dubdt = (boundary(1)%u(j-1,k,itp)-boundary(1)%u(j-1,k,itm))*idtb + boundary(1)%radcorrsingle(jpatch,kpatch) = boundary(1)%radcorrsingle(jpatch,kpatch) + & + rhobf(k)*(-up(2,j,k)+dubdt)*dzf(k)*dy*rhointi(kpatch)/dyint + end do + end do + ! Communicate integration between processes + call D_MPI_ALLREDUCE(boundary(1)%radcorrsingle,boundary(1)%radcorr,nypatch*nzpatch, & + MPI_SUM, commcol,mpierr) + ! Apply correction term + do j = 2,j1 + jpos = j + (myidy * jmax) - 1 + jpatch = int((jpos-0.5)*dy/dyint)+1 + do k = 1,kmax + kpatch = k + up(2,j,k) = up(2,j,k) + boundary(1)%radcorr(jpatch,kpatch) + end do + end do + case(2) ! East + ! Calculate correction term for each patch + boundary(2)%radcorrsingle = 0. + do j = 2,j1 + jpos = j + (myidy * jmax) - 1 + jpatch = int((jpos-0.5)*dy/dyint)+1 + do k = 1,kmax + kpatch = k + dubdt = (boundary(2)%u(j-1,k,itp)-boundary(2)%u(j-1,k,itm))*idtb + boundary(2)%radcorrsingle(jpatch,kpatch) = boundary(2)%radcorrsingle(jpatch,kpatch) + & + rhobf(k)*(-up(i2,j,k)+dubdt)*dzf(k)*dy*rhointi(kpatch)/dyint + end do + end do + ! Communicate integration between processes + call D_MPI_ALLREDUCE(boundary(2)%radcorrsingle,boundary(2)%radcorr,nypatch*nzpatch, & + MPI_SUM, commcol,mpierr) + ! Apply correction term + do j = 2,j1 + jpos = j + (myidy * jmax) - 1 + jpatch = int((jpos-0.5)*dy/dyint)+1 + do k = 1,kmax + kpatch = k + up(i2,j,k) = up(i2,j,k) + boundary(2)%radcorr(jpatch,kpatch) + end do + end do + case(3) ! South + ! Calculate correction term for each patch + boundary(3)%radcorrsingle= 0. + do i = 2,i1 + ipos = i + (myidx * imax) - 1 + ipatch = int((ipos-0.5)*dx/dxint)+1 + do k = 1,kmax + kpatch = k + dubdt = (boundary(3)%v(i-1,k,itp)-boundary(3)%v(i-1,k,itm))*idtb + boundary(3)%radcorrsingle(ipatch,kpatch) = boundary(3)%radcorrsingle(ipatch,kpatch) + & + rhobf(k)*(-vp(i,2,k)+dubdt)*dzf(k)*dx*rhointi(kpatch)/dxint + end do + end do + ! Communicate integration between processes + call D_MPI_ALLREDUCE(boundary(3)%radcorrsingle,boundary(3)%radcorr,nxpatch*nzpatch, & + MPI_SUM, commrow,mpierr) + ! Apply correction term + do i = 2,i1 + ipos = i + (myidx * imax) - 1 + ipatch = int((ipos-0.5)*dx/dxint)+1 + do k = 1,kmax + kpatch = k + vp(i,2,k) = vp(i,2,k) + boundary(3)%radcorr(ipatch,kpatch) + end do + end do + case(4) ! North + ! Calculate correction term for each patch + boundary(4)%radcorrsingle = 0. + do i = 2,i1 + ipos = i + (myidx * imax) - 1 + ipatch = int((ipos-0.5)*dx/dxint)+1 + do k = 1,kmax + kpatch = k + dubdt = (boundary(4)%v(i-1,k,itp)-boundary(4)%v(i-1,k,itm))*idtb + boundary(4)%radcorrsingle(ipatch,kpatch) = boundary(4)%radcorrsingle(ipatch,kpatch) + & + rhobf(k)*(-vp(i,j2,k)+dubdt)*dzf(k)*dx*rhointi(kpatch)/dxint + end do + end do + ! Communicate integration between processes + call D_MPI_ALLREDUCE(boundary(4)%radcorrsingle,boundary(4)%radcorr,nxpatch*nzpatch, & + MPI_SUM, commrow,mpierr) + ! Apply correction term + do i = 2,i1 + ipos = i + (myidx * imax) - 1 + ipatch = int((ipos-0.5)*dx/dxint)+1 + do k = 1,kmax + kpatch = k + vp(i,j2,k) = vp(i,j2,k) + boundary(4)%radcorr(ipatch,kpatch) + end do + end do + case(5) ! Top + ! Calculate correction term for each patch + boundary(5)%radcorrsingle = 0. + do i = 2,i1 + ipos = i + (myidx * imax) - 1 + ipatch = int((ipos-0.5)*dx/dxint)+1 + do j = 2,j1 + jpos = j + (myidy * jmax) - 1 + jpatch = int((jpos-0.5)*dy/dyint)+1 + dubdt = (boundary(5)%w(i-1,j-1,itp)-boundary(5)%w(i-1,j-1,itm))*idtb + boundary(5)%radcorrsingle(ipatch,jpatch) = boundary(5)%radcorrsingle(ipatch,jpatch) + & + (-wp(i,j,k1)+dubdt)*dx*dy/(dxint*dyint) + end do + end do + ! Communicate integration between processes + call D_MPI_ALLREDUCE(boundary(5)%radcorrsingle,boundary(5)%radcorr,nxpatch*nypatch, & + MPI_SUM, comm3d,mpierr) + ! Apply correction term + do i = 2,i1 + ipos = i + (myidx * imax) - 1 + ipatch = int((ipos-0.5)*dx/dxint)+1 + do j = 2,j1 + jpos = j + (myidy * jmax) - 1 + jpatch = int((jpos-0.5)*dy/dyint)+1 + wp(i,j,k1) = wp(i,j,k1) + boundary(5)%radcorr(ipatch,jpatch) + end do + end do + end select + end subroutine radcorrection + + subroutine take_prof(field0,fieldm,prof) + use modglobal, only : i1,j1,k1,ih,jh,kmax + implicit none + real(field_r), intent(inout), dimension(2-ih:i1+ih,2-jh:j1+jh,k1) :: field0,fieldm + real(field_r), intent(in), dimension(k1) :: prof + integer :: i,j,k + do k=1,kmax + do j=2,j1 + do i=2,i1 + field0(i,j,k) = prof(k) + fieldm(i,j,k) = prof(k) + end do + end do + end do + end subroutine take_prof + + subroutine handle_err(errcode) + + implicit none + + integer errcode + + write(6,*) 'Error: ', nf90_strerror(errcode) + stop 2 + + end subroutine handle_err + +end module modopenboundary diff --git a/src/modpois.f90 b/src/modpois.f90 index 8e9afa84..c2f915bc 100644 --- a/src/modpois.f90 +++ b/src/modpois.f90 @@ -46,10 +46,10 @@ module modpois contains subroutine initpois - use modglobal, only : solver_id !,i1,j1,ih,jh,kmax + use modglobal, only : solver_id,i1,j1,ih,jh,kmax,solver_id,maxiter,tolerance,precond_id,n_pre,n_post,psolver,maxiter_precond use modfft2d, only : fft2dinit use modfftw, only : fftwinit - use modhypre, only : inithypre + use modhypre, only : inithypre_grid, inithypre_solver implicit none @@ -63,19 +63,20 @@ subroutine initpois ! HYPRE based solver ! using FFT based solver as fallback - call fft2dinit(p, Fp, d, xyrt, ps,pe,qs,qe) + !call fft2dinit(p, Fp, d, xyrt, ps,pe,qs,qe) !NOTE: If you don't want to do that, you will need the line below !allocate(p(2-ih:i1+ih,2-jh:j1+jh,kmax)) - call inithypre + call inithypre_grid + call inithypre_solver(psolver,solver_id,maxiter,tolerance,precond_id,n_pre,n_post,maxiter_precond) endif end subroutine initpois subroutine exitpois - use modglobal, only : solver_id + use modglobal, only : solver_id,psolver use modfft2d, only : fft2dexit - use modhypre, only : exithypre + use modhypre, only : exithypre_grid, exithypre_solver use modfftw, only : fftwexit implicit none @@ -88,22 +89,27 @@ subroutine exitpois call fftwexit(p,Fp,d,xyrt) else ! HYPRE based solver - call fft2dexit(p,Fp,d,xyrt) - call exithypre + !call fft2dexit(p,Fp,d,xyrt) + deallocate(p) + call exithypre_grid + call exithypre_solver(psolver) endif end subroutine exitpois subroutine poisson - use modglobal, only : solver_id + use modglobal, only : solver_id,psolver use modmpi, only : myid - use modhypre, only : solve_hypre, set_initial_guess + use modhypre, only : solve_hypre, set_zero_guess use modfftw, only : fftwf, fftwb use modfft2d, only : fft2df, fft2db + use mpi implicit none - + !real wtime logical converged + !wtime = MPI_Wtime() + call fillps if (solver_id == 0) then @@ -123,27 +129,33 @@ subroutine poisson ! Backward FFT call fftwb(p, Fp) else - call solve_hypre(p, converged) + call solve_hypre(psolver, p, converged) if (.not. converged) then if (myid == 0) then - write (*,*) 'Falling back to fft2d solver.' + write (*,*) 'Not converged' endif - call fillps + !call set_zero_guess() + !call solve_hypre(psolver, p, converged) + !call fillps ! Forward FFT - call fft2df(p, Fp) + !call fft2df(p, Fp) - call solmpj + !call solmpj ! Backward FFT - call fft2db(p, Fp) + !call fft2db(p, Fp) ! Re-use our current solution as the next initial guess - call set_initial_guess(p) + !call set_initial_guess(p) endif endif call tderive + !if (myid == 0) then + ! wtime = MPI_Wtime() - wtime + ! write (*,*) 'Poisson, time spent:', wtime, 's' + !end if end subroutine poisson subroutine fillps @@ -157,8 +169,10 @@ subroutine fillps ! Adapted fillps for RK3 time loop use modfields, only : up, vp, wp, um, vm, wm, rhobf,rhobh - use modglobal, only : rk3step,i1,j1,kmax,k1,dx,dy,dzf,rdt,ih,jh + use modglobal, only : rk3step,i1,j1,kmax,k1,dx,dy,dzf,rdt,ih,jh, & + lboundary,lopenbc,i2,j2,lperiodic use modmpi, only : excjs + use modopenboundary, only : openboundary_excjs implicit none real(pois_r),allocatable :: pup(:,:,:), pvp(:,:,:), pwp(:,:,:) integer i,j,k @@ -170,9 +184,18 @@ subroutine fillps rk3coef = rdt / (4. - dble(rk3step)) + if(lopenbc) then + ! do exchanges here, because we have boundary conditions for up, vp but not for pup,pvp below + ! note: needs um, vm to be exchanged also + call openboundary_excjs(up , 2,i1,2,j1,1,k1,ih,jh,.not.lboundary(1:4).or.lperiodic(1:4)) + call openboundary_excjs(vp , 2,i1,2,j1,1,k1,ih,jh,.not.lboundary(1:4).or.lperiodic(1:4)) + call openboundary_excjs(um , 2,i1,2,j1,1,k1,ih,jh,.not.lboundary(1:4).or.lperiodic(1:4)) + call openboundary_excjs(vm , 2,i1,2,j1,1,k1,ih,jh,.not.lboundary(1:4).or.lperiodic(1:4)) + endif + do k=1,kmax - do j=2,j1 - do i=2,i1 + do j=2,j2 ! openbc needs these to i2,j2. Periodic bc needs them to i1,j1 + do i=2,i2 pup(i,j,k) = up(i,j,k) + um(i,j,k) / rk3coef pvp(i,j,k) = vp(i,j,k) + vm(i,j,k) / rk3coef pwp(i,j,k) = wp(i,j,k) + wm(i,j,k) / rk3coef @@ -189,17 +212,22 @@ subroutine fillps !************************************************************** - do j=2,j1 do i=2,i1 pwp(i,j,1) = 0. - pwp(i,j,k1) = 0. + pwp(i,j,k1) = wp(i,j,k1) + wm(i,j,k1) / rk3coef + !pwp(i,j,k1) = 0. end do end do - call excjs(pup,2,i1,2,j1,1,kmax,ih,jh) - call excjs(pvp,2,i1,2,j1,1,kmax,ih,jh) - + ! Already implemented in loop for pup and periodicity check for up and vp for openbc + !call excjs(pup,2,i1,2,j1,1,kmax,ih,jh) + !call excjs(pvp,2,i1,2,j1,1,kmax,ih,jh) + if(.not. lopenbc) then + call excjs( pup , 2,i1,2,j1,1,kmax,ih,jh) + call excjs( pvp , 2,i1,2,j1,1,kmax,ih,jh) + endif + do k=1,kmax do j=2,j1 do i=2,i1 @@ -237,14 +265,23 @@ subroutine tderive !-----------------------------------------------------------------| use modfields, only : up, vp, wp - use modglobal, only : i1,j1,kmax,dx,dy,dzh,ih,jh + use modglobal, only : i1,j1,kmax,dx,dy,dzh,ih,jh,lopenbc,lboundary,i2,j2,k1,lperiodic use modmpi, only : excjs + use modopenboundary, only : openboundary_excjs implicit none integer i,j,k ! ** Cyclic boundary conditions ************** ! ** set by the commcart communication in excj - call excjs( p, 2,i1,2,j1,1,kmax,ih,jh) + if(lopenbc) then ! If openboundaries are used only cyclic conditions for non-domain boundaries, hom. Neumann otherwise. + call openboundary_excjs(p , 2,i1,2,j1,1,kmax,ih,jh,.not.lboundary(1:4).or.lperiodic(1:4)) + if(lboundary(1).and. .not. lperiodic(1)) p(1,:,:) = p(2,:,:) + if(lboundary(2).and. .not. lperiodic(2)) p(i2,:,:) = p(i1,:,:) + if(lboundary(3).and. .not. lperiodic(3)) p(:,1,:) = p(:,2,:) + if(lboundary(4).and. .not. lperiodic(4)) p(:,j2,:) = p(:,j1,:) + else + call excjs( p, 2,i1,2,j1,1,kmax,ih,jh) + endif !***************************************************************** ! ** Calculate time-derivative for the velocities with known **** diff --git a/src/modstartup.f90 b/src/modstartup.f90 index 2eb79fd2..0fde6ca2 100644 --- a/src/modstartup.f90 +++ b/src/modstartup.f90 @@ -70,15 +70,18 @@ subroutine startup(path) lmoist,lcoriol,lpressgrad,igrw_damp,geodamptime,lmomsubs,cu, cv,ifnamopt,fname_options,llsadv,& ibas_prf,lambda_crit,iadv_mom,iadv_tke,iadv_thl,iadv_qt,iadv_sv,courant,peclet,ladaptive,author,& lnoclouds,lfast_thermo,lrigidlid,unudge,ntimedep,& - solver_id, maxiter, tolerance, n_pre, n_post, precond, checknamelisterror, loutdirs, output_prefix + solver_id, maxiter, maxiter_precond, tolerance, n_pre, n_post, precond_id, checknamelisterror, & + loutdirs, output_prefix, & + lopenbc,linithetero,lperiodic,dxint,dyint,dzint,taum,tauh,pbc,lsynturb,nmodes,tau,lambda,lambdas,lambdas_x,lambdas_y,lambdas_z,iturb, & + hypre_logging,rdt,rk3step,i1,j1,k1,ih,jh,lboundary,lconstexner use modforces, only : lforce_user use modsurfdata, only : z0,ustin,wtsurf,wqsurf,wsvsurf,ps,thls,isurf use modsurface, only : initsurface use moddatetime, only : initdatetime use modemission, only : initemission use modlsm, only : initlsm, kmax_soil - use modfields, only : initfields - use modpois, only : initpois + use modfields, only : initfields,um,vm,wm,u0,v0,w0,up,vp,wp + use modpois, only : initpois,poisson use modradiation, only : initradiation use modraddata, only : irad,iradiation,& rad_ls,rad_longw,rad_shortw,rad_smoke,useMcICA,& @@ -90,14 +93,18 @@ subroutine startup(path) use modthermodynamics, only : initthermodynamics,lqlnr, chi_half use modmicrophysics, only : initmicrophysics use modsubgrid, only : initsubgrid - use modmpi, only : initmpi,commwrld,myid,myidx,cmyidy,nprocx,nprocy,mpierr & + use modmpi, only : initmpi,commwrld,myid,myidx,cmyidy,nprocx,nprocy,mpierr,periods & , D_MPI_BCAST use modchem, only : initchem use modversion, only : git_version + use modopenboundary, only : initopenboundary,openboundary_divcorr,openboundary_excjs + use modchecksim, only : chkdiv implicit none integer :: ierr + logical,dimension(2) :: lper = .false. character(256), optional, intent(in) :: path + real rk3coef !declare namelists namelist/RUN/ & @@ -112,12 +119,16 @@ subroutine startup(path) namelist/PHYSICS/ & !cstep z0,ustin,wtsurf,wqsurf,wsvsurf,ps,thls,chi_half,lmoist,isurf,lneutraldrag,& z0,ustin,wtsurf,wqsurf,wsvsurf,ps,thls,lmoist,isurf,chi_half,& - lcoriol,lpressgrad,igrw_damp,geodamptime,lmomsubs,ltimedep,ltimedepuv,ltimedepsv,ntimedep,irad,timerad,iradiation,rad_ls,rad_longw,rad_shortw,rad_smoke,useMcICA,& - rka,dlwtop,dlwbot,sw0,gc,reff,isvsmoke,lforce_user,lcloudshading,lrigidlid,unudge,lfast_thermo + lcoriol,lpressgrad,igrw_damp,geodamptime,lmomsubs,ltimedep,ltimedepuv,ltimedepsv,& + ntimedep,irad,timerad,iradiation,rad_ls,rad_longw,rad_shortw,rad_smoke,useMcICA,& + rka,dlwtop,dlwbot,sw0,gc,reff,isvsmoke,lforce_user,lcloudshading,lrigidlid,unudge,& + lfast_thermo,lconstexner namelist/DYNAMICS/ & llsadv, lqlnr, lambda_crit, cu, cv, ibas_prf, iadv_mom, iadv_tke, iadv_thl, iadv_qt, iadv_sv, lnoclouds namelist/SOLVER/ & - solver_id, maxiter, tolerance, n_pre, n_post, precond + solver_id, maxiter, tolerance, n_pre, n_post, precond_id, maxiter_precond, hypre_logging + namelist/OPENBC/ & + lopenbc,linithetero,lper,dxint,dyint,dzint,taum,tauh,pbc,lsynturb,iturb,tau,lambda,nmodes,lambdas,lambdas_x,lambdas_y,lambdas_z ! get myid @@ -159,12 +170,24 @@ subroutine startup(path) read (ifnamopt,SOLVER,iostat=ierr) call checknamelisterror(ierr, ifnamopt, 'SOLVER') write(6 ,SOLVER) + rewind(ifnamopt) + read (ifnamopt,OPENBC,iostat=ierr) + call checknamelisterror(ierr, ifnamopt, 'OPENBC') + write(6 ,OPENBC) + close(ifnamopt) + if(lopenbc) then + ! Check if grid needs to be periodic + periods = (/lper(1),lper(2)/) + lperiodic(1:2) = lper(1) + lperiodic(3:4) = lper(2) + endif close(ifnamopt) end if ! these must be shared before initmpi sets up the cartesian grid ! commwrld is already set up + call D_MPI_BCAST(periods,2,0,commwrld,mpierr) call D_MPI_BCAST(nprocx ,1,0,commwrld,mpierr) call D_MPI_BCAST(nprocy ,1,0,commwrld,mpierr) @@ -272,7 +295,28 @@ subroutine startup(path) call D_MPI_BCAST(n_pre,1,0,commwrld,mpierr) call D_MPI_BCAST(n_post,1,0,commwrld,mpierr) call D_MPI_BCAST(tolerance,1,0,commwrld,mpierr) - call D_MPI_BCAST(precond,1,0,commwrld,mpierr) + call D_MPI_BCAST(precond_id,1,0,commwrld,mpierr) + call D_MPI_BCAST(maxiter_precond,1,0,commwrld,mpierr) + + ! Broadcast openboundaries Variables + call D_MPI_BCAST(lopenbc, 1, 0,commwrld,mpierr) + call D_MPI_BCAST(linithetero,1, 0,commwrld,mpierr) + call D_MPI_BCAST(lperiodic, 5, 0,commwrld,mpierr) + call D_MPI_BCAST(dxint, 1, 0,commwrld,mpierr) + call D_MPI_BCAST(dyint, 1, 0,commwrld,mpierr) + call D_MPI_BCAST(dzint, 1, 0,commwrld,mpierr) + call D_MPI_BCAST(taum, 1, 0,commwrld,mpierr) + call D_MPI_BCAST(tauh, 1, 0,commwrld,mpierr) + call D_MPI_BCAST(pbc, 1, 0,commwrld,mpierr) + call D_MPI_BCAST(lsynturb, 1, 0,commwrld,mpierr) + call D_MPI_BCAST(iturb, 1, 0,commwrld,mpierr) + call D_MPI_BCAST(lambda, 1, 0,commwrld,mpierr) + call D_MPI_BCAST(tau, 1, 0,commwrld,mpierr) + call D_MPI_BCAST(nmodes, 1, 0,commwrld,mpierr) + call D_MPI_BCAST(lambdas, 1, 0,commwrld,mpierr) + call D_MPI_BCAST(lambdas_x, 1, 0,commwrld,mpierr) + call D_MPI_BCAST(lambdas_y, 1, 0,commwrld,mpierr) + call D_MPI_BCAST(lambdas_z, 1, 0,commwrld,mpierr) call testwctime ! Allocate and initialize core modules @@ -280,7 +324,11 @@ subroutine startup(path) call initfields call inittestbed !reads initial profiles from scm_in.nc, to be used in readinitfiles - call initboundary + if(.not.lopenbc) then + call initboundary + else + call initopenboundary + endif call initthermodynamics call initradiation call initchem @@ -303,6 +351,28 @@ subroutine startup(path) call readinitfiles ! moved to obtain the correct btime for the timedependent forcings in case of a warmstart call inittimedep !depends on modglobal,modfields, modmpi, modsurf, modradiation call initpois ! hypre solver needs grid and baseprofiles + if(lopenbc) then ! Correct boundaries and initial field for divergence + call chkdiv + call openboundary_divcorr ! Remove divergence from large scale input + ! Use poisson solver to get rid of divergence in initial field, needs to + ! be here to avoid cross dependencies between modopenbondaries and modpois + if(myid==0) print *, 'Start divergence correction initial field' + call chkdiv + up = 0.; vp = 0.; wp = 0. ! Set tendencies to zero + call poisson + rk3coef = rdt / (4. - dble(rk3step)) + um = um + rk3coef * up + vm = vm + rk3coef * vp + wm = wm + rk3coef * wp + call openboundary_excjs(um , 2,i1,2,j1,1,k1,ih,jh,.not.lboundary(1:4).or.lperiodic(1:4)) + call openboundary_excjs(vm , 2,i1,2,j1,1,k1,ih,jh,.not.lboundary(1:4).or.lperiodic(1:4)) + call openboundary_excjs(wm , 2,i1,2,j1,1,k1,ih,jh,.not.lboundary(1:4).or.lperiodic(1:4)) + u0 = um; v0 = vm; w0 = wm + call chkdiv + ! Reset tendencies + up = 0.; vp = 0.; wp = 0. + if(myid==0) print *, 'Finished divergence correction initial field' + endif call checkinitvalues @@ -411,7 +481,7 @@ subroutine readinitfiles rtimee,timee,ntrun,btime,dt_lim,nsv,& zf,dzf,dzh,rv,rd,cp,rlv,pref0,om23_gs,& ijtot,cu,cv,e12min,dzh,cexpnr,ifinput,lwarmstart,ltotruntime,itrestart,& - trestart, ladaptive,llsadv,tnextrestart,longint + trestart, ladaptive,llsadv,tnextrestart,longint,lopenbc,linithetero use modsubgrid, only : ekm,ekh use modsurfdata, only : wsvsurf, & thls,tskin,tskinm,tsoil,tsoilm,phiw,phiwm,Wl,Wlm,thvs,qts,isurf,svs,obl,oblav,& @@ -425,6 +495,7 @@ subroutine readinitfiles use modtestbed, only : ltestbed,tb_ps,tb_thl,tb_qt,tb_u,tb_v,tb_w,tb_ug,tb_vg,& tb_dqtdxls,tb_dqtdyls,tb_qtadv,tb_thladv + use modopenboundary, only : openboundary_ghost,openboundary_readboundary,openboundary_initfields integer i,j,k,n,ierr logical negval !switch to allow or not negative values in randomnization @@ -473,7 +544,6 @@ subroutine readinitfiles !thls !wtsurf !wqsurf - else open (ifinput,file='prof.inp.'//cexpnr,status='old',iostat=ierr) if (ierr /= 0) then @@ -524,50 +594,6 @@ subroutine readinitfiles call D_MPI_BCAST(uprof ,kmax,0,comm3d,mpierr) call D_MPI_BCAST(vprof ,kmax,0,comm3d,mpierr) call D_MPI_BCAST(e12prof,kmax,0,comm3d,mpierr) - do k=1,kmax - do j=1,j2 - do i=1,i2 - thl0(i,j,k) = thlprof(k) - thlm(i,j,k) = thlprof(k) - qt0 (i,j,k) = qtprof (k) - qtm (i,j,k) = qtprof (k) - u0 (i,j,k) = uprof (k) - cu - um (i,j,k) = uprof (k) - cu - v0 (i,j,k) = vprof (k) - cv - vm (i,j,k) = vprof (k) - cv - w0 (i,j,k) = 0.0 - wm (i,j,k) = 0.0 - e120(i,j,k) = e12prof(k) - e12m(i,j,k) = e12prof(k) - ekm (i,j,k) = 0.0 - ekh (i,j,k) = 0.0 - end do - end do - end do - !--------------------------------------------------------------- - ! 1.2 randomnize fields - !--------------------------------------------------------------- - - krand = min(krand,kmax) - negval = .False. ! No negative perturbations for qt (negative moisture is non physical) - do k = 1,krand - call randomnize(qtm ,k,randqt ,irandom,ih,jh,negval) - call randomnize(qt0 ,k,randqt ,irandom,ih,jh,negval) - end do - negval = .True. ! negative perturbations allowed - do k = 1,krand - call randomnize(thlm,k,randthl,irandom,ih,jh,negval) - call randomnize(thl0,k,randthl,irandom,ih,jh,negval) - end do - - do k=krandumin,krandumax - call randomnize(um ,k,randu ,irandom,ih,jh,negval) - call randomnize(u0 ,k,randu ,irandom,ih,jh,negval) - call randomnize(vm ,k,randu ,irandom,ih,jh,negval) - call randomnize(v0 ,k,randu ,irandom,ih,jh,negval) - call randomnize(wm ,k,randu ,irandom,ih,jh,negval) - call randomnize(w0 ,k,randu ,irandom,ih,jh,negval) - end do svprof = 0. if(myid==0)then @@ -596,17 +622,81 @@ subroutine readinitfiles end if ! end if myid==0 call D_MPI_BCAST(wsvsurf,nsv ,0,comm3d,mpierr) - call D_MPI_BCAST(svprof ,k1*nsv,0,comm3d,mpierr) - do k=1,kmax + ! Initialize fields + if(lopenbc .and. linithetero) then! Openboundaries with heterogeneous initialisation + call openboundary_initfields() + do j = 1,j2 + do i = 1,i2 + wm(i,j,1) = 0. + w0(i,j,1) = 0. + end do + end do + do k = 1,kmax + do j = 1,j2 + do i = 1,i2 + ekm(i,j,k) = 0.0 + ekh(i,j,k) = 0.0 + end do + end do + end do + else + do k=1,kmax do j=1,j2 - do i=1,i2 - do n=1,nsv - sv0(i,j,k,n) = svprof(k,n) - svm(i,j,k,n) = svprof(k,n) + do i=1,i2 + thl0(i,j,k) = thlprof(k) + thlm(i,j,k) = thlprof(k) + qt0 (i,j,k) = qtprof (k) + qtm (i,j,k) = qtprof (k) + u0 (i,j,k) = uprof (k) - cu + um (i,j,k) = uprof (k) - cu + v0 (i,j,k) = vprof (k) - cv + vm (i,j,k) = vprof (k) - cv + w0 (i,j,k) = 0.0 + wm (i,j,k) = 0.0 + e120(i,j,k) = e12prof(k) + e12m(i,j,k) = e12prof(k) + ekm (i,j,k) = 0.0 + ekh (i,j,k) = 0.0 + end do + end do + end do + if(nsv>0) then + do k=1,kmax + do j=1,j2 + do i=1,i2 + do n=1,nsv + sv0(i,j,k,n) = svprof(k,n) + svm(i,j,k,n) = svprof(k,n) + end do + end do end do end do - end do + endif + endif + !--------------------------------------------------------------- + ! 1.2 randomnize fields + !--------------------------------------------------------------- + + krand = min(krand,kmax) + negval = .False. ! No negative perturbations for qt (negative moisture is non physical) + do k = 1,krand + call randomnize(qtm ,k,randqt ,irandom,ih,jh,negval) + call randomnize(qt0 ,k,randqt ,irandom,ih,jh,negval) + end do + negval = .True. ! negative perturbations allowed + do k = 1,krand + call randomnize(thlm,k,randthl,irandom,ih,jh,negval) + call randomnize(thl0,k,randthl,irandom,ih,jh,negval) + end do + + do k=krandumin,krandumax + call randomnize(um ,k,randu ,irandom,ih,jh,negval) + call randomnize(u0 ,k,randu ,irandom,ih,jh,negval) + call randomnize(vm ,k,randu ,irandom,ih,jh,negval) + call randomnize(v0 ,k,randu ,irandom,ih,jh,negval) + call randomnize(wm ,k,randu ,irandom,ih,jh,negval) + call randomnize(w0 ,k,randu ,irandom,ih,jh,negval) end do !----------------------------------------------------------------- @@ -636,6 +726,10 @@ subroutine readinitfiles case(10) call initsurf_user end select + if(lopenbc) then + call openboundary_readboundary + call openboundary_ghost + endif ! Set initial Obukhov length to -0.1 for iteration obl = -0.1 @@ -652,7 +746,11 @@ subroutine readinitfiles svs = svprof(1,:) call baseprofs ! call baseprofs before thermodynamics - call boundary + if(lopenbc) then + call openboundary_ghost() + else + call boundary + endif call thermodynamics call surface @@ -663,7 +761,11 @@ subroutine readinitfiles ! dsv(n) = (svprof(kmax,n)-svprof(kmax-1,n)) / dzh(kmax) ! end do - call boundary + if(lopenbc) then + call openboundary_ghost + else + call boundary + endif call thermodynamics ! save initial pressure profiles @@ -685,8 +787,8 @@ subroutine readinitfiles svm = sv0 e12m = e120 call calc_halflev - exnf = (presf/pref0)**(rd/cp) - exnh = (presh/pref0)**(rd/cp) + exnf = (initial_presf/pref0)**(rd/cp) + exnh = (initial_presh/pref0)**(rd/cp) do j=2,j1 do i=2,i1 @@ -747,6 +849,10 @@ subroutine readinitfiles ! CvH - only do this for fixed timestepping. In adaptive dt comes from restartfile if(ladaptive .eqv. .false.) rdt=dtmax + if(lopenbc) then + call openboundary_readboundary + endif + end if ! end if (.not. warmstart) !----------------------------------------------------------------- @@ -1249,7 +1355,7 @@ end subroutine testwctime subroutine exitmodules use modfields, only : exitfields - use modglobal, only : exitglobal + use modglobal, only : exitglobal,lopenbc use modmpi, only : exitmpi use modboundary, only : exitboundary use modmicrophysics, only : exitmicrophysics @@ -1261,6 +1367,7 @@ subroutine exitmodules use modlsm, only : exitlsm use modthermodynamics, only : exitthermodynamics use modemission, only : exitemission + use modopenboundary, only : exitopenboundary call exittimedep call exitthermodynamics @@ -1271,7 +1378,11 @@ subroutine exitmodules call exitpois call exitmicrophysics call exitemission - call exitboundary + if(lopenbc) then + call exitopenboundary + else + call exitboundary + endif call exitfields call exitglobal call exitmpi diff --git a/src/modsurface.f90 b/src/modsurface.f90 index 10d81e9a..e862337b 100644 --- a/src/modsurface.f90 +++ b/src/modsurface.f90 @@ -165,9 +165,9 @@ subroutine initsurface call D_MPI_BCAST(phiwp , 1, 0, comm3d, mpierr) call D_MPI_BCAST(R10 , 1, 0, comm3d, mpierr) call D_MPI_BCAST(lsplitleaf , 1, 0, comm3d, mpierr) - + call D_MPI_BCAST(land_use(1:mpatch,1:mpatch),mpatch*mpatch, 0, comm3d, mpierr) - + call D_MPI_BCAST(i_expemis , 1, 0, comm3d, mpierr) call D_MPI_BCAST(expemis0 , 1, 0, comm3d, mpierr) call D_MPI_BCAST(expemis1 , 1, 0, comm3d, mpierr) @@ -708,11 +708,12 @@ end subroutine initsurface !> Calculates the interaction with the soil, the surface temperature and humidity, and finally the surface fluxes. subroutine surface - use modglobal, only : i1,j1,i2,j2,fkar,zf,cu,cv,nsv,ijtot,rd,rv,rtimee + use modglobal, only : i1,j1,i2,j2,fkar,zf,cu,cv,nsv,ijtot,rd,rv,rtimee,lopenbc,lboundary,lperiodic use modfields, only : thl0, qt0, u0, v0, u0av, v0av use modmpi, only : mpierr, comm3d, mpi_sum, excjs & , D_MPI_ALLREDUCE, D_MPI_BCAST use moduser, only : surf_user + use modopenboundary, only : openboundary_excjs implicit none integer :: i, j, n, patchx, patchy @@ -1050,7 +1051,15 @@ subroutine surface ! Transfer ustar to neighbouring cells, do this like a 3D field ustar_3D(1:i2,1:j2,1:1) => ustar - call excjs(ustar_3D,2,i1,2,j1,1,1,1,1) + + if(lopenbc) then ! Only use periodicity for non-domain boundaries when openboundaries are used + call openboundary_excjs(ustar_3D, 2,i1,2,j1,1,1,1,1, & + & (.not.lboundary(1:4)).or.lperiodic(1:4)) + else + call excjs(ustar_3D,2,i1,2,j1,1,1,1,1) + !call excjs(ustar, 2,i1,2,j1,1,1,1,1) + endif + return end subroutine surface !> Calculate the surface humidity assuming saturation. diff --git a/src/modsynturb.f90 b/src/modsynturb.f90 new file mode 100644 index 00000000..3d022cfe --- /dev/null +++ b/src/modsynturb.f90 @@ -0,0 +1,757 @@ +!> \file modopenboundary.f90 +!! Creates synthetic turbulence for the open boundary implementation +!> +!! Creates synthetic turbulence for the open boundary implementation +!> +!! \author Frans Liqui Lung +! This file is part of DALES. +! To do: +! +! DALES 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. +! +! DALES 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 this program. If not, see . +! +! Copyright 1993-2009 Delft University of Technology, Wageningen University, Utrecht University, KNMI +! +module modsynturb +use netcdf +use modglobal,only: lsynturb,iturb,lboundary,lperiodic,boundary,nmodes,lambda,tau,dxint,dyint,itot,jtot,dx,dy,kmax +use RandomNumbers, only : getRandomReal,randomNumberSequence,new_RandomNumberSequence +use modprecision, only: field_r +implicit none +real, allocatable, dimension(:,:) :: kn,p,q,vturb,wturb,k_thl,k_qt +real, allocatable, dimension(:) :: omega,omega_thl,omega_qt,p_thl,p_qt,q_thl,q_qt +real, allocatable, dimension(:) :: xf,xh,yf,yh +real :: nisqrt,ctot,nisqrt2 +real, dimension(3) :: lambdasxyz +integer :: nxpatch,nypatch,nzpatch +integer, parameter :: isepsim_mom = 10,isepsim_all=11, isynturb_mom = 0, isynturb_all = 1 +integer :: ntturb,itimestep=1 +real, allocatable, dimension(:) :: tturb +real, allocatable, dimension(:,:,:) :: uturbin,vturbin,wturbin,thlturbin,qtturbin +character(len = nf90_max_name) :: RecordDimName +integer :: VARID,STATUS,NCID,mpierr,timeID +! ! Uncommend for netcdf output turbulent pertubations west boundary +! character (80) :: fname = 'turbOut.xxx.xxx.nc' +! integer :: ncid +! integer, parameter :: NDIMS = 3 +! character (len = *), parameter :: Z_NAME = "z" +! character (len = *), parameter :: Y_NAME = "y" +! character (len = *), parameter :: t_NAME = "time" +! integer :: z_dimid, y_dimid, t_dimid +! integer :: z_varid, y_varid, t_varid +! integer :: uturb_varid, vturb_varid, wturb_varid, thlturb_varid, qtturb_varid +! integer :: dimids(NDIMS) +! integer :: start(NDIMS), count(NDIMS) + +type(randomNumberSequence) :: noise +contains + subroutine initsynturb + use netcdf + use modglobal, only : dx,dy,imax,jmax,i1,j1,zf,lambdas,lambdas_x,lambdas_y,lambdas_z,kmax,k1,cexpnr,lmoist + use modmpi, only : myidx, myidy + implicit none + integer :: i,j,ib + if(.not.lsynturb) return + if(any(lboundary.and..not.(lperiodic))) then + if(iturb == isynturb_all .or. iturb == isynturb_mom) then + ! Constants + nisqrt = sqrt(2./nmodes) + nisqrt2 = sqrt(1./nmodes) + ctot = lambda/tau + noise = new_RandomNumberSequence(seed = 100) + nxpatch = int(dx/dxint*real(itot)); + nypatch = int(dy/dyint*real(jtot)); + nzpatch = kmax + lambdas = merge(lambda,lambdas,lambdas==-1.) + lambdasxyz = (/merge(lambdas,lambdas_x,lambdas_x==-1.), & + & merge(lambdas,lambdas_y,lambdas_y==-1.), & + & merge(lambdas,lambdas_z,lambdas_z==-1.)/) + ! Allocate variables + allocate(kn(nmodes,3),q(nmodes,3),p(nmodes,3),omega(nmodes),& + xf(imax),xh(i1),yf(jmax),yh(j1),k_thl(nmodes,3),k_qt(nmodes,3), & + omega_thl(nmodes),omega_qt(nmodes), & + p_thl(nmodes),p_qt(nmodes),q_thl(nmodes),q_qt(nmodes)) + ! allocate(vturb(jmax,kmax),wturb(jmax,kmax)) + ! Calculate coordinates + xf = (/((i-0.5)*dx,i=1,imax,1)/)+imax*myidx*dx + xh = (/(i*dx,i=1,i1,1)/)+imax*myidx*dx + yf = (/((i-0.5)*dy,i=1,jmax,1)/)+jmax*myidy*dy + yh = (/(i*dy,i=1,j1,1)/)+jmax*myidy*dy + ! Fill random distributed variables + do i = 1,3 + do j = 1,nmodes + kn(j,i) = gaussrand(0.,0.5) + k_thl(j,i) = gaussrand(0.,0.5) + k_qt(j,i) = gaussrand(0.,0.5) + if(i==1) then + omega(j) = gaussrand(0.,1.) + omega_thl(j) = gaussrand(0.,1.) + omega_qt(j) = gaussrand(0.,1.) + p_thl(j) = gaussrand(0.,1.) + q_thl(j) = gaussrand(0.,1.) + p_qt(j) = gaussrand(0.,1.) + q_qt(j) = gaussrand(0.,1.) + endif + end do + end do + ! Obtain p and q with cross product + do i = 1,nmodes + p(i,:) = cross((/gaussrand(0.,1.),gaussrand(0.,1.),gaussrand(0.,1.)/),kn(i,:)) + q(i,:) = cross((/gaussrand(0.,1.),gaussrand(0.,1.),gaussrand(0.,1.)/),kn(i,:)) + end do + do ib = 1,5 + if(.not.lboundary(ib)) cycle + allocate(boundary(ib)%eigvec(boundary(ib)%nx1patch,boundary(ib)%nx2patch,3,3), & + & boundary(ib)%ci(boundary(ib)%nx1patch,boundary(ib)%nx2patch,3))!, & + ! & boundary(ib)%randthl(boundary(ib)%nx1,boundary(ib)%nx2), & + ! & boundary(ib)%randqt(boundary(ib)%nx1,boundary(ib)%nx2)) + end do + elseif((iturb == isepsim_mom .or. iturb == isepsim_all) .and. lboundary(1)) then + !--- open nc file --- + STATUS = NF90_OPEN('turb.inp.'//cexpnr//'.nc', nf90_nowrite, NCID) + if (STATUS .ne. nf90_noerr) call handle_err(STATUS) + !--- get time dimensions + status = nf90_inq_dimid(ncid, "time", timeID) + if (status /= nf90_noerr) call handle_err(status) + status = nf90_inquire_dimension(NCID, timeID, len=ntturb, name=RecordDimName) + if (STATUS .ne. nf90_noerr) call handle_err(STATUS) + !--- read time + allocate(tturb(ntturb)) + STATUS = NF90_INQ_VARID(NCID, 'time', VARID) + if (STATUS .ne. nf90_noerr) call handle_err(STATUS) + STATUS = NF90_GET_VAR (NCID, VARID, tturb, start=(/1/), count=(/ntturb/) ) + if (STATUS .ne. nf90_noerr) call handle_err(STATUS) + ! --- allocate fields + allocate(uturbin(jmax,kmax,ntturb)) + allocate(vturbin(j1,kmax,ntturb)) + allocate(wturbin(jmax,k1,ntturb)) + ! --- read fields + ! Read u + STATUS = NF90_INQ_VARID(NCID,'uturbwest', VARID) + if (STATUS .ne. nf90_noerr) call handle_err(STATUS) + STATUS = NF90_GET_VAR (NCID, VARID, uturbin, start=(/myidy*jmax+1,1,1/), & + & count=(/jmax,kmax,ntturb/)) + ! Read v + STATUS = NF90_INQ_VARID(NCID,'vturbwest', VARID) + if (STATUS .ne. nf90_noerr) call handle_err(STATUS) + STATUS = NF90_GET_VAR (NCID, VARID, vturbin, start=(/myidy*jmax+1,1,1/), & + & count=(/j1,kmax,ntturb/)) + ! Read w + STATUS = NF90_INQ_VARID(NCID,'wturbwest', VARID) + if (STATUS .ne. nf90_noerr) call handle_err(STATUS) + STATUS = NF90_GET_VAR (NCID, VARID, wturbin, start=(/myidy*jmax+1,1,1/), & + & count=(/jmax,k1,ntturb/)) + if(iturb==isepsim_all) then + allocate(thlturbin(jmax,kmax,ntturb)) + ! Read thl + STATUS = NF90_INQ_VARID(NCID,'thlturbwest', VARID) + if (STATUS .ne. nf90_noerr) call handle_err(STATUS) + STATUS = NF90_GET_VAR (NCID, VARID, thlturbin, start=(/myidy*jmax+1,1,1/), & + & count=(/jmax,kmax,ntturb/)) + if(lmoist) then + allocate(qtturbin(jmax,kmax,ntturb)) + ! qt + STATUS = NF90_INQ_VARID(NCID,'wturbwest', VARID) + if (STATUS .ne. nf90_noerr) call handle_err(STATUS) + STATUS = NF90_GET_VAR (NCID, VARID, wturbin, start=(/myidy*jmax+1,1,1/), & + & count=(/jmax,k1,ntturb/)) + endif + endif + endif + ! ! Uncommend for netcdf output turbulent pertubations west boundary + ! if(lboundary(1)) then + ! write(fname,'(A,i3.3,A,i3.3,A)') 'turbOut.', myidx, '.', myidy, '.nc' + ! call check( nf90_create(fname, nf90_clobber, ncid) ) + ! call check( nf90_def_dim(ncid, z_NAME, kmax, z_dimid) ) + ! call check( nf90_def_dim(ncid, y_NAME, jmax, y_dimid) ) + ! call check( nf90_def_dim(ncid, t_NAME, NF90_UNLIMITED, t_dimid) ) + ! call check( nf90_def_var(ncid, z_NAME, NF90_REAL, z_dimid, z_varid) ) + ! call check( nf90_def_var(ncid, y_NAME, NF90_REAL, y_dimid, y_varid) ) + ! call check( nf90_def_var(ncid, t_NAME, NF90_REAL, t_dimid, t_varid) ) + ! dimids = (/y_dimid, z_dimid, t_dimid/) + ! call check( nf90_def_var(ncid, 'u', NF90_REAL, dimids, uturb_varid) ) + ! call check( nf90_def_var(ncid, 'v', NF90_REAL, dimids, vturb_varid) ) + ! call check( nf90_def_var(ncid, 'w', NF90_REAL, dimids, wturb_varid) ) + ! call check( nf90_def_var(ncid, 'thl', NF90_REAL, dimids, thlturb_varid) ) + ! call check( nf90_def_var(ncid, 'qt', NF90_REAL, dimids, qtturb_varid) ) + ! call check( nf90_enddef(ncid) ) + ! call check( nf90_put_var(ncid, z_varid, zf(1:kmax)) ) + ! call check( nf90_put_var(ncid, y_varid, yf) ) + ! count = (/ jmax, kmax, 1 /) + ! start = (/ 1, 1, 1 /) + ! endif + endif + end subroutine initsynturb + + subroutine handle_err(errcode) + + implicit none + + integer errcode + + write(6,*) 'Error: ', nf90_strerror(errcode) + stop 2 + + end subroutine handle_err + + subroutine exitsynturb + use modglobal, only : lmoist + implicit none + integer :: ib + if(.not.lsynturb) return + ! ! Uncommend for netcdf output turbulent pertubations + ! if(lboundary(1)) call check( nf90_close(ncid) ) + if(any(lboundary.and..not.(lperiodic))) then + if(iturb == isynturb_mom .or. iturb == isynturb_all) then + do ib = 1,5 + if(.not.lboundary(ib)) cycle + deallocate(boundary(ib)%u2,boundary(ib)%v2,boundary(ib)%w2,& + & boundary(ib)%uv,boundary(ib)%uw,boundary(ib)%vw,& + & boundary(ib)%thl2,boundary(ib)%qt2,boundary(ib)%wthl, & + & boundary(ib)%wqt,boundary(ib)%eigvec,boundary(ib)%ci) + end do + !deallocate(vturb,wturb) + deallocate(kn,q,p,omega,xf,xh,yf,yh,k_thl,k_qt,omega_thl,omega_qt,p_thl,p_qt,q_thl,q_qt) + elseif(iturb == isepsim_mom .and. lboundary(1)) then + deallocate(uturbin,vturbin,wturbin) + elseif(iturb == isepsim_all .and. lboundary(1)) then + deallocate(uturbin,vturbin,wturbin,thlturbin) + if(lmoist) deallocate(qtturbin) + endif + endif + end subroutine exitsynturb + + ! ! Uncommend for netcdf output turbulent pertubations + ! subroutine check(status) + ! integer, intent ( in) :: status + ! + ! if(status /= nf90_noerr) then + ! print *, trim(nf90_strerror(status)) + ! stop "Stopped" + ! end if + ! end subroutine check + + subroutine synturb() + implicit none + if(.not.lsynturb) return + select case(iturb) + case(isynturb_mom) + call synturb_mom() + case(isynturb_all) + call synturb_all() + case(isepsim_mom, isepsim_all) + call sepsim + end select + end subroutine synturb + + subroutine synturb_mom() + use modglobal, only : dx,dy,itot,jtot,zh,zf,imax,jmax,i1,j1,kmax,k1 + implicit none + integer :: ib + do ib = 1,5 + if(.not.lboundary(ib).or.lperiodic(ib)) cycle + call calc_eigdec(ib) + select case(ib) + case(1) + call calc_pert(ib,(/0./),yf,zf,1,jmax,kmax,boundary(ib)%uturb,1) + call calc_pert(ib,(/0./),yh,zf,1,j1,kmax,boundary(ib)%vturb,2) + call calc_pert(ib,(/0./),yf,zh,1,jmax,k1,boundary(ib)%wturb,3) + case(2) + call calc_pert(ib,(/(itot+1)*dx/),yf,zf,1,jmax,kmax,boundary(ib)%uturb,1) + call calc_pert(ib,(/(itot+1)*dx/),yh,zf,1,j1,kmax,boundary(ib)%vturb,2) + call calc_pert(ib,(/(itot+1)*dx/),yf,zh,1,jmax,k1,boundary(ib)%wturb,3) + case(3) + call calc_pert(ib,xh,(/0./),zf,i1,1,kmax,boundary(ib)%uturb,1) + call calc_pert(ib,xf,(/0./),zf,imax,1,kmax,boundary(ib)%vturb,2) + call calc_pert(ib,xh,(/0./),zh,imax,1,k1,boundary(ib)%wturb,1) + case(4) + call calc_pert(ib,xh,(/(jtot+1)*dy/),zf,i1,1,kmax,boundary(ib)%uturb,1) + call calc_pert(ib,xf,(/(jtot+1)*dy/),zf,imax,1,kmax,boundary(ib)%vturb,2) + call calc_pert(ib,xh,(/(jtot+1)*dy/),zh,imax,1,k1,boundary(ib)%wturb,1) + case(5) + call calc_pert(ib,xh,yf,(/zh(k1)/),i1,jmax,1,boundary(ib)%uturb,1) + call calc_pert(ib,xf,yh,(/zh(k1)/),imax,j1,1,boundary(ib)%vturb,2) + call calc_pert(ib,xf,yf,(/zh(k1)/),imax,jmax,1,boundary(ib)%wturb,3) + end select + end do + end subroutine synturb_mom + + subroutine synturb_all() + use modglobal, only : dx,dy,itot,jtot,zh,zf,imax,jmax,i1,j1,kmax,k1 + implicit none + integer :: ib + do ib = 1,5 + if(.not.lboundary(ib).or.lperiodic(ib)) cycle + call calc_eigdec(ib) + select case(ib) + case(1) + call calc_pert2(ib,(/0./),yf,zf,1,jmax,kmax,boundary(ib)%uturb,1, & + & boundary(ib)%thlturb,boundary(ib)%qtturb) + call calc_pert(ib,(/0./),yh,zf,1,j1,kmax,boundary(ib)%vturb,2) + call calc_pert(ib,(/0./),yf,zh,1,jmax,k1,boundary(ib)%wturb,3) + case(2) + call calc_pert2(ib,(/(itot+1)*dx/),yf,zf,1,jmax,kmax,boundary(ib)%uturb,1, & + & boundary(ib)%thlturb,boundary(ib)%qtturb) + call calc_pert(ib,(/(itot+1)*dx/),yh,zf,1,j1,kmax,boundary(ib)%vturb,2) + call calc_pert(ib,(/(itot+1)*dx/),yf,zh,1,jmax,k1,boundary(ib)%wturb,3) + case(3) + call calc_pert(ib,xh,(/0./),zf,i1,1,kmax,boundary(ib)%uturb,1) + call calc_pert2(ib,xf,(/0./),zf,imax,1,kmax,boundary(ib)%vturb,2, & + & boundary(ib)%thlturb,boundary(ib)%qtturb) + call calc_pert(ib,xh,(/0./),zh,imax,1,k1,boundary(ib)%wturb,1) + case(4) + call calc_pert(ib,xh,(/(jtot+1)*dy/),zf,i1,1,kmax,boundary(ib)%uturb,1) + call calc_pert2(ib,xf,(/(jtot+1)*dy/),zf,imax,1,kmax,boundary(ib)%vturb,2, & + & boundary(ib)%thlturb,boundary(ib)%qtturb) + call calc_pert(ib,xh,(/(jtot+1)*dy/),zh,imax,1,k1,boundary(ib)%wturb,1) + case(5) + call calc_pert(ib,xh,yf,(/zh(k1)/),i1,jmax,1,boundary(ib)%uturb,1) + call calc_pert(ib,xf,yh,(/zh(k1)/),imax,j1,1,boundary(ib)%vturb,2) + call calc_pert2(ib,xf,yf,(/zh(k1)/),imax,jmax,1,boundary(ib)%wturb,3, & + boundary(ib)%thlturb,boundary(ib)%qtturb) + end select + end do + end subroutine synturb_all + + subroutine sepsim() + use modglobal, only : rtimee,lmoist + use modmpi, only : myid + implicit none + integer :: ib + do ib = 1,5 + if(.not.lboundary(ib).or.lperiodic(ib)) cycle + select case(ib) + case(1) + do while(tturb(itimestep)0.01 .and. real(rtimee)>5.) then + print *, 'mistake in time', itimestep,tturb(itimestep),real(rtimee) + stop + endif + boundary(ib)%uturb = uturbin(:,:,itimestep) + boundary(ib)%vturb = vturbin(:,:,itimestep) + boundary(ib)%wturb = wturbin(:,:,itimestep) + if(iturb==11) then + boundary(ib)%thlturb = thlturbin(:,:,itimestep) + if(lmoist) boundary(ib)%qtturb = qtturbin(:,:,itimestep) + endif + case(2) + ! Do nothing (needs to be added) + case(3) + ! Do nothing (needs to be added) + case(4) + ! Do nothing (needs to be added) + case(5) + ! Do nothing (needs to be added) + end select + end do + end subroutine sepsim + + subroutine calc_eigdec(ib) + use modglobal, only : rtimee,tboundary,ntboundary + implicit none + integer, intent(in) :: ib + real*8,dimension(3,3) :: r,eigvec + real*8,dimension(3) :: eigval + integer :: i,j,itp,itm,ii + real :: fm,fp + ! Interpolate covariance to current time + itm = 1 + if(ntboundary>1) then + do while(rtimee>tboundary(itm)) + itm = itm+1 + end do + if (rtimee>tboundary(1)) then + itm = itm-1 + end if + itp = itm+1 + fm = (tboundary(itp)-rtimee)/(tboundary(itp)-tboundary(itm)) + fp = (rtimee-tboundary(itm))/(tboundary(itp)-tboundary(itm)) + else + itm = 1; itp = 1; fp = 1.; fm = 1. + endif + ! Calculate eigenvalues and eigenvectors for each patch + do i = 1,boundary(ib)%nx1patch + do j = 1,boundary(ib)%nx2patch + r(1,1) = fp*boundary(ib)%u2(i,j,itp)+fm*boundary(ib)%u2(i,j,itm) + r(2,2) = fp*boundary(ib)%v2(i,j,itp)+fm*boundary(ib)%v2(i,j,itm) + r(3,3) = fp*boundary(ib)%w2(i,j,itp)+fm*boundary(ib)%w2(i,j,itm) + r(1,2) = fp*boundary(ib)%uv(i,j,itp)+fm*boundary(ib)%uv(i,j,itm) + r(1,3) = fp*boundary(ib)%uw(i,j,itp)+fm*boundary(ib)%uw(i,j,itp) + r(2,3) = fp*boundary(ib)%vw(i,j,itp)+fm*boundary(ib)%vw(i,j,itm) + r(2,1) = r(1,2); r(3,1) = r(1,3); r(3,2) = r(2,3) + call DSYEVJ3(r,eigvec,eigval) + do ii = 1,3 + !if(eigval(ii)<-1.e-8) print *,"warning negative eigenvalue ",eigval(ii), " value set to 1e-8" + eigval(ii) = max(eigval(ii),1.e-8) + end do + boundary(ib)%eigvec(i,j,:,:) = real(eigvec) + boundary(ib)%ci(i,j,:) = real(sqrt(eigval)) + end do + end do + end subroutine calc_eigdec + + subroutine calc_pert(ib,x,y,z,nx,ny,nz,uturb,iuturb) + use modglobal, only : rtimee,dxint,dyint + use modmpi, only : myidx,myidy + implicit none + real, dimension(:), intent(in) :: x,y,z + integer, intent(in) :: nx,ny,nz,iuturb,ib + real(field_r), dimension(:,:), intent(out) :: uturb + integer,target :: i,j,k,ipatch,jpatch,kpatch + integer :: ii + integer, pointer :: pi1, pi2,pi1patch,pi2patch + real, dimension(3) :: xx,ci + real, dimension(3,3) :: eigvec + real :: t,utemp,vtemp,wtemp + t = rtimee + select case(ib) + case(1,2) + pi1 => j; pi2 => k; pi1patch => jpatch; pi2patch => kpatch + case(3,4) + pi1 => i; pi2 => k; pi1patch => ipatch; pi2patch => kpatch + case(5) + pi1 => i; pi2 => j; pi1patch => ipatch; pi2patch => jpatch + end select + do i = 1,nx + xx(1) = x(i); ipatch = min(int(x(i)/dxint)+1,nxpatch) + do j = 1,ny + xx(2) = y(j); jpatch = min(int(y(j)/dyint)+1,nypatch) + do k = 1,nz + xx(3) = z(k); kpatch = min(k,nzpatch) + ci = boundary(ib)%ci(pi1patch,pi2patch,:) + eigvec = boundary(ib)%eigvec(pi1patch,pi2patch,:,:) + utemp = 0.; vtemp = 0.; wtemp = 0. + do ii = 1,nmodes + utemp = utemp + p(ii,1)*cos(dot(kn(ii,:)/ci(1)*ctot,xx/lambda)+omega(ii)*t/tau) + & + & q(ii,1)*sin(dot(kn(ii,:)/ci(1)*ctot,xx/lambda)+omega(ii)*t/tau) + vtemp = vtemp + p(ii,2)*cos(dot(kn(ii,:)/ci(2)*ctot,xx/lambda)+omega(ii)*t/tau) + & + & q(ii,2)*sin(dot(kn(ii,:)/ci(2)*ctot,xx/lambda)+omega(ii)*t/tau) + wtemp = wtemp + p(ii,3)*cos(dot(kn(ii,:)/ci(3)*ctot,xx/lambda)+omega(ii)*t/tau) + & + & q(ii,3)*sin(dot(kn(ii,:)/ci(3)*ctot,xx/lambda)+omega(ii)*t/tau) + end do + ! Scale velocity fields + utemp = utemp*ci(1) + vtemp = vtemp*ci(2) + wtemp = wtemp*ci(3) + ! Reproject to cartesian velocity pertubations + uturb(pi1,pi2) = nisqrt*dot(eigvec(iuturb,:),(/utemp,vtemp,wtemp/)) + end do + end do + end do + nullify(pi1,pi2,pi1patch,pi2patch) + end subroutine calc_pert + + subroutine calc_pert2(ib,x,y,z,nx,ny,nz,uturb,iuturb,thlturb,qtturb) + use modglobal, only : rtimee,tboundary,ntboundary + use modmpi, only : myidx,myidy + implicit none + real, dimension(:), intent(in) :: x,y,z + integer, intent(in) :: nx,ny,nz,iuturb,ib + real(field_r), dimension(:,:), intent(out) :: uturb,thlturb,qtturb + integer,target :: i,j,k,ipatch,jpatch,kpatch + integer :: ii,itp,itm + integer, pointer :: pi1, pi2,pi1patch,pi2patch + real, dimension(3) :: xx,ci + real, dimension(3,3) :: eigvec + real :: wthl,wqt,w2,thl2,qt2,utemp,vtemp,wtemp,wturbf,thltemp,qttemp + real :: t,fp,fm,rho + t = rtimee + itm = 1 + ! Interpolate covariance to current time + if(ntboundary>1) then + do while(t>tboundary(itm)) + itm = itm+1 + end do + if(t>tboundary(1)) then + itm = itm-1 + end if + itp = itm+1 + fm = (tboundary(itp)-t)/(tboundary(itp)-tboundary(itm)) + fp = (t-tboundary(itm))/(tboundary(itp)-tboundary(itm)) + else + itm = 1; itp = 1; fp = 1.; fm = 1. + endif + select case(ib) + case(1,2) + pi1 => j; pi2 => k; pi1patch => jpatch; pi2patch => kpatch + case(3,4) + pi1 => i; pi2 => k; pi1patch => ipatch; pi2patch => kpatch + case(5) + pi1 => i; pi2 => j; pi1patch => ipatch; pi2patch => jpatch + end select + do i = 1,nx + xx(1) = x(i); ipatch = min(int(x(i)/dxint)+1,nxpatch) + do j = 1,ny + xx(2) = y(j); jpatch = min(int(y(j)/dyint)+1,nypatch) + do k = 1,nz + xx(3) = z(k); kpatch = min(k,nzpatch) + ci = boundary(ib)%ci(pi1patch,pi2patch,:) + eigvec = boundary(ib)%eigvec(pi1patch,pi2patch,:,:) + utemp = 0.; vtemp = 0.; wtemp = 0.; thltemp = 0.; qttemp = 0. + do ii = 1,nmodes + utemp = utemp + p(ii,1)*cos(dot(kn(ii,:)/ci(1)*ctot,xx/lambda)+omega(ii)*t/tau) + & + & q(ii,1)*sin(dot(kn(ii,:)/ci(1)*ctot,xx/lambda)+omega(ii)*t/tau) + vtemp = vtemp + p(ii,2)*cos(dot(kn(ii,:)/ci(2)*ctot,xx/lambda)+omega(ii)*t/tau) + & + & q(ii,2)*sin(dot(kn(ii,:)/ci(2)*ctot,xx/lambda)+omega(ii)*t/tau) + wtemp = wtemp + p(ii,3)*cos(dot(kn(ii,:)/ci(3)*ctot,xx/lambda)+omega(ii)*t/tau) + & + & q(ii,3)*sin(dot(kn(ii,:)/ci(3)*ctot,xx/lambda)+omega(ii)*t/tau) + thltemp = thltemp + p_thl(ii)*cos(dot(k_thl(ii,:),xx/lambdasxyz)+omega_thl(ii)*t/tau) + & + & q_thl(ii)*sin(dot(k_thl(ii,:),xx/lambdasxyz)+omega_thl(ii)*t/tau) + qttemp = qttemp + p_qt(ii)*cos(dot(k_qt(ii,:),xx/lambdasxyz)+omega_qt(ii)*t/tau) + & + & q_qt(ii)*sin(dot(k_qt(ii,:),xx/lambdasxyz)+omega_qt(ii)*t/tau) + end do + ! Scale velocity fields + utemp = utemp*ci(1) + vtemp = vtemp*ci(2) + wtemp = wtemp*ci(3) + ! Reproject to cartesian velocity pertubations + uturb(pi1,pi2) = nisqrt*dot(eigvec(iuturb,:),(/utemp,vtemp,wtemp/)) + ! Calculate thlturb and qtturb + thltemp = thltemp * nisqrt2 + qttemp = qttemp * nisqrt2 + wturbf = nisqrt*dot(eigvec(3,:),(/utemp,vtemp,wtemp/)) + wthl = fp*boundary(ib)%wthl(pi1patch,pi2patch,itp) + fm*boundary(ib)%wthl(pi1patch,pi2patch,itm) + wqt = fp*boundary(ib)%wqt(pi1patch,pi2patch,itp) + fm*boundary(ib)%wqt(pi1patch,pi2patch,itm) + w2 = fp*boundary(ib)%w2(pi1patch,pi2patch,itp) + fm*boundary(ib)%w2(pi1patch,pi2patch,itm) + thl2 = fp*boundary(ib)%thl2(pi1patch,pi2patch,itp) + fm*boundary(ib)%thl2(pi1patch,pi2patch,itm) + qt2 = fp*boundary(ib)%qt2(pi1patch,pi2patch,itp) + fm*boundary(ib)%qt2(pi1patch,pi2patch,itm) + if(thl2==0. .or. w2==0.) then + thlturb(pi1,pi2) = thltemp*sqrt(thl2) + else + rho = min(max(wthl/sqrt(thl2*w2),-1.),1.) + thlturb(pi1,pi2) = (rho*wturbf/sqrt(w2) + sqrt(1-rho**2)*thltemp)*sqrt(thl2) + endif + if(qt2==0. .or. w2==0.) then + qtturb(pi1,pi2) = qttemp*sqrt(qt2) + else + rho = min(max(wqt/sqrt(qt2*w2),-1.),1.) + qtturb(pi1,pi2) = (rho*wturbf/sqrt(w2) + sqrt(1-rho**2)*qttemp)*sqrt(qt2) + endif + !if(ib==1) then + ! vturb(pi1,pi2) = nisqrt*dot(eigvec(2,:),(/utemp,vtemp,wtemp/)) + ! wturb(pi1,pi2) = nisqrt*dot(eigvec(3,:),(/utemp,vtemp,wtemp/)) + !endif + end do + end do + end do + ! ! Uncommend for netcdf output turbulent pertubations + ! if(ib==1) then + ! call check( nf90_put_var(ncid, uturb_varid, uturb, start = start, & + ! count = count) ) + ! call check( nf90_put_var(ncid, vturb_varid, vturb, start = start, & + ! count = count) ) + ! call check( nf90_put_var(ncid, wturb_varid, wturb, start = start, & + ! count = count) ) + ! call check( nf90_put_var(ncid, thlturb_varid, thlturb, start = start, & + ! count = count) ) + ! call check( nf90_put_var(ncid, qtturb_varid, qtturb, start = start, & + ! count = count) ) + ! call check( nf90_put_var(ncid, t_varid, (/t/), start = (/start(3)/), & + ! count = (/count(3)/)) ) + ! start(3) = start(3)+1 + ! endif + nullify(pi1,pi2,pi1patch,pi2patch) + end subroutine calc_pert2 + + function gaussrand(mu,sigma) + use modglobal, only : pi,eps1 + implicit none + real :: gaussrand + real, intent(in) :: mu, sigma + real :: temp1,temp2 + temp1 = 0. + do while(temp1