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