def compute_richardson_number( t0, q0_vapor, qcon, pkz, delp, peln, gz, u0, v0, xvir, t_max, t_min ): tv1 = t0[0, 0, -1] * (1.0 + xvir * q0_vapor[0, 0, -1] - qcon[0, 0, -1]) tv2 = t0 * (1.0 + xvir * q0_vapor - qcon) pt1 = tv1 / pkz[0, 0, -1] pt2 = tv2 / pkz ri = ( (gz[0, 0, -1] - gz) * (pt1 - pt2) / ( 0.5 * (pt1 + pt2) * ((u0[0, 0, -1] - u0) ** 2 + (v0[0, 0, -1] - v0) ** 2 + USTAR2) ) ) if tv1 > t_max and tv1 > tv2: ri = 0 elif tv2 < t_min: ri = ri if ri < 0.1 else 0.1 ri_ref = ( RI_MIN + (RI_MAX - RI_MIN) * dim(400.0e2, delp / (peln[0, 0, 1] - peln)) / 200.0e2 ) if RI_MAX < ri_ref: ri_ref = RI_MAX return ri, ri_ref
def melt_snow( pt1, cvm, fac_smlt, qs, ql, qr, q_liq, q_sol, lhi, icp2, mc_air, qv, c_vap, qs_mlt ): dtmp = pt1 - (TICE + 0.1) tmp = 0.0 sink = 0.0 snowfac = 0.0 sinktmp = 0.0 dimqs = dim(qs_mlt, ql) if qs > 1e-7 and dtmp > 0.0: snowfac = (dtmp * 0.1) ** 2 tmp = ( qs if 1.0 < snowfac else snowfac * qs ) # no limiter on melting above 10 deg c sinktmp = fac_smlt * dtmp / icp2 sink = tmp if tmp < sinktmp else sinktmp tmp = sink if sink < dimqs else dimqs qs = qs - sink ql = ql + tmp qr = qr + sink - tmp q_liq = q_liq + sink q_sol = q_sol - sink cvm = compute_cvm(mc_air, qv, c_vap, q_liq, q_sol) pt1 = subtract_sink_pt1(pt1, sink, lhi, cvm) return qs, ql, qr, q_liq, q_sol, cvm, pt1
def m_loop( ri: sd, ri_ref: sd, pm: sd, u0: sd, v0: sd, w0: sd, t0: sd, hd: sd, gz: sd, qcon: sd, delp: sd, pkz: sd, q0_vapor: sd, pt1: sd, pt2: sd, tv2: sd, t_min: float, t_max: float, ratio: float, xvir: float, ): with computation(BACKWARD), interval( ...): # interval(1, None): -- from doing the full stencil tv1 = t0[0, 0, -1] * (1.0 + xvir * q0_vapor[0, 0, -1] - qcon[0, 0, -1]) tv2 = t0 * (1.0 + xvir * q0_vapor - qcon) pt1 = tv1 / pkz[0, 0, -1] pt2 = tv2 / pkz ri = ((gz[0, 0, -1] - gz) * (pt1 - pt2) / (0.5 * (pt1 + pt2) * ((u0[0, 0, -1] - u0)**2 + (v0[0, 0, -1] - v0)**2 + USTAR2))) if tv1 > t_max and tv1 > tv2: ri = 0 elif tv2 < t_min: ri = ri if ri < 0.1 else 0.1 # Adjustment for K-H instability: # Compute equivalent mass flux: mc # Add moist 2-dz instability consideration: ri_ref = RI_MIN + (RI_MAX - RI_MIN) * dim(400.0e2, pm) / 200.0e2 if RI_MAX < ri_ref: ri_ref = RI_MAX
def satadjust_part1( wqsat: sd, dq2dt: sd, dpln: sd, den: sd, pt1: sd, cvm: sd, mc_air: sd, peln: sd, qv: sd, ql: sd, q_liq: sd, qi: sd, qr: sd, qs: sd, q_sol: sd, qg: sd, pt: sd, dp: sd, delz: sd, te0: sd, qpz: sd, lhl: sd, lhi: sd, lcp2: sd, icp2: sd, tcp3: sd, zvir: float, hydrostatic: bool, consv_te: bool, c_air: float, c_vap: float, fac_imlt: float, d0_vap: float, lv00: float, fac_v2l: float, fac_l2v: float, ): with computation(FORWARD), interval(1, None): if hydrostatic: delz = delz[0, 0, -1] with computation(PARALLEL), interval(...): dpln = peln[0, 0, 1] - peln q_liq = ql + qr q_sol = qi + qs + qg qpz = q_liq + q_sol pt1 = pt / ((1.0 + zvir * qv) * (1.0 - qpz)) t0 = pt1 # true temperature qpz = qpz + qv # total_wat conserved in this routine # define air density based on hydrostatical property den = ( dp / (dpln * constants.RDGAS * pt) if hydrostatic else -dp / (constants.GRAV * delz) ) # define heat capacity and latend heat coefficient mc_air = (1.0 - qpz) * c_air cvm = compute_cvm(mc_air, qv, c_vap, q_liq, q_sol) lhi, icp2 = update_latent_heat_coefficient_i(pt1, cvm) # fix energy conservation if consv_te: if hydrostatic: te0 = -c_air * t0 else: te0 = -cvm * t0 # fix negative cloud ice with snow if qi < 0.0: qs = qs + qi qi = 0.0 # melting of cloud ice to cloud water and rain qi, ql, q_liq, q_sol, cvm, pt1 = melt_cloud_ice( qv, qi, ql, q_liq, q_sol, pt1, icp2, fac_imlt, mc_air, c_vap, lhi, cvm ) # update latend heat coefficient lhi, icp2 = update_latent_heat_coefficient_i(pt1, cvm) # fix negative snow with graupel or graupel with available snow qs, qg = fix_negative_snow(qs, qg) # after this point cloud ice & snow are positive definite # fix negative cloud water with rain or rain with available cloud water ql, qr = fix_negative_cloud_water(ql, qr) # enforce complete freezing of cloud water to cloud ice below - 48 c ql, qi, q_liq, q_sol, cvm, pt1 = complete_freezing( qv, ql, qi, q_liq, q_sol, pt1, cvm, icp2, mc_air, lhi, c_vap ) wqsat, dq2dt = wqs2_fn_w(pt1, den) # update latent heat coefficient lhl, lhi, lcp2, icp2 = update_latent_heat_coefficient(pt1, cvm, lv00, d0_vap) diff_ice = dim(TICE, pt1) / 48.0 dimmin = min(1.0, diff_ice) tcp3 = lcp2 + icp2 * dimmin dq0 = (qv - wqsat) / ( 1.0 + tcp3 * dq2dt ) # compute_dq0(qv, wqsat, dq2dt, tcp3) #(qv - wqsat) / (1.0 + tcp3 * dq2dt) # TODO Might be able to get rid of these temporary allocations when not used? if dq0 > 0: # whole grid - box saturated src = min( spec.namelist.sat_adj0 * dq0, max(spec.namelist.ql_gen - ql, fac_v2l * dq0), ) else: # TODO We'd like to use this abstraction rather than duplicate # code, but inside the if conditional complains 'not implemented' # factor, src = ql_evaporation(wqsat, qv, ql, dq0,fac_l2v) factor = -1.0 * min(1, fac_l2v * 10.0 * (1.0 - qv / wqsat)) src = -1.0 * min(ql, factor * dq0) qv, ql, q_liq, cvm, pt1 = wqsat_correct( src, pt1, lhl, qv, ql, q_liq, q_sol, mc_air, c_vap ) # update latent heat coefficient lhl, lhi, lcp2, icp2 = update_latent_heat_coefficient(pt1, cvm, lv00, d0_vap) # TODO: Remove duplicate diff_ice = dim(TICE, pt1) / 48.0 dimmin = min(1.0, diff_ice) tcp3 = lcp2 + icp2 * dimmin
def ap1_for_wqs2(ta): ap1 = 10.0 * dim(ta, TMIN) + 1.0 return min(ap1, QS_LENGTH) - 1
def satadjust( wqsat: sd, dq2dt: sd, dpln: sd, den: sd, pt1: sd, cvm: sd, mc_air: sd, peln: sd, qv: sd, ql: sd, q_liq: sd, qi: sd, qr: sd, qs: sd, cappa: sd, q_sol: sd, qg: sd, pt: sd, dp: sd, tin: sd, delz: sd, te0: sd, q_cond: sd, q_con: sd, qa: sd, area: sd, qpz: sd, hs: sd, pkz: sd, lhl: sd, lhi: sd, lcp2: sd, icp2: sd, tcp3: sd, sdt: float, zvir: float, fac_i2s: float, do_qa: bool, hydrostatic: bool, consv_te: bool, c_air: float, c_vap: float, mdt: float, fac_r2g: float, fac_smlt: float, fac_l2r: float, fac_imlt: float, d0_vap: float, lv00: float, fac_v2l: float, fac_l2v: float, last_step: bool, rad_snow: bool, rad_rain: bool, rad_graupel: bool, tintqs: bool, ): with computation(FORWARD), interval(1, None): if hydrostatic: delz = delz[0, 0, -1] with computation(PARALLEL), interval(...): dpln = peln[0, 0, 1] - peln q_liq = ql + qr q_sol = qi + qs + qg qpz = q_liq + q_sol pt1 = pt / ((1.0 + zvir * qv) * (1.0 - qpz)) t0 = pt1 # true temperature qpz = qpz + qv # total_wat conserved in this routine # define air density based on hydrostatical property den = ( dp / (dpln * constants.RDGAS * pt) if hydrostatic else -dp / (constants.GRAV * delz) ) # define heat capacity and latend heat coefficient mc_air = (1.0 - qpz) * c_air cvm = compute_cvm(mc_air, qv, c_vap, q_liq, q_sol) lhi, icp2 = update_latent_heat_coefficient_i(pt1, cvm) # fix energy conservation if consv_te: if hydrostatic: te0 = -c_air * t0 else: te0 = -cvm * t0 # fix negative cloud ice with snow if qi < 0.0: qs = qs + qi qi = 0.0 # melting of cloud ice to cloud water and rain qi, ql, q_liq, q_sol, cvm, pt1 = melt_cloud_ice( qv, qi, ql, q_liq, q_sol, pt1, icp2, fac_imlt, mc_air, c_vap, lhi, cvm ) # update latend heat coefficient lhi, icp2 = update_latent_heat_coefficient_i(pt1, cvm) # fix negative snow with graupel or graupel with available snow qs, qg = fix_negative_snow(qs, qg) # after this point cloud ice & snow are positive definite # fix negative cloud water with rain or rain with available cloud water ql, qr = fix_negative_cloud_water(ql, qr) # enforce complete freezing of cloud water to cloud ice below - 48 c ql, qi, q_liq, q_sol, cvm, pt1 = complete_freezing( qv, ql, qi, q_liq, q_sol, pt1, cvm, icp2, mc_air, lhi, c_vap ) wqsat, dq2dt = wqs2_fn_w(pt1, den) # update latent heat coefficient lhl, lhi, lcp2, icp2 = update_latent_heat_coefficient(pt1, cvm, lv00, d0_vap) diff_ice = dim(TICE, pt1) / 48.0 dimmin = min(1.0, diff_ice) tcp3 = lcp2 + icp2 * dimmin dq0 = (qv - wqsat) / ( 1.0 + tcp3 * dq2dt ) # compute_dq0(qv, wqsat, dq2dt, tcp3) #(qv - wqsat) / (1.0 + tcp3 * dq2dt) # TODO Might be able to get rid of these temporary allocations when not used? if dq0 > 0: # whole grid - box saturated src = min( spec.namelist.sat_adj0 * dq0, max(spec.namelist.ql_gen - ql, fac_v2l * dq0), ) else: # TODO We'd like to use this abstraction rather than duplicate # code, but inside the if conditional complains 'not implemented' # factor, src = ql_evaporation(wqsat, qv, ql, dq0,fac_l2v) factor = -1.0 * min(1, fac_l2v * 10.0 * (1.0 - qv / wqsat)) src = -1.0 * min(ql, factor * dq0) qv, ql, q_liq, cvm, pt1 = wqsat_correct( src, pt1, lhl, qv, ql, q_liq, q_sol, mc_air, c_vap ) # update latent heat coefficient lhl, lhi, lcp2, icp2 = update_latent_heat_coefficient(pt1, cvm, lv00, d0_vap) # TODO: Remove duplicate diff_ice = dim(TICE, pt1) / 48.0 dimmin = min(1.0, diff_ice) tcp3 = lcp2 + icp2 * dimmin dq0 = 0.0 if last_step: wqsat, dq2dt = wqs2_fn_w(pt1, den) dq0 = compute_dq0(qv, wqsat, dq2dt, tcp3) if dq0 > 0: src = dq0 else: # TODO: We'd like to use this abstraction rather than duplicate # code, but inside the if conditional complains 'not # implemented'. # factor, src = ql_evaporation(wqsat, qv, ql, dq0,fac_l2v) factor = -1.0 * min(1, fac_l2v * 10.0 * (1.0 - qv / wqsat)) src = -1.0 * min(ql, factor * dq0) # TODO Causes a visit_if error 'NoneType' object has no attribute 'inputs' # qv, ql, q_liq, cvm, pt1 = wqsat_correct(src, pt1, lhl, qv, ql, # q_liq, q_sol, mc_air, c_vap) # lhl, lhi, lcp2, icp2 = update_latent_heat_coefficient(pt1, cvm, # lv00, d0_vap) qv = qv - src ql = ql + src q_liq = q_liq + src cvm = compute_cvm(mc_air, qv, c_vap, q_liq, q_sol) pt1 = add_src_pt1(pt1, src, lhl, cvm) # pt1 + src * lhl / cvm # TODO: Revisit when gt4py updated, causes an Assertion error # lhl, lhi, lcp2, icp2 = update_latent_heat_coefficient(pt1, cvm, # lv00, d0_vap) lhl = lv00 + d0_vap * pt1 lhi = LI00 + DC_ICE * pt1 lcp2 = lhl / cvm icp2 = lhi / cvm # homogeneous freezing of cloud water to cloud ice ql, qi, q_liq, q_sol, cvm, pt1 = homogenous_freezing( qv, ql, qi, q_liq, q_sol, pt1, cvm, icp2, mc_air, lhi, c_vap ) # update some of the latent heat coefficients lhi, icp2 = update_latent_heat_coefficient_i(pt1, cvm) exptc = exp(0.66 * (TICE0 - pt1)) # bigg mechanism (heterogeneous freezing of cloud water to cloud ice) ql, qi, q_liq, q_sol, cvm, pt1 = heterogeneous_freezing( exptc, pt1, cvm, ql, qi, q_liq, q_sol, den, icp2, mdt, mc_air, lhi, qv, c_vap, ) lhi, icp2 = update_latent_heat_coefficient_i(pt1, cvm) # freezing of rain to graupel qr, qg, q_liq, q_sol, cvm, pt1 = make_graupel( pt1, cvm, fac_r2g, qr, qg, q_liq, q_sol, lhi, icp2, mc_air, qv, c_vap ) lhi, icp2 = update_latent_heat_coefficient_i(pt1, cvm) # melting of snow to rain or cloud water qs, ql, qr, q_liq, q_sol, cvm, pt1 = melt_snow( pt1, cvm, fac_smlt, qs, ql, qr, q_liq, q_sol, lhi, icp2, mc_air, qv, c_vap, spec.namelist.qs_mlt, ) # autoconversion from cloud water to rain ql, qr = autoconversion_cloud_to_rain(ql, qr, fac_l2r, spec.namelist.ql0_max) iqs2, dqsdt = wqs2_fn_2(pt1, den) expsubl = exp(0.875 * log(qi * den)) lhl, lhi, lcp2, icp2 = update_latent_heat_coefficient(pt1, cvm, lv00, d0_vap) tcp2 = lcp2 + icp2 if last_step: adj_fac = 1.0 else: adj_fac = spec.namelist.sat_adj0 qv, qi, q_sol, cvm, pt1 = sublimation( pt1, cvm, expsubl, qv, qi, q_liq, q_sol, iqs2, tcp2, den, dqsdt, sdt, adj_fac, mc_air, c_vap, lhl, lhi, spec.namelist.t_sub, spec.namelist.qi_gen, spec.namelist.qi_lim, ) # virtual temp updated q_con = q_liq + q_sol tmp = 1.0 + zvir * qv pt = pt1 * tmp * (1.0 - q_con) tmp = constants.RDGAS * tmp cappa = tmp / (tmp + cvm) # fix negative graupel with available cloud ice maxtmp = 0.0 if qg < 0: maxtmp = 0.0 if 0.0 > qi else qi tmp = -qg if -qg < maxtmp else maxtmp qg = qg + tmp qi = qi - tmp else: qg = qg # autoconversion from cloud ice to snow qim = spec.namelist.qi0_max / den sink = 0.0 if qi > qim: sink = fac_i2s * (qi - qim) qi = qi - sink qs = qs + sink # fix energy conservation if consv_te: if hydrostatic: te0 = dp * (te0 + c_air * pt1) else: te0 = dp * (te0 + cvm * pt1) # update latent heat coefficient cvm = mc_air + (qv + q_liq + q_sol) * c_vap lhl, lhi, lcp2, icp2 = update_latent_heat_coefficient(pt1, cvm, lv00, d0_vap) # compute cloud fraction tin = 0.0 if do_qa and last_step: # combine water species if rad_snow: if rad_graupel: q_sol = qi + qs + qg else: q_sol = qi + qs else: q_sol = qi if rad_rain: q_liq = ql + qr else: q_liq = ql q_cond = q_sol + q_liq # use the "liquid - frozen water temperature" (tin) to compute # saturated specific humidity if tintqs: tin = pt1 else: tin = pt1 - (lcp2 * q_cond + icp2 * q_sol) # CK : Additions from satadjust_part3_laststep_qa it, ap1 = ap1_and_index(tin) wqs1 = wqs1_fn_w(it, ap1, tin, den) iqs1 = wqs1_fn_2(it, ap1, tin, den) # Determine saturated specific humidity if tin < T_WFR: # ice phase qstar = iqs1 elif tin >= TICE: qstar = wqs1 else: # qsw = wqs1 if q_cond > 1e-6: rqi = q_sol / q_cond else: rqi = (TICE - tin) / (TICE - T_WFR) qstar = rqi * iqs1 + (1.0 - rqi) * wqs1 # higher than 10 m is considered "land" and will have higher subgrid # variability mindw = min(1.0, abs(hs) / (10.0 * constants.GRAV)) dw = ( spec.namelist.dw_ocean + (spec.namelist.dw_land - spec.namelist.dw_ocean) * mindw ) # "scale - aware" subgrid variability: 100 - km as the base dbl_sqrt_area = dw * (area ** 0.5 / 100.0e3) ** 0.5 maxtmp = 0.01 if 0.01 > dbl_sqrt_area else dbl_sqrt_area hvar = min(0.2, maxtmp) # partial cloudiness by pdf: # assuming subgrid linear distribution in horizontal; this is # effectively a smoother for the binary cloud scheme; # qa = 0.5 if qstar == qpz rh = qpz / qstar # icloud_f = 0: bug - fixed # icloud_f = 1: old fvgfs gfdl) mp implementation # icloud_f = 2: binary cloud scheme (0 / 1) if rh > 0.75 and qpz > 1.0e-8: dq = hvar * qpz q_plus = qpz + dq q_minus = qpz - dq if spec.namelist.icloud_f == 2: # TODO untested if qpz > qstar: qa = 1.0 elif (qstar < q_plus) and (q_cond > 1.0e-8): qa = min(1.0, ((q_plus - qstar) / dq) ** 2) else: qa = 0.0 else: if qstar < q_minus: qa = 1.0 else: if qstar < q_plus: if spec.namelist.icloud_f == 0: qa = (q_plus - qstar) / (dq + dq) else: qa = (q_plus - qstar) / (2.0 * dq * (1.0 - q_cond)) else: qa = 0.0 # impose minimum cloudiness if substantial q_cond exist if q_cond > 1.0e-8: qa = max(spec.namelist.cld_min, qa) qa = min(1, qa) else: qa = 0.0 if not hydrostatic: pkz = compute_pkz_func(dp, delz, pt, cappa)