!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! !!
!! GNU General Public License !!
!! !!
!! This file is part of the Flexible Modeling System (FMS). !!
!! !!
!! FMS is free software; you can redistribute it and/or modify !!
!! it and are expected to follow the terms of the GNU General Public !!
!! License as published by the Free Software Foundation. !!
!! !!
!! FMS 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 FMS; if not, write to: !!
!! Free Software Foundation, Inc. !!
!! 59 Temple Place, Suite 330 !!
!! Boston, MA 02111-1307 USA !!
!! or see: !!
!! http://www.gnu.org/licenses/gpl.txt !!
!! !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
! ============================================================================
module surface_flux_mod
!-----------------------------------------------------------------------
! GNU General Public License
!
! This program is free software; you can redistribute it and/or modify it and
! are expected to follow the terms of the GNU General Public License
! as published by the Free Software Foundation; either version 2 of
! the License, or (at your option) any later version.
!
! MOM 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.
!
! For the full text of the GNU General Public License,
! write to: Free Software Foundation, Inc.,
! 675 Mass Ave, Cambridge, MA 02139, USA.
! or see: http://www.gnu.org/licenses/gpl.html
!-----------------------------------------------------------------------
!
! Steve Klein
! Isaac Held
! Bruce Wyman
! V. Balaji
! Sergey Malyshev
! Elena Shevliakova
!
!
!
!
! Driver program for the calculation of fluxes on the exchange grids.
!
!
!
!
!
!
! ============================================================================
use fms_mod, only: FATAL, close_file, mpp_pe, mpp_root_pe, write_version_number
use fms_mod, only: file_exist, check_nml_error, open_namelist_file, stdlog
use monin_obukhov_mod, only: mo_drag, mo_profile
use sat_vapor_pres_mod, only: escomp, descomp
use constants_mod, only: cp_air, hlv, stefan, rdgas, rvgas, grav, vonkarm
implicit none
private
! ==== public interface ======================================================
public surface_flux
! ==== end of public interface ===============================================
!
!
! For the calculation of fluxes on the exchange grids.
!
!
! For the calculation of fluxes on the exchange grids.
!
!
!
! Air temp lowest atmospheric level.
!
!
! Mixing ratio at lowest atmospheric level (kg/kg).
!
!
! Zonal wind velocity at lowest atmospheric level.
!
!
! Meridional wind velocity at lowest atmospheric level.
!
!
! Pressure lowest atmospheric level.
!
!
! Height lowest atmospheric level.
!
!
! Pressure at the earth's surface
!
!
! Temp at the earth's surface
!
!
! Air temp at the canopy
!
!
! Mixing ratio at earth surface (kg/kg).
!
!
! Zonal wind velocity at earth surface.
!
!
! Meridional wind velocity at earth surface.
!
!
! Momentum roughness length
!
!
! Heat roughness length
!
!
!
!
! Scale factor used to topographic roughness calculation
!
!
! Gustiness factor
!
!
! Sensible heat flux
!
!
! Evaporative water flux
!
!
! Radiative energy flux
!
!
! Zonal momentum flux
!
!
! Meridional momentum flux
!
!
! Momentum exchange coefficient
!
!
! Heat exchange coefficient
!
!
! Moisture exchange coefficient
!
!
! Absolute wind at the lowest atmospheric level
!
!
! Turbulent velocity scale
!
!
! Turbulent buoyant scale
!
!
! Turbulent moisture scale
!
!
! Sensible heat flux temperature sensitivity
!
!
! Moisture flux temperature sensitivity
!
!
! Moisture flux humidity sensitivity
!
!
! Radiative energy flux temperature sensitivity
!
!
! Derivative of sensible heat flux over temp at the lowest atmos level.
!
!
! Derivative of water vapor flux over temp at the lowest atmos level.
!
!
! Derivative of zonal wind stress w.r.t the lowest level zonal
! wind speed of the atmos
!
!
! Derivative of meridional wind stress w.r.t the lowest level meridional
! wind speed of the atmos
!
!
! Time step (it is not used presently)
!
!
! Indicates where land exists (true if exchange cell is on land).
!
!
! Indicates where liquid ocean water exists
! (true if exchange cell is on liquid ocean water).
!
!
! True where the exchange cell is active.
!
interface surface_flux
! module procedure surface_flux_0d
module procedure surface_flux_1d
! module procedure surface_flux_2d
end interface
!
!-----------------------------------------------------------------------
character(len=*), parameter :: version = '$Id: surface_flux.F90,v 17.0.2.1 2009/09/14 14:49:06 nnz Exp $'
character(len=*), parameter :: tagname = '$Name: mom4p1_pubrel_dec2009_nnz $'
logical :: do_init = .true.
real, parameter :: d622 = rdgas/rvgas
real, parameter :: d378 = 1.-d622
real, parameter :: hlars = hlv/rvgas
real, parameter :: gcp = grav/cp_air
real, parameter :: kappa = rdgas/cp_air
real :: d608 = d378/d622
! d608 set to zero at initialization if the use of
! virtual temperatures is turned off in namelist
! ---- namelist with default values ------------------------------------------
!
!
! If q_atm_in (specific humidity) is negative (because of numerical truncation),
! then override with 0.
!
!
! If true, use virtual potential temp to calculate the stability of the surface layer.
! if false, use potential temp.
!
!
! An alternative formulation for gustiness calculation.
! A minimum bound on the wind speed used influx calculations, with the bound
! equal to gust_const
!
!
! The derivative of surface wind stress w.r.t. the zonal wind and
! meridional wind are approximated by the same tendency.
!
!
! An option to provide capability to run the Manabe Climate form of the
! surface flux (coded for legacy purposes).
!
!
! Constant for alternative gustiness calculation.
!
!
! Minimum gustiness used when alt_gustiness = false.
!
!
! Use NCAR climate model turbulent flux calculation described by
! Large and Yeager, NCAR Technical Document, 2004
!
!
! Use NCAR climate model turbulent flux calculation described by
! Large and Yeager, NCAR Technical Document, 2004, using the original
! GFDL implementation, which contains a bug in the specification of
! the exchange coefficient for the sensible heat. This option is available
! for legacy purposes, and is not recommended for new experiments.
!
!
! Reduce saturation vapor pressures to account for seawater salinity.
!
!
logical :: no_neg_q = .false. ! for backwards compatibility
logical :: use_virtual_temp = .true.
logical :: alt_gustiness = .false.
logical :: old_dtaudv = .false.
logical :: use_mixing_ratio = .false.
real :: gust_const = 1.0
real :: gust_min = 0.0
logical :: ncar_ocean_flux = .false.
logical :: ncar_ocean_flux_orig = .false. ! for backwards compatibility
logical :: raoult_sat_vap = .false.
logical :: do_simple = .false.
namelist /surface_flux_nml/ no_neg_q, &
use_virtual_temp, &
alt_gustiness, &
gust_const, &
gust_min, &
old_dtaudv, &
use_mixing_ratio, &
ncar_ocean_flux, &
ncar_ocean_flux_orig, &
raoult_sat_vap, &
do_simple
contains
! ============================================================================
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
subroutine surface_flux_1d ( &
t_atm, q_atm_in, u_atm, v_atm, p_atm, z_atm, &
p_surf, t_surf, t_ca, q_surf, &
u_surf, v_surf, &
rough_mom, rough_heat, rough_moist, rough_scale, gust, &
flux_t, flux_q, flux_r, flux_u, flux_v, &
cd_m, cd_t, cd_q, &
w_atm, u_star, b_star, q_star, &
dhdt_surf, dedt_surf, dedq_surf, drdt_surf, &
dhdt_atm, dedq_atm, dtaudu_atm, dtaudv_atm, &
dt, land, seawater, avail )
!
! slm Mar 28 2002 -- remove agument drag_q since it is just cd_q*wind
! ============================================================================
! ---- arguments -----------------------------------------------------------
logical, intent(in), dimension(:) :: land, seawater, avail
real, intent(in), dimension(:) :: &
t_atm, q_atm_in, u_atm, v_atm, &
p_atm, z_atm, t_ca, &
p_surf, t_surf, u_surf, v_surf, &
rough_mom, rough_heat, rough_moist, rough_scale, gust
real, intent(out), dimension(:) :: &
flux_t, flux_q, flux_r, flux_u, flux_v, &
dhdt_surf, dedt_surf, dedq_surf, drdt_surf, &
dhdt_atm, dedq_atm, dtaudu_atm,dtaudv_atm, &
w_atm, u_star, b_star, q_star, &
cd_m, cd_t, cd_q
real, intent(inout), dimension(:) :: q_surf
real, intent(in) :: dt
! ---- local constants -----------------------------------------------------
! temperature increment and its reciprocal value for comp. of derivatives
real, parameter:: del_temp=0.1, del_temp_inv=1.0/del_temp
! ---- local vars ----------------------------------------------------------
real, dimension(size(t_atm(:))) :: &
thv_atm, th_atm, tv_atm, thv_surf, &
e_sat, e_sat1, q_sat, q_sat1, p_ratio, &
t_surf0, t_surf1, u_dif, v_dif, &
rho_drag, drag_t, drag_m, drag_q, rho, &
q_atm, q_surf0, dw_atmdu, dw_atmdv, w_gust
integer :: i, nbad
if (do_init) call surface_flux_init
!---- use local value of surf temp ----
t_surf0 = 200. ! avoids out-of-bounds in es lookup
where (avail)
where (land)
t_surf0 = t_ca
elsewhere
t_surf0 = t_surf
endwhere
endwhere
t_surf1 = t_surf0 + del_temp
call escomp ( t_surf0, e_sat ) ! saturation vapor pressure
call escomp ( t_surf1, e_sat1 ) ! perturbed vapor pressure
if(use_mixing_ratio) then
! surface mixing ratio at saturation
q_sat = d622*e_sat /(p_surf-e_sat )
q_sat1 = d622*e_sat1/(p_surf-e_sat1)
elseif(do_simple) then !rif:(09/02/09)
q_sat = d622*e_sat / p_surf
q_sat1 = d622*e_sat1/ p_surf
else
! surface specific humidity at saturation
q_sat = d622*e_sat /(p_surf-d378*e_sat )
q_sat1 = d622*e_sat1/(p_surf-d378*e_sat1)
endif
! initilaize surface air humidity according to surface type
where (land)
q_surf0 = q_surf ! land calculates it
elsewhere
q_surf0 = q_sat ! everything else assumes saturated sfc humidity
endwhere
if (raoult_sat_vap) where (seawater) q_surf0 = 0.98 * q_surf0
! check for negative atmospheric humidities
where(avail) q_atm = q_atm_in
if(no_neg_q) then
where(avail .and. q_atm_in < 0.0) q_atm = 0.0
endif
! generate information needed by monin_obukhov
where (avail)
p_ratio = (p_surf/p_atm)**kappa
tv_atm = t_atm * (1.0 + d608*q_atm) ! virtual temperature
th_atm = t_atm * p_ratio ! potential T, using p_surf as refernce
thv_atm = tv_atm * p_ratio ! virt. potential T, using p_surf as reference
thv_surf= t_surf0 * (1.0 + d608*q_surf0 ) ! surface virtual (potential) T
! thv_surf= t_surf0 ! surface virtual (potential) T -- just for testing tun off the q_surf
u_dif = u_surf - u_atm ! velocity components relative to surface
v_dif = v_surf - v_atm
endwhere
if(alt_gustiness) then
do i = 1, size(avail)
if (.not.avail(i)) cycle
w_atm(i) = max(sqrt(u_dif(i)**2 + v_dif(i)**2), gust_const)
! derivatives of surface wind w.r.t. atm. wind components
if(w_atm(i) > gust_const) then
dw_atmdu(i) = u_dif(i)/w_atm(i)
dw_atmdv(i) = v_dif(i)/w_atm(i)
else
dw_atmdu(i) = 0.0
dw_atmdv(i) = 0.0
endif
enddo
else
if (gust_min > 0.0) then
where(avail)
w_gust = max(gust,gust_min) ! minimum gustiness
end where
else
where(avail)
w_gust = gust
end where
endif
where(avail)
w_atm = sqrt(u_dif*u_dif + v_dif*v_dif + w_gust*w_gust)
! derivatives of surface wind w.r.t. atm. wind components
dw_atmdu = u_dif/w_atm
dw_atmdv = v_dif/w_atm
endwhere
endif
! monin-obukhov similarity theory
call mo_drag (thv_atm, thv_surf, z_atm, &
rough_mom, rough_heat, rough_moist, w_atm, &
cd_m, cd_t, cd_q, u_star, b_star, avail )
! override with ocean fluxes from NCAR calculation
if (ncar_ocean_flux .or. ncar_ocean_flux_orig) then
call ncar_ocean_fluxes (w_atm, th_atm, t_surf0, q_atm, q_surf0, z_atm, &
seawater, cd_m, cd_t, cd_q, u_star, b_star )
end if
where (avail)
! scale momentum drag coefficient on orographic roughness
cd_m = cd_m*(log(z_atm/rough_mom+1)/log(z_atm/rough_scale+1))**2
! surface layer drag coefficients
drag_t = cd_t * w_atm
drag_q = cd_q * w_atm
drag_m = cd_m * w_atm
! density
rho = p_atm / (rdgas * tv_atm)
! sensible heat flux
rho_drag = cp_air * drag_t * rho
flux_t = rho_drag * (t_surf0 - th_atm) ! flux of sensible heat (W/m**2)
dhdt_surf = rho_drag ! d(sensible heat flux)/d(surface temperature)
dhdt_atm = -rho_drag*p_ratio ! d(sensible heat flux)/d(atmos temperature)
! evaporation
rho_drag = drag_q * rho
flux_q = rho_drag * (q_surf0 - q_atm) ! flux of water vapor (Kg/(m**2 s))
where (land)
dedq_surf = rho_drag
dedt_surf = 0
elsewhere
dedq_surf = 0
dedt_surf = rho_drag * (q_sat1 - q_sat) *del_temp_inv
endwhere
dedq_atm = -rho_drag ! d(latent heat flux)/d(atmospheric mixing ratio)
q_star = flux_q / (u_star * rho) ! moisture scale
! ask Chris and Steve K if we still want to keep this for diagnostics
q_surf = q_atm + flux_q / (rho*cd_q*w_atm) ! surface specific humidity
! upward long wave radiation
flux_r = stefan*t_surf**4 ! (W/m**2)
drdt_surf = 4*stefan*t_surf**3 ! d(upward longwave)/d(surface temperature)
! stresses
rho_drag = drag_m * rho
flux_u = rho_drag * u_dif ! zonal component of stress (Nt/m**2)
flux_v = rho_drag * v_dif ! meridional component of stress
elsewhere
! zero-out un-available data in output only fields
flux_t = 0.0
flux_q = 0.0
flux_r = 0.0
flux_u = 0.0
flux_v = 0.0
dhdt_surf = 0.0
dedt_surf = 0.0
dedq_surf = 0.0
drdt_surf = 0.0
dhdt_atm = 0.0
dedq_atm = 0.0
u_star = 0.0
b_star = 0.0
q_star = 0.0
q_surf = 0.0
w_atm = 0.0
endwhere
! calculate d(stress component)/d(atmos wind component)
dtaudu_atm = 0.0
dtaudv_atm = 0.0
if (old_dtaudv) then
where(avail)
dtaudv_atm = -rho_drag
dtaudu_atm = -rho_drag
endwhere
else
where(avail)
dtaudu_atm = -cd_m*rho*(dw_atmdu*u_dif + w_atm)
dtaudv_atm = -cd_m*rho*(dw_atmdv*v_dif + w_atm)
endwhere
endif
end subroutine surface_flux_1d
!
!#######################################################################
subroutine surface_flux_0d ( &
t_atm_0, q_atm_0, u_atm_0, v_atm_0, p_atm_0, z_atm_0, &
p_surf_0, t_surf_0, t_ca_0, q_surf_0, &
u_surf_0, v_surf_0, &
rough_mom_0, rough_heat_0, rough_moist_0, rough_scale_0, gust_0, &
flux_t_0, flux_q_0, flux_r_0, flux_u_0, flux_v_0, &
cd_m_0, cd_t_0, cd_q_0, &
w_atm_0, u_star_0, b_star_0, q_star_0, &
dhdt_surf_0, dedt_surf_0, dedq_surf_0, drdt_surf_0, &
dhdt_atm_0, dedq_atm_0, dtaudu_atm_0, dtaudv_atm_0, &
dt, land_0, seawater_0, avail_0 )
! ---- arguments -----------------------------------------------------------
logical, intent(in) :: land_0, seawater_0, avail_0
real, intent(in) :: &
t_atm_0, q_atm_0, u_atm_0, v_atm_0, &
p_atm_0, z_atm_0, t_ca_0, &
p_surf_0, t_surf_0, u_surf_0, v_surf_0, &
rough_mom_0, rough_heat_0, rough_moist_0, rough_scale_0, gust_0
real, intent(out) :: &
flux_t_0, flux_q_0, flux_r_0, flux_u_0, flux_v_0, &
dhdt_surf_0, dedt_surf_0, dedq_surf_0, drdt_surf_0, &
dhdt_atm_0, dedq_atm_0, dtaudu_atm_0,dtaudv_atm_0, &
w_atm_0, u_star_0, b_star_0, q_star_0, &
cd_m_0, cd_t_0, cd_q_0
real, intent(inout) :: q_surf_0
real, intent(in) :: dt
! ---- local vars ----------------------------------------------------------
logical, dimension(1) :: land, seawater, avail
real, dimension(1) :: &
t_atm, q_atm, u_atm, v_atm, &
p_atm, z_atm, t_ca, &
p_surf, t_surf, u_surf, v_surf, &
rough_mom, rough_heat, rough_moist, rough_scale, gust
real, dimension(1) :: &
flux_t, flux_q, flux_r, flux_u, flux_v, &
dhdt_surf, dedt_surf, dedq_surf, drdt_surf, &
dhdt_atm, dedq_atm, dtaudu_atm,dtaudv_atm, &
w_atm, u_star, b_star, q_star, &
cd_m, cd_t, cd_q
real, dimension(1) :: q_surf
avail = .true.
t_atm(1) = t_atm_0
q_atm(1) = q_atm_0
u_atm(1) = u_atm_0
v_atm(1) = v_atm_0
p_atm(1) = p_atm_0
z_atm(1) = z_atm_0
t_ca(1) = t_ca_0
p_surf(1) = p_surf_0
t_surf(1) = t_surf_0
u_surf(1) = u_surf_0
v_surf(1) = v_surf_0
rough_mom(1) = rough_mom_0
rough_heat(1) = rough_heat_0
rough_moist(1) = rough_moist_0
rough_scale(1) = rough_scale_0
gust(1) = gust_0
q_surf(1) = q_surf_0
land(1) = land_0
seawater(1) = seawater_0
avail(1) = avail_0
call surface_flux_1d ( &
t_atm, q_atm, u_atm, v_atm, p_atm, z_atm, &
p_surf, t_surf, t_ca, q_surf, &
u_surf, v_surf, &
rough_mom, rough_heat, rough_moist, rough_scale, gust, &
flux_t, flux_q, flux_r, flux_u, flux_v, &
cd_m, cd_t, cd_q, &
w_atm, u_star, b_star, q_star, &
dhdt_surf, dedt_surf, dedq_surf, drdt_surf, &
dhdt_atm, dedq_atm, dtaudu_atm, dtaudv_atm, &
dt, land, seawater, avail )
flux_t_0 = flux_t(1)
flux_q_0 = flux_q(1)
flux_r_0 = flux_r(1)
flux_u_0 = flux_u(1)
flux_v_0 = flux_v(1)
dhdt_surf_0 = dhdt_surf(1)
dedt_surf_0 = dedt_surf(1)
dedq_surf_0 = dedq_surf(1)
drdt_surf_0 = drdt_surf(1)
dhdt_atm_0 = dhdt_atm(1)
dedq_atm_0 = dedq_atm(1)
dtaudu_atm_0 = dtaudu_atm(1)
dtaudv_atm_0 = dtaudv_atm(1)
w_atm_0 = w_atm(1)
u_star_0 = u_star(1)
b_star_0 = b_star(1)
q_star_0 = q_star(1)
q_surf_0 = q_surf(1)
cd_m_0 = cd_m(1)
cd_t_0 = cd_t(1)
cd_q_0 = cd_q(1)
end subroutine surface_flux_0d
subroutine surface_flux_2d ( &
t_atm, q_atm_in, u_atm, v_atm, p_atm, z_atm, &
p_surf, t_surf, t_ca, q_surf, &
u_surf, v_surf, &
rough_mom, rough_heat, rough_moist, rough_scale, gust, &
flux_t, flux_q, flux_r, flux_u, flux_v, &
cd_m, cd_t, cd_q, &
w_atm, u_star, b_star, q_star, &
dhdt_surf, dedt_surf, dedq_surf, drdt_surf, &
dhdt_atm, dedq_atm, dtaudu_atm, dtaudv_atm, &
dt, land, seawater, avail )
! ---- arguments -----------------------------------------------------------
logical, intent(in), dimension(:,:) :: land, seawater, avail
real, intent(in), dimension(:,:) :: &
t_atm, q_atm_in, u_atm, v_atm, &
p_atm, z_atm, t_ca, &
p_surf, t_surf, u_surf, v_surf, &
rough_mom, rough_heat, rough_moist, rough_scale, gust
real, intent(out), dimension(:,:) :: &
flux_t, flux_q, flux_r, flux_u, flux_v, &
dhdt_surf, dedt_surf, dedq_surf, drdt_surf, &
dhdt_atm, dedq_atm, dtaudu_atm,dtaudv_atm, &
w_atm, u_star, b_star, q_star, &
cd_m, cd_t, cd_q
real, intent(inout), dimension(:,:) :: q_surf
real, intent(in) :: dt
! ---- local vars -----------------------------------------------------------
integer :: j
do j = 1, size(t_atm,2)
call surface_flux_1d ( &
t_atm(:,j), q_atm_in(:,j), u_atm(:,j), v_atm(:,j), p_atm(:,j), z_atm(:,j), &
p_surf(:,j), t_surf(:,j), t_ca(:,j), q_surf(:,j), &
u_surf(:,j), v_surf(:,j), &
rough_mom(:,j), rough_heat(:,j), rough_moist(:,j), rough_scale(:,j), gust(:,j), &
flux_t(:,j), flux_q(:,j), flux_r(:,j), flux_u(:,j), flux_v(:,j), &
cd_m(:,j), cd_t(:,j), cd_q(:,j), &
w_atm(:,j), u_star(:,j), b_star(:,j), q_star(:,j), &
dhdt_surf(:,j), dedt_surf(:,j), dedq_surf(:,j), drdt_surf(:,j), &
dhdt_atm(:,j), dedq_atm(:,j), dtaudu_atm(:,j), dtaudv_atm(:,j), &
dt, land(:,j), seawater(:,j), avail(:,j) )
end do
end subroutine surface_flux_2d
! ============================================================================
! Initialization of the surface flux module--reads the nml.
!
subroutine surface_flux_init
! ---- local vars ----------------------------------------------------------
integer :: unit, ierr, io
! read namelist
if ( file_exist('input.nml')) then
unit = open_namelist_file ()
ierr=1;
do while (ierr /= 0)
read (unit, nml=surface_flux_nml, iostat=io, end=10)
ierr = check_nml_error(io,'surface_flux_nml')
enddo
10 call close_file (unit)
endif
! write version number
call write_version_number(version, tagname)
unit = stdlog()
if ( mpp_pe() == mpp_root_pe() ) write (unit, nml=surface_flux_nml)
if(.not. use_virtual_temp) d608 = 0.0
do_init = .false.
end subroutine surface_flux_init
!~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~!
! Over-ocean fluxes following Large and Yeager (used in NCAR models) !
! Original code: Michael.Winton@noaa.gov
! Update Jul2007: Stephen.Griffies@noaa.gov (ch and ce exchange coeff bugfix)
!~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~!
!
subroutine ncar_ocean_fluxes (u_del, t, ts, q, qs, z, avail, &
cd, ch, ce, ustar, bstar )
real , intent(in) , dimension(:) :: u_del, t, ts, q, qs, z
logical, intent(in) , dimension(:) :: avail
real , intent(inout), dimension(:) :: cd, ch, ce, ustar, bstar
real :: cd_n10, ce_n10, ch_n10, cd_n10_rt ! neutral 10m drag coefficients
real :: cd_rt ! full drag coefficients @ z
real :: zeta, x2, x, psi_m, psi_h ! stability parameters
real :: u, u10, tv, tstar, qstar, z0, xx, stab
integer, parameter :: n_itts = 2
integer i, j
if(ncar_ocean_flux_orig) then
do i=1,size(u_del(:))
if (avail(i)) then
tv = t(i)*(1+0.608*q(i));
u = max(u_del(i), 0.5); ! 0.5 m/s floor on wind (undocumented NCAR)
u10 = u; ! first guess 10m wind
cd_n10 = (2.7/u10+0.142+0.0764*u10)/1e3; ! L-Y eqn. 6a
cd_n10_rt = sqrt(cd_n10);
ce_n10 = 34.6 *cd_n10_rt/1e3; ! L-Y eqn. 6b
stab = 0.5 + sign(0.5,t(i)-ts(i))
ch_n10 = (18.0*stab+32.7*(1-stab))*cd_n10_rt/1e3; ! L-Y eqn. 6c
cd(i) = cd_n10; ! first guess for exchange coeff's at z
ch(i) = ch_n10;
ce(i) = ce_n10;
do j=1,n_itts ! Monin-Obukhov iteration
cd_rt = sqrt(cd(i));
ustar(i) = cd_rt*u; ! L-Y eqn. 7a
tstar = (ch(i)/cd_rt)*(t(i)-ts(i)); ! L-Y eqn. 7b
qstar = (ce(i)/cd_rt)*(q(i)-qs(i)); ! L-Y eqn. 7c
bstar(i) = grav*(tstar/tv+qstar/(q(i)+1/0.608));
zeta = vonkarm*bstar(i)*z(i)/(ustar(i)*ustar(i)); ! L-Y eqn. 8a
zeta = sign( min(abs(zeta),10.0), zeta ); ! undocumented NCAR
x2 = sqrt(abs(1-16*zeta)); ! L-Y eqn. 8b
x2 = max(x2, 1.0); ! undocumented NCAR
x = sqrt(x2);
if (zeta > 0) then
psi_m = -5*zeta; ! L-Y eqn. 8c
psi_h = -5*zeta; ! L-Y eqn. 8c
else
psi_m = log((1+2*x+x2)*(1+x2)/8)-2*(atan(x)-atan(1.0)); ! L-Y eqn. 8d
psi_h = 2*log((1+x2)/2); ! L-Y eqn. 8e
end if
u10 = u/(1+cd_n10_rt*(log(z(i)/10)-psi_m)/vonkarm); ! L-Y eqn. 9
cd_n10 = (2.7/u10+0.142+0.0764*u10)/1e3; ! L-Y eqn. 6a again
cd_n10_rt = sqrt(cd_n10);
ce_n10 = 34.6*cd_n10_rt/1e3; ! L-Y eqn. 6b again
stab = 0.5 + sign(0.5,zeta)
ch_n10 = (18.0*stab+32.7*(1-stab))*cd_n10_rt/1e3; ! L-Y eqn. 6c again
z0 = 10*exp(-vonkarm/cd_n10_rt); ! diagnostic
xx = (log(z(i)/10)-psi_m)/vonkarm;
cd(i) = cd_n10/(1+cd_n10_rt*xx)**2; ! L-Y 10a
xx = (log(z(i)/10)-psi_h)/vonkarm;
ch(i) = ch_n10/(1+ch_n10*xx/cd_n10_rt)**2; ! 10b (this code is wrong)
ce(i) = ce_n10/(1+ce_n10*xx/cd_n10_rt)**2; ! 10c (this code is wrong)
end do
end if
end do
else
do i=1,size(u_del(:))
if (avail(i)) then
tv = t(i)*(1+0.608*q(i));
u = max(u_del(i), 0.5); ! 0.5 m/s floor on wind (undocumented NCAR)
u10 = u; ! first guess 10m wind
cd_n10 = (2.7/u10+0.142+0.0764*u10)/1e3; ! L-Y eqn. 6a
cd_n10_rt = sqrt(cd_n10);
ce_n10 = 34.6 *cd_n10_rt/1e3; ! L-Y eqn. 6b
stab = 0.5 + sign(0.5,t(i)-ts(i))
ch_n10 = (18.0*stab+32.7*(1-stab))*cd_n10_rt/1e3; ! L-Y eqn. 6c
cd(i) = cd_n10; ! first guess for exchange coeff's at z
ch(i) = ch_n10;
ce(i) = ce_n10;
do j=1,n_itts ! Monin-Obukhov iteration
cd_rt = sqrt(cd(i));
ustar(i) = cd_rt*u; ! L-Y eqn. 7a
tstar = (ch(i)/cd_rt)*(t(i)-ts(i)); ! L-Y eqn. 7b
qstar = (ce(i)/cd_rt)*(q(i)-qs(i)); ! L-Y eqn. 7c
bstar(i) = grav*(tstar/tv+qstar/(q(i)+1/0.608));
zeta = vonkarm*bstar(i)*z(i)/(ustar(i)*ustar(i)); ! L-Y eqn. 8a
zeta = sign( min(abs(zeta),10.0), zeta ); ! undocumented NCAR
x2 = sqrt(abs(1-16*zeta)); ! L-Y eqn. 8b
x2 = max(x2, 1.0); ! undocumented NCAR
x = sqrt(x2);
if (zeta > 0) then
psi_m = -5*zeta; ! L-Y eqn. 8c
psi_h = -5*zeta; ! L-Y eqn. 8c
else
psi_m = log((1+2*x+x2)*(1+x2)/8)-2*(atan(x)-atan(1.0)); ! L-Y eqn. 8d
psi_h = 2*log((1+x2)/2); ! L-Y eqn. 8e
end if
u10 = u/(1+cd_n10_rt*(log(z(i)/10)-psi_m)/vonkarm); ! L-Y eqn. 9
cd_n10 = (2.7/u10+0.142+0.0764*u10)/1e3; ! L-Y eqn. 6a again
cd_n10_rt = sqrt(cd_n10);
ce_n10 = 34.6*cd_n10_rt/1e3; ! L-Y eqn. 6b again
stab = 0.5 + sign(0.5,zeta)
ch_n10 = (18.0*stab+32.7*(1-stab))*cd_n10_rt/1e3; ! L-Y eqn. 6c again
z0 = 10*exp(-vonkarm/cd_n10_rt); ! diagnostic
xx = (log(z(i)/10)-psi_m)/vonkarm;
cd(i) = cd_n10/(1+cd_n10_rt*xx)**2; ! L-Y 10a
xx = (log(z(i)/10)-psi_h)/vonkarm;
ch(i) = ch_n10/(1+ch_n10*xx/cd_n10_rt)*sqrt(cd(i)/cd_n10) ! 10b (corrected code)
ce(i) = ce_n10/(1+ce_n10*xx/cd_n10_rt)*sqrt(cd(i)/cd_n10) ! 10c (corrected code)
end do
end if
end do
endif
end subroutine ncar_ocean_fluxes
end module surface_flux_mod