function A=hfbulktc(ur,zr,Ta,zt,rh,zq,Pa,Ts,sal,dlw,dsw,nsw)
% HFBULKTC: computes sensible and latent heat fluxes and other variables.
% A=HFBULKTC(ur,zr,Ta,zt,rh,zq,Pa,Ts,sal,dlw,dsw,nsw) computes the following:
%
%         Hs      = sensible heat flux INTO ocean [W/m^2]
%         Hl      = latent heat flux INTO ocean [W/m^2]
%         Hl_webb = Webb correction to latent heat flux INTO ocean [W/m^2]
%         stress  = wind stress [N/m^2]
%         U_star  = velocity friction scale [m/s]
%         T_star  = temperature scale [deg C]
%         Q_star  = humidity scale [kg/kg]
%         L       = Monin-Obukhov length [m]
%         zetu    = zr/L
%         CD      = drag coefficient
%         CT      = temperature transfer coefficient (Stanton number)
%         CQ      = moisture transfer coefficient (Dalton number)
%         RI      = bulk Richardson number
%         Dter    = cool-skin temperature difference (optional output) [C]; 
%                   positive if surface is cooler than bulk (presently no 
%                   warm skin permitted by model)
%                    
% Based on the following buoy input data:
%
%           ur     = wind speed [m/s] measured at height zr [m] 
%           Ta     = air temperature [C] measured at height zt [m]
%           rh     = relative humidity [%] measured at height zq [m]
%           Pa     = air pressure [mb]
%           Ts     = sea surface temperature [C]
%           sal    = salinity [psu (PSS-78)]
%                    (optional - only needed for cool-skin)
%           dlw    = downwelling (INTO water) longwave radiation [W/m^2]
%                    (optional - only needed for cool-skin)
%           dsw    = measured insolation [W/m^2]
%                    (optional - only needed for cool-skin)
%           nsw    = net shortwave radiation INTO the water [W/m^2]
%                    (optional - only needed for cool-skin)
%
% where ur, Ta, rh, Pa, Ts, zr, zt, and zq (and optional sal, dlw,
% dsw, and nsw) may be either row or column vectors; and rh, Pa, 
% zr, zt, and zq (and optional sal) may also be fixed scalars.
%
% Output variables are given as column vectors in A:
%
% 1) without cool-skin correction:
%
%   A=[Hs Hl Hl_webb stress U_star T_star Q_star L zetu CD CT CQ RI]
%
% 2) with cool-skin correction: 
%
%   A=[Hs Hl Hl_webb stress U_star T_star Q_star L zetu CD CT CQ RI Dter];

% Code follows Edson and Fairall TOGA COARE code (version 2.0), modified 
% to include Rogers' weighting factor for unstable conditions.  Code does
% include gustiness, and assumes that the marine boundary layer height is
% known and constant over time for simiplicity. zr/L is limited to 
% be <=3.0 to ensure that the code converges to nonzero stress and heat 
% flux values for strongly stable conditions.  The bulk Richardson number
% is computed between the sea surface and zr as a diagnostic about whether
% turbulent boundary layer theory is applicable.  Code does not include 
% warm layer effects to modify Ts.  See Fairall et al (1996), J. Geophys. 
% Res., 101, 3747-3764, for description of full TOGA COARE code and 
% comparison with data. 

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% 8/19/98: version 1.1 (rewritten by RP to remove inconsistencies in 
%          virtual and real temperatures, improve loop structure, 
%          correct gustiness component of stress computation) 
% 4/9/99: version 1.2 (rewritten by AA to clarify some variable names
%         and include cool-skin effect and Webb correction to latent 
%         heat flux added to output matrix)
% 8/5/99: version 2.0
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

M=length(ur);

% change to column vectors
ur = ur(:);
Ta = Ta(:);
rh = rh(:);
Ts = Ts(:);
Pa = Pa(:);
zr = zr(:);
zt = zt(:);
zq = zq(:);

% create vectors for rh, Pa, zr, zt, and zq, if scalars are input
if length(rh)==1 & M>1
  rh=rh*ones(M,1);
end
if length(Pa)==1 & M>1
  Pa=Pa*ones(M,1);
end
if length(zr)==1 & M>1
  zr=zr*ones(M,1);
end
if length(zt)==1 & M>1
  zt=zt*ones(M,1);
end
if length(zq)==1 & M>1
  zq=zq*ones(M,1);
end

if nargin > 8
  % optional cool-skin stuff
  sal = sal(:);
  dlw = dlw(:);
  dsw = dsw(:);
  nsw = nsw(:);
  % create vector for sal if scalar is input
  if length(sal)==1 & M>1
    sal=sal*ones(M,1);
  end
end

% initialize various constants
as_consts;

tol=.001;    % tolerance on Re changes to make sure soln has converged.

onethird=1./3;
o61=1/eps_air-1;   % 0.61 (moisture correction for temperature)

visc=viscair(Ta);                 % viscosity
Qsats=Qsat_coeff*qsat(Ts,Pa);     % saturation specific humidity; the Qsat_coeff
                                  % value is set in routine as_consts.m
Q=(0.01.*rh).*qsat(Ta,Pa);        % specific humidity of air [kg/kg]
T =Ta+CtoK;                       % convert to K
Tv=T.*(1 + o61*Q);                % air virtual temperature
rho=(100*Pa)./(gas_const_R*Tv);   % air density
Dt=(Ta+0.0098.*zt)-Ts;            % adiabatic temperature difference
Dq=Q-Qsats;                       % humidity difference

% compute initial neutral scaling coefficients
S=sqrt(ur.^2 + min_gustiness.^2);
cdnhf=sqrt(cdntc(S,zr,Ta)); % Smith's neutral cd as first guess

z0t=7.5*10^(-5);
ctnhf=kappa./log(zt./z0t);

z0q=z0t;
cqnhf=kappa./log(zq./z0q);

U_star = cdnhf.*S;      % (includes gustiness)
T_star = ctnhf.*Dt;     % 
Q_star = cqnhf.*Dq;     %

Dter   = 0;
Dqer   = 0;
if nargin > 8
% initial cool-skin thickness guess  
  delta = 0.001*ones(size(Ts));
end

Reu=0;Ret=0;Req=0;
% begin iteration loop to compute best U_star, T_star, and Q_star
for iter1=1:80;

    ReuO=Reu; RetO=Ret; ReqO=Req; % Save old values
    
    % Compute Monin-Obukov length (NB - definition given as eqn (7)
    % of Fairall et al (1996) probably wrong, following, e.g.
    % Godfrey and Bellars (1991), JGR, 96, 22043-22048 and original code)
    bs=g*(T_star.*(1 + o61*Q) + o61*T.*Q_star)./Tv; 
    L=(U_star.^2)./(kappa*bs);
    % set upper limit on zr/L = 3.0 to force convergence under 
    % very stable conditions. Assume that zr, zt and zq comparable.
    index_limit   = (L<zr/3 & L>0);
    L(index_limit)=zr(index_limit)/3;
    
    zetu=zr./L;  % nondimensionalized heights
    zett=zt./L;
    zetq=zq./L;

    % surface roughness
    z0=(Charnock_alpha/g).*U_star.^2 + R_roughness.*visc./U_star;

    % compute U_star correction for non-neutral conditions
    cdnhf=kappa./(log(zr./z0)-psiutc(zetu));
    U_star=cdnhf.*S;
  
    Reu=z0.*U_star./visc;   % roughness Reynolds #
    [Ret,Req]=LKB(Reu);  % compute other roughness Reynolds #s

    % compute t and q roughness scales from roughness R#s
    z0t=visc.*Ret./U_star;
    z0q=visc.*Req./U_star;

    % compute new transfer coefficients at measurement heights
    cthf=kappa./(log(zt./z0t)-psittc(zett));
    cqhf=kappa./(log(zq./z0q)-psittc(zetq));

    % compute new values of T_star, Q_star
    T_star=cthf.*(Dt + Dter);
    Q_star=cqhf.*(Dq + Dqer);

    % estimate new gustiness
    Ws=U_star.*(-CVB_depth./(kappa*L)).^onethird;
    wg=min_gustiness*ones(M,1);
    j=find(zetu<0);                 % convection in unstable conditions only
    wg(j)=max(min_gustiness,beta_conv.*Ws(j)); % set minimum gustiness
    S=sqrt(ur.^2 + wg.^2);

    if nargin > 8
    % compute cool-skin parameters
      [delta,Dter,Dqer] = cool_skin(sal,Ts-Dter,rho,cp,Pa, ...
                                    U_star,T_star,Q_star, ...
                                    dlw,dsw,nsw,delta,g,gas_const_R, ...
                                    CtoK,Qsat_coeff);
    end

end % end of iteration loop

ii= abs(Reu-ReuO)>tol | abs(Ret-RetO)>tol | abs(Req-ReqO)>tol;
if any(ii),
 disp(['Algorithm did not converge for ' int2str(sum(ii)) ' values. Indices are:']);
 disp(find(ii)');
 warning('Not converged!');
end;


% compute latent heat
Le=(2.501-0.00237*(Ts-Dter))*10^6;

% compute fluxes into ocean
Hs=rho.*cp.*U_star.*T_star;
Hl=rho.*Le.*U_star.*Q_star;

% compute transfer coefficients at measurement heights
CD=(U_star./S).^2;
CT=U_star.*T_star./(S.*(Dt + Dter)); % Stanton number
CQ=U_star.*Q_star./(S.*(Dq + Dqer)); % Dalton number

% to compute mean stress, we don't want to include the effects
% of gustiness which average out (in a vector sense).
stress=rho.*CD.*S.*ur;

% compute bulk Richardson number (as a diagnostic) - the "T"
% is probably not quite right - assumes T \ approx. Ts (good enough though)
RI=g.*zr.*((Dt + Dter) + o61*T.*(Dq + Dqer))./(Tv.*S.^2);

% compute Webb correction to latent heat flux into ocean
W = 1.61.*U_star.*Q_star + (1 + 1.61.*Q).*U_star.*T_star./T; % eqn. 21
Hl_webb = rho.*Le.*W.*Q; % eqn. 22, Fairall et al. (1996), JGR, 101, p3751.

% output array
if nargin > 8
  % output additional cool-skin parameters 
  A=[Hs Hl Hl_webb stress U_star T_star Q_star L zetu CD CT CQ RI Dter];
else
  % otherwise
  A=[Hs Hl Hl_webb stress U_star T_star Q_star L zetu CD CT CQ RI];
end



function y=psiutc(zet)
% PSIUTC: computes velocity profile function following TOGA/COARE.
% y=PSIUTC(zet) computes the turbulent velocity profile function given 
% zet = (z/L), L the Monin-Obukoff length scale, following Edson and
% Fairall TOGA COARE code (version 2.0) as modified to include Rogers' 
% weighting factor to combine the Dyer and free convection forms for 
% unstable conditions. 
 
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% 8/28/98: version 1.1
% 8/5/99: version 1.2
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

c13=1.0./3.0;
sq3=sqrt(3.0);

% stable conditions
y=-4.7*zet;

% unstable conditions
j=find(zet<0);
zneg=zet(j);

% nearly stable (standard functions)
 x=(1-16.0.*zneg).^0.25;
 y1=2.0.*log((1+x)./2) + log((1+x.^2)./2) -2.*atan(x) + pi/2;

% free convective limit
 x=(1-12.87*zneg).^c13;
 y2=1.5*log((x.^2+x+1)./3) - sq3*atan((2.*x+1)/sq3) + pi/sq3;
	
% weighted sum of the two
 F=1.0./(1+zneg.^2);
 y(j)=F.*y1+(1-F).*y2;




function y=psittc(zet)
% PSITTC: computes potential temperature profile following TOGA/COARE.
% y=PSITTC(zet) computes the turbulent potential temperature profile 
% function given zet = (z/L), L the Monin-Obukoff length scale, following 
% Edson and Fairall TOGA COARE code (version 2.0), as modified to use
% Rogers' weighting factor to combine the Dyer and free convective 
% forms for unstable conditions. 

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% 8/28/98: version 1.1
% 8/5/99: version 1.2
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

c13=1.0./3.0;
sq3=sqrt(3.0);

% stable conditions
y=-4.7.*zet;

% unstable conditions
j=find(zet<0);
zneg=zet(j);

% nearly stable (standard functions)
 x=(1-16.0.*zneg).^0.25;
 y1=2.0*log((1+x.^2)./2);

% free convective limit
 x=(1-12.87*zneg).^c13;
 y2=1.5.*log((x.^2+x+1)./3.0) - sq3.*atan((2.*x+1)./sq3) + pi./sq3;

% weighted sum of the two
 F=1.0./(1+zneg.^2);
 y(j)=F.*y1 + (1-F).*y2;





function [Ret,Req]=LKB(Reu);
% LKB: computes rougness Reynolds numbers for temperature and humidity
% [Ret,Req]=LKB(Reu) computes the roughness Reynolds for temperature
% and humidity following Liu, Katsaros and Businger (1979), J. Atmos.
% Sci., 36, 1722-1735.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% 8/28/98: version 1.1
% 8/5/99: version 1.2
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

   Ret=.177*ones(size(Reu));
   Req=.292*ones(size(Reu));
j=find(Reu>.11 & Reu<=.825);
   Ret(j)=1.376.*Reu(j).^0.929;
   Req(j)=1.808.*Reu(j).^0.826;
j=find(Reu>.825 & Reu<=3);
   Ret(j)=1.026./Reu(j).^0.599;
   Req(j)=1.393./Reu(j).^0.528;
j=find(Reu>3 & Reu<=10);
   Ret(j)=1.625./Reu(j).^1.018;
   Req(j)=1.956./Reu(j).^0.870;
j=find(Reu>10 & Reu<=30);
   Ret(j)=4.661./Reu(j).^1.475;
   Req(j)=4.994./Reu(j).^1.297;
j=find(Reu>30);
   Ret(j)=34.904./Reu(j).^2.067;
   Req(j)=30.790./Reu(j).^1.845;
