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 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 compute_cloud_base_top_cover(grid, q, tmp, UpdVar): i_gm, i_env, i_uds, i_sd = q.domain_idx() k_2 = grid.first_interior(Zmax()) for i in i_uds: UpdVar[i].cloud_base = grid.z_half[k_2] UpdVar[i].cloud_top = 0.0 UpdVar[i].cloud_cover = 0.0 for k in grid.over_elems_real(Center()): a_ik = q['a', i][k] z_k = grid.z_half[k] if tmp['q_liq', i][k] > 1e-8 and a_ik > 1e-3: UpdVar[i].cloud_base = np.fmin(UpdVar[i].cloud_base, z_k) UpdVar[i].cloud_top = np.fmax(UpdVar[i].cloud_top, z_k) UpdVar[i].cloud_cover = np.fmax(UpdVar[i].cloud_cover, a_ik) 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 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 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 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
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 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): self.values[0:grid.boundary(Zmin()) + 1] = value self.values[grid.boundary(Zmax()):] = value
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