C $Header: /u/gcmpack/MITgcm/pkg/exf/exf_bulkformulae.F,v 1.32 2017/01/27 17:22:55 jmc Exp $
C $Name:  $

#include "EXF_OPTIONS.h"
#ifdef ALLOW_AUTODIFF
# include "AUTODIFF_OPTIONS.h"
#endif

CBOP
C     !ROUTINE: EXF_BULKFORMULAE
C     !INTERFACE:
      SUBROUTINE EXF_BULKFORMULAE( myTime, myIter, myThid )

C     !DESCRIPTION: \bv
C     *==========================================================*
C     | SUBROUTINE EXF_BULKFORMULAE
C     | o Calculate bulk formula fluxes over open ocean
C     |   either following
C     |   Large and Pond, 1981 & 1982
C     |   or (if defined ALLOW_BULK_LARGEYEAGER04)
C     |   Large and Yeager, 2004, NCAR/TN-460+STR.
C     *==========================================================*
C     \ev
C
C
C     NOTES:
C     ======
C
C     See EXF_OPTIONS.h for a description of the various possible
C     ocean-model forcing configurations.
C
C     The bulk formulae of pkg/exf are not valid for sea-ice covered
C     oceans but they can be used in combination with a sea-ice model,
C     for example, pkg/seaice, to specify open water flux contributions.
C
C     ==================================================================
C
C     for Large and Pond, 1981 & 1982
C
C     The calculation of the bulk surface fluxes has been adapted from
C     the NCOM model which uses the formulae given in Large and Pond
C     (1981 & 1982 )
C
C
C     Header taken from NCOM version: ncom1.4.1
C     -----------------------------------------
C
C     Following procedures and coefficients in Large and Pond
C     (1981 ; 1982)
C
C     Output: Bulk estimates of the turbulent surface fluxes.
C     -------
C
C     hs  - sensible heat flux  (W/m^2), into ocean
C     hl  - latent   heat flux  (W/m^2), into ocean
C
C     Input:
C     ------
C
C     us  - mean wind speed (m/s)     at height hu (m)
C     th  - mean air temperature (K)  at height ht (m)
C     qh  - mean air humidity (kg/kg) at height hq (m)
C     sst - sea surface temperature (K)
C     tk0 - Kelvin temperature at 0 Celsius (K)
C
C     Assume 1) a neutral 10m drag coefficient =
C
C               cdn = .0027/u10 + .000142 + .0000764 u10
C
C            2) a neutral 10m stanton number =
C
C               ctn = .0327 sqrt(cdn), unstable
C               ctn = .0180 sqrt(cdn), stable
C
C            3) a neutral 10m dalton number =
C
C               cen = .0346 sqrt(cdn)
C
C            4) the saturation humidity of air at
C
C               t(k) = exf_BulkqSat(t)  (kg/m^3)
C
C     Note:  1) here, tstar = <wt>/u*, and qstar = <wq>/u*.
C            2) wind speeds should all be above a minimum speed,
C               say 0.5 m/s.
C            3) with optional iteration loop, niter=3, should suffice.
C            4) this version is for analyses inputs with hu = 10m and
C               ht = hq.
C            5) sst enters in Celsius.
C
C     ==================================================================
C
C       started: Christian Eckert eckert@mit.edu 27-Aug-1999
C
C     changed: Christian Eckert eckert@mit.edu 14-Jan-2000
C            - restructured the original version in order to have a
C              better interface to the MITgcmUV.
C
C            Christian Eckert eckert@mit.edu  12-Feb-2000
C            - Changed Routine names (package prefix: exf_)
C
C            Patrick Heimbach, heimbach@mit.edu  04-May-2000
C            - changed the handling of precip and sflux with respect
C              to CPP options ALLOW_BULKFORMULAE and ALLOW_ATM_TEMP
C            - included some CPP flags ALLOW_BULKFORMULAE to make
C              sure ALLOW_ATM_TEMP, ALLOW_ATM_WIND are used only in
C              conjunction with defined ALLOW_BULKFORMULAE
C            - statement functions discarded
C
C            Ralf.Giering@FastOpt.de 25-Mai-2000
C            - total rewrite using new subroutines
C
C            Detlef Stammer: include river run-off. Nov. 21, 2001
C
C            heimbach@mit.edu, 10-Jan-2002
C            - changes to enable field swapping
C
C     mods for pkg/seaice: menemenlis@jpl.nasa.gov 20-Dec-2002
C
C     martin.losch@awi.de: merged with exf_bulk_largeyeager04, 21-May-2010
C
C     ==================================================================
C
C     for Large and Yeager, 2004
C
C === Turbulent Fluxes :
C  * use the approach "B": shift coeff to height & stability of the
C    atmosphere state (instead of "C": shift temp & humid to the height
C    of wind, then shift the coeff to this height & stability of the atmos).
C  * similar to EXF (except over sea-ice) ; default parameter values
C    taken from Large & Yeager.
C  * assume that Qair & Tair inputs are from the same height (zq=zt)
C  * formulae in short:
C     wind stress = (ust,vst) = rhoA * Cd * Ws * (del.u,del.v)
C     Sensib Heat flux = fsha = rhoA * Ch * Ws * del.T * CpAir
C     Latent Heat flux = flha = rhoA * Ce * Ws * del.Q * Lvap
C                      = -Evap * Lvap
C   with Ws = wind speed = sqrt(del.u^2 +del.v^2) ;
C        del.T = Tair - Tsurf ; del.Q = Qair - Qsurf ;
C        Cd,Ch,Ce = drag coefficient, Stanton number and Dalton number
C              respectively [no-units], function of height & stability

C     !USES:
       IMPLICIT NONE
C     === Global variables ===
#include "EEPARAMS.h"
#include "SIZE.h"
#include "PARAMS.h"
#include "DYNVARS.h"
#include "GRID.h"

#include "EXF_PARAM.h"
#include "EXF_FIELDS.h"
#include "EXF_CONSTANTS.h"

#ifdef ALLOW_AUTODIFF_TAMC
#include "tamc.h"
#endif

C     !INPUT/OUTPUT PARAMETERS:
C     input:
C     myTime  :: Current time in simulation
C     myIter  :: Current iteration number in simulation
C     myThid  :: My Thread Id number
      _RL     myTime
      INTEGER myIter
      INTEGER myThid
C     output:
CEOP

#ifdef ALLOW_BULKFORMULAE
#ifdef ALLOW_ATM_TEMP
C     == external Functions

C     == Local variables ==
C     i,j      :: grid point indices
C     bi,bj    :: tile indices
C     ssq      :: saturation specific humidity [kg/kg] in eq. with Sea-Surface water
      INTEGER i,j,bi,bj

      _RL czol
      _RL Tsf                   ! surface temperature [K]
      _RL wsm                   ! limited wind speed [m/s] (> umin)
      _RL t0                    ! virtual temperature [K]
C     these need to be 2D-arrays for vectorizing code
      _RL tstar (1:sNx,1:sNy)   ! turbulent temperature scale [K]
      _RL qstar (1:sNx,1:sNy)   ! turbulent humidity scale  [kg/kg]
      _RL ustar (1:sNx,1:sNy)   ! friction velocity [m/s]
      _RL tau   (1:sNx,1:sNy)   ! surface stress coef = rhoA * Ws * sqrt(Cd)
      _RL rdn   (1:sNx,1:sNy)   ! neutral, zref (=10m) values of rd
      _RL rd    (1:sNx,1:sNy)   ! = sqrt(Cd)          [-]
      _RL delq  (1:sNx,1:sNy)   ! specific humidity difference [kg/kg]
      _RL deltap(1:sNx,1:sNy)
#ifdef EXF_CALC_ATMRHO
      _RL atmrho_loc(1:sNx,1:sNy) ! local atmospheric density [kg/m^3]
#endif

#ifdef ALLOW_BULK_LARGEYEAGER04
      _RL dzTmp
#endif
      _RL SSTtmp
      _RL ssq
      _RL re                    ! = Ce / sqrt(Cd)     [-]
      _RL rh                    ! = Ch / sqrt(Cd)     [-]
      _RL ren, rhn              ! neutral, zref (=10m) values of re, rh
      _RL usn, usm
      _RL stable                ! = 1 if stable ; = 0 if unstable
      _RL huol                  ! stability parameter at zwd [-] (=z/Monin-Obuklov length)
      _RL htol                  ! stability parameter at zth [-]
      _RL hqol
      _RL x                     ! stability function  [-]
      _RL xsq                   ! = x^2               [-]
      _RL psimh                 ! momentum stability function
      _RL psixh                 ! latent & sensib. stability function
      _RL zwln                  ! = log(zwd/zref)
      _RL ztln                  ! = log(zth/zref)
      _RL tmpbulk
      _RL recip_rhoConstFresh
      INTEGER ksrf, ksrfp1
      INTEGER iter
C     solve4Stress :: if F, by-pass momentum turb. flux (wind-stress) calculation
      LOGICAL solve4Stress
      _RL windStress            ! surface wind-stress (@ grid cell center)

#ifdef ALLOW_AUTODIFF_TAMC
      INTEGER ikey_1
      INTEGER ikey_2
#endif

C---+----1----+----2----+----3----+----4----+----5----+----6----+----7-|--+----|

      IF ( useAtmWind ) THEN
        solve4Stress = .TRUE.
      ELSE
#ifdef ALLOW_BULK_LARGEYEAGER04
        solve4Stress = wspeedfile .NE. ' '
#else
        solve4Stress = .FALSE.
#endif
      ENDIF

C-- Set surface parameters :
      zwln = LOG(hu/zref)
      ztln = LOG(ht/zref)
      czol = hu*karman*gravity_mks
C--   abbreviation
      recip_rhoConstFresh = 1. _d 0/rhoConstFresh

C     Loop over tiles.
#ifdef ALLOW_AUTODIFF_TAMC
C--   HPF directive to help TAMC
CHPF$ INDEPENDENT
#endif
      DO bj = myByLo(myThid),myByHi(myThid)
#ifdef ALLOW_AUTODIFF_TAMC
C--    HPF directive to help TAMC
CHPF$  INDEPENDENT
#endif
       DO bi = myBxLo(myThid),myBxHi(myThid)
        ksrf   = 1
        ksrfp1 = 2
        DO j = 1,sNy
         DO i = 1,sNx

#ifdef ALLOW_AUTODIFF_TAMC
          act1 = bi - myBxLo(myThid)
          max1 = myBxHi(myThid) - myBxLo(myThid) + 1
          act2 = bj - myByLo(myThid)
          max2 = myByHi(myThid) - myByLo(myThid) + 1
          act3 = myThid - 1
          max3 = nTx*nTy
          act4 = ikey_dynamics - 1

          ikey_1 = i
     &         + sNx*(j-1)
     &         + sNx*sNy*act1
     &         + sNx*sNy*max1*act2
     &         + sNx*sNy*max1*max2*act3
     &         + sNx*sNy*max1*max2*max3*act4
#endif

#ifdef ALLOW_AUTODIFF
          deltap(i,j) = 0. _d 0
          delq(i,j)   = 0. _d 0
#endif
C--- Compute turbulent surface fluxes
C-   Pot. Temp and saturated specific humidity
          IF ( atemp(i,j,bi,bj) .NE. 0. _d 0 ) THEN
C-   Surface Temp.
           Tsf = theta(i,j,ksrf,bi,bj) + cen2kel
           IF ( Nr.GE.2 .AND. sstExtrapol.GT.0. _d 0 ) THEN
            SSTtmp = sstExtrapol
     &              *( theta(i,j,ksrf,bi,bj)-theta(i,j,ksrfp1,bi,bj) )
     &              *  maskC(i,j,ksrfp1,bi,bj)
            Tsf = Tsf + MAX( SSTtmp, 0. _d 0 )
           ENDIF

           tmpbulk = cvapor_fac*exp(-cvapor_exp/Tsf)
#ifdef EXF_CALC_ATMRHO
           atmrho_loc(i,j) = apressure(i,j,bi,bj) /
     &                  (287.04 _d 0*atemp(i,j,bi,bj)
     &                  *(1. _d 0 + humid_fac*aqh(i,j,bi,bj)))
           ssq = saltsat*tmpbulk/atmrho_loc(i,j)
#else
           ssq = saltsat*tmpbulk/atmrho
#endif
           deltap(i,j) = atemp(i,j,bi,bj) + gamma_blk*ht - Tsf
           delq(i,j)   = aqh(i,j,bi,bj) - ssq
C--  no negative evaporation over ocean:
           IF ( noNegativeEvap ) delq(i,j) = MIN( 0. _d 0, delq(i,j) )

C--  initial guess for exchange coefficients:
C    take U_N = del.U ; stability from del.Theta ;
           stable = exf_half + SIGN(exf_half, deltap(i,j))

           IF ( solve4Stress ) THEN
#ifdef ALLOW_AUTODIFF_TAMC
CADJ STORE sh(i,j,bi,bj)     = comlev1_exf_1, key = ikey_1
#endif /* ALLOW_AUTODIFF_TAMC */
C--   Wind speed
            wsm        = sh(i,j,bi,bj)
            tmpbulk    = exf_scal_BulkCdn *
     &           ( cdrag_1/wsm + cdrag_2 + cdrag_3*wsm )
            rdn(i,j)   = SQRT(tmpbulk)
            ustar(i,j) = rdn(i,j)*wsm
           ELSE
            rdn(i,j)   = 0. _d 0
            windStress = wStress(i,j,bi,bj)
#ifdef EXF_CALC_ATMRHO
            ustar(i,j) = SQRT(windStress/atmrho_loc(i,j))
            tau(i,j)   = SQRT(windStress*atmrho_loc(i,j))
#else
            ustar(i,j) = SQRT(windStress/atmrho)
c           tau(i,j)   = windStress/ustar(i,j)
            tau(i,j)   = SQRT(windStress*atmrho)
#endif
           ENDIF

C--  initial guess for exchange other coefficients:
           rhn = (exf_one-stable)*cstanton_1 + stable*cstanton_2
           ren = cDalton
C--  calculate turbulent scales
           tstar(i,j)=rhn*deltap(i,j)
           qstar(i,j)=ren*delq(i,j)

          ELSE
C     atemp(i,j,bi,bj) .EQ. 0.
           tstar (i,j) = 0. _d 0
           qstar (i,j) = 0. _d 0
           ustar (i,j) = 0. _d 0
           tau   (i,j) = 0. _d 0
           rdn   (i,j) = 0. _d 0
          ENDIF
         ENDDO
        ENDDO
        DO iter=1,niter_bulk
         DO j = 1,sNy
          DO i = 1,sNx
           IF ( atemp(i,j,bi,bj) .NE. 0. _d 0 ) THEN
C--- iterate with psi-functions to find transfer coefficients

#ifdef ALLOW_AUTODIFF_TAMC
            ikey_2 = i
     &           + sNx*(j-1)
     &           + sNx*sNy*(iter-1)
     &           + sNx*sNy*niter_bulk*act1
     &           + sNx*sNy*niter_bulk*max1*act2
     &           + sNx*sNy*niter_bulk*max1*max2*act3
     &           + sNx*sNy*niter_bulk*max1*max2*max3*act4
CADJ STORE rdn   (i,j)       = comlev1_exf_2, key = ikey_2
CADJ STORE ustar (i,j)       = comlev1_exf_2, key = ikey_2
CADJ STORE qstar (i,j)       = comlev1_exf_2, key = ikey_2
CADJ STORE tstar (i,j)       = comlev1_exf_2, key = ikey_2
CADJ STORE sh    (i,j,bi,bj) = comlev1_exf_2, key = ikey_2
CADJ STORE wspeed(i,j,bi,bj) = comlev1_exf_2, key = ikey_2
#endif

            t0   = atemp(i,j,bi,bj)*
     &           (exf_one + humid_fac*aqh(i,j,bi,bj))
            huol = ( tstar(i,j)/t0 +
     &               qstar(i,j)/(exf_one/humid_fac+aqh(i,j,bi,bj))
     &              )*czol/(ustar(i,j)*ustar(i,j))
#ifdef ALLOW_BULK_LARGEYEAGER04
            tmpbulk = MIN(abs(huol),10. _d 0)
            huol   = SIGN(tmpbulk , huol)
#else
C--   Large&Pond1981:
            huol   = max(huol,zolmin)
#endif /* ALLOW_BULK_LARGEYEAGER04 */
            htol   = huol*ht/hu
            hqol   = huol*hq/hu
            stable = exf_half + sign(exf_half, huol)

C     Evaluate all stability functions assuming hq = ht.
            IF ( solve4Stress ) THEN
#ifdef ALLOW_BULK_LARGEYEAGER04
C--   Large&Yeager04:
             xsq    = SQRT( ABS(exf_one - huol*16. _d 0) )
#else
C--   Large&Pond1981:
             xsq    = MAX(SQRT(ABS(exf_one - 16.*huol)),exf_one)
#endif /* ALLOW_BULK_LARGEYEAGER04 */
             x      = SQRT(xsq)
             psimh  = -psim_fac*huol*stable
     &               +(exf_one-stable)*
     &                ( LOG( (exf_one + exf_two*x + xsq)*
     &                       (exf_one+xsq)*.125 _d 0 )
     &                  -exf_two*ATAN(x) + exf_half*pi )
            ELSE
             psimh  = 0. _d 0
            ENDIF
#ifdef ALLOW_BULK_LARGEYEAGER04
C--   Large&Yeager04:
            xsq   = SQRT( ABS(exf_one - htol*16. _d 0) )
#else
C--   Large&Pond1981:
            xsq   = MAX(SQRT(ABS(exf_one - 16.*htol)),exf_one)
#endif /* ALLOW_BULK_LARGEYEAGER04 */
            psixh = -psim_fac*htol*stable + (exf_one-stable)*
     &               ( exf_two*LOG(exf_half*(exf_one+xsq)) )

            IF ( solve4Stress ) THEN
CADJ STORE rdn(i,j)       = comlev1_exf_2, key = ikey_2
C-   Shift wind speed using old coefficient
#ifdef ALLOW_BULK_LARGEYEAGER04
C--   Large&Yeager04:
             dzTmp = (zwln-psimh)/karman
             usn   = wspeed(i,j,bi,bj)/(exf_one + rdn(i,j)*dzTmp )
#else
C--   Large&Pond1981:
c           rd(i,j)= rdn(i,j)/(exf_one - rdn(i,j)/karman*psimh )
c           usn    = sh(i,j,bi,bj)*rd(i,j)/rdn(i,j)
C     ML: the original formulation above is replaced below to be
C     similar to largeyeager04, but does not give the same results, strange
             usn   = sh(i,j,bi,bj)/(exf_one - rdn(i,j)/karman*psimh)
#endif /* ALLOW_BULK_LARGEYEAGER04 */
             usm   = MAX(usn, umin)

C-   Update the 10m, neutral stability transfer coefficients (momentum)
             tmpbulk  = exf_scal_BulkCdn *
     &            ( cdrag_1/usm + cdrag_2 + cdrag_3*usm )
             rdn(i,j)   = SQRT(tmpbulk)
#ifdef ALLOW_BULK_LARGEYEAGER04
             rd(i,j)    = rdn(i,j)/(exf_one + rdn(i,j)*dzTmp)
#else
             rd(i,j)    = rdn(i,j)/(exf_one - rdn(i,j)/karman*psimh)
#endif /* ALLOW_BULK_LARGEYEAGER04 */
             ustar(i,j) = rd(i,j)*sh(i,j,bi,bj)
C-   Coeff:
#ifdef EXF_CALC_ATMRHO
             tau(i,j)   = atmrho_loc(i,j)*rd(i,j)*wspeed(i,j,bi,bj)
#else
             tau(i,j)   = atmrho*rd(i,j)*wspeed(i,j,bi,bj)
#endif
            ENDIF

C-   Update the 10m, neutral stability transfer coefficients (sens&evap)
            rhn = (exf_one-stable)*cstanton_1 + stable*cstanton_2
            ren = cDalton

C-   Shift all coefficients to the measurement height and stability.
            rh = rhn/(exf_one + rhn*(ztln-psixh)/karman)
            re = ren/(exf_one + ren*(ztln-psixh)/karman)

C--  Update ustar, tstar, qstar using updated, shifted coefficients.
            qstar(i,j) = re*delq(i,j)
            tstar(i,j) = rh*deltap(i,j)

           ENDIF
          ENDDO
         ENDDO
C end of iteration loop
        ENDDO
        DO j = 1,sNy
         DO i = 1,sNx
          IF ( atemp(i,j,bi,bj) .NE. 0. _d 0 ) THEN

#ifdef ALLOW_AUTODIFF_TAMC
           ikey_1 = i
     &          + sNx*(j-1)
     &          + sNx*sNy*act1
     &          + sNx*sNy*max1*act2
     &          + sNx*sNy*max1*max2*act3
     &          + sNx*sNy*max1*max2*max3*act4
CADJ STORE qstar(i,j)    = comlev1_exf_1, key = ikey_1
CADJ STORE tstar(i,j)    = comlev1_exf_1, key = ikey_1
CADJ STORE tau  (i,j)    = comlev1_exf_1, key = ikey_1
CADJ STORE rd   (i,j)    = comlev1_exf_1, key = ikey_1
#endif /* ALLOW_AUTODIFF_TAMC */

C-   Turbulent Fluxes
           hs(i,j,bi,bj) = atmcp*tau(i,j)*tstar(i,j)
           hl(i,j,bi,bj) = flamb*tau(i,j)*qstar(i,j)
#ifndef EXF_READ_EVAP
C   change sign and convert from kg/m^2/s to m/s via rhoConstFresh
c          evap(i,j,bi,bj) = -recip_rhonil*tau(i,j)*qstar(i,j)
           evap(i,j,bi,bj) = -recip_rhoConstFresh*tau(i,j)*qstar(i,j)
C   but older version was using rhonil instead:
c           evap(i,j,bi,bj) = -recip_rhonil*tau(i,j)*qstar(i,j)
#endif
           IF ( useAtmWind ) THEN
c           ustress(i,j,bi,bj) = tau(i,j)*rd(i,j)*ws*cw(i,j,bi,bj)
c           vstress(i,j,bi,bj) = tau(i,j)*rd(i,j)*ws*sw(i,j,bi,bj)
C- jmc: below is how it should be written ; different from above when
C       both wind-speed & 2 compon. of the wind are specified, and in
C-      this case, formula below is better.
            tmpbulk =  tau(i,j)*rd(i,j)
            ustress(i,j,bi,bj) = tmpbulk*uwind(i,j,bi,bj)
            vstress(i,j,bi,bj) = tmpbulk*vwind(i,j,bi,bj)
           ENDIF

c IF ( ATEMP(i,j,bi,bj) .NE. 0. )
          ELSE
           IF ( useAtmWind ) ustress(i,j,bi,bj) = 0. _d 0
           IF ( useAtmWind ) vstress(i,j,bi,bj) = 0. _d 0
           hflux  (i,j,bi,bj) = 0. _d 0
           evap   (i,j,bi,bj) = 0. _d 0
           hs     (i,j,bi,bj) = 0. _d 0
           hl     (i,j,bi,bj) = 0. _d 0

c IF ( ATEMP(i,j,bi,bj) .NE. 0. )
          ENDIF

         ENDDO
        ENDDO
       ENDDO
      ENDDO

#endif /* ALLOW_ATM_TEMP */
#endif /* ALLOW_BULKFORMULAE */

      RETURN
      END
