REMORA
Regional Modeling of Oceans Refined Adaptively
Loading...
Searching...
No Matches
REMORA_bulk_flux.cpp
Go to the documentation of this file.
1#include <REMORA.H>
2
3using namespace amrex;
4
5/**
6 * @param[in ] lev level to operate on
7 * @param[in ] mf_cons scalar data: temperature, salinity, passsive scalar, etc
8 * @param[in ] mf_uwind u-direction wind dvelocity
9 * @param[in ] mf_vwind v-direction wind dvelocity
10 * @param[in ] mf_Tair air temperature [°C]
11 * @param[in ] mf_qair specific humidity [kg/kg]
12 * @param[in ] mf_Pair air pressure [mb]
13 * @param[in ] mf_srflx shortwave radiation flux [W/m²]
14 * @param[inout] mf_evap evaporation rate
15 * @param[ out] mf_sustr u-direction surface momentum stress
16 * @param[ out] mf_svstr v-direction surface momentum stress
17 * @param[ out] mf_stflux surface scalar flux (temperature, salinity)
18 * @param[ out] mf_lrflx longwave radiation flux
19 * @param[inout] mf_lhflx latent heat flux
20 * @param[inout] mf_shflx sensible heat flux
21 * @param[in ] N number of vertical levels
22 */
23void
24REMORA::bulk_fluxes (int lev, MultiFab* mf_cons, MultiFab* mf_uwind, MultiFab* mf_vwind,
25 MultiFab* mf_Tair, MultiFab* mf_qair, MultiFab* mf_Pair,
26 MultiFab* mf_srflx,
27 MultiFab* mf_evap, MultiFab* mf_sustr, MultiFab* mf_svstr,
28 MultiFab* mf_stflux, MultiFab* mf_lrflx, MultiFab* mf_lhflx,
29 MultiFab* mf_shflx,
30 const int N)
31{
32 BL_PROFILE("REMORA::bulk_fluxes()");
33 const int IterMax = 3;
34 const BoxArray& ba = mf_cons->boxArray();
35 const DistributionMapping& dm = mf_cons->DistributionMap();
36 MultiFab mf_Taux(ba, dm, 1, IntVect(NGROW,NGROW,0));
37 MultiFab mf_Tauy(ba, dm, 1, IntVect(NGROW,NGROW,0));
38
39 // temps: Taux, Tauy,
40 for ( MFIter mfi(*mf_cons, TilingIfNotGPU()); mfi.isValid(); ++mfi) {
41 Array4<Real const> const& uwind = mf_uwind->const_array(mfi);
42 Array4<Real const> const& vwind = mf_vwind->const_array(mfi);
43 Array4<Real const> const& Tair_arr = mf_Tair->const_array(mfi);
44 Array4<Real const> const& qair_arr = mf_qair->const_array(mfi);
45 Array4<Real const> const& Pair_arr = mf_Pair->const_array(mfi);
46 Array4<Real const> const& srflx_arr = mf_srflx->const_array(mfi);
47 Array4<Real const> const& cons = mf_cons->const_array(mfi);
48 Array4<Real> const& sustr = mf_sustr->array(mfi);
49 Array4<Real> const& svstr = mf_svstr->array(mfi);
50 Array4<Real> const& stflux = mf_stflux->array(mfi);
51 Array4<Real> const& lrflx = mf_lrflx->array(mfi);
52 Array4<Real> const& lhflx = mf_lhflx->array(mfi);
53 Array4<Real> const& shflx = mf_shflx->array(mfi);
54 Array4<Real> const& evap = mf_evap->array(mfi);
55 Array4<Real> const& Taux = mf_Taux.array(mfi);
56 Array4<Real> const& Tauy = mf_Tauy.array(mfi);
57
58 Array4<const Real> const& mskr = vec_mskr[lev]->const_array(mfi);
59 Array4<const Real> const& msku = vec_msku[lev]->const_array(mfi);
60 Array4<const Real> const& mskv = vec_mskv[lev]->const_array(mfi);
61 Array4<const Real> const& rain = vec_rain[lev]->const_array(mfi);
62 Array4<const Real> const& cloud_arr = vec_cloud[lev]->const_array(mfi);
63
64 Real Hscale = solverChoice.rho0 * Cp;
65 Real Hscale2 = 1.0_rt / (solverChoice.rho0 * Cp);
66 Real blk_ZQ = solverChoice.blk_ZQ;
67 Real blk_ZT = solverChoice.blk_ZT;
68 Real blk_ZW = solverChoice.blk_ZW;
69
70 Real eps = 1e-20_rt;
71
72 Box bx = mfi.tilebox();
73 Box ubx = mfi.grownnodaltilebox(0,IntVect(NGROW-1,NGROW-1,0));
74 Box vbx = mfi.grownnodaltilebox(1,IntVect(NGROW-1,NGROW-1,0));
75 Box gbx1 = bx; gbx1.grow(IntVect(NGROW,NGROW,0));
76
77 ParallelFor(makeSlab(gbx1,2,0), [=] AMREX_GPU_DEVICE (int i, int j, int ) {
78 // Get spatially-varying atmospheric forcing from input arrays
79 Real PairM = Pair_arr(i,j,0); // Air pressure [mb]
80 Real TairC = Tair_arr(i,j,0); // Air temperature [°C]
81 Real TairK = TairC + 273.16_rt; // Air temperature [K]
82 Real Hair = qair_arr(i,j,0); // Specific humidity [kg/kg] or RH [fraction]
83 Real srflux = srflx_arr(i,j,0); // Shortwave radiation flux [W/m²]
84 Real cloud = cloud_arr(i,j,0); // Cloud cover fraction [0-1]
85
86 // Input bulk parametrization fields
87 Real wind_mag = std::sqrt(uwind(i,j,0)*uwind(i,j,0) + vwind(i,j,0) * vwind(i,j,0)) + eps;
88 Real TseaK = cons(i,j,N,Temp_comp) + 273.16_rt;
89
90 // Initialize
91 Real delTc = 0.0_rt;
92 Real delQc = 0.0_rt;
93
94 Real LHeat = lhflx(i,j,0) * Hscale;
95 Real SHeat = shflx(i,j,0) * Hscale;
96 Real Taur = 0.0_rt;
97 Taux(i,j,0) = 0.0_rt;
98 Tauy(i,j,0) = 0.0_rt;
99
100
101 /*-----------------------------------------------------------------------
102 Compute net longwave radiation (W/m2), LRad.
103 -----------------------------------------------------------------------
104
105 Use Berliand (1952) formula to calculate net longwave radiation.
106 The equation for saturation vapor pressure is from Gill (Atmosphere-
107 Ocean Dynamics, pp 606). Here the coefficient in the cloud term
108 is assumed constant, but it is a function of latitude varying from
109 1.0 at poles to 0.5 at the Equator).
110
111 */
112 Real RH = Hair;
113 Real cff=(0.7859_rt+0.03477_rt*TairC)/(1.0_rt+0.00412_rt*TairC);
114 Real e_sat=std::pow(10.0_rt,cff); // saturation vapor pressure (hPa or mbar)
115 Real vap_p=e_sat*RH; // water vapor pressure (hPa or mbar)
116 Real cff2=TairK*TairK*TairK;
117 Real cff1=cff2*TairK;
118 Real LRad=-emmiss*StefBo*
119 (cff1*(0.39_rt-0.05_rt*std::sqrt(vap_p))*
120 (1.0_rt-0.6823_rt*cloud*cloud)+
121 cff2*4.0_rt*(TseaK-TairK));
122
123 /*
124 -----------------------------------------------------------------------
125 Compute specific humidities (kg/kg).
126
127 note that Qair is the saturation specific humidity at Tair
128 Q is the actual specific humidity
129 Qsea is the saturation specific humidity at Tsea
130
131 Saturation vapor pressure in mb is first computed and then
132 converted to specific humidity in kg/kg
133
134 The saturation vapor pressure is computed from Teten formula
135 using the approach of Buck (1981):
136
137 Esat(mb) = (1.0007_rt+3.46E-6_rt*PairM(mb))*6.1121_rt*
138 EXP(17.502_rt*TairC(C)/(240.97_rt+TairC(C)))
139
140 The ambient vapor is found from the definition of the
141 Relative humidity:
142
143 RH = W/Ws*100 ~ E/Esat*100 E = RH/100*Esat if RH is in %
144 E = RH*Esat if RH fractional
145
146 The specific humidity is then found using the relationship:
147
148 Q = 0.622 E/(P + (0.622-1)e)
149
150 Q(kg/kg) = 0.62197_rt*(E(mb)/(PairM(mb)-0.378_rt*E(mb)))
151
152 -----------------------------------------------------------------------
153 */
154
155 // Compute air saturation vapor pressure (mb), using Teten formula.
156
157 Real cff_saturation_air=(1.0007_rt+3.46E-6_rt*PairM)*6.1121_rt*
158 std::exp(17.502_rt*TairC/(240.97_rt+TairC));
159
160 // Compute specific humidity at Saturation, Qair (kg/kg).
161
162 Real Qair = 0.62197_rt*(cff_saturation_air/(PairM-0.378_rt*cff_saturation_air+eps));
163
164 // Compute specific humidity, Q (kg/kg).
165 Real Q;
166 if (RH < 2.0) {
167 Real cff_Q = cff_saturation_air*RH; //Vapor pressure (mb)
168 Q=0.62197_rt*(cff_Q/(PairM-0.378_rt*cff_Q+eps)); //Spec hum (kg/kg)
169 } else { // RH input was actually specific humidity in g/kg
170 Q=RH/1000.0_rt; //!Spec Hum (kg/kg)
171 }
172
173 // Compute water saturation vapor pressure (mb), using Teten formula.
174
175 Real cff_saturation_water=(1.0007_rt+3.46E-6_rt*PairM)*6.1121_rt*
176 std::exp(17.502_rt*cons(i,j,N,Temp_comp)/(240.97_rt+cons(i,j,N,Temp_comp)));
177
178 // Compute water saturation vapor pressure (mb), using Teten formula.
179 // Vapor Pressure reduced for salinity (Kraus and Businger, 1994, pp42).
180 Real cff_vp=cff_saturation_water*0.98_rt;
181
182 // Compute Qsea (kg/kg) from vapor pressure.
183
184 Real Qsea=0.62197_rt*(cff_vp/(PairM-0.378_rt*cff_vp));
185 //
186 // -----------------------------------------------------------------------
187 // Compute Monin-Obukhov similarity parameters for wind (Wstar),
188 // heat (Tstar), and moisture (Qstar), Liu et al. (1979).
189 // -----------------------------------------------------------------------
190 //
191 // Moist air density (kg/m3).
192
193 Real rhoAir=PairM*100.0_rt/(blk_Rgas*TairK*(1.0_rt+0.61_rt*Q));
194
195 // Kinematic viscosity of dry air (m2/s), Andreas (1989).
196
197 Real VisAir=1.326E-5_rt*(1.0_rt+TairC*(6.542E-3_rt+TairC*
198 (8.301E-6_rt-4.84E-9_rt*TairC)));
199\
200 // Compute latent heat of vaporization (J/kg) at sea surface, Hlv.
201
202 Real Hlv = (2.501_rt-0.00237_rt*cons(i,j,N,Temp_comp))*1.0e6_rt;
203
204 // Assume that wind is measured relative to sea surface and include
205 // gustiness.
206
207 Real Wgus=0.5_rt;
208 Real delW=std::sqrt(wind_mag*wind_mag+Wgus*Wgus);
209 Real delQ=Qsea-Q;
210 Real delT=cons(i,j,N,Temp_comp)-TairC;
211
212 // Neutral coefficients.
213 Real ZoW=0.0001_rt;
214 Real u10=delW*std::log(10.0_rt/ZoW)/std::log(blk_ZW/ZoW);
215 Real Wstar=0.035_rt * u10;
216 Real Zo10=0.011_rt*Wstar*Wstar/g+0.11_rt*VisAir/Wstar;
217 Real Cd10 =(vonKar/std::log(10.0_rt/Zo10));
218 Cd10 = Cd10 * Cd10;
219 Real Ch10 =0.00115_rt;
220 Real Ct10 = Ch10/std::sqrt(Cd10);
221 Real ZoT10=10.0_rt/std::exp(vonKar/Ct10);
222 Real Cd=(vonKar/std::log(blk_ZW/Zo10));
223 Cd = Cd * Cd;
224
225 // Compute Richardson number.
226 Real Ct=vonKar/std::log(blk_ZT/ZoT10); // T transfer coefficient
227 Real CC=vonKar*Ct/Cd;
228
229 Real Ribcu = -blk_ZW/(blk_Zabl*0.004_rt*blk_beta*blk_beta*blk_beta);
230 Real Ri = -g*blk_ZW*((delT-delTc)+0.61_rt*TairK*delQ)/
231 (TairK*delW*delW+eps);
232 Real Zetu;
233 if (Ri < 0.0) {
234 Zetu=CC*Ri/(1.0_rt+Ri/Ribcu); // Unstable
235 } else {
236 Zetu=CC*Ri/(1.0_rt+3.0_rt*Ri/CC); // Stable
237 }
238 Real L10 = blk_ZW/Zetu;
239
240 // First guesses for Monon-Obukhov similarity scales.
241 Wstar=delW*vonKar/(std::log(blk_ZW/Zo10)-
242 bulk_psiu(blk_ZW/L10));
243 Real Tstar=-(delT-delTc)*vonKar/(std::log(blk_ZT/ZoT10)-
244 bulk_psit(blk_ZT/L10));
245 Real Qstar=-(delQ-delQc)*vonKar/(std::log(blk_ZQ/ZoT10)-
246 bulk_psit(blk_ZQ/L10));
247
248 // Modify Charnock for high wind speeds. The 0.125 factor below is for
249 // 1.0/(18.0-10.0).
250
251 Real charn;
252 if (delW > 18.0_rt) {
253 charn=0.018_rt;
254 } else if ((10.0_rt < delW) and (delW <= 18.0_rt)) {
255 charn=0.011_rt+0.125_rt*(0.018_rt-0.011_rt)*(delW-10.0_rt);
256 } else {
257 charn=0.011_rt;
258 }
259
260 // Iterate until convergence. It usually converges within 3 iterations.
261 for (int it=0; it<IterMax; it++) {
262 ZoW=charn*Wstar*Wstar/g+0.11_rt*VisAir/(Wstar+eps);
263 Real Rr=ZoW*Wstar/VisAir;
264 // Compute Monin-Obukhov stability parameter, Z/L.
265 Real ZoQ=std::min(1.15e-4_rt,5.5e-5_rt/std::pow(Rr,0.6_rt));
266 Real ZoT=ZoQ;
267 Real ZoL=vonKar*g*blk_ZW*(Tstar*(1.0_rt+0.61_rt*Q)+
268 0.61_rt*TairK*Qstar)/
269 (TairK*Wstar*Wstar*(1.0_rt+0.61_rt*Q)+eps);
270 Real L=blk_ZW/(ZoL+eps);
271
272 // Evaluate stability functions at Z/L.
273 Real Wpsi=bulk_psiu(ZoL);
274 Real Tpsi=bulk_psit(blk_ZT/L);
275 Real Qpsi=bulk_psit(blk_ZQ/L);
276
277 // Compute wind scaling parameters, Wstar.
278 Wstar=std::max(eps,delW*vonKar/(std::log(blk_ZW/ZoW)-Wpsi));
279 Tstar=-(delT-delTc)*vonKar/(std::log(blk_ZT/ZoT)-Tpsi);
280 Qstar=-(delQ-delQc)*vonKar/(std::log(blk_ZQ/ZoQ)-Qpsi);
281
282 // Compute gustiness in wind speed.
283 Real Bf=-g/TairK*Wstar*(Tstar+0.61_rt*TairK*Qstar);
284 if (Bf>0.0_rt) {
285 Wgus=blk_beta*std::pow(Bf*blk_Zabl,1.0_rt/3.0_rt);
286 } else {
287 Wgus=0.2_rt;
288 }
289 delW=std::sqrt(wind_mag*wind_mag+Wgus*Wgus);
290 }
291
292 // Compute transfer coefficients for momentum (Cd).
293 Real Wspeed=std::sqrt(wind_mag*wind_mag+Wgus*Wgus);
294 Cd=Wstar*Wstar/(Wspeed*Wspeed+eps);
295
296 // Compute turbulent sensible heat flux (W/m2), Hs.
297 Real Hs=-blk_Cpa*rhoAir*Wstar*Tstar;
298
299 // Compute sensible heat flux (W/m2) due to rainfall (kg/m2/s), Hsr.
300 Real diffw=2.11E-5_rt*std::pow(TairK/273.16_rt,1.94_rt);
301 Real diffh=0.02411_rt*(1.0_rt+TairC*
302 (3.309E-3_rt-1.44E-6_rt*TairC))/
303 (rhoAir*blk_Cpa+eps);
304 cff=Qair*Hlv/(blk_Rgas*TairK*TairK);
305 Real wet_bulb=1.0_rt/(1.0_rt+0.622_rt*(cff*Hlv*diffw)/
306 (blk_Cpa*diffh));
307 Real Hsr=rain(i,j,0)*wet_bulb*blk_Cpw*
308 ((cons(i,j,N,Temp_comp)-TairC)+(Qsea-Q)*Hlv/blk_Cpa);
309 SHeat=(Hs+Hsr) * mskr(i,j,0);
310
311 // Compute turbulent latent heat flux (W/m2), Hl.
312
313 Real Hl=-Hlv*rhoAir*Wstar*Qstar;
314
315 // Compute Webb correction (Webb effect) to latent heat flux, Hlw.
316 Real upvel=-1.61_rt*Wstar*Qstar-
317 (1.0_rt+1.61_rt*Q)*Wstar*Tstar/TairK;
318 Real Hlw=rhoAir*Hlv*upvel*Q;
319 LHeat=(Hl+Hlw) * mskr(i,j,0);
320
321 // Compute momentum flux (N/m2) due to rainfall (kg/m2/s).
322 Taur=0.85_rt*rain(i,j,0)*wind_mag;
323
324 // Compute wind stress components (N/m2), Tau.
325 cff=rhoAir*Cd*Wspeed;
326 // amrex::Print() << "rhoAir: " << rhoAir << " Cd: " << Cd << " Wspeed: " << Wspeed << " cff: " << cff << "\n";
327 Real sign_u = (uwind(i,j,0) >= 0.0_rt) ? 1 : -1;
328 Real sign_v = (vwind(i,j,0) >= 0.0_rt) ? 1 : -1;
329 Taux(i,j,0)=(cff*uwind(i,j,0)+Taur*sign_u) * mskr(i,j,0);
330 Tauy(i,j,0)=(cff*vwind(i,j,0)+Taur*sign_v) * mskr(i,j,0);
331 // amrex::Print() << "Taux: " << Taux(i,j,0) << " Tauy: " << Tauy(i,j,0) << "\n";
332
333 //=======================================================================
334 // Compute surface net heat flux and surface wind stress.
335 //=======================================================================
336 //
337 // Compute kinematic, surface, net heat flux (degC m/s). Notice that
338 // the signs of latent and sensible fluxes are reversed because fluxes
339 // calculated from the bulk formulations above are positive out of the
340 // ocean.
341 //
342 // For EMINUSP option, EVAP = LHeat (W/m2) / Hlv (J/kg) = kg/m2/s
343 // PREC = rain = kg/m2/s
344 //
345 // To convert these rates to m/s divide by freshwater density, rhow.
346 //
347 // Note that when the air is undersaturated in water vapor (Q < Qsea)
348 // the model will evaporate and LHeat > 0:
349 //
350 // LHeat positive out of the ocean
351 // evap positive out of the ocean
352 //
353 // Note that if evaporating, the salt flux is positive
354 // and if raining, the salt flux is negative
355 //
356 // Note that stflux(:,:,isalt) is the E-P flux. The fresh water flux
357 // is positive out of the ocean and the salt flux is positive into the
358 // ocean. It is multiplied by surface salinity when computing state
359 // variable stflx(:,:,isalt) in "set_vbc.F".
360
361// Real one_over_rhow=1.0_rt/rhow;
362 lrflx(i,j,0) = LRad*Hscale2;
363 lhflx(i,j,0) = -LHeat*Hscale2;
364 shflx(i,j,0) = -SHeat*Hscale2;
365 // Note: srflx from NetCDF is in W/m², convert to degC m/s by multiplying by Hscale2
366 stflux(i,j,0,Temp_comp)=(srflux*Hscale2 + lrflx(i,j,0) + lhflx(i,j,0) + shflx(i,j,0)) * mskr(i,j,0);
367 evap(i,j,0) = (LHeat / Hlv+eps) * mskr(i,j,0);
368 stflux(i,j,0,Salt_comp) = mskr(i,j,0) * (evap(i,j,0)-rain(i,j,0)) / rhow;
369 });
370
371 Real cff_rho = 0.5_rt / solverChoice.rho0;
372 ParallelFor(makeSlab(ubx,2,0), [=] AMREX_GPU_DEVICE (int i, int j, int ) {
373 sustr(i,j,0) = cff_rho*(Taux(i-1,j,0) + Taux(i,j,0)) * msku(i,j,0);
374 });
375 ParallelFor(makeSlab(vbx,2,0), [=] AMREX_GPU_DEVICE (int i, int j, int ) {
376 svstr(i,j,0) = cff_rho*(Tauy(i,j-1,0) + Tauy(i,j,0)) * mskv(i,j,0);
377 });
378 }
379
380}
constexpr amrex::Real blk_Zabl
constexpr amrex::Real g
constexpr amrex::Real vonKar
constexpr amrex::Real rhow
constexpr amrex::Real blk_Rgas
constexpr amrex::Real blk_beta
constexpr amrex::Real emmiss
constexpr amrex::Real Cp
constexpr amrex::Real StefBo
constexpr amrex::Real blk_Cpa
constexpr amrex::Real blk_Cpw
#define NGROW
#define Temp_comp
#define Salt_comp
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_mskr
land/sea mask at cell centers (2D)
Definition REMORA.H:387
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_rain
precipitation rate [kg/m^2/s]
Definition REMORA.H:345
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_msku
land/sea mask at x-faces (2D)
Definition REMORA.H:389
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_mskv
land/sea mask at y-faces (2D)
Definition REMORA.H:391
void bulk_fluxes(int lev, amrex::MultiFab *mf_cons, amrex::MultiFab *mf_uwind, amrex::MultiFab *mf_vwind, amrex::MultiFab *mf_Tair, amrex::MultiFab *mf_qair, amrex::MultiFab *mf_Pair, amrex::MultiFab *mf_srflx, amrex::MultiFab *mf_evap, amrex::MultiFab *mf_sustr, amrex::MultiFab *mf_svstr, amrex::MultiFab *mf_stflux, amrex::MultiFab *mf_lrflx, amrex::MultiFab *mf_lhflx, amrex::MultiFab *mf_shflx, const int N)
Calculate bulk temperature, salinity, wind fluxes.
AMREX_GPU_HOST_DEVICE static AMREX_FORCE_INLINE amrex::Real bulk_psiu(amrex::Real ZoL)
Evaluate stability function psi for wind speed.
Definition REMORA.H:1530
static SolverChoice solverChoice
Container for algorithmic choices.
Definition REMORA.H:1329
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_cloud
cloud cover fraction [0-1], defined at rho-points
Definition REMORA.H:349
AMREX_GPU_HOST_DEVICE static AMREX_FORCE_INLINE amrex::Real bulk_psit(amrex::Real ZoL)
Evaluate stability function psi for moisture and heat.
Definition REMORA.H:1562
amrex::Real blk_ZT
amrex::Real blk_ZW
amrex::Real blk_ZQ