예제 #1
0
 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
예제 #2
0
 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
예제 #3
0
    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
예제 #4
0
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
예제 #5
0
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
예제 #6
0
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
예제 #7
0
    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
예제 #8
0
 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
예제 #9
0
 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]
예제 #10
0
 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]
예제 #11
0
 def apply_Dirichlet(self, grid, value):
     self.values[0:grid.boundary(Zmin()) + 1] = value
     self.values[grid.boundary(Zmax()):] = value
예제 #12
0
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