PUBLIC INTERFACE / ROUTINES / NAMELIST / CHANGES / ERRORS / REFERENCES / NOTES

## module monin_obukhov

         Contact:   Isaac Held
         Reviewers:

         Tags/Status

#### OVERVIEW

          Routines for computing surface drag coefficients from
          data at the lowest model level using Monin-Obukhov scaling,
          for estimating the wind, temperature, and buoyancy profiles
          between the lowest model level and surface, and for computing
          diffusivities consistent these profiles

      Monin-Obukhov similarity (MOS) theory is the standard method
      for computing  surface fluxes from the lowest level winds,
      temperatures, and tracer mixing ratios in GCMs.  The lowest level
      is assumed to lie within the "surface layer" in which turbulent
      fluxes have negligible vertical variation, and in which MOS assumes
      that the wind and buoyancy profiles are a function only of the
      surface stress, the surface buoyancy flux, and the height z.
      A good reference is Garratt, J. R. "The Atmospheric Boundary Layer",
      Cambridge University Press, 1992.  For a  discussion of the detailed
      form of the similarity theory used in this module, see
      monin_obukhov.tech.ps

      The particular form of the monin-obukhov similarity theory utilized
      is defined by the similarity functions phi_m and phi_t chosen, where
              du/dzeta  = phi_m * u_star/( vonkarm * zeta )
    	  db/dzeta( = phi_t * b_star/( vonkarm * zeta )
    	    (where b = buoyancy or specific humidity,
    	           vonkarm = Von Karman's constant
    		   u_star = friction velocity (stress = density*u_star**2)
    		   b_star = buoyancy scale (flux = density*u_star*b_star)
    		   zeta = z/L
    		   z = height of lowest model level
    		   L = monin_obukhov length )
      We use:
         on the unstable side
                 phi_m = (1 - 16.0*zeta)**(-0.25)
                 phi_t = (1 - 16.0*zeta)**(-0.5)

         on the stable side -- phi_t = phi_m
           there are two options

           option_1: phi =  1.0 + zeta*(5.0 + b_stab*zeta)/(1.0 + zeta)
                  where b_stab = 1/rich_crit
                  (rich_crit is a namelist parameter)

           option_2: phi = 1 + 5.0*zeta (zeta < zeta_trans)
                         = 1.0 + (5.0 - b_stab)*zeta_trans + b_stab*zeta
                  (zeta_trans also a namelist parameter)

      In order to change these similarity functions, one would need to
         1) modify the defintion of phi_m and phi_t in the internal
            subroutines mo_derivative_m and mo_derivative_t
         2) provide analytical versions of the integral similarity
            functions  (PHI_m = int(zeta_0 to zeta) of phi/zeta)
    	(see notes) in the subroutines mo_integral_m and mo_integral_tq
         3) modify stable_mix to be consistent with the new similarity functions
            (see notes) (mod_diff will take care of itself)

#### OTHER MODULES USED

        This modules uses
           constants_mod, only : grav, vonkarm
           utilities_mod, only : error_mesg, FATAL, file_exist,   &
                                 check_nml_error, open_file,      &
                                 get_my_pe, close_file

#### PUBLIC INTERFACE

        use monin_obukhov_mod [,only: mo_drag    , &
                                      mo_profile , &
                                      mo_diff    , &
    				  stable_mix   ]

        mo_drag   : computes the drag coefficients for momentum, heat, and moisture;
                    also returns u_star (the friction velocity) and
                    b_star (the buoyancy scale)

        mo_profile :  provides the profile of momentum, temperature, and
                      specific humidity in the constant flux layer
    		  -- useful when one needs
                      model output at a standard level below the lowest
                      model level.  For example, the term "surface wind"
    		  in meteorology often refers to
                      the wind 10 meters above the surface.

        mo_diff : computes the difusivity that, acting in isolation,
                  would produce the correct profiles in ths surface layer

        stable_mix : returns f(Ri) under stable conditions where Ri is the
                 local Richardson's number,  from which on can compute the
    	     diffusivity that  produces the correct profile in the constant
    	     flux layer from D = l**2 |dv\dz| F(Ri) where l = vonkarm*z .
    	     Useful for matching a local Ri-dependent diffusivity in the free
    	     atmosphere under stable conditions o that implied by the
    	     surface layer profile

      Notes:

           all of the interfaces can be called with either 2d, 1d or 0d in/output.
           The base routines for mo_drag and mo_profile are 1d, while
           those for mo_diff and stable_mix are 2d, as these are the
           versions called within our GCMs.  (mo_drag and mo_profile are
           called from the exchange_grid, which is 1d).  Other versions call these
           base routines -- they are included for convenience -- the presumption is
           that efficiency is not of particular importance for these alternative
           versions)

#### PUBLIC ROUTINES

    ==========================================================================

     call  mo_drag (pt, pt0, z, z0, zt, zq, speed, drag_m, drag_t, drag_q, &
                    u_star, b_star, [mask])

       input:

          pt,  real, dimension(:)
             virtual potential temperature at lowest model level
             degrees Kelvin

          pt0, real, dimension(:)
             virtual potential temperature at surface
             degrees Kelvin

          z, real, dimension(:)
             height above surface of lowest model layer
             meters

          z0, real, dimension(:)
              surface roughness for momentum
              meters

          zt, real, dimension(:)
              surface roughness for temperature
              meters

          zq, real, dimension(:)
              surface roughness for moisture
              meters

          speed, real, dimension(:)
              wind speed at lowest model level with respect to surface
                 (any "gustiness" factor should be included in speed)
              meters/sec

       output:

            drag_m, real, dimension(:)
                  non-dimensional drag coefficient for momentum

            drag_t, real, dimension(:)
                  non-dimensional drag coefficient for temperature

            drag_q, real, dimension(:)
                  non-dimensional drag coefficient for specific humidity

            u_star, real, dimension(:)
               friction velocity
               meters/sec

            b_star, real, dimension(:)
               buoyancy scale
               (meters/sec)**2

                The magnitude of the wind stress is
                     density*(ustar**2)
                The drag coefficient for momentum is
                     (u_star/speed)**2
                The buoyancy flux is
                     density*ustar*bstar
                The drag coefficient for heat etc is
                     (u_star/speed)*(b_star/delta_b)
                     where delta_b is the buoyancy difference between
                      the surface and the lowest model level
                The buoyancy == grav*(p0 - p)/p0

        optional:
           mask    : logical, dimension(:)
                       computation performed only where mask = .true.

      Also:

         can be called with all 1d arrays replaced by 2d arrays or by 0d scalars
         NOTE:  in the 0d and 2d cases, there is no avail optional argument
    ==========================================================================

     subroutine mo_profile(zref_m, zref_t, z, z0, zt, zq, &
          u_star, b_star, q_star, del_m, del_t, del_q, [mask])

      input:

         zref_m, real
                 height above surface to which interpolation is requested
    	     for horizontal components of momentum
                 meters

         zref_t, real
                 height above surface to which interpolation is requested
    	     for temperature and specific humidity
                 meters

         z, real, dimension(:)
            height of lowest model layer above surface
            meters

         z0, real, dimension(:)
             surface roughness for momentum
             meters

         zt, real, dimension(:)
             surface roughness for temperature
             meters

         zq, real, dimension(:)
             surface roughness for moisture
             meters

         u_star, real, dimension(:)
                 friction velocity
                 meters/sec

         b_star, real, dimension(:)
                 buoyancy scale
                 (meters/sec)**2

         q_star, real, dimension(:)
                 moisture scale
                 kg/kg

              (Note:  u_star and b_star are output from mo_drag,
                      q_star = flux_q/(u_star * density)

        optional input:

           mask, logical, dimension(:)
                       computation performed only where mask = .true.

        output:

           del_m, real, dimension(:)
                  dimensionless ratio, as defined below, for momentum

           del_t, real, dimension(:)
                  dimensionless ratio, as defined below, for temperature

           del_q, real, dimension(:) o
                  dimensionless ratio, as defined below, for specific humidity

              Ratios are  (f(zref) - f_surf)/(f(z) - f_surf)

      Also:

         can be called with all 1d arrays replaced by 2d arrays or by 0d scalars
         NOTE:  in the 0d and 2d cases, there is no avail optional argument

         in addition, this routine can be called with the interface

         subroutine mo_profile(zref, z, z0, zt, zq, &
               u_star, b_star, q_star, del_m, del_h, del_q)

          in which zref is a 1d array of the heights at which the profiles
            are requested, and all other input is 0d, 1d, or 2d as above,
    	while the output del_m, del_h, del_q is dimensioned
    	in the 0d case:  dimension (size(zref))
    	in the 1d case:  dimension (size(input), size(zref))
    	in the 2d case:  dimension (size(input,1), size(input,2), size(zref))
    	(note that the two inputs zref_m and zref_h and here replaced by
    	the input vector zref)

    ==========================================================================

      subroutine mo_diff(z, u_star, b_star, k_m, k_h)

        input:

            u_star, real, dimension(:,:) -- 2d horizontal array
                    surface friction velocity
                    (meters/sec)

            b_star, real, dimension(:,:) -- 2d horizontal array
                    buoyancy scale
                    (meters/sec**2)

              (Note:  u_star and b_star are output from mo_drag)

    	z, real, dimension(:,:,:)
    	        (first two dimensions conform to u_star, b_star)
                    height above surface at which diffusivities
                    are desired -- i.e., diffusivities returned at
    		z(i,j,:) for each point (i,j)
                    (meters)

        output:

            k_m   : real, dimension(:,:,:) conforms to z
                    kinematic diffusivity for momentum
                    (meters**2/sec)

            k_h   : real, dimension(:,:,:)
                    kinematic diffusivity for temperature
                    (meters**2/sec)

    Also 0d and 1d versions available
      0d:  u_star, b_star = scalars;
           z, k_m, k_h = dimension(:)
      1d   u_star, b_star = dimension(:),
           z, k_m, k_h = dimension(size(u_star), :)

    Also, can be called so as to return diffusivities at one level
           (the height of this level can still vary from column to column)
      0d:  u_star, b_star, z, k_m, k_h  = scalars
      1d:  u_star, b_star, z, k_m, k_h  = dimension(:)
      2d   u_star, b_star, z, k_m, k_h  = dimension(:,:)

    ==========================================================================

      subroutine stable_mix(rich, mix)

        input:

            rich, real, dimension(:,:,:)
    	      local Richardson's number
                    (none)

        output:

            mix,  real, dimension(:,:,:)
    	      dimensional factor that produces the diffusivity
    	      consistent with the similarity profile
    	      under stable conditions when multiplied by
    	      (L**2) * |dv/dz| where v is the vector horizontal wind
    	      and L = vonkarm*z

    Also 0d, 1d, and 2d versions vailable

#### NAMELIST

       (default values provided)

       logical :: neutral    = .false.

          If set to .true., all stability dependence is suppressed and
           neutral logarithmic profiles are used for all calculations;
           all other namelist parameters ignored

       real  :: drag_min   = 1.e-05   (non-dimensional)

           The drag coefficients (for both momentum and temperature)
           are not allowed to fall below drag_min

       integer :: stable_option = 1

           must equal either 1 or 2
           two version for the similarity functions on the stable side

       real :: rich_crit  = 2.0      (non-dimensional)

           The first step in applying the similarity theory is computing
           the bulk Richardson's  number Ri between the lowest model level
           and the surface. If Ri is greater than rich_crit, the drag
           coefficients are set to drag_min.

           This value also affects the drag coefficients under stable conditions
           for either choice of stable_option, as the drag is arranged to
           becomes very small as rich_crit is approached

       real :: zeta_trans = 0.5 (non-dimensional)

           A parameter in the stable-option = 2 piecewise linear similarity
           function for the stable side.  Increasing zeta_trans reduces
           the drag and the implied diffusivities

#### CHANGE HISTORY

    Revision history

    changes (10/4/1999)

         MPP version created. Minor changes for open_file, error_mesg,
         and Fortran write statements. Answers should reproduce the
         previous version.

    more changes  (10/01)

        -- time smoothing option removed
        -- second optional similarity function on stable side added
        -- stable_mix interface added
        -- code rearranged substantially for clarity, removing initial
           aggregation of points into unstable and stable sets
        -- answers reproduce for stable_option = 1 except in the very
           rare instance of points that are very close to neutral.
           These need to be treated separately to avoid a divide by sero.
           In this version, the drag coefficients at these points are simply
           set to the neutral value

#### ERROR MESSAGES

    FATAL ERRORS in MONIN_OBUKHOV_INIT in MONIN_OBUKHOV_MOD

        -- rich_crit in monin_obukhov_mod must be > 0.25

        -- drag_min in monin_obukhov_mod must be >= 0.0

        -- the only allowable values of stable_option are 1 and 2

        -- zeta_trans must be positive

    FATAL ERROR in solve_zeta in monin_obukhov_mod

        -- surface drag iteration did not converge

#### REFERENCES

#### KNOWN BUGS

    None known

#### NOTES

    If iteration convergence is ever a problem, one can consider
    precomputation and table interpolation

    It would be convenient if changing the derivative similarity functions
    automatically modified the integral functions and stable_mix
    (this would presumably require numerical integration and table look-ups
    as opposed to analytical expressions)

#### FUTURE PLANS