def apply_Neumann(self, grid, value): n_hat_zmin = grid.n_hat(Zmin()) n_hat_zmax = grid.n_hat(Zmax()) k_1 = grid.first_interior(Zmin()) k_2 = grid.first_interior(Zmax()) self.values[0:k_1] = self.values[k_1] - n_hat_zmin * grid.dz * value self.values[k_2 + 1:] = self.values[k_2] - n_hat_zmax * grid.dz * value
def apply_Neumann(self, grid, value): n_hat_zmin = grid.n_hat(Zmin()) n_hat_zmax = grid.n_hat(Zmax()) k_1 = grid.boundary(Zmin()) k_2 = grid.boundary(Zmax()) self.values[0:k_1] = 2.0 * self.values[k_1] - self.values[ k_1 - n_hat_zmin] + 2.0 * grid.dz * value * n_hat_zmin self.values[k_2 + 1:] = 2.0 * self.values[k_2] - self.values[ k_2 - n_hat_zmax] + 2.0 * grid.dz * value * n_hat_zmax
def compute_tendencies_gm(grid, q_tendencies, q, Case, TS, tmp, tri_diag): i_gm, i_env, i_uds, i_sd = q.domain_idx() k_1 = grid.first_interior(Zmin()) dzi = grid.dzi α_1 = tmp['α_0'][k_1] ae_1 = q['a', i_env][k_1] slice_all_c = grid.slice_all(Center()) q_tendencies['q_tot', i_gm][slice_all_c] += [ tmp['mf_tend_q_tot'][k] + tmp['prec_src_q_tot', i_gm][k] * TS.Δti for k in grid.over_elems(Center()) ] q_tendencies['q_tot', i_gm][k_1] += Case.Sur.rho_q_tot_flux * dzi * α_1 / ae_1 q_tendencies['θ_liq', i_gm][slice_all_c] += [ tmp['mf_tend_θ_liq'][k] + tmp['prec_src_θ_liq', i_gm][k] * TS.Δti for k in grid.over_elems(Center()) ] q_tendencies['θ_liq', i_gm][k_1] += Case.Sur.rho_θ_liq_flux * dzi * α_1 / ae_1 q_tendencies['U', i_gm][k_1] += Case.Sur.rho_uflux * dzi * α_1 / ae_1 q_tendencies['V', i_gm][k_1] += Case.Sur.rho_vflux * dzi * α_1 / ae_1 return
def update(self, grid, q, tmp): i_gm, i_env, i_uds, i_sd = tmp.domain_idx() k_1 = grid.first_interior(Zmin()) ρ_0_surf = tmp.surface(grid, 'ρ_0') α_0_surf = tmp.surface(grid, 'α_0') T_1 = tmp['T', i_gm][k_1] θ_liq_1 = q['θ_liq', i_gm][k_1] q_tot_1 = q['q_tot', i_gm][k_1] V_1 = q['V', i_gm][k_1] U_1 = q['U', i_gm][k_1] cp_ = cpm_c(q_tot_1) lv = latent_heat(T_1) windspeed = compute_windspeed(grid, q, 0.01) self.rho_q_tot_flux = -self.cq * windspeed * (q_tot_1 - self.qsurface) * ρ_0_surf self.rho_θ_liq_flux = -self.ch * windspeed * (θ_liq_1 - self.Tsurface/exner_c(self.Ref.Pg)) * ρ_0_surf self.lhf = lv * self.rho_q_tot_flux self.shf = cp_ * self.rho_θ_liq_flux self.bflux = buoyancy_flux(self.shf, self.lhf, T_1, q_tot_1, α_0_surf) self.ustar = np.sqrt(self.cm) * windspeed self.obukhov_length = compute_MO_len(self.ustar, self.bflux) self.rho_uflux = - ρ_0_surf * self.ustar * self.ustar / windspeed * U_1 self.rho_vflux = - ρ_0_surf * self.ustar * self.ustar / windspeed * V_1 return
def initialize_vars(self, grid, q, q_tendencies, tmp, tmp_O2, UpdVar, Case, TS, tri_diag): i_gm, i_env, i_uds, i_sd = q.domain_idx() self.zi = compute_inversion(grid, q, Case.inversion_option, tmp, self.Ri_bulk_crit, tmp['temp_C']) zs = self.zi self.wstar = compute_convective_velocity(Case.Sur.bflux, zs) ws = self.wstar ws3 = ws**3.0 us3 = Case.Sur.ustar**3.0 k_1 = grid.first_interior(Zmin()) cv_θ_liq_1 = q['cv_θ_liq', i_gm][k_1] cv_q_tot_1 = q['cv_q_tot', i_gm][k_1] cv_θ_liq_q_tot_1 = q['cv_θ_liq_q_tot', i_gm][k_1] reset_surface_covariance(grid, q, tmp, Case, ws) if ws > 0.0: for k in grid.over_elems(Center()): z = grid.z_half[k] temp = ws * 1.3 * np.cbrt(us3 / ws3 + 0.6 * z / zs) * np.sqrt( np.fmax(1.0 - z / zs, 0.0)) q['tke', i_gm][k] = temp q['cv_θ_liq', i_gm][k] = cv_θ_liq_1 * temp q['cv_q_tot', i_gm][k] = cv_q_tot_1 * temp q['cv_θ_liq_q_tot', i_gm][k] = cv_θ_liq_q_tot_1 * temp reset_surface_covariance(grid, q, tmp, Case, ws) compute_mixing_length(grid, q, tmp, Case.Sur.obukhov_length, self.zi, self.wstar) self.pre_compute_vars(grid, q, q_tendencies, tmp, tmp_O2, UpdVar, Case, TS, tri_diag) return
def compute_cv_env_tendencies(grid, q_tendencies, tmp_O2, cv): i_gm, i_env, i_uds, i_sd = q_tendencies.domain_idx() k_1 = grid.first_interior(Zmin()) for k in grid.over_elems_real(Center()): q_tendencies[cv, i_env][k] = tmp_O2[cv]['press'][k] + tmp_O2[cv][ 'buoy'][k] + tmp_O2[cv]['shear'][k] + tmp_O2[cv]['entr_gain'][ k] + tmp_O2[cv]['rain_src'][k] q_tendencies[cv, i_env][k_1] = 0.0 return
def calculate_radiation(self, tmp): """ see eq. 3 in Stevens et. al. 2005 DYCOMS paper """ # find z_i (level of 8.0 g/kg isoline of q_tot) k_1 = grid.first_interior(Zmin()) z_i = grid.z[k_1] for k in grid.over_elems_real(Center()): if (q['q_tot', i_gm][k] < 8.0 / 1000): idx_zi = k # will be used at cell edges z_i = grid.z[idx_zi] rhoi = tmp['ρ_0'][idx_zi] break self.f_rad = Full(grid) k_2 = grid.boundary(Zmax()) k_1 = 0 k_2 = grid.nzg - 1 # cloud-top cooling q_0 = 0.0 self.f_rad[k_2] = self.F0 * np.exp(-q_0) for k in range(k_2 - 1, -1, -1): q_0 += self.kappa * tmp['ρ_0'][k] * tmp['q_liq', i_gm][k] * grid.dz self.f_rad[k] = self.F0 * np.exp(-q_0) # cloud-base warming q_1 = 0.0 self.f_rad[k_1] += self.F1 * np.exp(-q_1) for k in range(1, k_2 + 1): q_1 += self.kappa * tmp['ρ_0'][k - 1] * tmp['q_liq', i_gm][k - 1] * grid.dz self.f_rad[k] += self.F1 * np.exp(-q_1) # cooling in free troposphere for k in range(k_1, k_2): if grid.z[k] > z_i: cbrt_z = np.cbrt(grid.z[k] - z_i) self.f_rad[ k] += rhoi * dycoms_cp * self.divergence * self.alpha_z * ( np.power(cbrt_z, 4) / 4.0 + z_i * cbrt_z) # condition at the top cbrt_z = np.cbrt(grid.z[k] + grid.dz - z_i) self.f_rad[ k_2] += rhoi * dycoms_cp * self.divergence * self.alpha_z * ( np.power(cbrt_z, 4) / 4.0 + z_i * cbrt_z) for k in grid.over_elems_real(Center()): self.dTdt[k] = -(self.f_rad[k + 1] - self.f_rad[k] ) / grid.dz / tmp['ρ_0'][k] / dycoms_cp return
def construct_tridiag_diffusion_O1(grid, dt, tri_diag, rho, ae): k_1 = grid.first_interior(Zmin()) k_2 = grid.first_interior(Zmax()) dzi = grid.dzi for k in grid.over_elems_real(Center()): ρaK_dual = tri_diag.ρaK.Dual(k) X = rho[k] * ae[k] / dt Z = ρaK_dual[0] * dzi * dzi Y = ρaK_dual[1] * dzi * dzi if k == k_1: Z = 0.0 elif k == k_2: Y = 0.0 tri_diag.a[k] = -Z / X tri_diag.b[k] = 1.0 + Y / X + Z / X tri_diag.c[k] = -Y / X return
def update_cv_env(grid, q, q_tendencies, tmp, tmp_O2, TS, cv, tri_diag, tke_diss_coeff): i_gm, i_env, i_uds, i_sd = q.domain_idx() construct_tridiag_diffusion_O2(grid, q, tmp, TS, tri_diag, tke_diss_coeff) k_1 = grid.first_interior(Zmin()) slice_all_c = grid.slice_all(Center()) a_e = q['a', i_env] tri_diag.f[slice_all_c] = [ tmp['ρ_0'][k] * a_e[k] * q[cv, i_env][k] * TS.Δti + q_tendencies[cv, i_env][k] for k in grid.over_elems(Center()) ] tri_diag.f[k_1] = tmp['ρ_0'][k_1] * a_e[k_1] * q[ cv, i_env][k_1] * TS.Δti + q[cv, i_env][k_1] solve_tridiag_wrapper(grid, q[cv, i_env], tri_diag) return
def construct_tridiag_diffusion_O2(grid, q, tmp, TS, tri_diag, tke_diss_coeff): i_gm, i_env, i_uds, i_sd = q.domain_idx() dzi = grid.dzi dzi2 = grid.dzi**2.0 dti = TS.Δti k_1 = grid.first_interior(Zmin()) k_2 = grid.first_interior(Zmax()) a_env = q['a', i_env] w_env = q['w', i_env] ρ_0_half = tmp['ρ_0'] for k in grid.over_elems_real(Center()): ρ_0_cut = ρ_0_half.Cut(k) ae_cut = a_env.Cut(k) w_cut = w_env.DualCut(k) ρa_K_cut = a_env.DualCut(k) * tmp['K_h'].DualCut(k) * ρ_0_half.DualCut( k) D_env = sum([ ρ_0_cut[1] * q['a', i][k] * q['w', i].Mid(k) * tmp['entr_sc', i][k] for i in i_uds ]) l_mix = np.fmax(tmp['l_mix'][k], 1.0) tke_env = np.fmax(q['tke', i_env][k], 0.0) tri_diag.a[k] = (-ρa_K_cut[0] * dzi2) tri_diag.b[k] = ( ρ_0_cut[1] * ae_cut[1] * dti - ρ_0_cut[1] * ae_cut[1] * w_cut[1] * dzi + ρa_K_cut[1] * dzi2 + ρa_K_cut[0] * dzi2 + D_env + ρ_0_cut[1] * ae_cut[1] * tke_diss_coeff * np.sqrt(tke_env) / l_mix) tri_diag.c[k] = (ρ_0_cut[2] * ae_cut[2] * w_cut[2] * dzi - ρa_K_cut[1] * dzi2) tri_diag.a[k_1] = 0.0 tri_diag.b[k_1] = 1.0 tri_diag.c[k_1] = 0.0 tri_diag.b[k_2] += tri_diag.c[k_2] tri_diag.c[k_2] = 0.0 return
def update(self, grid, q, tmp): i_gm, i_env, i_uds, i_sd = tmp.domain_idx() k_1 = grid.first_interior(Zmin()) z_1 = grid.z_half[k_1] p_0_1 = tmp['p_0'][k_1] α_0_1 = tmp['α_0'][k_1] ρ_0_surf = tmp.surface(grid, 'ρ_0') T_1 = tmp['T', i_gm][k_1] θ_liq_1 = q['θ_liq', i_gm][k_1] q_tot_1 = q['q_tot', i_gm][k_1] V_1 = q['V', i_gm][k_1] U_1 = q['U', i_gm][k_1] self.qsurface = qv_star_t(self.Ref.Pg, self.Tsurface) theta_rho_g = theta_rho_c(self.Ref.Pg, self.Tsurface, self.qsurface, self.qsurface) theta_rho_b = theta_rho_c(p_0_1, T_1, self.qsurface, self.qsurface) lv = latent_heat(T_1) T0 = p_0_1 * α_0_1/Rd theta_flux = 0.24 θ_liq_star = thetali_c(self.Ref.Pg, self.Tsurface, self.qsurface, 0.0, 0.0) self.windspeed = compute_windspeed(grid, q, 0.0) Nb2 = g/theta_rho_g*(theta_rho_b-theta_rho_g)/z_1 Ri = Nb2 * z_1 * z_1/(self.windspeed * self.windspeed) self.cm, self.ch, self.obukhov_length = exchange_coefficients_byun(Ri, z_1, self.zrough) self.rho_uflux = -self.cm * self.windspeed * U_1 * ρ_0_surf self.rho_vflux = -self.cm * self.windspeed * V_1 * ρ_0_surf self.rho_θ_liq_flux = -self.ch * self.windspeed * (θ_liq_1 - θ_liq_star) * ρ_0_surf self.rho_q_tot_flux = -self.ch * self.windspeed * (q_tot_1 - self.qsurface) * ρ_0_surf self.lhf = lv * self.rho_q_tot_flux self.shf = cpm_c(q_tot_1) * self.rho_θ_liq_flux self.bflux = g * theta_flux * exner_c(p_0_1) / T0 self.ustar = sqrt(self.cm) * self.windspeed self.obukhov_length = compute_MO_len(self.ustar, self.bflux) return
def reset_surface_covariance(grid, q, tmp, Case, wstar): i_gm, i_env, i_uds, i_sd = q.domain_idx() flux1 = Case.Sur.rho_θ_liq_flux flux2 = Case.Sur.rho_q_tot_flux k_1 = grid.first_interior(Zmin()) zLL = grid.z_half[k_1] alpha0LL = tmp['α_0'][k_1] ustar = Case.Sur.ustar oblength = Case.Sur.obukhov_length q['tke', i_gm][k_1] = surface_tke(Case.Sur.ustar, wstar, zLL, Case.Sur.obukhov_length) q['cv_θ_liq', i_gm][k_1] = surface_variance(flux1 * alpha0LL, flux1 * alpha0LL, ustar, zLL, oblength) q['cv_q_tot', i_gm][k_1] = surface_variance(flux2 * alpha0LL, flux2 * alpha0LL, ustar, zLL, oblength) q['cv_θ_liq_q_tot', i_gm][k_1] = surface_variance(flux1 * alpha0LL, flux2 * alpha0LL, ustar, zLL, oblength) return
def update(self, grid, q, tmp): i_gm, i_env, i_uds, i_sd = tmp.domain_idx() k_1 = grid.first_interior(Zmin()) z_1 = grid.z_half[k_1] ρ_0_surf = tmp.surface(grid, 'ρ_0') α_0_surf = tmp.surface(grid, 'α_0') T_1 = tmp['T', i_gm][k_1] θ_liq_1 = q['θ_liq', i_gm][k_1] q_tot_1 = q['q_tot', i_gm][k_1] V_1 = q['V', i_gm][k_1] U_1 = q['U', i_gm][k_1] rho_tflux = self.shf /(cpm_c(self.qsurface)) self.windspeed = compute_windspeed(grid, q, 0.0) self.rho_q_tot_flux = self.lhf/(latent_heat(self.Tsurface)) self.rho_θ_liq_flux = rho_tflux / exner_c(self.Ref.Pg) self.bflux = buoyancy_flux(self.shf, self.lhf, T_1, q_tot_1, α_0_surf) if not self.ustar_fixed: # Correction to windspeed for free convective cases (Beljaars, QJRMS (1994), 121, pp. 255-270) # Value 1.2 is empirical, but should be O(1) if self.windspeed < 0.1: # Limit here is heuristic if self.bflux > 0.0: self.free_convection_windspeed(grid, q, tmp) else: print('WARNING: Low windspeed + stable conditions, need to check ustar computation') print('self.bflux ==>', self.bflux) print('self.shf ==>', self.shf) print('self.lhf ==>', self.lhf) print('U_1 ==>', U_1) print('V_1 ==>', V_1) print('q_tot_1 ==>', q_tot_1) print('α_0_surf ==>', α_0_surf) self.ustar = compute_ustar(self.windspeed, self.bflux, self.zrough, z_1) self.obukhov_length = compute_MO_len(self.ustar, self.bflux) self.rho_uflux = - ρ_0_surf * self.ustar * self.ustar / self.windspeed * U_1 self.rho_vflux = - ρ_0_surf * self.ustar * self.ustar / self.windspeed * V_1 return
def set_updraft_surface_bc(self, grid, q, tmp, UpdVar, Case): i_gm, i_env, i_uds, i_sd = tmp.domain_idx() k_1 = grid.first_interior(Zmin()) zLL = grid.z_half[k_1] θ_liq_1 = q['θ_liq', i_gm][k_1] q_tot_1 = q['q_tot', i_gm][k_1] alpha0LL = tmp['α_0'][k_1] S = Case.Sur cv_q_tot = surface_variance(S.rho_q_tot_flux * alpha0LL, S.rho_q_tot_flux * alpha0LL, S.ustar, zLL, S.obukhov_length) cv_θ_liq = surface_variance(S.rho_θ_liq_flux * alpha0LL, S.rho_θ_liq_flux * alpha0LL, S.ustar, zLL, S.obukhov_length) for i in i_uds: UpdVar[i].area_surface_bc = self.surface_area / self.n_updrafts UpdVar[i].w_surface_bc = 0.0 UpdVar[i].θ_liq_surface_bc = ( θ_liq_1 + UpdVar[i].surface_scalar_coeff * np.sqrt(cv_θ_liq)) UpdVar[i].q_tot_surface_bc = ( q_tot_1 + UpdVar[i].surface_scalar_coeff * np.sqrt(cv_q_tot)) return
def compute_inversion_height(theta_rho, u, v, grid, Ri_bulk_crit): theta_rho_b = theta_rho.first_interior(grid) h = 0.0 Ri_bulk=0.0 Ri_bulk_low = 0.0 kmin = grid.first_interior(Zmin()) k = kmin z = grid.z_half # test if we need to look at the free convective limit if (u[kmin] * u[kmin] + v[kmin] * v[kmin]) <= 0.01: for k in grid.over_elems_real(Center()): if theta_rho[k] > theta_rho_b: break h = (z[k] - z[k-1])/(theta_rho[k] - theta_rho[k-1]) * (theta_rho_b - theta_rho[k-1]) + z[k-1] else: for k in grid.over_elems_real(Center()): Ri_bulk_low = Ri_bulk Ri_bulk = g * (theta_rho[k] - theta_rho_b) * z[k]/theta_rho_b / (u[k] * u[k] + v[k] * v[k]) if Ri_bulk > Ri_bulk_crit: break h = (z[k] - z[k-1])/(Ri_bulk - Ri_bulk_low) * (Ri_bulk_crit - Ri_bulk_low) + z[k-1] return h
def setup_stats_file(self, grid): k_b_1 = grid.boundary(Zmin()) k_b_2 = grid.boundary(Zmax()) k_1 = k_b_1 k_2 = k_b_2 # k_1 = grid.first_interior(Zmin()) # IO assumes full and half fields are equal sizes # k_2 = grid.first_interior(Zmax()) # IO assumes full and half fields are equal sizes root_grp = nc.Dataset(self.path_plus_file, 'w', format='NETCDF4') # Set profile dimensions profile_grp = root_grp.createGroup('profiles') profile_grp.createDimension('z', grid.nz) profile_grp.createDimension('t', None) z = profile_grp.createVariable('z', 'f8', ('z')) z[:] = np.array(grid.z[k_b_1:k_b_2]) z_half = profile_grp.createVariable('z_half', 'f8', ('z')) z_half[:] = np.array(grid.z_half[k_1:k_2]) profile_grp.createVariable('t', 'f8', ('t')) del z del z_half reference_grp = root_grp.createGroup('reference') reference_grp.createDimension('z', grid.nz) z = reference_grp.createVariable('z', 'f8', ('z')) z[:] = np.array(grid.z[k_b_1:k_b_2]) z_half = reference_grp.createVariable('z_half', 'f8', ('z')) z_half[:] = np.array(grid.z_half[k_1:k_2]) del z del z_half ts_grp = root_grp.createGroup('timeseries') ts_grp.createDimension('t', None) ts_grp.createVariable('t', 'f8', ('t')) root_grp.close() return
def initialize_updrafts(grid, tmp, q, updraft_fraction): i_gm, i_env, i_uds, i_sd = q.domain_idx() k_1 = grid.first_interior(Zmin()) n_updrafts = len(i_uds) for i in i_uds: for k in grid.over_elems(Center()): q['w', i][k] = 0.0 q['a', i][k] = 0.0 q['q_tot', i][k] = q['q_tot', i_gm][k] tmp['q_liq', i][k] = tmp['q_liq', i_gm][k] q['q_rai', i][k] = q['q_rai', i_gm][k] q['θ_liq', i][k] = q['θ_liq', i_gm][k] tmp['T', i][k] = tmp['T', i_gm][k] tmp['B', i][k] = 0.0 q['a', i][k_1] = updraft_fraction / n_updrafts for i in i_uds: q['q_tot', i].apply_bc(grid, 0.0) for i in i_uds: q['q_rai', i].apply_bc(grid, 0.0) for i in i_uds: q['θ_liq', i].apply_bc(grid, 0.0) for k in grid.over_elems(Center()): q['a', i_env][k] = 1.0 - np.sum([q['a', i][k] for i in i_uds]) return
def first_interior(self, grid): k_b = grid.boundary(Zmin()) return (self.values[k_b] + self.values[k_b + 1]) / 2.0
def apply_Dirichlet(self, grid, value): self.values[0:grid.boundary(Zmin()) + 1] = value self.values[grid.boundary(Zmax()):] = value
def surface(self, grid): k_i = grid.first_interior(Zmin()) return (self.values[k_i] + self.values[k_i - 1]) / 2.0
def first_interior(self, grid): k_i = grid.first_interior(Zmin()) return self.values[k_i]
def extrap(self, grid): for k in reversed(grid.over_elems_ghost(Center(), Zmin())): self.values[k] = 2.0 * self.values[k + 1] - self.values[k + 2] for k in grid.over_elems_ghost(Center(), Zmax()): self.values[k] = 2.0 * self.values[k - 1] - self.values[k - 2]
def apply_Dirichlet(self, grid, value): k_1 = grid.first_interior(Zmin()) k_2 = grid.first_interior(Zmax()) self.values[0:k_1] = 2 * value - self.values[k_1] self.values[k_2 + 1:] = 2 * value - self.values[k_2]
def solve_updraft_scalars(grid, q_new, q, q_tendencies, tmp, UpdVar, TS, params): i_gm, i_env, i_uds, i_sd = q.domain_idx() dzi = grid.dzi k_1 = grid.first_interior(Zmin()) for i in i_uds: q_new['θ_liq', i][k_1] = UpdVar[i].θ_liq_surface_bc q_new['q_tot', i][k_1] = UpdVar[i].q_tot_surface_bc for k in grid.over_elems_real(Center())[1:]: θ_liq_env = q['θ_liq', i_env][k] q_tot_env = q['q_tot', i_env][k] if q_new['a', i][k] >= params.minimum_area: a_k = q['a', i][k] a_cut = q['a', i].Cut(k) a_k_new = q_new['a', i][k] θ_liq_cut = q['θ_liq', i].Cut(k) q_tot_cut = q['q_tot', i].Cut(k) ρ_k = tmp['ρ_0'][k] ρ_cut = tmp['ρ_0'].Cut(k) w_cut = q['w', i].DualCut(k) ε_sc = tmp['entr_sc', i][k] δ_sc = tmp['detr_sc', i][k] ρa_k = ρ_k * a_k ρaw_cut = ρ_cut * a_cut * w_cut ρawθ_liq_cut = ρaw_cut * θ_liq_cut ρawq_tot_cut = ρaw_cut * q_tot_cut ρa_new_k = ρ_k * a_k_new tendencies_θ_liq = -advect( ρawθ_liq_cut, w_cut, grid) + ρaw_cut[1] * ( ε_sc * θ_liq_env - δ_sc * θ_liq_cut[1]) tendencies_q_tot = -advect( ρawq_tot_cut, w_cut, grid) + ρaw_cut[1] * ( ε_sc * q_tot_env - δ_sc * q_tot_cut[1]) q_new['θ_liq', i][k] = ρa_k / ρa_new_k * θ_liq_cut[ 1] + TS.Δt_up * tendencies_θ_liq / ρa_new_k q_new['q_tot', i][k] = ρa_k / ρa_new_k * q_tot_cut[ 1] + TS.Δt_up * tendencies_q_tot / ρa_new_k else: q_new['θ_liq', i][k] = q['θ_liq', i_gm][k] q_new['q_tot', i][k] = q['q_tot', i_gm][k] if params.use_local_micro: for i in i_uds: for k in grid.over_elems_real(Center()): θ_liq = q_new['θ_liq', i][k] q_tot = q_new['q_tot', i][k] p_0 = tmp['p_0'][k] T, q_liq = eos(p_0, q_tot, θ_liq) tmp['T', i][k] = T tmp_qr = acnv_instant(q_liq, q_tot, params.max_supersaturation, T, p_0) s = -tmp_qr tmp['prec_src_q_tot', i][k] = s r_src = rain_source_to_thetal(p_0, T, q_tot, q_liq, 0.0, tmp_qr) tmp['prec_src_θ_liq', i][k] = r_src q_new['q_tot', i][k] += s q_new['q_rai', i][k] -= s q_new['θ_liq', i][k] += r_src tmp['q_liq', i][k] = q_liq + s q_new['q_rai', i][k_1] = 0.0 return
def surface(self, grid, var_name, i_sd=0): k = grid.first_interior(Zmin()) return self[var_name, i_sd].Dual(k)[0]
def surface(self, grid): return self.values[grid.boundary(Zmin())]
def compute_windspeed(grid, q, windspeed_min): i_gm, i_env, i_uds, i_sd = q.domain_idx() k_1 = grid.first_interior(Zmin()) return np.maximum(np.sqrt(q['U', i_gm][k_1]**2.0 + q['V', i_gm][k_1]**2.0), windspeed_min)
def solve_updraft_velocity_area(grid, q_new, q, q_tendencies, tmp, UpdVar, TS, params): i_gm, i_env, i_uds, i_sd = q.domain_idx() k_1 = grid.first_interior(Zmin()) kb_1 = grid.boundary(Zmin()) dzi = grid.dzi # Solve for area fraction for i in i_uds: au_lim = UpdVar[i].area_surface_bc * params.max_area_factor for k in grid.over_elems_real(Center()): a_k = q['a', i][k] α_0_kp = tmp['α_0'][k] w_k = q['w', i].Mid(k) w_cut = q['w', i].DualCut(k) a_cut = q['a', i].Cut(k) ρ_cut = tmp['ρ_0'].Cut(k) tendencies = 0.0 ρaw_cut = ρ_cut * a_cut * w_cut adv = -α_0_kp * advect(ρaw_cut, w_cut, grid) tendencies += adv ε_term = a_k * w_k * (+tmp['entr_sc', i][k]) tendencies += ε_term δ_term = a_k * w_k * (-tmp['detr_sc', i][k]) tendencies += δ_term a_predict = a_k + TS.Δt_up * tendencies needs_limiter = a_predict > au_lim q_new['a', i][k] = np.fmin(np.fmax(a_predict, 0.0), au_lim) unsteady = (q_new['a', i][k] - a_k) / TS.Δt_up # δ_limiter = unsteady - tendencies if needs_limiter else 0.0 # tendencies+=δ_limiter # a_correct = a_k + TS.Δt_up * tendencies if needs_limiter: δ_term_new = unsteady - adv - ε_term if a_k > 0.0: tmp['detr_sc', i][k] = δ_term_new / (-a_k * w_k) else: tmp['detr_sc', i][k] = δ_term_new / (-au_lim * w_k) tmp['entr_sc', i][k_1] = 2.0 * dzi tmp['detr_sc', i][k_1] = 0.0 q_new['a', i][k_1] = UpdVar[i].area_surface_bc # Solve for updraft velocity for i in i_uds: q_new['a', i][kb_1] = UpdVar[i].w_surface_bc for k in grid.over_elems_real(Center()): a_new_k = q_new['a', i].Mid(k) if a_new_k >= params.minimum_area: ρ_k = tmp['ρ_0'].Mid(k) w_i = q['w', i][k] w_env = q['w', i_env].values[k] a_k = q['a', i].Mid(k) entr_w = tmp['entr_sc', i].Mid(k) detr_w = tmp['detr_sc', i].Mid(k) B_k = tmp['B', i].Mid(k) a_cut = q['a', i].DualCut(k) ρ_cut = tmp['ρ_0'].DualCut(k) w_cut = q['w', i].Cut(k) ρa_k = ρ_k * a_k ρa_new_k = ρ_k * a_new_k ρaw_k = ρa_k * w_i ρaww_cut = ρ_cut * a_cut * w_cut * w_cut adv = -advect(ρaww_cut, w_cut, grid) exch = ρaw_k * (-detr_w * w_i + entr_w * w_env) buoy = ρa_k * B_k press_buoy = -ρa_k * B_k * params.pressure_buoy_coeff p_coeff = params.pressure_drag_coeff / params.pressure_plume_spacing press_drag = -ρa_k * (p_coeff * (w_i - w_env)**2.0 / np.sqrt( np.fmax(a_k, params.minimum_area))) nh_press = press_buoy + press_drag q_new['w', i][k] = ρaw_k / ρa_new_k + TS.Δt_up / ρa_new_k * ( adv + exch + buoy + nh_press) # Filter results for i in i_uds: for k in grid.over_elems_real(Center()): if q_new['a', i].Mid(k) >= params.minimum_area: if q_new['w', i][k] <= 0.0: q_new['w', i][k:] = 0.0 q_new['a', i][k + 1:] = 0.0 break else: q_new['w', i][k:] = 0.0 q_new['a', i][k + 1:] = 0.0 break return
def initialize_ref_state(grid, Stats, p_0, ρ_0, α_0, loc, sg, Pg, Tg, qtg): sg = t_to_entropy_c(Pg, Tg, qtg, 0.0, 0.0) # Form a right hand side for integrating the hydrostatic equation to # determine the reference pressure def rhs(p, z): T, q_l = eos_entropy(np.exp(p), qtg, sg) q_i = 0.0 R_m = Rd * (1.0 - qtg + eps_vi * (qtg - q_l - q_i)) return -g / (R_m * T) # Construct arrays for integration points z_full = [grid.z[k] for k in grid.over_elems_real(Node())] z_half = [grid.z_half[k] for k in grid.over_elems_real(Center())] z = z_full if isinstance(loc, Node) else z_half # We are integrating the log pressure so need to take the log of the # surface pressure q_liq = Field.field(grid, loc) q_ice = Field.field(grid, loc) q_vap = Field.field(grid, loc) temperature = Field.field(grid, loc) p0 = np.log(Pg) p_0[grid.slice_real(loc)] = odeint(rhs, p0, z, hmax=1.0)[:, 0] p_0.apply_Neumann(grid, 0.0) p_0[:] = np.exp(p_0[:]) # Compute reference state thermodynamic profiles for k in grid.over_elems_real(loc): temperature[k], q_liq[k] = eos_entropy(p_0[k], qtg, sg) q_vap[k] = qtg - (q_liq[k] + q_ice[k]) α_0[k] = alpha_c(p_0[k], temperature[k], qtg, q_vap[k]) ρ_0[k] = 1.0 / α_0[k] # Sanity check: make sure Reference State entropy is uniform for k in grid.over_elems(loc): s = t_to_entropy_c(p_0[k], temperature[k], qtg, q_liq[k], q_ice[k]) if np.abs(s - sg) / sg > 0.01: print('Error in reference profiles entropy not constant !') print('Likely error in saturation adjustment') α_0.extrap(grid) p_0.extrap(grid) ρ_0.extrap(grid) p_0_name = nice_name('p_0') + str(loc.__class__.__name__) ρ_0_name = nice_name('ρ_0') + str(loc.__class__.__name__) α_0_name = nice_name('α_0') + str(loc.__class__.__name__) plt.plot(p_0.values, grid.z) plt.title(p_0_name + ' vs z') plt.xlabel(p_0_name) plt.ylabel('z') plt.savefig(Stats.figpath + p_0_name + '.png') plt.close() plt.plot(ρ_0.values, grid.z) plt.title(ρ_0_name + ' vs z') plt.xlabel(ρ_0_name) plt.ylabel('z') plt.savefig(Stats.figpath + ρ_0_name + '.png') plt.close() plt.plot(α_0.values, grid.z) plt.title(α_0_name + ' vs z') plt.xlabel(α_0_name) plt.ylabel('z') plt.savefig(Stats.figpath + α_0_name + '.png') plt.close() p_0.export_data(grid, Stats.outpath + p_0_name + '.dat') ρ_0.export_data(grid, Stats.outpath + ρ_0_name + '.dat') α_0.export_data(grid, Stats.outpath + α_0_name + '.dat') k_1 = grid.boundary(Zmin()) k_2 = grid.boundary(Zmax()) Stats.add_reference_profile(p_0_name) Stats.write_reference_profile(p_0_name, α_0[k_1:k_2]) Stats.add_reference_profile(ρ_0_name) Stats.write_reference_profile(ρ_0_name, p_0[k_1:k_2]) Stats.add_reference_profile(α_0_name) Stats.write_reference_profile(α_0_name, ρ_0[k_1:k_2]) return
def write_profile_new(self, var_name, grid, data): var = self.profiles_grp.variables[var_name] k_1 = grid.boundary(Zmin()) k_2 = grid.boundary(Zmax()) var[-1, :] = np.array(data[k_1:k_2]) return