module module_madwrf 1

  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  !                                          !
  ! Purpose: MAD-WRF model components        !
  !                                          !
  ! Author: Pedro A. Jimenez                 !
  !                                          !
  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

    use module_model_constants, only : G, T0, RCP
#if ( ! NMM_CORE == 1 )
    use module_soil_pre, only : Skip_middle_points_t
#endif

    implicit none

    private
    public :: Init_madwrf_tracers, Init_madwrf_clouds

  contains


    function Calc_cldtopz_from_brtemp (cldmask, ts, dzs, brtemp, tropoz, ht, kts, kte) result (return_value)

    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    !                                                                                         !
    ! Purpose: Calculate cloud top height from brigtness temperature                          !
    !                                                                                         !
    ! Authors: Pedro A. Jimenez version of Greg Thompson code                                 !
    !                                                                                         !
    ! Comments: Staring at model top, go downwards first to the level of the tropopause,      !
    !           then keep going until the model temperature is greater (warmer) than the      !
    !           incoming GOES satellite longwave IR tempeature (channel13 on GOES-R series).  !
    !           As version1.0, this will be the highest altitude of potential cloud based on  !
    !           IR temperature whereas an improvement is possible for inversions in which the !
    !           cloud top could be lower altitude that might be detected using the RH field.  !
    !                                                                                         !
    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    
      implicit none

      real :: return_value

      real, intent (in) :: cldmask, brtemp, tropoz, ht
      integer, intent (in) :: kts, kte
      real, dimension (kts:kte), intent (in) :: ts, dzs

      real, parameter :: MISSING_CLDTOPZ = -999.9, MISSING_BRTEMP = -999.9
      real :: this_height
      integer :: k


      return_value = MISSING_CLDTOPZ
      if (brtemp == MISSING_BRTEMP) return
      if (cldmask > 0.0) then
        this_height = sum (dzs(kts:kte - 1)) + ht
        do k = kte - 2, kts, -1
          this_height = this_height - dzs(k + 1)
          if (this_height > tropoz) cycle
          if (ts(k) > brtemp) then
            return_value = this_height + dzs(k) * (brtemp - ts(k)) / (ts(k + 1) - ts(k))
            if ((return_value < this_height) .or. (return_value > this_height + dzs(k))) &
                return_value = tropoz
            exit
          end if
        end do
      end if

    end function Calc_cldtopz_from_brtemp


    subroutine Init_madwrf_clouds (moist, p_qv, p_qc, p_qi, p_qs, p00, t, p, ph_2, phb, alt, xland, cldmask, cldtopz, cldbasez, & 1,5
         brtemp, ht,  dx, dy, flag_cldmask, flag_cldtopz, flag_cldbasez, flag_brtemp, em_width, hold_ups, ids, ide, jds, jde,   &
         its, ims, ime, jms, jme, kms, kme, ite, jts, jte, kts, kte, cldfra)

    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    !                                          !
    ! Purpose: Cloud initialization            !
    !                                          !
    ! Author: Pedro A. Jimenez                 !
    !                                          !
    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

      implicit none

        ! t, p, ph_2, phb, alt, xland
      real, dimension (ims:ime, kms:kme, jms:jme, *), intent (inout) :: moist
      real, dimension (ims:ime, kms:kme, jms:jme), intent (in) :: t, p, ph_2, phb, alt
      real, dimension (ims:ime, jms:jme), intent (in) :: xland, cldbasez, brtemp, ht
      real, dimension (ims:ime, jms:jme), intent (inout) :: cldmask, cldtopz
      logical, intent (in) :: hold_ups
      integer, intent (in) :: p_qv, p_qc, p_qi, p_qs, flag_cldmask, flag_cldtopz, flag_cldbasez, flag_brtemp, em_width, &
          ids, ide, jds, jde, ims, ime, jms, jme, kms, kme, its, ite, jts, jte, kts, kte
      real, intent (in) :: p00, dx, dy
      real, dimension (ims:ime, kms:kme, jms:jme), intent (out) :: cldfra

      real, parameter :: CONVERT_M_TO_KM = 0.001, MISSING_CLDTOPZ = -999.9, MISSING_CLDMASK = -999.9
      real, dimension (kts:kte - 1) :: dzs, ts
      real :: gridkm, tropoz, cldtopz_agl
      integer :: i, j, k, insert_clouds, k_tropo
      logical, parameter :: LOCAL_DEBUG = .false.


        ! Identify the cloud initialization method
      insert_clouds = 0
      if (flag_cldmask == 1 .and. flag_cldtopz == 0 .and. flag_brtemp == 0) insert_clouds = 1
      if ((flag_cldmask == 1 .and. flag_brtemp == 1) .or. flag_cldtopz == 1) insert_clouds = 2
      if (insert_clouds == 2 .and. flag_cldbasez == 1) insert_clouds = 3
      if (LOCAL_DEBUG) then
        print *, 'flag_cldmask = ', flag_cldmask
        print *, 'flag_cldtopz = ', flag_cldtopz
        print *, 'flag_brtemp = ', flag_brtemp
        print *, 'insert_clouds = ', insert_clouds
      end if

      gridkm = sqrt (dx * dy) * CONVERT_M_TO_KM

      Loop_j: do j = jts, min (jte, jde - 1)
        Loop_i: do i = its, min (ite, ide - 1)
#if ( ! NMM_CORE == 1 )
          if (Skip_middle_points_t (ids, ide, jds, jde, i, j, em_width, hold_ups)) cycle
#endif

          if (LOCAL_DEBUG) print *, 'Calculations for i, j = ', i, j

          Loop_k: do k = kts, kte - 1
              ! Convert theta pert to T
            ts(k) = (t(i, k, j) + T0) / ((p00 / p(i, k, j)) ** RCP) 
              ! Calc layer thickness
            dzs(k) = (ph_2(i, k + 1, j) + phb(i, k + 1, j) - (ph_2(i, k, j) + phb(i, k, j))) / G
          end do Loop_k

            ! Calc troposphere height
          call Calc_tropo_height (ts, p(i, :, j), dzs, kts, kte, LOCAL_DEBUG, k_tropo, tropoz)
          tropoz = tropoz + ht(i, j)

            ! Calc cloud top height agl if necessary
          if (insert_clouds > 0) then
            cldtopz_agl = MISSING_CLDTOPZ
            if (flag_cldtopz == 1) then
                ! cloud mask
              if (cldtopz(i, j) > 0.0) then
                cldmask(i, j) = 1.0
              elseif (cldtopz(i, j) == 0.0) then 
                cldmask(i, j) = 0.0
              else
                cldmask(i, j) = MISSING_CLDMASK
              end if
                ! cloud top height
              if (cldtopz(i, j) > 0.0) cldtopz_agl = max (0.0, cldtopz(i, j) -  ht(i, j))
            else if (flag_brtemp == 1) then
                ! Only cloud top height needed
              cldtopz(i, j) = Calc_cldtopz_from_brtemp (cldmask(i, j), ts, dzs, brtemp(i, j), tropoz, ht(i, j), kts, kte)
              if (cldtopz(i, j) > 0.0) cldtopz_agl = max (0.0, cldtopz(i, j) -  ht(i, j))
            end if
          end if

          select_cld_impro: select case (insert_clouds)
            case (0)
                ! Use the analysis field
              call cal_cldfra3_madwrf (cldfra(i, :, j), moist(i, :, j, p_qv), moist(i, :, j, p_qc), &
                  moist(i, :, j, p_qi), moist(i, :, j, p_qs), dzs, p(i, :, j), ts(:),   &
                  xland(i, j), gridkm, .true., 1.5, tropoz, kts, kte, .false.)

            case (1)
                ! Remove clouds if clear (cldmask = 0)
              call cal_cldfra3_madwrf (cldfra(i, :, j), moist(i, :, j, p_qv), moist(i, :, j, p_qc), &
                  moist(i, :, j, p_qi), moist(i, :, j, p_qs), dzs, p(i, :, j), ts(:),   &
                  xland(i, j), gridkm, .true., 1.5, tropoz, kts, kte, .false., &
                  cldmask = cldmask(i, j))

            case (2)
                ! Remove clouds if clear (cldmask = 0)
                ! Reduce / extend cloud top heights to match observations
                ! Add clouds over clear sky regions (cldmask = 1)
              call cal_cldfra3_madwrf (cldfra(i, :, j), moist(i, :, j, p_qv), moist(i, :, j, p_qc), &
                  moist(i, :, j, p_qi), moist(i, :, j, p_qs), dzs, p(i, :, j), ts(:),   &
                  xland(i, j), gridkm, .true., 1.5, tropoz, kts, kte, .false., &
                  cldmask = cldmask(i, j), cldtopz = cldtopz_agl)

            case (3)
                ! Remove clouds if clear (cldmask = 0)
                ! Reduce / extend cloud top / base heights to match observations
              call cal_cldfra3_madwrf (cldfra(i, :, j), moist(i, :, j, p_qv), moist(i, :, j, p_qc), &
                  moist(i, :, j, p_qi), moist(i, :, j, p_qs), dzs, p(i, :, j), ts(:),   &
                  xland(i, j), gridkm, .true., 1.5, tropoz, kts, kte, .false.,  &
                  cldmask = cldmask(i, j), cldtopz = cldtopz_agl, cldbasez = cldbasez(i, j))

          end select select_cld_impro

!               do k = kts, kte - 1
!                  moist(i,k,j,P_QV) = MAX(temp_Qv(k), moist(i,k,j,P_QV))
!                  moist(i,k,j,P_QC) = temp_Qc(k) / temp_R(k)
!                  moist(i,k,j,P_QI) = temp_Qi(k) / temp_R(k)
!                  if (P_QNI .gt. 1) then
!                     scalar(i,k,j,P_QNI) = temp_Ni(k) / temp_R(k)
!                  endif
!                  if (P_QNC .gt. 1) then
!                     scalar(i,k,j,P_QNC) = temp_Nc(k) / temp_R(k)
!                  endif
!               enddo
        end do Loop_i
      end do Loop_j

    end subroutine Init_madwrf_clouds


    subroutine Init_madwrf_tracers (tracer, moist, p_qc, p_qi, p_qs, p_tr_qc, p_tr_qi, p_tr_qs, & 1
        ids, ide, jds, jde, kds, kde, ims, ime, jms, jme, kms, kme, its , ite , jts , jte , kts , kte)

    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    !                                          !
    ! Purpose: Initializes MAD-WRF tracers     !
    !                                          !
    ! Author: Pedro A. Jimenez                 !
    !                                          !
    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

      implicit none

      real, dimension (ims:ime, kms:kme, jms:jme, *), intent (inout) :: tracer
      real, dimension (ims:ime, kms:kme, jms:jme, *), intent (in) :: moist
      integer, intent (in) :: p_qc, p_qi, p_qs, p_tr_qc, p_tr_qi, p_tr_qs, ids, ide, jds, jde, &
          kds, kde, ims, ime, jms, jme, kms, kme, its , ite , jts , jte , kts , kte

      integer :: i, j, k


      do j = jts, min (jte, jde - 1)
        do k = kts, kte - 1
          do i = its, min (ite, ide - 1)
            tracer(i, k, j, p_tr_qc) = moist(i, k, j, p_qc)
            tracer(i, k, j, p_tr_qi) = moist(i, k, j, p_qi)
            tracer(i, k, j, p_tr_qs) = moist(i, k, j, p_qs)
          end do
        end do
      end do

    end subroutine Init_madwrf_tracers

!+---+-----------------------------------------------------------------+


      SUBROUTINE cal_cldfra3_madwrf(CLDFRA, qv, qc, qi, qs, dz,                & 4,14
     &                 p, t, XLAND, gridkm,                             &
     &                 modify_qvapor, max_relh,                         &
     &                 tropo_z, kts,kte, debug_flag, k_tropo, cldmask,  &
     &                 cldtopz, cldbasez)
!
      USE module_mp_thompson   , ONLY : rsif, rslf
      IMPLICIT NONE
!
      INTEGER, INTENT(IN):: kts, kte
      LOGICAL, INTENT(IN):: modify_qvapor
      REAL, DIMENSION(kts:kte), INTENT(INOUT):: qv, qc, qi, qs, cldfra
      REAL, DIMENSION(kts:kte), INTENT(IN):: p, t, dz
      REAL, INTENT(IN):: gridkm, XLAND, max_relh
      REAL, INTENT(INOUT):: tropo_z
      LOGICAL, INTENT(IN):: debug_flag
      integer, intent(in), optional :: k_tropo
      real, intent(in), optional :: cldmask, cldtopz, cldbasez

!..Local vars.
      REAL:: RH_00L, RH_00O, RH_00
      REAL:: entrmnt=0.5
      INTEGER:: k
      REAL:: TC, qvsi, qvsw, RHUM, delz, cldbasek_tmp
      REAL, DIMENSION(kts:kte):: qvs, rh, rhoa

      character*512 dbg_msg
      logical :: is_tropo_init, impose_cldmask, impose_cldtopz, &
          impose_cldbasetopz, keep_clouds_below_lcl
      INTEGER :: cldtopk, cldbasek, cldthick_bot_k, cldfra_thresh_k
      REAL, PARAMETER :: CLDTHICK_DEF = -999.9  ! Default thickness [m] of new clouds
      REAL, PARAMETER :: CLDFRA_DEF = 0.5  ! Default cloud fraction to insert in new clouds
      REAL, PARAMETER :: CLDFRA_THRESH = 0.0  ! Default threshold cloud fraction to determine presence of clouds
      REAL, PARAMETER :: RH_NOCLOUD = 0.3
      logical, parameter :: LOCAL_DEBUG = .false.


        ! Define logical vars to handle optional
        ! arguments
      if (present(k_tropo)) then
        is_tropo_init = .true.
      else
        is_tropo_init = .false.
      end if

      keep_clouds_below_lcl = .false.
      impose_cldmask = .false.
      impose_cldtopz = .false.
      impose_cldbasetopz = .false.

      if (present(cldmask) .and. .not. present(cldtopz) .and. .not. present(cldbasez)) then
        impose_cldmask = .true.
      elseif (present(cldmask) .and. present(cldtopz) .and. .not. present(cldbasez)) then
        impose_cldtopz = .true.
      elseif (present(cldmask) .and. present(cldtopz) .and. present(cldbasez)) then
        impose_cldbasetopz = .true.
      end if

      !! Initialize variables
      cldtopk = -999
      cldbasek = -999
      cldthick_bot_k = -999
      cldfra_thresh_k = -999

        ! Remove hydrometeors if necessary and
        ! calculate height related variables
      if (impose_cldmask) then
        if (cldmask == 0.0) then
          do k = kts, kte - 1
            cldfra(k) = 0.0
            qc(k) = 0.0
            qi(k) = 0.0
            qs(k) = 0.0
          end do
          return
        end if
      end if

      if_impose_cldtopz: if (impose_cldtopz .or. impose_cldbasetopz) then
         if_cldmask: if (cldmask == 0.0) then
            do k = kts, kte - 1
               cldfra(k) = 0.0
               qc(k) = 0.0
               qi(k) = 0.0
               qs(k) = 0.0
            end do
            return
         else if (cldmask > 0.0 .and. cldmask <= 1.0 .and. cldtopz > 0.0) then
              ! Zero out hydrometeors above cldtopz
            call find_k_from_z_agl(cldtopz, cldtopk, dz, kts, kte)
            if (cldtopk > 0 .and. cldtopk < kte) then
              do k = cldtopk + 1, kte - 1
                 cldfra(k) = 0.0
                 qc(k) = 0.0
                 qi(k) = 0.0
                 qs(k) = 0.0
              end do
            end if

              ! Calculate the cloud bottom k if the specified thickness were to be applied
              ! If that would be below ground, then set the k level to 1
            if (CLDTHICK_DEF > 0.0) then
              if (cldtopz - CLDTHICK_DEF > 0.0) then
                 call find_k_from_z_agl(cldtopz - CLDTHICK_DEF, cldthick_bot_k, dz, kts, kte)
              else
                 cldthick_bot_k = 1
              end if
            end if
         end if if_cldmask
      end if if_impose_cldtopz


      if (impose_cldbasetopz) then
         !! If cloud base information (cldbzin) is also provided in met_em files, then...
         !! Zero out hydrometeors below observed cloud base in columns where cldmask > 0.0
         !! Note that hydrometeors have already been zeroed out in columns where cldmask = 0.0
         if (cldmask > 0.0 .and. cldmask <= 1.0 .and. cldbasez > 0.0) then
            ! First, find out level of observed cloud base
            call find_k_from_z_agl(cldbasez, cldbasek, dz, kts, kte)
            !! Trust satellite estimations of cloud top height more than METAR estimates/extrapolations of cloud base height
            !! Thus, if cldtopk < cldbasek, then pretend we don't have cldbasek
            if (cldtopk < cldbasek) cldbasek = -999
            if (cldbasek > 0 .and. cldbasek > kts) then
               do k = kts, cldbasek-1
                  cldfra(k) = 0.0
                  qc(k) = 0.0
                  qi(k) = 0.0
                  qs(k) = 0.0
               end do
            end if
         end if
      end if

!+---+

!..Initialize cloud fraction, compute RH, and rho-air.

         DO k = kts, kte - 1
            CLDFRA(K) = 0.0
            tc = t(k) - 273.15

            qvsw = rslf(P(k), t(k))
            if (debug_flag) print *, 'k, p, t, qvsw, qv =', k, p(k), t(k), qvsw, qv(k)
            if (tc .lt. 0.0) then
               qvsi = rsif(P(k), t(k)+0.025)    !..To ensure a tiny bit ice supersaturated
            else
               qvsi = qvsw
            endif

            if (tc .ge. -12.0) then
               qvs(k) = qvsw
            elseif (tc .lt. -35.0) then
               qvs(k) = qvsi
            else
               qvs(k) = qvsw - (qvsw-qvsi)*(-12.0-tc)/(-12.0+35.)
            endif

            if (modify_qvapor) then
               if (qc(k).gt.1.E-8) then
                  qv(k) = MAX(qv(k), qvsw)
                  qvs(k) = qvsw
               endif
               if (qc(k).le.1.E-8 .and. qi(k).ge.1.E-9) then
                  qv(k) = MAX(qv(k), qvsi)
                  qvs(k) = qvsi
               endif
            endif


            rh(k) = MAX(0.01, qv(k)/qvs(k))
            if (debug_flag) print *, '  qv, qvs, qvsi, qc, qi, rh =', qv(k), qvs(k), qvsi, qc(k), qi(k), rh(k)
            rhoa(k) = p(k)/(287.0*t(k))
         ENDDO

          ! Over ocean, find the level at which RH is lower than RH_NOCLOUD starting from cloud top
          ! to avoid putting clouds in very dry layers
        if ((impose_cldtopz .or. impose_cldbasetopz) .and. (XLAND - 1.5) > 0.0) then
          if (cldtopk > 0 .and. cldtopk < kte) then
            if (cldbasek < 0 .or. cldbasek > cldtopk) then
              if (rh(cldtopk) < RH_NOCLOUD) then
                cldbasek = cldtopk + 1
              else
                cldbasek = cldtopk
              end if
            else
              cldbasek_tmp = cldtopk + 1
              do k = cldtopk, kts, -1
                if (rh(k) < RH_NOCLOUD) exit
                  cldbasek_tmp = k
              end do
              if (cldbasek_tmp > cldbasek) cldbasek = cldbasek_tmp
            end if
          end if
        end if

!..First cut scale-aware. Higher resolution should require closer to
!.. saturated grid box for higher cloud fraction.  Simple functions
!.. chosen based on Mocko and Cotton (1995) starting point and desire
!.. to get near 100% RH as grid spacing moves toward 1.0km, but higher
!.. RH over ocean required as compared to over land.

      DO k = kts, kte - 1

         delz = MAX(100., dz(k))
         RH_00L = 0.65 + SQRT(1./(25.0+gridkm*gridkm*delz*0.01))
         RH_00O = 0.81 + SQRT(1./(50.0+gridkm*gridkm*delz*0.01))
         RHUM = rh(k)

         if (qc(k).gt.1.E-7 .or. qi(k).ge.1.E-7                         &
     &                    .or. (qs(k).gt.1.E-6 .and. t(k).lt.273.)) then
            CLDFRA(K) = 1.0
         else

            IF ((XLAND-1.5).GT.0.) THEN                                  !--- Ocean
               RH_00 = RH_00O
            ELSE                                                         !--- Land
               RH_00 = RH_00L
            ENDIF

            tc = t(k) - 273.15
            if (tc .lt. -12.0) RH_00 = RH_00L

            if (tc .ge. 25.0) then
               CLDFRA(K) = 0.0
            elseif (tc .ge. -12.0) then
               RHUM = MIN(rh(k), 1.0)
               CLDFRA(K) = MAX(0., 1.0-SQRT((1.005-RHUM)/(1.005-RH_00)))
            else
               if (max_relh.gt.1.12 .or. (.NOT.(modify_qvapor)) ) then
!..For HRRR model, the following look OK.
                  RHUM = MIN(rh(k), 1.45)
                  RH_00 = RH_00 + (1.45-RH_00)*(-12.0-tc)/(-12.0+100.)
                  CLDFRA(K) = MAX(0., 1.0-SQRT((1.5-RHUM)/(1.5-RH_00)))
               else
!..but for the GFS model, RH is way lower.
                  RHUM = MIN(rh(k), 1.05)
                  RH_00 = RH_00 + (1.05-RH_00)*(-12.0-tc)/(-12.0+100.)
                  CLDFRA(K) = MAX(0., 1.0-SQRT((1.05-RHUM)/(1.05-RH_00)))
               endif
            endif
            if (CLDFRA(K).gt.0.) CLDFRA(K) = MAX(0.01, MIN(CLDFRA(K),0.9))

            if (debug_flag) then
              WRITE (dbg_msg,*) 'DEBUG-GT: cloud fraction: ', RH_00, RHUM, CLDFRA(K)
              CALL wrf_debug (150, dbg_msg)
            endif

         endif
      ENDDO

      if ((impose_cldtopz .or. impose_cldbasetopz) .and. cldtopk > 0 .and. cldtopk < kte) then
          ! Remove clouds above observed cloud top
        CLDFRA(cldtopk + 1:kte) = 0.0

        keep_clouds_below_lcl = .true.

          ! Find the first cloudy level
        call find_thresh_k_downward(cldfra, CLDFRA_THRESH, cldfra_thresh_k, cldtopk, kts, kts, kte)
        if (cldfra_thresh_k > 0) then
             ! Extend cloud until cldtopz
           if (cldfra_thresh_k < cldtopk) cldfra(cldfra_thresh_k + 1:cldtopk) = CLDFRA_DEF
        else
            ! No clouds present
          if (CLDTHICK_DEF > 0.0) then
            cldfra(cldthick_bot_k:cldtopk) = CLDFRA_DEF
!          else
!            if (rh(cldtopk) > RH_NOCLOUD) cldfra(cldtopk) = CLDFRA_DEF
          end if
        end if
      end if

      if (impose_cldbasetopz .and. cldbasek > kts .and. cldbasek <= kte) then
         !! Remove clouds below observed cloud base
         CLDFRA(kts:cldbasek-1) = 0.0

         !! If necessary, add clouds with default CLDFRA from observed cloud base up to base of already-existing cloud
         cldfra_thresh_k = -999  ! reset this variable
         call find_thresh_k_upward(cldfra, CLDFRA_THRESH, cldfra_thresh_k, cldbasek, kte, kts, kte)
         if (cldfra_thresh_k > cldbasek) then
           do k = cldbasek, cldfra_thresh_k - 1
             if (rh(k) > RH_NOCLOUD) cldfra(k) = CLDFRA_DEF
           end do
         end if
      end if


      if (is_tropo_init) then
        call find_cloudLayers(qvs, cldfra, T, P, Dz, entrmnt,             &
       &                      debug_flag, qc, qi, qs, tropo_z, kts,kte, keep_clouds_below_lcl, k_tropo)
      else
        call find_cloudLayers(qvs, cldfra, T, P, Dz, entrmnt,             &
       &                      debug_flag, qc, qi, qs, tropo_z, kts,kte, keep_clouds_below_lcl)
      end if

!..Do a final total column adjustment since we may have added more than 1mm
!.. LWP/IWP for multiple cloud decks.

      call adjust_cloudFinal(cldfra, qc, qi, rhoa, dz, kts,kte)

      if (debug_flag) then
        WRITE (dbg_msg,*) 'DEBUG-GT:  Made-up fake profile of clouds'
        CALL wrf_debug (150, dbg_msg)
        do k = kte - 1, kts, -1
           write(dbg_msg,'(f7.2, 2x, f7.2, 2x, f6.4, 2x, f7.3, 2x,  f15.7, 2x, f15.7)') &
     &          T(k)-273.15, P(k)*0.01, rh(k), cldfra(k)*100., qc(k)*1000.,qi(k)*1000.
           CALL wrf_debug (150, dbg_msg)
        enddo
      endif

      if (modify_qvapor) then
         DO k = kts, kte - 1
            if (cldfra(k).gt.0.20 .and. cldfra(k).lt.1.0) then
               qv(k) = MAX(qv(k),qvs(k))
            endif
         ENDDO
      endif

      END SUBROUTINE cal_cldfra3_madwrf


      SUBROUTINE find_k_from_z_agl(z_agl, k_lev, dz, kts, kte) 3

      IMPLICIT NONE

      INTEGER, INTENT(IN)  :: kts, kte
      REAL, INTENT(IN)     :: z_agl
      INTEGER, INTENT(OUT) :: k_lev
      REAL, DIMENSION(kts:kte), INTENT(IN) :: dz
      INTEGER :: k
      REAL    :: z_this, z_next, z_full_lev

      !! Identify the k-level of a given height AGL, starting from ground level
      z_full_lev = 0.0
      do k = kts, kte
         z_this = z_full_lev
         z_next = z_this + dz(k)
         if (z_agl > 0.0) then
            if (z_agl > z_this .and. z_agl <= z_next) then
               k_lev = k
            end if
         end if
         z_full_lev = z_next
      end do

      END SUBROUTINE find_k_from_z_agl


      SUBROUTINE find_thresh_k_downward(var, var_thresh, k_lev, k_top, k_bot, kts, kte) 1

      IMPLICIT NONE

      INTEGER, INTENT(IN)  :: kts, kte
      INTEGER, INTENT(IN)  :: k_top, k_bot
      REAL, DIMENSION(kts:kte), INTENT(IN) :: var
      REAL, INTENT(IN)     :: var_thresh
      INTEGER, INTENT(OUT) :: k_lev
      INTEGER :: k

      !! Identify the k-level where the input variable first exceeds a threshold, searching downward over a given range
      do k = k_top, k_bot, -1
         if (var(k) > var_thresh) then
            k_lev = k
            exit
         end if
      end do

      END SUBROUTINE find_thresh_k_downward


      SUBROUTINE find_thresh_k_upward(var, var_thresh, k_lev, k_bot, k_top, kts, kte) 1

      IMPLICIT NONE

      INTEGER, INTENT(IN)  :: kts, kte
      INTEGER, INTENT(IN)  :: k_top, k_bot
      REAL, DIMENSION(kts:kte), INTENT(IN) :: var
      REAL, INTENT(IN)     :: var_thresh
      INTEGER, INTENT(OUT) :: k_lev
      INTEGER :: k

      !! Identify the k-level where the input variable first exceeds a threshold, searching upward over a given range
      do k = k_bot, k_top
         if (var(k) > var_thresh) then
            k_lev = k
            exit
         end if
      end do

      END SUBROUTINE

!+---+-----------------------------------------------------------------+
!..From cloud fraction array, find clouds of multi-level depth and compute
!.. a reasonable value of LWP or IWP that might be contained in that depth,
!.. unless existing LWC/IWC is already there.


      SUBROUTINE find_cloudLayers(qvs1d, cfr1d, T1d, P1d, Dz1d, entrmnt,& 3,24
     &                            debugfl, qc1d, qi1d, qs1d,            &
     &                            tropo_z, kts,kte, keep_clouds_below_lcl, ktropo)
!
      IMPLICIT NONE
!
      INTEGER, INTENT(IN):: kts, kte
      LOGICAL, INTENT(IN):: debugfl, keep_clouds_below_lcl
      REAL, INTENT(IN):: entrmnt
      REAL, INTENT(INOUT):: tropo_z
      REAL, DIMENSION(kts:kte), INTENT(IN):: qs1d,qvs1d,T1d,P1d,Dz1d
      REAL, DIMENSION(kts:kte), INTENT(INOUT):: cfr1d, qc1d, qi1d
      integer, intent(in), optional :: ktropo

!..Local vars.
      REAL, DIMENSION(kts:kte):: theta
      REAL:: theta1, theta2, delz
      INTEGER:: k, k2, k_tropo, k_m12C, k_m40C, k_cldb, k_cldt, kbot
      LOGICAL:: in_cloud
      character*512 dbg_msg
      logical :: is_tropo_init

!+---+

      if (present(ktropo)) then
        is_tropo_init = .true.
      else
        is_tropo_init = .false.
      end if

      k_m12C = 0
      k_m40C = 0
      DO k = kte - 1, kts, -1
         theta(k) = T1d(k)*((100000.0/P1d(k))**(287.05/1004.))
         if (T1d(k)-273.16 .gt. -40.0 .and. P1d(k).gt.7000.0) k_m40C = MAX(k_m40C, k)
         if (T1d(k)-273.16 .gt. -12.0 .and. P1d(k).gt.10100.0) k_m12C = MAX(k_m12C, k)
      ENDDO
      if (k_m40C .le. kts) k_m40C = kts
      if (k_m12C .le. kts) k_m12C = kts

      if (k_m40C.gt.kte-2 .OR. k_m12C.gt.kte-3) then
        WRITE (dbg_msg,*) 'DEBUG-GT: WARNING, no possible way neg40 or neg12C can occur this high up: ', k_m40C, k_m12C
        CALL wrf_debug (0, dbg_msg)
        do k = kte - 1, kts, -1
           WRITE (dbg_msg,*) 'DEBUG-GT,   P, T : ', P1d(k)*0.01,T1d(k)-273.16
           CALL wrf_debug (0, dbg_msg)
        enddo
        call wrf_error_fatal ('FATAL ERROR, problem in temperature profile.')
      endif

      if (is_tropo_init) then
       k_tropo = ktropo
      else
        call Calc_tropo_height (T1d, P1d, dz1d, kts, &
          kte, debugfl, k_tropo, tropo_z)
      end if

!..Eliminate possible fractional clouds above supposed tropopause.
      DO k = k_tropo+1, kte - 1
         if (cfr1d(k).gt.0.0 .and. cfr1d(k).lt.1.0) then
!            print *, 'Removing stratospheric cloud', k
            cfr1d(k) = 0.
         endif
      ENDDO

!..We would like to prevent fractional clouds below LCL in idealized
!.. situation with deep well-mixed convective PBL, that otherwise is
!.. likely to get clouds in more realistic capping inversion layer.
      kbot = kts+2
      DO k = kbot, k_m12C
         if ( (theta(k)-theta(k-1)) .gt. 0.010E-3*Dz1d(k)) EXIT
      ENDDO
      kbot = MAX(kts+1, k-2)
      if (.not. keep_clouds_below_lcl) then
        DO k = kts, kbot
!           print *, 'Reducing cloud below LCL', k
           if (cfr1d(k).gt.0.0 .and. cfr1d(k).lt.1.0) cfr1d(k) = 0.5*cfr1d(k)
        ENDDO
      else
        kbot = kts + 1
      end if


!..Starting below tropo height, if cloud fraction greater than 1 percent,
!.. compute an approximate total layer depth of cloud, determine a total 
!.. liquid water/ice path (LWP/IWP), then reduce that amount with tuning 
!.. parameter to represent entrainment factor, then divide up LWP/IWP
!.. into delta-Z weighted amounts for individual levels per cloud layer. 

      k_cldb = k_tropo
      in_cloud = .false.
      k = k_tropo
      DO WHILE (.not. in_cloud .AND. k.gt.k_m12C+1)
         k_cldt = 0
         if (cfr1d(k).ge.0.01) then
            in_cloud = .true.
            k_cldt = MAX(k_cldt, k)
         endif
         if (in_cloud) then
            DO k2 = k_cldt-1, k_m12C, -1
               if (cfr1d(k2).lt.0.01 .or. k2.eq.k_m12C) then
                  k_cldb = k2+1
                  goto 87
               endif
            ENDDO
 87         continue
            in_cloud = .false.
         endif
         if ((k_cldt - k_cldb + 1) .ge. 2) then
      if (debugfl) then
        WRITE (dbg_msg,*) 'DEBUG-GT: An ice cloud layer is found between ', k_cldt, k_cldb, P1d(k_cldt)*0.01, P1d(k_cldb)*0.01
        CALL wrf_debug (150, dbg_msg)
      endif
            call adjust_cloudIce(cfr1d, qi1d, qs1d, qvs1d, T1d, Dz1d,   &
     &                           entrmnt, k_cldb,k_cldt,kts,kte)
            k = k_cldb
         elseif ((k_cldt - k_cldb + 1) .eq. 1) then
      if (debugfl) then
        WRITE (dbg_msg,*) 'DEBUG-GT: A single-layer ice cloud layer is found on ', k_cldb, P1d(k_cldb)*0.01
        CALL wrf_debug (150, dbg_msg)
      endif
            if (cfr1d(k_cldb).gt.0.and.cfr1d(k_cldb).lt.1.)             &
     &               qi1d(k_cldb)=0.05*qvs1d(k_cldb)*cfr1d(k_cldb)*cfr1d(k_cldb)
            k = k_cldb
         endif
         k = k - 1
      ENDDO


      k_cldb = k_m12C + 3
      in_cloud = .false.
      k = k_m12C + 2
      DO WHILE (.not. in_cloud .AND. k.gt.kbot)
         k_cldt = 0
         if (cfr1d(k).ge.0.01) then
            in_cloud = .true.
            k_cldt = MAX(k_cldt, k)
         endif
         if (in_cloud) then
            DO k2 = k_cldt-1, kbot, -1
               if (cfr1d(k2).lt.0.01 .or. k2.eq.kbot) then
                  k_cldb = k2+1
                  goto 88
               endif
            ENDDO
 88         continue
            in_cloud = .false.
         endif
         if ((k_cldt - k_cldb + 1) .ge. 2) then
      if (debugfl) then
        WRITE (dbg_msg,*) 'DEBUG-GT: A water cloud layer is found between ', k_cldt, k_cldb, P1d(k_cldt)*0.01, P1d(k_cldb)*0.01
        CALL wrf_debug (150, dbg_msg)
      endif
            call adjust_cloudH2O(cfr1d, qc1d, qvs1d, T1d, Dz1d,         &
     &                           entrmnt, k_cldb,k_cldt,kts,kte)
            k = k_cldb
         elseif ((k_cldt - k_cldb + 1) .eq. 1) then
            if (cfr1d(k_cldb).gt.0.and.cfr1d(k_cldb).lt.1.)             &
     &               qc1d(k_cldb)=0.05*qvs1d(k_cldb)*cfr1d(k_cldb)*cfr1d(k_cldb)
            k = k_cldb
         endif
         k = k - 1
      ENDDO

      END SUBROUTINE find_cloudLayers


      subroutine Calc_tropo_height (T1d, P1d, dz1d, kts, kte, debugfl, k_tropo, tropo_z) 2,7

        !..Find tropopause height, best surrogate, because we would not really
        !.. wish to put fake clouds into the stratosphere.  The 10/1500 ratio
        !.. d(Theta)/d(Z) approximates a vertical line on typical SkewT chart
        !.. near typical (mid-latitude) tropopause height.  Since messy data
        !.. could give us a false signal of such a transition, do the check over 
        !.. three K-level change, not just a level-to-level check.  This method
        !.. has potential failure in arctic-like conditions with extremely low
        !.. tropopause height, as would any other diagnostic, so ensure resulting
        !.. k_tropo level is above 700hPa.

        implicit none

        REAL, DIMENSION(kts:kte), INTENT(IN) :: T1d, P1d, Dz1d
        integer, intent(in) :: kts, kte
        LOGICAL, INTENT(IN):: debugfl
        integer, intent(out) :: k_tropo
        real, intent (out) :: tropo_z

          ! Local vars
        integer :: k, k_p200
        real :: theta1, theta2, delz
        character*512 dbg_msg
        REAL, DIMENSION(kts:kte):: theta


        k_p200 = 0
        DO k = kte - 1, kts, -1
          theta(k) = T1d(k)*((100000.0/P1d(k))**(287.05/1004.))
          if (P1d(k).gt.19999.0 .and. k_p200.eq.0) k_p200 = k
        END DO

        if ( (kte-k_p200) .lt. 3) k_p200 = kte-3
        DO k = k_p200-2, kts, -1
           theta1 = theta(k)
           theta2 = theta(k+2)
           delz = 0.5*dz1d(k) + dz1d(k+1) + 0.5*dz1d(k+2)
           if ( (((theta2-theta1)/delz).lt.10./1500.) .OR. P1d(k).gt.70000.) EXIT
        ENDDO
        k_tropo = MAX(kts+2, MIN(k+2, kte-1))

        if (k_tropo .gt. k_p200) then
           DO k = kte-3, k_p200-2, -1
              theta1 = theta(k)
              theta2 = theta(k+2)
              delz = 0.5*dz1d(k) + dz1d(k+1) + 0.5*dz1d(k+2)
              if ( (((theta2-theta1)/delz).lt.10./1500.) .AND. P1d(k).gt.9500.) EXIT
           ENDDO
           k_tropo = MAX(k_p200-1, MIN(k+2, kte-1))
        endif
        tropo_z = SUM(dz1d(kts:k_tropo))

        if (k_tropo.gt.kte-2) then
          WRITE (dbg_msg,*) 'DEBUG-GT: CAUTION, tropopause appears to be very high up: ', k_tropo
          CALL wrf_debug (150, dbg_msg)
          do k = kte - 1, kts, -1
             WRITE (dbg_msg,*) 'DEBUG-GT,   P, T : ', k,P1d(k)*0.01,T1d(k)-273.16
             CALL wrf_debug (150, dbg_msg)
          enddo
        elseif (debugfl) then
          WRITE (dbg_msg,*) 'DEBUG-GT: FOUND TROPOPAUSE k=', k_tropo
          CALL wrf_debug (150, dbg_msg)
        endif

        if (debugfl) then
          print *, 'FOUND TROPOPAUSE k, height=', k_tropo, tropo_z
        end if

      end subroutine Calc_tropo_height


      SUBROUTINE adjust_cloudIce(cfr,qi,qs,qvs,T,dz,entr, k1,k2,kts,kte) 2
!
      IMPLICIT NONE
!
      INTEGER, INTENT(IN):: k1,k2, kts,kte
      REAL, INTENT(IN):: entr
      REAL, DIMENSION(kts:kte), INTENT(IN):: cfr, qs, qvs, T, dz
      REAL, DIMENSION(kts:kte), INTENT(INOUT):: qi
      REAL:: iwc, max_iwc, tdz, this_iwc, this_dz
      INTEGER:: k

      tdz = 0.
      do k = k1, k2
         tdz = tdz + dz(k)
      enddo
      max_iwc = ABS(qvs(k2)-qvs(k1))
!     print*, ' max_iwc = ', max_iwc, ' over DZ=',tdz

      do k = k1, k2
         max_iwc = MAX(1.E-6, max_iwc - (qi(k)+qs(k)))
      enddo
      max_iwc = MIN(1.E-3, max_iwc)

      this_dz = 0.0
      do k = k1, k2
         if (k.eq.k1) then
            this_dz = this_dz + 0.5*dz(k)
         else
            this_dz = this_dz + dz(k)
         endif
         this_iwc = max_iwc*this_dz/tdz
         iwc = MAX(1.E-6, this_iwc*(1.-entr))
         if (cfr(k).gt.0.0.and.cfr(k).lt.1.0.and.T(k).ge.203.16) then
            qi(k) = qi(k) + cfr(k)*cfr(k)*iwc
         endif
      enddo

      END SUBROUTINE adjust_cloudIce

!+---+-----------------------------------------------------------------+


      SUBROUTINE adjust_cloudH2O(cfr, qc, qvs,T,dz,entr, k1,k2,kts,kte) 2
!
      IMPLICIT NONE
!
      INTEGER, INTENT(IN):: k1,k2, kts,kte
      REAL, INTENT(IN):: entr
      REAL, DIMENSION(kts:kte), INTENT(IN):: cfr, qvs, T, dz
      REAL, DIMENSION(kts:kte), INTENT(INOUT):: qc
      REAL:: lwc, max_lwc, tdz, this_lwc, this_dz
      INTEGER:: k

      tdz = 0.
      do k = k1, k2
         tdz = tdz + dz(k)
      enddo
      max_lwc = ABS(qvs(k2)-qvs(k1))
!     print*, ' max_lwc = ', max_lwc, ' over DZ=',tdz

      do k = k1, k2
         max_lwc = MAX(1.E-6, max_lwc - qc(k))
      enddo
      max_lwc = MIN(1.E-3, max_lwc)

      this_dz = 0.0
      do k = k1, k2
         if (k.eq.k1) then
            this_dz = this_dz + 0.5*dz(k)
         else
            this_dz = this_dz + dz(k)
         endif
         this_lwc = max_lwc*this_dz/tdz
         lwc = MAX(1.E-6, this_lwc*(1.-entr))
         if (cfr(k).gt.0.0.and.cfr(k).lt.1.0.and.T(k).ge.253.16) then
            qc(k) = qc(k) + cfr(k)*cfr(k)*lwc
         endif
      enddo

      END SUBROUTINE adjust_cloudH2O

!+---+-----------------------------------------------------------------+

!..Do not alter any grid-explicitly resolved hydrometeors, rather only
!.. the supposed amounts due to the cloud fraction scheme.


      SUBROUTINE adjust_cloudFinal(cfr, qc, qi, Rho,dz, kts,kte) 2
!
      IMPLICIT NONE
!
      INTEGER, INTENT(IN):: kts,kte
      REAL, DIMENSION(kts:kte), INTENT(IN):: cfr, Rho, dz
      REAL, DIMENSION(kts:kte), INTENT(INOUT):: qc, qi
      REAL:: lwp, iwp, xfac
      INTEGER:: k

      lwp = 0.
      iwp = 0.
      do k = kts, kte - 1
         if (cfr(k).gt.0.0 .and. cfr(k).lt.1.0) then
            lwp = lwp + qc(k)*Rho(k)*dz(k)
            iwp = iwp + qi(k)*Rho(k)*dz(k)
         endif
      enddo

      if (lwp .gt. 1.0) then
         xfac = 1.0/lwp
         do k = kts, kte - 1
            if (cfr(k).gt.0.0 .and. cfr(k).lt.1.0) then
               qc(k) = qc(k)*xfac
            endif
         enddo
      endif

      if (iwp .gt. 1.0) then
         xfac = 1.0/iwp
         do k = kts, kte - 1
            if (cfr(k).gt.0.0 .and. cfr(k).lt.1.0) then
               qi(k) = qi(k)*xfac
            endif
         enddo
      endif

      END SUBROUTINE adjust_cloudFinal

  end module module_madwrf