| 1 |
function HT = calcHeatTransDirect(varargin) |
| 2 |
|
| 3 |
% HT = calcHeatTransDirect(d,g,time,flu,blkFile,[optional]); |
| 4 |
% HT = calcHeatTransDirect(d,g,time,flu,blkFile,'grav',9.81); |
| 5 |
% |
| 6 |
% Input arguements: |
| 7 |
% The incoming field data (d) and grid data (g) must be in a structured |
| 8 |
% array format (which is the format that comes from rdmnc): |
| 9 |
% d [Field data] hUtave,hVtave,uVeltave,vVeltave,Ttave,UTtave, |
| 10 |
% VTtave,(Stave,UStave,VStave for atm) |
| 11 |
% g [Grid data ] drF,dxG,dyG,dxC,dyC,HFacW,HFacS,rA |
| 12 |
% Other input parameters: |
| 13 |
% time (vec) Time levels to analyze ([] for all) |
| 14 |
% flu (str) 'O' or 'A' for ocean or atmosphere |
| 15 |
% blkFile (str) Broken line file (eg 'isoLat_cs32_59.mat') |
| 16 |
% Optional parameters: |
| 17 |
% 'grav' (num, default 9.81) Acceleration due to gravity |
| 18 |
% 'LhVap' (num, default 2501) Latent heat of vaporization |
| 19 |
% 'CpO' (num, default 3994) Specific heat capacity of water |
| 20 |
% 'RhoO' (num, default 1030) Density of sea water |
| 21 |
% 'CpA' (num, default 1004) Specific heat capacity of water |
| 22 |
% %'DiffKh' (num, default 800) Horizontal diffusivity |
| 23 |
% |
| 24 |
% Output: |
| 25 |
% HT_Out is a structured array with the following fields: |
| 26 |
% time ([nt,1]) Time axis |
| 27 |
% ylatHT ([ny,1]) Heat transport latitude axis |
| 28 |
% ylatSF ([ny-1,1]) Implied heating latitude axis |
| 29 |
% AreaZon ([ny,1]) Area in zonal bands |
| 30 |
% SenHT ([ny,nBas,nt,nFld]) Sensible heat transport |
| 31 |
% SenSF ([ny-1,nBas,nt,nFld]) Implied heating above |
| 32 |
% LatHT ([ny,nBas,nt,nFld]) Latent heat transport (atm only) |
| 33 |
% LatSF ([ny-1,nBas,nt,nFld]) Implied heating from aboce (atm only) |
| 34 |
% Currently, the routine is only configured to handle the global basin, |
| 35 |
% so nBas = 1 for the output. ny is defined by the broken line file used |
| 36 |
% for the cube calculation. nFld is the heat transport component: |
| 37 |
% nFld = 1 = Eulerian circulation HT |
| 38 |
% nFld = 2 = HT by time mean circulation |
| 39 |
% nFld = 3 = Residual [3=1-2] |
| 40 |
% nFld = 4 = HT by zonal mean circulation |
| 41 |
% nFld = 5 = Residual [5=2-4] |
| 42 |
% Ocn only: |
| 43 |
% If GM advective form: |
| 44 |
% nFld = 6 = HT by horizontal diffusion |
| 45 |
% If Skew flux form: |
| 46 |
% nFld = 6 = total (GM+Redi) eddy transport |
| 47 |
% nFld = 7 = GM (advective) eddy transport |
| 48 |
% |
| 49 |
% Description: |
| 50 |
% Calculation heat transport, and to degree possible, decomposition. |
| 51 |
% Heat transport is given in PW and the implied surface heating/flux |
| 52 |
% in W/m^2. The incoming data arrays are all assumed to be of the |
| 53 |
% dimensions [6*nc,nc,nr,nt]. |
| 54 |
% |
| 55 |
% Original Author: Jean-Michel Campin |
| 56 |
% Modifications: Daniel Enderton |
| 57 |
|
| 58 |
% Default constants |
| 59 |
LhVap = 2501; |
| 60 |
grav = 9.81; |
| 61 |
CpO = 3994; |
| 62 |
RhoO = 1030; |
| 63 |
CpA = 1004; |
| 64 |
kappa = 2/7; |
| 65 |
masking=0; |
| 66 |
|
| 67 |
PHIref = grav * [ 431.199 2105.998 5536.327 10440.915 18190.889]; |
| 68 |
%PHIref = grav * [ 173.888 529.598 896.724 1276.210 1669.052 ... |
| 69 |
% 2076.380 2499.554 2940.137 3399.937 3881.061 ... |
| 70 |
% 4385.993 4917.689 5480.084 6078.962 6722.700 ... |
| 71 |
% 7420.744 8183.151 9023.181 9959.131 11019.028... |
| 72 |
% 12253.003 13723.604 15599.238 18412.089 24539.570]; |
| 73 |
|
| 74 |
% Read input parameters. |
| 75 |
d = varargin{1}; |
| 76 |
g = varargin{2}; |
| 77 |
dE= varargin{3}; |
| 78 |
GM= varargin{4}; |
| 79 |
nt= varargin{5}; |
| 80 |
flu = varargin{6}; |
| 81 |
blkFile = varargin{7}; |
| 82 |
%for ii = 8:2:length(varargin) |
| 83 |
% temp = varargin{ii+1}; eval([varargin{ii},' = temp;']); |
| 84 |
%end |
| 85 |
if length(varargin) >= 8 & ~isempty(varargin{8}) |
| 86 |
mask = varargin{8}; |
| 87 |
masking = 1; |
| 88 |
disp('Masking is on...') |
| 89 |
end |
| 90 |
if length(varargin) == 9 |
| 91 |
PHIref= varargin{9}; |
| 92 |
end |
| 93 |
|
| 94 |
nBas = 0; |
| 95 |
if isequal(flu,'A'), nout = 5; end |
| 96 |
if isequal(flu,'O'), nout = 7; end |
| 97 |
flag_tot=0; |
| 98 |
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| 99 |
% Prepare / reform incoming data % |
| 100 |
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| 101 |
|
| 102 |
% Determine time indecies. |
| 103 |
%if isempty(time), time = d.iters_read_from_file; i_time = 1:length(time); |
| 104 |
%else [dump,i_time] = ismember(time,d.iters_read_from_file); end |
| 105 |
|
| 106 |
nc = size(g.HFacC,2); |
| 107 |
nr = size(g.HFacC,3); |
| 108 |
i_time=1:nt; |
| 109 |
|
| 110 |
if nr~=5 |
| 111 |
LhVap = 2500e3; |
| 112 |
grav = 9.79764; |
| 113 |
RDGAS = 287.04; |
| 114 |
CpA = RDGAS/kappa; |
| 115 |
end |
| 116 |
|
| 117 |
ac = reshape(g.rA ,[6*nc*nc, 1]); |
| 118 |
hw = reshape(g.HFacW(1:6*nc,1:nc,1:nr),[6*nc*nc,nr]); |
| 119 |
hs = reshape(g.HFacS(1:6*nc,1:nc,1:nr),[6*nc*nc,nr]); |
| 120 |
dxc = reshape(g.dxC(1:6*nc,1:nc) ,[6*nc*nc, 1]); |
| 121 |
dyc = reshape(g.dyC(1:6*nc,1:nc) ,[6*nc*nc, 1]); |
| 122 |
dxg = reshape(g.dxG(1:6*nc,1:nc) ,[6*nc*nc, 1]); |
| 123 |
dyg = reshape(g.dyG(1:6*nc,1:nc) ,[6*nc*nc, 1]); |
| 124 |
drf = reshape(g.drF,[1,length(g.drF)]); |
| 125 |
Z = reshape(g.Z,[1,length(g.Z)]); |
| 126 |
|
| 127 |
%u = reshape(d.UVEL(1:6*nc,1:nc,:,i_time),[6*nc*nc,nr,nt]); |
| 128 |
%v = reshape(d.VVEL(1:6*nc,1:nc,:,i_time),[6*nc*nc,nr,nt]); |
| 129 |
ut = reshape(dE.UTHMASS(1:6*nc,1:nc,:,i_time) ,[6*nc*nc,nr,nt]); |
| 130 |
vt = reshape(dE.VTHMASS(1:6*nc,1:nc,:,i_time) ,[6*nc*nc,nr,nt]); |
| 131 |
hu = reshape(dE.UVELMASS(1:6*nc,1:nc,:,i_time) ,[6*nc*nc,nr,nt]); |
| 132 |
hv = reshape(dE.VVELMASS(1:6*nc,1:nc,:,i_time) ,[6*nc*nc,nr,nt]); |
| 133 |
t = d.THETA(1:6*nc,1:nc,:,i_time); |
| 134 |
if isequal(flu,'O') |
| 135 |
Kux = reshape(GM.GM_Kux(1:6*nc,1:nc,:,i_time),[6*nc*nc,nr,nt]); |
| 136 |
Kvy = reshape(GM.GM_Kvy(1:6*nc,1:nc,:,i_time),[6*nc*nc,nr,nt]); |
| 137 |
|
| 138 |
if isfield(GM,'GM_ubT') %%% if advective form of GM |
| 139 |
UbTgm = reshape(GM.GM_ubT(1:6*nc,1:nc,:,i_time),[6*nc*nc,nr,nt]); |
| 140 |
VbTgm = reshape(GM.GM_vbT(1:6*nc,1:nc,:,i_time),[6*nc*nc,nr,nt]); |
| 141 |
elseif isfield(GM,'GM_KuzTz') %%% if skew-flux form of GM |
| 142 |
UbTgm = reshape(GM.GM_KuzTz(1:6*nc,1:nc,:,i_time),[6*nc*nc,nr,nt]); |
| 143 |
VbTgm = reshape(GM.GM_KvzTz(1:6*nc,1:nc,:,i_time),[6*nc*nc,nr,nt]); |
| 144 |
elseif isfield(dE,'ADVx_TH') %%% if total advective field |
| 145 |
flag_tot = 1; |
| 146 |
UbTgm = reshape(dE.ADVx_TH(1:6*nc,1:nc,:,i_time),[6*nc*nc,nr,nt]); |
| 147 |
VbTgm = reshape(dE.ADVy_TH(1:6*nc,1:nc,:,i_time),[6*nc*nc,nr,nt]); |
| 148 |
else |
| 149 |
UbTgm = zeros(6*nc*nc,nr,nt); |
| 150 |
VbTgm = zeros(6*nc*nc,nr,nt); |
| 151 |
end |
| 152 |
end |
| 153 |
|
| 154 |
if isequal(flu,'A') |
| 155 |
up = reshape(dE.UVELPHI(1:6*nc,1:nc,:,i_time) ,[6*nc*nc,nr,nt]); |
| 156 |
vp = reshape(dE.VVELPHI(1:6*nc,1:nc,:,i_time) ,[6*nc*nc,nr,nt]); |
| 157 |
uq = reshape(dE.USLTMASS(1:6*nc,1:nc,:,i_time) ,[6*nc*nc,nr,nt]); |
| 158 |
vq = reshape(dE.VSLTMASS(1:6*nc,1:nc,:,i_time) ,[6*nc*nc,nr,nt]); |
| 159 |
q = d.SALT(1:6*nc,1:nc,:,i_time); |
| 160 |
p = d.PHIHYD(1:6*nc,1:nc,:,i_time); |
| 161 |
ut = ut .* ( repmat(Z,[6*nc*nc 1 nt]) / 1e5 ).^kappa; |
| 162 |
vt = vt .* ( repmat(Z,[6*nc*nc 1 nt]) / 1e5 ).^kappa; |
| 163 |
t = t .* ( repmat(reshape(Z,[1 1 nr]),[6*nc nc 1 nt]) / 1e5 ).^kappa; |
| 164 |
end |
| 165 |
|
| 166 |
if masking == 1 |
| 167 |
hu = repmat(reshape(mask.maskW(:,:,1),6*nc*nc,1),[1 nr nt]) .* hu; |
| 168 |
hv = repmat(reshape(mask.maskS(:,:,1),6*nc*nc,1),[1 nr nt]) .* hv; |
| 169 |
ut = repmat(reshape(mask.maskW(:,:,1),6*nc*nc,1),[1 nr nt]) .* ut; |
| 170 |
vt = repmat(reshape(mask.maskS(:,:,1),6*nc*nc,1),[1 nr nt]) .* vt; |
| 171 |
if isequal(flu,'O') |
| 172 |
Kux = repmat(reshape(mask.maskW(:,:,1),6*nc*nc,1),[1 nr nt]) .* Kux; |
| 173 |
Kvy = repmat(reshape(mask.maskS(:,:,1),6*nc*nc,1),[1 nr nt]) .* Kvy; |
| 174 |
UbTgm = repmat(reshape(mask.maskW(:,:,1),6*nc*nc,1),[1 nr nt]) .* UbTgm; |
| 175 |
VbTgm = repmat(reshape(mask.maskS(:,:,1),6*nc*nc,1),[1 nr nt]) .* VbTgm; |
| 176 |
end |
| 177 |
end |
| 178 |
|
| 179 |
|
| 180 |
% Load broken line information. Compute (tracer point) cell area between |
| 181 |
% broken lines for each basin. There are nbkl broken lines and nbkl+1 |
| 182 |
% bands between broken lines. The variable "bkl_Zon" gives the zone number |
| 183 |
% (nbkl+1 total) for a given index between 0 and nbkl, that is, nbkl+1 |
| 184 |
% total zones. Comments block is for eventual addition of multiple basin |
| 185 |
% calculations. |
| 186 |
load(blkFile); |
| 187 |
ydim=length(bkl_Ylat); |
| 188 |
AreaZon=zeros(ydim+1,1+nBas); |
| 189 |
for j = 1:ydim+1 |
| 190 |
izon = find(bkl_Zon == j-1); |
| 191 |
AreaZon(j,1) = sum(ac(izon)); |
| 192 |
% for b = 1:nBas, |
| 193 |
% tmp = ac.*mskBc(:,b); |
| 194 |
% AreaZon(j,1+b) = sum(tmp(izon)); |
| 195 |
% end |
| 196 |
end |
| 197 |
|
| 198 |
% Latitute plotting information. Average latitude of a broken line |
| 199 |
% (ylatHF) is calculated from a mean value of the y value of all the edges. |
| 200 |
% The latitude at the surface flux points is a mean of the broken line mean |
| 201 |
% values. |
| 202 |
YlatAv=sum(bkl_Ysg,1)./(1+bkl_Npts'); %' |
| 203 |
ylatHT = [-90,YlatAv,90]; |
| 204 |
ylatSF = ( ylatHT(2:ydim+2) + ylatHT(1:ydim+1) )./2; |
| 205 |
|
| 206 |
% The variable "bkl_Flg" is -1/1 if edge (on a given broken) has a u point |
| 207 |
% and -2/2 if it has a v point. Positive/negative values contribute |
| 208 |
% positively/negatively to northward heat transport (this depends on the |
| 209 |
% orientation of the cell). A zero value indicates an end of edges that |
| 210 |
% contribute to a broken line. The u and v information is parced into two |
| 211 |
% seperate fields, ufac and vfac (-2/2 are reduced to -1/1 for vfac). |
| 212 |
ufac = zeros([size(bkl_Flg),1+nBas]); |
| 213 |
vfac = zeros([size(bkl_Flg),1+nBas]); |
| 214 |
ufac(:,:,1) = rem(bkl_Flg,2); |
| 215 |
vfac(:,:,1) = fix(bkl_Flg/2); |
| 216 |
% for jl=1:ydim, |
| 217 |
% ie=bkl_Npts(jl); |
| 218 |
% for b=1:nBas, |
| 219 |
% ufac(1:ie,jl,1+b)=mskBw(bkl_IJuv(1:ie,jl),b).*ufac(1:ie,jl,1); |
| 220 |
% vfac(1:ie,jl,1+b)=mskBs(bkl_IJuv(1:ie,jl),b).*vfac(1:ie,jl,1); |
| 221 |
% end |
| 222 |
% end |
| 223 |
ufacabs = abs(ufac); |
| 224 |
vfacabs = abs(vfac); |
| 225 |
|
| 226 |
% Prepare mask(s). |
| 227 |
% ??? I temporarily took out the code to configure the masks beyond this |
| 228 |
% global one. Does this need to account for a ridge if present? |
| 229 |
mskG=ones(ydim+1,1+nBas); |
| 230 |
|
| 231 |
% Area factors. "ArW_Dif" and "ArS_Dif" the areas for the western and |
| 232 |
% southern edge of cells, respectively The "_Dif" suffix indicates the |
| 233 |
% areas used for the diffusivity because there is an extra "dxc" or "dyc" |
| 234 |
% from the gradient (here for computational efficiency reasons). The |
| 235 |
% division by "dxc" and "dyc" is associates with gradient of temperature. |
| 236 |
% |
| 237 |
% ??? Why is the land mask (hw and hs) only in the diffusive area term? |
| 238 |
ArW = dyg*reshape(drf,[1,length(drf)]); |
| 239 |
ArS = dxg*reshape(drf,[1,length(drf)]); |
| 240 |
ArW_Dif=hw.*((dyg./dxc)*reshape(drf,[1,length(drf)])); |
| 241 |
ArS_Dif=hs.*((dxg./dyc)*reshape(drf,[1,length(drf)])); |
| 242 |
|
| 243 |
% Compute the temperature and its gradient and the velocity points: |
| 244 |
% tbi/tdi = temperature between/difference i points (at u points) |
| 245 |
% tbj/tdj = temperature between/difference j points (at v points) |
| 246 |
% The cube is first split and the extra points are added to the files. |
| 247 |
% Then the means and differences are taken. Note that the division of dxc |
| 248 |
% and dyc for the gradient is not applied until later for computational |
| 249 |
% efficiency purposes. The arrays are then croped and reshaped to the |
| 250 |
% format for the other field variables, [6*nc*nc,nr]. Note that the |
| 251 |
% split_C_cub function adds a row/column of tracer points in front of the |
| 252 |
% first row/column of the tile matries. This, when the differences and |
| 253 |
% gradients are computed and cropped, the off indecies are selected from |
| 254 |
% [2:nc+1] rather than [1:nc]. (This was a bit mystifying to me). |
| 255 |
t6bi=zeros(nc,nc+1,nr,nt,6); t6di=t6bi; q6bi=t6bi; |
| 256 |
t6bj=zeros(nc+1,nc,nr,nt,6); t6dj=t6bj; q6bj=t6bj; |
| 257 |
t6t=split_C_cub(t); |
| 258 |
t6bi([1:nc],:,:,:,:) = ( t6t([1:nc],:,:,:,:) + t6t([2:nc+1],:,:,:,:) )./2; |
| 259 |
t6bj(:,[1:nc],:,:,:) = ( t6t(:,[1:nc],:,:,:) + t6t(:,[2:nc+1],:,:,:) )./2; |
| 260 |
t6di([1:nc],:,:,:,:) = ( t6t([1:nc],:,:,:,:) - t6t([2:nc+1],:,:,:,:) ); |
| 261 |
t6dj(:,[1:nc],:,:,:) = ( t6t(:,[1:nc],:,:,:) - t6t(:,[2:nc+1],:,:,:) ); |
| 262 |
tbi = t6bi([1:nc],[2:nc+1],:,:,:); |
| 263 |
tbj = t6bj([2:nc+1],[1:nc],:,:,:); |
| 264 |
tdi = t6di([1:nc],[2:nc+1],:,:,:); |
| 265 |
tdj = t6dj([2:nc+1],[1:nc],:,:,:); |
| 266 |
tbi=reshape(permute(tbi,[1,5,2,3,4]),[6*nc*nc,nr,nt]); |
| 267 |
tbj=reshape(permute(tbj,[1,5,2,3,4]),[6*nc*nc,nr,nt]); |
| 268 |
tdi=reshape(permute(tdi,[1,5,2,3,4]),[6*nc*nc,nr,nt]); |
| 269 |
tdj=reshape(permute(tdj,[1,5,2,3,4]),[6*nc*nc,nr,nt]); |
| 270 |
if isequal(flu,'A') |
| 271 |
q6t=split_C_cub(q); |
| 272 |
q6bi([1:nc],:,:,:,:) = (q6t([1:nc],:,:,:,:)+q6t([2:nc+1],:,:,:,:))./2; |
| 273 |
q6bj(:,[1:nc],:,:,:) = (q6t(:,[1:nc],:,:,:)+q6t(:,[2:nc+1],:,:,:))./2; |
| 274 |
qbi = q6bi([1:nc],[2:nc+1],:,:,:); |
| 275 |
qbj = q6bj([2:nc+1],[1:nc],:,:,:); |
| 276 |
qbi=reshape(permute(qbi,[1,5,2,3,4]),[6*nc*nc,nr,nt]); |
| 277 |
qbj=reshape(permute(qbj,[1,5,2,3,4]),[6*nc*nc,nr,nt]); |
| 278 |
|
| 279 |
p6t=split_C_cub(p); |
| 280 |
p6bi([1:nc],:,:,:,:) = (p6t([1:nc],:,:,:,:)+p6t([2:nc+1],:,:,:,:))./2; |
| 281 |
p6bj(:,[1:nc],:,:,:) = (p6t(:,[1:nc],:,:,:)+p6t(:,[2:nc+1],:,:,:))./2; |
| 282 |
pbi = p6bi([1:nc],[2:nc+1],:,:,:); |
| 283 |
pbj = p6bj([2:nc+1],[1:nc],:,:,:); |
| 284 |
pbi=reshape(permute(pbi,[1,5,2,3,4]),[6*nc*nc,nr,nt]); |
| 285 |
pbj=reshape(permute(pbj,[1,5,2,3,4]),[6*nc*nc,nr,nt]); |
| 286 |
end |
| 287 |
|
| 288 |
% Prepare output arrays. "nout" is the number of transport output fields. |
| 289 |
% It is currently hard-coded, but could eventually be an input parameters |
| 290 |
% to set which output fields are desired if some of then become |
| 291 |
% computationally expensive. |
| 292 |
% SenHT = Sensible heat transport |
| 293 |
% SenSF = Sensible implied surface flux |
| 294 |
% IntV = Integrated volume transport |
| 295 |
% IntT = Integrated temperature |
| 296 |
SenHT = zeros(ydim+2,1+nBas,nt,nout); |
| 297 |
SenSF = zeros(ydim+1,1+nBas,nt,nout); |
| 298 |
IntV = zeros(ydim,nr,1+nBas,nt); |
| 299 |
IntT = zeros(ydim,nr,1+nBas,nt); |
| 300 |
IntM = zeros(ydim,nr,1+nBas,nt); |
| 301 |
if isequal(flu,'A') |
| 302 |
LatHT = zeros(ydim+2,1+nBas,nt,nout); |
| 303 |
PotHT = zeros(ydim+2,1+nBas,nt,nout); |
| 304 |
LatSF = zeros(ydim+1,1+nBas,nt,nout); |
| 305 |
IntQ = zeros(ydim,nr,1+nBas,nt); |
| 306 |
IntP = zeros(ydim,nr,1+nBas,nt); |
| 307 |
end |
| 308 |
Psi = zeros(ydim+2,1+nBas,nt); |
| 309 |
|
| 310 |
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| 311 |
% Make heat transport calculations % |
| 312 |
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| 313 |
|
| 314 |
% Preparation for calculation of zonal average temperature. The |
| 315 |
% tempereature multiplied by the appropriate length scale ("tbi_temp", |
| 316 |
% "tbj_temp") is summed up ("IntT" in the next section) and |
| 317 |
% divided by the total length ("IntM", composed from summing "hw_temp", |
| 318 |
% "hs_temp"). |
| 319 |
hw_temp = zeros(size(hw)); |
| 320 |
hs_temp = zeros(size(hs)); |
| 321 |
for k=1:nr, |
| 322 |
hw_temp(:,k) = dyg.*hw(:,k); |
| 323 |
hs_temp(:,k) = dxg.*hs(:,k); |
| 324 |
end |
| 325 |
|
| 326 |
for it = 1:length(i_time) |
| 327 |
|
| 328 |
% uz / vz = Volume transport though cell faces (velocity times area). |
| 329 |
% Used for zonal mean volume transport (4). |
| 330 |
% utz1/vtz1 = Eulerian sensible heat transport through cell faces (1). |
| 331 |
% utz2/vtz2 = Sensible heat transport through cell faces by Eulerian |
| 332 |
% mean circulations (2). |
| 333 |
% dtx1/dty1 = Temperatude gradient at cell face times the area (when |
| 334 |
% multiplied by the diffusion will be the horizontal |
| 335 |
% diffusion heat transport) (6). |
| 336 |
uz = ArW.*hu(:,:,it); |
| 337 |
vz = ArS.*hv(:,:,it); |
| 338 |
utz1 = sum(ArW.*ut(:,:,it),2); |
| 339 |
vtz1 = sum(ArS.*vt(:,:,it),2); |
| 340 |
utz2 = sum(ArW.*hu(:,:,it).*tbi(:,:,it),2); |
| 341 |
vtz2 = sum(ArS.*hv(:,:,it).*tbj(:,:,it),2); |
| 342 |
if isequal(flu,'A') |
| 343 |
uqz1 = sum(ArW.*uq(:,:,it),2); |
| 344 |
vqz1 = sum(ArS.*vq(:,:,it),2); |
| 345 |
uqz2 = sum(ArW.*hu(:,:,it).*qbi(:,:,it),2); |
| 346 |
vqz2 = sum(ArS.*hv(:,:,it).*qbj(:,:,it),2); |
| 347 |
upz1 = sum(ArW.*(up(:,:,it)+hu(:,:,it).*repmat(PHIref,[6*nc*nc 1])),2); |
| 348 |
vpz1 = sum(ArS.*(vp(:,:,it)+hv(:,:,it).*repmat(PHIref,[6*nc*nc 1])),2); |
| 349 |
upz2 = sum(ArW.*hu(:,:,it).*(pbi(:,:,it)+repmat(PHIref,[6*nc*nc 1])),2); |
| 350 |
vpz2 = sum(ArS.*hv(:,:,it).*(pbj(:,:,it)+repmat(PHIref,[6*nc*nc 1])),2); |
| 351 |
end |
| 352 |
if isequal(flu,'O') |
| 353 |
dtx1 = sum(ArW_Dif.*Kux(:,:,it).*tdi(:,:,it),2); |
| 354 |
dty1 = sum(ArS_Dif.*Kvy(:,:,it).*tdj(:,:,it),2); |
| 355 |
Kuztz1 = sum(UbTgm(:,:,it),2); |
| 356 |
Kvztz1 = sum(VbTgm(:,:,it),2); |
| 357 |
end |
| 358 |
|
| 359 |
% Preparation for calculation of zonal average temperature. The |
| 360 |
% temperature multiplied by the appropriate length scale ("tbi_temp", |
| 361 |
% "tbj_temp") is summed up ("IntT" in the next section) and |
| 362 |
% divided by the total length ("IntM", composed from summing "hw_temp", |
| 363 |
% "hs_temp"). |
| 364 |
tbi_temp = hw_temp.*tbi(:,:,it); |
| 365 |
tbj_temp = hs_temp.*tbj(:,:,it); |
| 366 |
if isequal(flu,'A') |
| 367 |
qbi_temp = hw_temp.*qbi(:,:,it); |
| 368 |
qbj_temp = hs_temp.*qbj(:,:,it); |
| 369 |
pbi_temp = hw_temp.*(pbi(:,:,it)+repmat(PHIref,[6*nc*nc 1])); |
| 370 |
pbj_temp = hs_temp.*(pbj(:,:,it)+repmat(PHIref,[6*nc*nc 1])); |
| 371 |
end |
| 372 |
|
| 373 |
% Block 1: |
| 374 |
% With the vertical integral of heat transport calculated across cell |
| 375 |
% edges, the zonal integral (along the broken line) is computed to |
| 376 |
% determine the total northward heat transport. The first for loop is |
| 377 |
% over the individual broken lines (determining the northward HT at the |
| 378 |
% representative latitude). The second loop is over the basins. The |
| 379 |
% third loop is over the individual edges along a specific broken line. |
| 380 |
% Note that an individual cell can have two edges (u and v) that have a |
| 381 |
% HT contributions. Hence the variable containing indecies of cells |
| 382 |
% with edges along the broken lines, "bkl_IJuv", has some repeats. |
| 383 |
% Note that the variable "bkl_Npts" is the number of edges along a |
| 384 |
% given broken line. Note also that the latitude axis starts at 2 |
| 385 |
% because heat transport at extremes (latitute = -90/90) is zero by |
| 386 |
% definition. Recall that index (1) is total Eulerian transport, (2) |
| 387 |
% is from the mean circulations, and (3) is from the horizontal |
| 388 |
% diffusion. |
| 389 |
% |
| 390 |
% Block 2: |
| 391 |
% Here zonal average circulation and temperature / moisture is |
| 392 |
% calculated. The zonal average volume transport v (IntV) and t |
| 393 |
% (IntT/IntM) are computed first and are multiplied together at the |
| 394 |
% end. |
| 395 |
for jl=1:ydim |
| 396 |
ie=bkl_Npts(jl); |
| 397 |
for b=1:1+nBas |
| 398 |
ij=bkl_IJuv(1:ie,jl); |
| 399 |
% Block 1: |
| 400 |
SenHT(1+jl,b,it,1) = SenHT(1+jl,b,it,1) + ... |
| 401 |
sum(ufac(1:ie,jl,b).*utz1(ij) + ... |
| 402 |
vfac(1:ie,jl,b).*vtz1(ij)); |
| 403 |
SenHT(1+jl,b,it,2) = SenHT(1+jl,b,it,2) + ... |
| 404 |
sum(ufac(1:ie,jl,b).*utz2(ij) + ... |
| 405 |
vfac(1:ie,jl,b).*vtz2(ij)); |
| 406 |
if isequal(flu,'A') |
| 407 |
LatHT(1+jl,b,it,1) = LatHT(1+jl,b,it,1) + ... |
| 408 |
sum(ufac(1:ie,jl,b).*uqz1(ij) + ... |
| 409 |
vfac(1:ie,jl,b).*vqz1(ij)); |
| 410 |
LatHT(1+jl,b,it,2) = LatHT(1+jl,b,it,2) + ... |
| 411 |
sum(ufac(1:ie,jl,b).*uqz2(ij) + ... |
| 412 |
vfac(1:ie,jl,b).*vqz2(ij)); |
| 413 |
PotHT(1+jl,b,it,1) = PotHT(1+jl,b,it,1) + ... |
| 414 |
sum(ufac(1:ie,jl,b).*upz1(ij) + ... |
| 415 |
vfac(1:ie,jl,b).*vpz1(ij)); |
| 416 |
PotHT(1+jl,b,it,2) = PotHT(1+jl,b,it,2) + ... |
| 417 |
sum(ufac(1:ie,jl,b).*upz2(ij) + ... |
| 418 |
vfac(1:ie,jl,b).*vpz2(ij)); |
| 419 |
end |
| 420 |
if isequal(flu,'O') |
| 421 |
SenHT(1+jl,b,it,6) = SenHT(1+jl,b,it,6) + ... |
| 422 |
sum(ufac(1:ie,jl,b).*dtx1(ij) + ... |
| 423 |
vfac(1:ie,jl,b).*dty1(ij)); |
| 424 |
SenHT(1+jl,b,it,7) = SenHT(1+jl,b,it,7) + ... |
| 425 |
sum(ufac(1:ie,jl,b).*Kuztz1(ij) + ... |
| 426 |
vfac(1:ie,jl,b).*Kvztz1(ij)); |
| 427 |
end |
| 428 |
% Block 2: |
| 429 |
IntV(jl,:,b,it) = IntV(jl,:,b,it) ... |
| 430 |
+ ufac(1:ie,jl,b)'*uz(ij,:) ... |
| 431 |
+ vfac(1:ie,jl,b)'*vz(ij,:) ; |
| 432 |
IntT(jl,:,b,it) = IntT(jl,:,b,it) ... |
| 433 |
+ ufacabs(1:ie,jl,b)'*tbi_temp(ij,:) ... |
| 434 |
+ vfacabs(1:ie,jl,b)'*tbj_temp(ij,:); |
| 435 |
IntM(jl,:,b,it) = IntM(jl,:,b,it) ... |
| 436 |
+ ufacabs(1:ie,jl,b)'*hw_temp(ij,:) ... |
| 437 |
+ vfacabs(1:ie,jl,b)'*hs_temp(ij,:); |
| 438 |
if isequal(flu,'A') |
| 439 |
IntQ(jl,:,b,it) = IntQ(jl,:,b,it) ... |
| 440 |
+ ufacabs(1:ie,jl,b)'*qbi_temp(ij,:) ... |
| 441 |
+ vfacabs(1:ie,jl,b)'*qbj_temp(ij,:); |
| 442 |
IntP(jl,:,b,it) = IntP(jl,:,b,it) ... |
| 443 |
+ ufacabs(1:ie,jl,b)'*pbi_temp(ij,:) ... |
| 444 |
+ vfacabs(1:ie,jl,b)'*pbj_temp(ij,:); |
| 445 |
end |
| 446 |
end |
| 447 |
end |
| 448 |
|
| 449 |
% Prepare HT output: Calculate HT by zonal flows and tabulate |
| 450 |
% residuals. Also, the multiplicative constants (Cp,rho,grav,LhVap) are |
| 451 |
% applied here to put moisture and potential temperature fluxes in |
| 452 |
% terms of heat transports. |
| 453 |
tmp = IntV(:,:,:,it) .* IntT(:,:,:,it) ... |
| 454 |
./ change(IntM(:,:,:,it),'==',0,NaN); |
| 455 |
SenHT(2:ydim+1,:,it,4) = sum(change(tmp,'==',NaN,0),2); |
| 456 |
% SenHT(2:ydim+1,:,it,4) = sum( IntV(:,:,:,it) ... |
| 457 |
% .* IntT(:,:,:,it) ... |
| 458 |
% ./ IntM(:,:,:,it),2); |
| 459 |
SenHT(:,:,it,3) = SenHT(:,:,it,1) - SenHT(:,:,it,2); |
| 460 |
SenHT(:,:,it,5) = SenHT(:,:,it,2) - SenHT(:,:,it,4); |
| 461 |
if isequal(flu,'O') |
| 462 |
% SenHT(:,:,it,6) = DiffKh.*SenHT(:,:,it,6); |
| 463 |
SenHT(:,:,it,:) = (CpO*RhoO)*SenHT(:,:,it,:); |
| 464 |
if flag_tot==1 |
| 465 |
SenHT(:,:,it,7)=SenHT(:,:,it,7)-SenHT(:,:,it,1); |
| 466 |
end |
| 467 |
elseif isequal(flu,'A') |
| 468 |
SenHT(:,:,it,:) = (CpA./grav).*SenHT(:,:,it,:); |
| 469 |
LatHT(2:ydim+1,:,it,4) = sum( IntV(:,:,:,it) ... |
| 470 |
.* IntQ(:,:,:,it) ... |
| 471 |
./ IntM(:,:,:,it),2); |
| 472 |
LatHT(:,:,it,3) = LatHT(:,:,it,1) - LatHT(:,:,it,2); |
| 473 |
LatHT(:,:,it,5) = LatHT(:,:,it,2) - LatHT(:,:,it,4); |
| 474 |
LatHT(:,:,it,:) = (LhVap./grav).*LatHT(:,:,it,:); |
| 475 |
|
| 476 |
PotHT(2:ydim+1,:,it,4) = sum( IntV(:,:,:,it) ... |
| 477 |
.* IntP(:,:,:,it) ... |
| 478 |
./ IntM(:,:,:,it),2); |
| 479 |
PotHT(:,:,it,3) = PotHT(:,:,it,1) - PotHT(:,:,it,2); |
| 480 |
PotHT(:,:,it,5) = PotHT(:,:,it,2) - PotHT(:,:,it,4); |
| 481 |
PotHT(:,:,it,:) = 1/grav * PotHT(:,:,it,:); |
| 482 |
end |
| 483 |
Psi(2:ydim+1,:,it) = - sum( IntV(:,:,:,it) ,2 ); |
| 484 |
|
| 485 |
% Implied surface heat flux from heat transports (implied heating). |
| 486 |
% Tabulated as the difference in heat transports between two broken |
| 487 |
% lines divided by the zonal band area. |
| 488 |
mskG = reshape(mskG,(ydim+1)*(1+nBas),1); |
| 489 |
I = find(mskG==0); |
| 490 |
mskG = reshape(mskG,ydim+1,1+nBas); |
| 491 |
var = zeros(ydim+1,1+nBas); |
| 492 |
for n=1:min(nout,6), |
| 493 |
varT = SenHT([2:ydim+2],:,it,n) ... |
| 494 |
- SenHT([1:ydim+1],:,it,n); |
| 495 |
varT = reshape(varT,(ydim+1)*(1+nBas),1); varT(I)=NaN; |
| 496 |
varT = reshape(varT,ydim+1,1+nBas); |
| 497 |
SenSF([1:ydim+1],:,it,n) = varT./AreaZon; |
| 498 |
if isequal(flu,'A') |
| 499 |
for n=1:min(nout,6) |
| 500 |
varQ = LatHT([2:ydim+2],:,it,n) ... |
| 501 |
- LatHT([1:ydim+1],:,it,n); |
| 502 |
varQ = reshape(varQ,(ydim+1)*(1+nBas),1); varQ(I)=NaN; |
| 503 |
varQ = reshape(varQ,ydim+1,1+nBas); |
| 504 |
LatSF([1:ydim+1],:,it,n) = varQ./AreaZon; |
| 505 |
end |
| 506 |
end |
| 507 |
end |
| 508 |
end |
| 509 |
|
| 510 |
|
| 511 |
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| 512 |
% Assign outputs, put in units of PW % |
| 513 |
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| 514 |
|
| 515 |
SenHT = SenHT*1e-15; |
| 516 |
|
| 517 |
HT.time = i_time; |
| 518 |
HT.SenHT = reshape(SenHT,ydim+2,nt,nout); |
| 519 |
HT.SenSF = reshape(SenSF,ydim+1,nt,nout); |
| 520 |
HT.ylatHT = ylatHT; |
| 521 |
HT.ylatSF = ylatSF; |
| 522 |
HT.AreaZon = AreaZon; |
| 523 |
HT.PsiSurf = 1e-6*reshape(Psi,ydim+2,nt); |
| 524 |
|
| 525 |
if isequal(flu,'A') |
| 526 |
LatHT = LatHT*1e-15; |
| 527 |
PotHT = PotHT*1e-15; |
| 528 |
HT.LatHT = reshape(LatHT,ydim+2,nt,nout); |
| 529 |
HT.LatSF = reshape(LatSF,ydim+1,nt,nout); |
| 530 |
HT.PotHT = reshape(PotHT,ydim+2,nt,nout); |
| 531 |
end |
| 532 |
|
| 533 |
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| 534 |
% Stuff that might need to be added back in later (for basins) % |
| 535 |
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| 536 |
|
| 537 |
% Block for constructing mskG for different basins: |
| 538 |
% if nBas > 0, |
| 539 |
% mskBc=rdda([Rac,'maskC_bas.bin'],[6*nc*nc 3],1,'real*4','b'); |
| 540 |
% mskBw=rdda([Rac,'maskW_bas.bin'],[6*nc*nc 3],1,'real*4','b'); |
| 541 |
% mskBs=rdda([Rac,'maskS_bas.bin'],[6*nc*nc 3],1,'real*4','b'); |
| 542 |
% if nBas==2, |
| 543 |
% mskBc(:,2)=mskBc(:,2)+mskBc(:,3); |
| 544 |
% mskBw(:,2)=mskBw(:,2)+mskBw(:,3); |
| 545 |
% mskBs(:,2)=mskBs(:,2)+mskBs(:,3); |
| 546 |
% mskBc=min(1,mskBc); mskBw=min(1,mskBw); mskBs=min(1,mskBs); |
| 547 |
% end |
| 548 |
% %- load: np_Sep, ij_Sep, tp_Sep: |
| 549 |
% sep_lineF=[Rac,'sepBas_cs32_60']; |
| 550 |
% load(sep_lineF); |
| 551 |
% fprintf([' + bassin mask & Sep.line:',sep_lineF,' \n']); |
| 552 |
% %- compute mask for each bassin: ----------- |
| 553 |
% kMsep=1; |
| 554 |
% if kMsep, |
| 555 |
% mskW=1+min(1,ceil(hw(:,1))); |
| 556 |
% mskS=1+min(1,ceil(hs(:,1))); |
| 557 |
% for b=1:nBas, |
| 558 |
% bs=b; b1=1+bs; b2=2+rem(bs,nBas); |
| 559 |
% if nBas == 2, bs=b+b-1; b1=2; b2=3 ; end |
| 560 |
% for j=1:ydim+1, |
| 561 |
% for i=1:np_Sep(bs,j), |
| 562 |
% ij=ij_Sep(bs,j,i); typ=abs(tp_Sep(bs,j,i)); |
| 563 |
% if typ == 1, |
| 564 |
% mskG(j,b1)=mskG(j,b1)*mskW(ij); |
| 565 |
% mskG(j,b2)=mskG(j,b2)*mskW(ij); |
| 566 |
% elseif typ == 2, |
| 567 |
% mskG(j,b1)=mskG(j,b1)*mskS(ij); |
| 568 |
% mskG(j,b2)=mskG(j,b2)*mskS(ij); |
| 569 |
% end |
| 570 |
% end |
| 571 |
% end |
| 572 |
% end |
| 573 |
% mskG=2-min(2,mskG); |
| 574 |
% end |
| 575 |
% end |
| 576 |
|
| 577 |
% % Masking for different basin. |
| 578 |
% mskZ = ones(ydim+2,1+nBas); |
| 579 |
% mskZ([2:ydim+1],:) = mskG([1:ydim],:) + mskG([2:ydim+1],:); |
| 580 |
% mskZ = reshape(min(mskZ,1),(ydim+2)*(1+nBas),1); |
| 581 |
% I = find(mskZ == 0); |
| 582 |
% mskZ = reshape(mskZ,(ydim+2),1+nBas); |
| 583 |
% for n=1:min(6,nout) |
| 584 |
% var=SenHT(:,:,n); |
| 585 |
% var=reshape(var,(ydim+2)*(1+nBas),1); var(I) = NaN; |
| 586 |
% var=reshape(var,(ydim+2),1+nBas); SenHT(:,:,n) = var; |
| 587 |
% end |
| 588 |
|
| 589 |
% Makes sure that there are no zeros in area AreaZon: |
| 590 |
% - set AreaZon to 1 if = zero |
| 591 |
% AreaZon=reshape(AreaZon,(ydim+1)*(1+nBas),1); |
| 592 |
% [I]=find(AreaZon==0); AreaZon(I)=1; |
| 593 |
% AreaZon=reshape(AreaZon,ydim+1,1+nBas); |