modtrees.f90 Source File


This file depends on

sourcefile~~modtrees.f90~~EfferentGraph sourcefile~modtrees.f90 modtrees.f90 sourcefile~modfields.f90 modfields.f90 sourcefile~modtrees.f90->sourcefile~modfields.f90 sourcefile~modglobal.f90 modglobal.f90 sourcefile~modtrees.f90->sourcefile~modglobal.f90 sourcefile~modibmdata.f90 modibmdata.f90 sourcefile~modtrees.f90->sourcefile~modibmdata.f90 sourcefile~modmpi.f90 modmpi.f90 sourcefile~modtrees.f90->sourcefile~modmpi.f90 sourcefile~modsubgriddata.f90 modsubgriddata.f90 sourcefile~modtrees.f90->sourcefile~modsubgriddata.f90 sourcefile~modsurfdata.f90 modsurfdata.f90 sourcefile~modtrees.f90->sourcefile~modsurfdata.f90 sourcefile~modfields.f90->sourcefile~modglobal.f90 sourcefile~modglobal.f90->sourcefile~modmpi.f90

Files dependent on this one

sourcefile~~modtrees.f90~~AfferentGraph sourcefile~modtrees.f90 modtrees.f90 sourcefile~program.f90 program.f90 sourcefile~program.f90->sourcefile~modtrees.f90

Source Code

!> \file modetrees.f90
!!tg3315, ns4513, 20 Mar 2017 

!> Input trees into DALES model.

module modtrees
use mpi
implicit none
save

contains
    subroutine createtrees
    use modglobal,  only : ltrees,ntrees,tree,cexpnr,ifinput,zh,zf,dzh,dzfi,dzhi,dzf,Qstar,&
                           dec,lad,kb,ke,cp,rhoa,ntree_max,dQdt,tr_A,dy,xh
    use modfields,  only : um,vm,wm,thlm,qt0,svp,up,vp,wp,thlp,qtp,Rn,clai,qc,qa,ladzh,ladzf
    use modmpi,     only : myid,comm3d,mpierr,MY_REAL
    use modsurfdata,only : wtsurf
    use modibmdata ,only : bctfz
    implicit none
    integer :: n,k
    real :: Rq
    character(80) chmess

    if ((ltrees .eqv. .false.) .or. (ntrees==0)) return

      allocate(tree(ntrees,6))

      ! read global trees
      if(myid==0) then
        ! write(*,*) '1, myid, ntrees, ltrees, cexpnr', myid, ntrees, ltrees, cexpnr
        if (ntrees>0) then
          open (ifinput,file='trees.inp.'//cexpnr)
          read (ifinput,'(a80)') chmess
          read (ifinput,'(a80)') chmess
          do n=1,ntrees
            read (ifinput,*) &
                  tree(n,1), &
                  tree(n,2), &
                  tree(n,3), &
                  tree(n,4), &
                  tree(n,5), &
                  tree(n,6) 
          end do

          ! write (6,*) 'Tree number,   il, iu, jl, ju, kl, ku '
          ! do n=1,ntrees
          !   write (1,*) &
          !         n , &
          !         tree(n,1), &
          !         tree(n,2), &
          !         tree(n,3), &
          !         tree(n,4), &
          !         tree(n,5), &
          !         tree(n,6)                 
          ! end do
          close (ifinput)
        end if

      end if ! end if myid==0

      ! call MPI_BCAST(ntrees ,1,MPI_INTEGER ,0,comm3d,mpierr)
      call MPI_BCAST(tree ,6*ntrees,MPI_INTEGER ,0,comm3d,mpierr)

      !! capability to read lad of trees from a lad.inp.xxx file
      !! tg3315 commented - for now assume constant lad or hardcode as seen below
      !if (myid==0) then

      !  allocate(ladt(nlad)) 

      !  if (nlad>0) then
      !    open (ifinput,file='lad.inp.'//cexpnr)
      !    read (ifinput,'(a80)') chmess
      !    do n=1,nlad
      !      read (ifinput,*) &
      !            ladt(n)
      !      end do

      !    write (*,*) 'lad'
      !    do n=1,nlad
      !      write (1,*) &
      !            n , &
      !            ladt(n)
      !    end do
      !  end if

      ! input into leaf area density array (cell faces)
      !lad(tree(1,5):tree(1,6)+1) = ladt !defined at cell faces

      ! initialise tree variables that are constant in time
      ! calculate clai for tallest possible tree
      ! assume no grid stretching in lowest layer with trees
      if (myid==0) then

        ntree_max = maxval(tree(:,6))-minval(tree(:,5))+1

        ! hard code non-uniform lad profiles - temporary
        if (.false.) then
          ladzh(1:ntree_max) = (/  0.1, 0.11, 0.12, 0.14, 0.16, 0.19, 0.22, 0.265, 0.31, 0.335, 0.36, 0.37, 0.38, 0.3725, 0.365, 0.3375, 0.31, 0.2375, 0.165, 0.0875 /) !LAI =2 from Shaw, 1992
          ! assumes lad at ntree_max+1 is 0.!
        else
          ladzh(1:ntree_max+1) = lad !up to ntree_max+1 cos ladzh is at cell faces
        end if

        ! interpolate to find lad at cell centres
        do k=1,ntree_max
          ladzf(k) = 0.5*(ladzh(k)+ladzh(k+1))
        end do

        ! clai at cell faces using lad at cell centre
        do k = 1,ntree_max
          clai(k) = sum(0.5*(ladzh(k:ntree_max)+ladzh(k+1:ntree_max+1))*(dzf(maxval(tree(:,6))-ntree_max+k:maxval(tree(:,6)))))
        end do

        ! Net radiation at cell faces! !W/m^2
        do k = 1,ntree_max+1
          Rn(k) = Qstar*exp(-dec*clai(k))
        end do

        ! Change in radiation over each layer (at cell centres) W/m^2
        do k = 1,ntree_max
          qc(k) = Rn(k+1) - Rn(k)
        end do

        ! incorporate the distributed storage term - updated for zero heat storage in tree
        do k = 1,ntree_max
          qa(k) = qc(k) !(1 - Rq*0.11) * qc(k) - 0.11*Rq*dQdt + Rq*12.3
        end do

        ! write relevant fields
        ! write(*,*) 'ntree_max', ntree_max
        ! write(*,*) 'qa', qa(1:ntree_max)
        ! write(*,*) 'qc', qc(1:ntree_max)
        ! write(*,*) 'clai', clai(1:ntree_max+1)
        ! write(*,*) 'Rn', Rn(1:ntree_max+1)
        ! write(*,*) 'ladzf', ladzf
        ! write(*,*) 'ladzh', ladzh

      end if

      !apply storage to set wtsurf and bctfz as a function of Qstar
      wtsurf = -((1-0.7)*Qstar-0.33*dQdt+38)/(rhoa*cp)
      bctfz  = -((1-0.7)*Qstar-0.33*dQdt+38)/(rhoa*cp)

      ! calc tree cover area - used to apply steady-state boundary condition
      if (myid==0) then
        do n = 1,ntrees
          tr_A = tr_A + (xh(tree(n,2)+1) - xh(tree(n,1)))*dy*(tree(n,4) - tree(n,3) + 1)
        end do
      end if

      ! write updated ground and roof heat fluxes
      write(*,*) 'wtsurf', wtsurf
      write(*,*) 'bctfz', bctfz

      ! broadcast variables
      call MPI_BCAST(ntree_max , 1,MY_REAL ,0,comm3d,mpierr)
      call MPI_BCAST(tr_A , 1,MY_REAL ,0,comm3d,mpierr)
      call MPI_BCAST(Rn , ke+1,MY_REAL ,0,comm3d,mpierr)
      call MPI_BCAST(qc , ke+1,MY_REAL ,0,comm3d,mpierr)
      call MPI_BCAST(clai  , ke+1,MY_REAL ,0,comm3d,mpierr)  
      call MPI_BCAST(qa  , ke+1,MY_REAL ,0,comm3d,mpierr)
      call MPI_BCAST(ladzf  , ke+1,MY_REAL ,0,comm3d,mpierr)
      call MPI_BCAST(ladzh , ke+1,MY_REAL ,0,comm3d,mpierr)

    end subroutine createtrees

    subroutine trees
    use modglobal,  only : ib,ie,jb,je,kb,ke,ih,jh,dzf,xh,dxf,numol,prandtlmol,rlv,cp,tree,&
                           ntrees,ltrees,itot,jtot,cd,ud,lmoist,nsv,dxf,dy,dzf,dzfi,zf,dy,zh,&
                           ltempeq,pref0,r_s,lad,rhoa,ntree_max,lsize,Qstar,dQdt,BCxs,tr_A,&
                           rslabs,kmax,rv,rd
    use modfields,  only : um,vm,wm,thlm,qtm,svp,up,vp,wp,thlp,qtp,svm,Rn,qc,qa,thlm,clai,&
                           ladzf,ladzh,IIcs,tr_omega,&
                           tr_u,tr_v,tr_w,tr_qt,tr_qtR,tr_qtA,tr_thl,tr_sv,thlpcar
    use modmpi,     only : myidx,myidy,mpi_sum,mpierr,comm3d,mpierr,my_real,nprocx,nprocy
    use modsurfdata,only : wtsurf,wttop,wqtop
    use modibmdata, only : bctfz
    use modsubgriddata, only : ekh
    implicit none
    integer :: i,j,k,n,m,il,iu,jl,ju,kl,ku
    real :: e_sat,e_vap,qh,qe,shade,r_a,s,D,numoli,gam,Rq,qhmin,qhmax,omega
    real :: Vq_dum,Vq_dum2,Vq_dum3,VT_dum,VT_dum2,VT_dum3,wTbot_dum,wTbot_dum2,wTbot,wTbot_dum3

    numoli = 1/numol
    gam = (cp*pref0*rv)/(rlv*rd)
    qhmin=0.;qhmax=0.

    if (ltrees .eqv. .false.) return

    ! dummy variables used for steady-state BC calculation
    VT_dum2 = 0.
    Vq_dum2 = 0.
    wTbot_dum2 = 0.

    ! loop over all tree canopies
    do n = 1,ntrees
 
      ! drag in z-direction
      il = tree(n,1) - myidx*itot/nprocx
      iu = tree(n,2) - myidx*itot/nprocx
      kl = tree(n,5)
      ku = tree(n,6) + 1
      jl = tree(n,3) - myidy*jtot/nprocy
      ju = tree(n,4) - myidy*jtot/nprocy
      if (iu < ib .or. il > ie .or. ju < jb .or. jl > je) then
        cycle
      else
        if (iu > ie) iu=ie
        if (il < ib) il=ib 
        if (ju > je) ju=je
        if (jl < jb) jl=jb  

        do k = kl,ku 
          do j = jl,ju
            do i = il,iu
              tr_w(i,j,k) = - cd * ladzh(ntree_max-(ku-k)+1) * wm(i,j,k) * &
                            sqrt( wm(i,j,k)**2 &
                            + (0.25*(um(i,j,k) + um(i+1,j,k) + um(i,j,k-1) + um(i+1,j,k-1)))**2 &
                            + (0.25*(vm(i,j,k) + vm(i,j+1,k) + vm(i,j,k-1) + vm(i,j+1,k-1)))**2 )
            end do
          end do
        end do
      end if

      ! drag in y-direction
      il = tree(n,1) - myidx*itot/nprocx
      iu = tree(n,2) - myidx*itot/nprocx
      kl = tree(n,5)
      ku = tree(n,6)
      jl = tree(n,3) - myidy*jtot/nprocy
      ju = tree(n,4) + 1 - myidy*jtot/nprocy
      if (iu < ib .or. il > ie .or. ju < jb .or. jl > je) then
        cycle
      else
        if (iu > ie) iu=ie
        if (il < ib) il=ib 
        if (ju > je) ju=je
        if (jl < jb) jl=jb 

        do k = kl,ku
          do j = jl,ju
            do i = il,iu
              tr_v(i,j,k) = - cd * ladzf(ntree_max-(ku-k)) * vm(i,j,k) * &
                           sqrt( vm(i,j,k)**2 &
                          + (0.25*(um(i,j,k) + um(i+1,j,k) + um(i,j-1,k) + um(i+1,j-1,k)))**2 &
                          + (0.25*(wm(i,j,k) + wm(i,j,k+1) + wm(i,j-1,k) + wm(i,j-1,k+1)))**2 )
            end do
          end do
        end do
      end if

      ! drag in x-direction
      il = tree(n,1) - myidx*itot/nprocx
      iu = tree(n,2) + 1 - myidx*itot/nprocx
      kl = tree(n,5)
      ku = tree(n,6)
      jl = tree(n,3) - myidy*jtot/nprocy
      ju = tree(n,4) - myidy*jtot/nprocy
      if (iu < ib .or. il > ie .or. ju < jb .or. jl > je) then
        cycle
      else
        if (iu > ie) iu=ie
        if (il < ib) il=ib 
        if (ju > je) ju=je
        if (jl < jb) jl=jb 
 
        do k = kl,ku
          do j = jl,ju
            do i = il,iu
              tr_u(i,j,k) = - cd * ladzf(ntree_max-(ku-k)) * um(i,j,k) * &
                           sqrt( um(i,j,k)**2 &
                          + (0.25*(vm(i,j,k) + vm(i,j+1,k) + vm(i-1,j,k) + vm(i-1,j+1,k)))**2 &
                          + (0.25*(wm(i,j,k) + wm(i,j,k+1) + wm(i-1,j,k) + wm(i-1,j,k+1)))**2 )
            end do
          end do
        end do
      end if

      ! scalar volumetric sources/sinks

      ! Canopy EB
      if (lmoist .and. ltempeq) then

        il = tree(n,1) - myidx*itot/nprocx
        iu = tree(n,2) - myidx*itot/nprocx
        kl = tree(n,5)
        ku = tree(n,6)
        jl = tree(n,3) - myidy*jtot/nprocy
        ju = tree(n,4) - myidy*jtot/nprocy
        if (iu < ib .or. il > ie .or. ju < jb .or. jl > je) then
          cycle
        else
          if (iu > ie) iu=ie
          if (il < ib) il=ib 
          if (ju > je) ju=je
          if (jl < jb) jl=jb 
       
          do k = kl,ku
            do j = jl,ju
              do i = il,iu

                 ! psychometrics
                 ! saturation vapour pressure pressure
                 e_sat = 610.8*exp((17.27*(thlm(i,j,k)-273.15))/(thlm(i,j,k)-35.85))

                 ! water vapour partial pressure
                 e_vap = (qtm(i,j,k) * pref0) / (0.378 * qtm(i,j,k) + 0.622)
                 ! qtcheck.m and https://earthscience.stackexchange.com/questions/2360/how-do-i-convert-specific-humidity-to-relative-humidity

                 ! vapour pressure deficit
                 D = max(e_sat - e_vap,0.)

                 ! output warning - cannot physically occur
                 if (e_sat<e_vap) then
                   write(*,*) 'D, e_sat, e_vap, thlm, qtm', D, e_sat, e_vap, thlm(i,j,k), qtm(i,j,k)
                 end if

                 ! slope of the curve relating saturation vapour pressure to temperature
                 s = (4098*e_sat)/((thlm(i,j,k)-35.85)**2)

                 ! aerodynamic resistance
                 r_a = 130*sqrt(lsize/(sqrt((0.5*(um(i,j,k)+um(i+1,j,k)))**2+(0.5*(vm(i,j,k)+vm(i,j+1,k)))**2+(0.5*(wm(i,j,k)+wm(i,j,k+1)))**2)))

                 ! decoupling factor
                 omega = 1/(1 + 2*(gam/(s+2*gam)) * ((r_s)/r_a) )

                 ! latent heat
                 qe = omega*(s/(s+2*gam))*(qa(ntree_max-(ku-k))/(dzf(k)*ladzf(ntree_max-(ku-k)))) + (1-omega)*(1/(gam*(r_s)))*rhoa*cp*D

                 ! sensible heat
                 qh = qa(ntree_max-(ku-k))/(dzf(k)*ladzf(ntree_max-(ku-k))) - qe

                 ! volumetric sinks/source of specific humidity and temp
                 tr_qt(i,j,k) = ladzf(ntree_max-(ku-k))*qe/(rhoa*rlv)
                 tr_qtR(i,j,k) = ladzf(ntree_max-(ku-k))*( omega*(s/(s+2*gam))*(qa(ntree_max-(ku-k))/(dzf(k)*ladzf(ntree_max-(ku-k)))))/(rhoa*rlv)
                 tr_qtA(i,j,k) = ladzf(ntree_max-(ku-k))*((1-omega)*(1/(gam*(r_s)))*rhoa*cp*D)/(rhoa*rlv)
                 tr_thl(i,j,k) = ladzf(ntree_max-(ku-k))*qh/(rhoa*cp)

                 tr_omega(i,j,k) = omega

                 ! add to rhs
                 qtp(i,j,k) = qtp(i,j,k) + tr_qt(i,j,k) !ladz(ntree_max-(ku-k))*qe/(rhoa*rlv)
                 thlp(i,j,k) = thlp(i,j,k) + tr_thl(i,j,k) !ladz(ntree_max-(ku-k))*qh/(rhoa*cp)

                 ! fill dummy variables for steady-state BCs
                 VT_dum2 = VT_dum2 - tr_thl(i,j,k)*dzf(k)*dxf(i)*dy
                 Vq_dum2 = Vq_dum2 - tr_qt(i,j,k)*dzf(k)*dxf(i)*dy

              end do
            end do
          end do

          ! fraction of total net radiation reaching the bottom of the tree
          Rq = Rn(ntree_max+1-(ku+1-kl)) / Qstar

          ! sensible heat flux from shaded surfaces
          shade = ( (1 - 0.7 ) * Rn(ntree_max+1-(ku+1-kl) ) - 0.33*Rq*dQdt + Rq*38 )/ (rhoa*cp)

          ! dummy var for steady-state BC
          wTbot_dum2 = wTbot_dum2 - shade*(xh(iu+1)-xh(il))*dy*(ju-jl+1)

          ! overwrite standard heat flux defined in modibm in shaded positions
          thlp(il:iu,jl:ju,kb+1) = thlp(il:iu,jl:ju,kb+1) + (bctfz + shade) * dzfi(kb+1)
 
         end if

      end if !lmoist and ltempeq

      ! scalar deposition
      if (nsv>0) then

        ! specific to infinite canyon study (tg3315 thesis - Chapter 5)
        if (BCxs==2) then ! no deposition effect from first or last tree to avoid interaction with BCs
          if (n==1) cycle
          if (n==ntrees) cycle
        end if
      
        il = tree(n,1) - myidx*itot/nprocx
        iu = tree(n,2) - myidx*itot/nprocx
        kl = tree(n,5)
        ku = tree(n,6)
        jl = tree(n,3) - myidy*jtot/nprocy
        ju = tree(n,4) - myidy*jtot/nprocy
        if (iu < ib .or. il > ie .or. ju < jb .or. jl > je) then
          cycle
        else
          if (iu > ie) iu=ie
          if (il < ib) il=ib 
          if (ju > je) ju=je
          if (jl < jb) jl=jb 

          do m = 1,nsv ! define this for scalar variables that are deposited
            do k = kl,ku
              do j = jl,ju
                do i = il,iu
                  tr_sv(i,j,k,m) = - svm(i,j,k,m) * ladzf(ntree_max-(ku-k)) * ud
                  svp(i,j,k,m) = svp(i,j,k,m) + tr_sv(i,j,k,m)
                end do
              end do
            end do
          end do
        end if
      end if !nsv

    end do ! ntrees

    ! define these outside loop to avoid double counting cell faces
    wp(ib:ie,jb:je,kb:ke) = wp(ib:ie,jb:je,kb:ke) + tr_w(ib:ie,jb:je,kb:ke)
    vp(ib:ie,jb:je,kb:ke) = vp(ib:ie,jb:je,kb:ke) + tr_v(ib:ie,jb:je,kb:ke)
    up(ib:ie,jb:je,kb:ke) = up(ib:ie,jb:je,kb:ke) + tr_u(ib:ie,jb:je,kb:ke)

    ! steady-state BC calc.
    if (lmoist .and. ltempeq) then

      ! dummy vars - not all used?
      wTbot_dum3 = 0.
      wTbot_dum = 0.
      wTbot = 0.
      VT_dum3 = 0.
      Vq_dum3 = 0.
      VT_dum = 0.
      Vq_dum = 0.

      ! sum dummy variables assigned in main loop
      call MPI_ALLREDUCE(wTbot_dum2,   wTbot_dum3,1,MY_REAL,MPI_SUM,comm3d,mpierr)    
      call MPI_ALLREDUCE(VT_dum2, VT_dum3  ,1,MY_REAL,MPI_SUM,comm3d,mpierr)
      call MPI_ALLREDUCE(Vq_dum2, Vq_dum3  ,1,MY_REAL,MPI_SUM,comm3d,mpierr)

      ! not necessary
      wTbot_dum = wTbot_dum3
      Vq_dum = Vq_dum3
      VT_dum = VT_dum3

      ! total flux of temp from ground and roofs accounting for shading
      wTbot = wTbot_dum + ((xh(ie+1)-xh(ib))*jtot*dy - tr_A)*bctfz

      ! temp flux at top equal to half of the total flux of heat (ground, roofs and canopies) - consistent with tg3315 thesis Chapter 4 - method iii
      wttop = 0.5*(wTbot + VT_dum)/((xh(ie+1)-xh(ib))*jtot*dy)

      ! volumetric sink term throughout domain to account for the remaining heating - ""
      thlpcar = wttop/((sum(real(IIcs(kb+1:ke))/rslabs)/real(kmax-1.))*(zh(ke+1)-zh(kb+1)))

      ! top flux of qt equal to total from canopies - therefore uniform flux profile
      wqtop = (Vq_dum ) / ((xh(ie+1)-xh(ib))*jtot*dy)

    end if

    end subroutine trees

  end module modtrees