import numpy as np from veros import VerosSetup, veros_routine from veros.variables import allocate, Variable from veros.core.operators import numpy as npx, update, at from veros.pyom_compat import load_pyom, setup_pyom from test_base import compare_state yt_start = -39.0 yt_end = 43 yu_start = -40.0 yu_end = 42 def set_parameter_pyom(pyom_obj): m = pyom_obj.main_module (m.nx, m.ny, m.nz) = (30, 42, 15) m.dt_mom = 4800 m.dt_tracer = 86400 / 2.0 m.runlen = 86400 * 365 m.coord_degree = 1 m.enable_cyclic_x = 1 m.congr_epsilon = 1e-8 m.congr_max_iterations = 10_000 m.ab_eps = 0.1 i = pyom_obj.isoneutral_module i.enable_neutral_diffusion = 1 i.k_iso_0 = 1000.0 i.k_iso_steep = 500.0 i.iso_dslope = 0.005 i.iso_slopec = 0.01 i.enable_skew_diffusion = 1 m.enable_hor_friction = 1 m.a_h = 2.2e5 m.enable_hor_friction_cos_scaling = 1 m.hor_friction_cospower = 1 m.enable_bottom_friction = 1 m.r_bot = 1e-5 m.enable_streamfunction = True m.enable_implicit_vert_friction = 1 t = pyom_obj.tke_module t.enable_tke = 1 t.c_k = 0.1 t.c_eps = 0.7 t.alpha_tke = 30.0 t.mxl_min = 1e-8 t.tke_mxl_choice = 2 t.kappam_min = 2e-4 i.k_gm_0 = 1000.0 e = pyom_obj.eke_module e.enable_eke = 1 e.eke_k_max = 1e4 e.eke_c_k = 0.4 e.eke_c_eps = 0.5 e.eke_cross = 2.0 e.eke_crhin = 1.0 e.eke_lmin = 100.0 e.enable_eke_superbee_advection = 1 e.enable_eke_isopycnal_diffusion = 1 i = pyom_obj.idemix_module i.enable_idemix = 1 i.enable_idemix_hor_diffusion = 1 i.enable_eke_diss_surfbot = 1 i.eke_diss_surfbot_frac = 0.2 i.enable_idemix_superbee_advection = 1 i.tau_v = 86400.0 i.jstar = 10.0 i.mu0 = 4.0 / 3.0 i.gamma = 1.57 m.eq_of_state_type = 3 def set_grid_pyom(pyom_obj): m = pyom_obj.main_module ddz = [50.0, 70.0, 100.0, 140.0, 190.0, 240.0, 290.0, 340.0, 390.0, 440.0, 490.0, 540.0, 590.0, 640.0, 690.0] m.dxt[:] = 2.0 m.dyt[:] = 2.0 m.x_origin = 0.0 m.y_origin = -40.0 m.dzt[:] = ddz[::-1] m.dzt[:] *= 1 / 2.5 def set_coriolis_pyom(pyom_obj): m = pyom_obj.main_module m.coriolis_t[:, :] = 2 * m.omega * np.sin(m.yt[None, :] / 180.0 * np.pi) def set_topography_pyom(pyom_obj): m = pyom_obj.main_module (X, Y) = np.meshgrid(m.xt, m.yt) X = X.transpose() Y = Y.transpose() m.kbot[...] = (X > 1.0) | (Y < -20) def set_initial_conditions_pyom(pyom_obj): m = pyom_obj.main_module # initial conditions m.temp[:, :, :, :] = ((1 - m.zt[None, None, :] / m.zw[0]) * 15 * m.maskt)[..., None] m.salt[:, :, :, :] = 35.0 * m.maskt[..., None] # wind stress forcing taux = np.zeros(m.ny + 1) yt = m.yt[2 : m.ny + 3] taux = (0.1e-3 * np.sin(np.pi * (m.yu[2 : m.ny + 3] - yu_start) / (-20.0 - yt_start))) * (yt < -20) + ( 0.1e-3 * (1 - np.cos(2 * np.pi * (m.yu[2 : m.ny + 3] - 10.0) / (yu_end - 10.0))) ) * (yt > 10) m.surface_taux[:, 2 : m.ny + 3] = taux * m.masku[:, 2 : m.ny + 3, -1] t = pyom_obj.tke_module t.forc_tke_surface[2:-2, 2:-2] = ( np.sqrt( (0.5 * (m.surface_taux[2:-2, 2:-2] + m.surface_taux[1:-3, 2:-2])) ** 2 + (0.5 * (m.surface_tauy[2:-2, 2:-2] + m.surface_tauy[2:-2, 1:-3])) ** 2 ) ** 1.5 ) def set_forcing_pyom(pyom_obj): m = pyom_obj.main_module t_star = ( 15 * np.invert((m.yt < -20) | (m.yt > 20)) + 15 * (m.yt - yt_start) / (-20 - yt_start) * (m.yt < -20) + 15 * (1 - (m.yt - 20) / (yt_end - 20)) * (m.yt > 20.0) ) t_rest = m.dzt[None, -1] / (30.0 * 86400.0) * m.maskt[:, :, -1] m.forc_temp_surface = t_rest * (t_star - m.temp[:, :, -1, m.tau - 1]) class ACCSetup(VerosSetup): @veros_routine def set_parameter(self, state): settings = state.settings settings.identifier = "acc" settings.nx, settings.ny, settings.nz = 30, 42, 15 settings.dt_mom = 4800 settings.dt_tracer = 86400 / 2.0 settings.runlen = 86400 * 365 settings.x_origin = 0.0 settings.y_origin = -40.0 settings.coord_degree = True settings.enable_cyclic_x = True settings.enable_streamfunction = True settings.enable_neutral_diffusion = True settings.K_iso_0 = 1000.0 settings.K_iso_steep = 500.0 settings.iso_dslope = 0.005 settings.iso_slopec = 0.01 settings.enable_skew_diffusion = True settings.enable_hor_friction = True settings.A_h = 2.2e5 settings.enable_hor_friction_cos_scaling = True settings.hor_friction_cosPower = 1 settings.enable_bottom_friction = True settings.r_bot = 1e-5 settings.enable_implicit_vert_friction = True settings.enable_tke = True settings.c_k = 0.1 settings.c_eps = 0.7 settings.alpha_tke = 30.0 settings.mxl_min = 1e-8 settings.tke_mxl_choice = 2 settings.kappaM_min = 2e-4 settings.K_gm_0 = 1000.0 settings.enable_eke = True settings.eke_k_max = 1e4 settings.eke_c_k = 0.4 settings.eke_c_eps = 0.5 settings.eke_cross = 2.0 settings.eke_crhin = 1.0 settings.eke_lmin = 100.0 settings.enable_eke_superbee_advection = True settings.enable_eke_isopycnal_diffusion = True settings.enable_idemix = 1 settings.enable_idemix_hor_diffusion = 1 settings.enable_eke_diss_surfbot = 1 settings.eke_diss_surfbot_frac = 0.2 settings.enable_idemix_superbee_advection = 1 settings.tau_v = 86400.0 settings.jstar = 10.0 settings.mu0 = 4.0 / 3.0 settings.eq_of_state_type = 3 var_meta = state.var_meta var_meta.update( t_star=Variable("t_star", ("yt",), "deg C", "Reference surface temperature"), t_rest=Variable("t_rest", ("xt", "yt"), "1/s", "Surface temperature restoring time scale"), ) @veros_routine def set_grid(self, state): vs = state.variables ddz = npx.array( [50.0, 70.0, 100.0, 140.0, 190.0, 240.0, 290.0, 340.0, 390.0, 440.0, 490.0, 540.0, 590.0, 640.0, 690.0] ) vs.dxt = update(vs.dxt, at[...], 2.0) vs.dyt = update(vs.dyt, at[...], 2.0) vs.dzt = update(vs.dzt, at[...], ddz[::-1] / 2.5) @veros_routine def set_coriolis(self, state): vs = state.variables settings = state.settings vs.coriolis_t = update( vs.coriolis_t, at[...], 2 * settings.omega * npx.sin(vs.yt[None, :] / 180.0 * settings.pi) ) @veros_routine def set_topography(self, state): vs = state.variables x, y = npx.meshgrid(vs.xt, vs.yt, indexing="ij") vs.kbot = npx.logical_or(x > 1.0, y < -20).astype("int") @veros_routine def set_initial_conditions(self, state): vs = state.variables settings = state.settings # initial conditions vs.temp = update(vs.temp, at[...], ((1 - vs.zt[None, None, :] / vs.zw[0]) * 15 * vs.maskT)[..., None]) vs.salt = update(vs.salt, at[...], 35.0 * vs.maskT[..., None]) # wind stress forcing taux = allocate(state.dimensions, ("yt",)) taux = npx.where(vs.yt < -20, 0.1e-3 * npx.sin(settings.pi * (vs.yu - yu_start) / (-20.0 - yt_start)), taux) taux = npx.where(vs.yt > 10, 0.1e-3 * (1 - npx.cos(2 * settings.pi * (vs.yu - 10.0) / (yu_end - 10.0))), taux) vs.surface_taux = taux * vs.maskU[:, :, -1] # surface heatflux forcing vs.t_star = allocate(state.dimensions, ("yt",), fill=15) vs.t_star = npx.where(vs.yt < -20, 15 * (vs.yt - yt_start) / (-20 - yt_start), vs.t_star) vs.t_star = npx.where(vs.yt > 20, 15 * (1 - (vs.yt - 20) / (yt_end - 20)), vs.t_star) vs.t_rest = vs.dzt[npx.newaxis, -1] / (30.0 * 86400.0) * vs.maskT[:, :, -1] if settings.enable_tke: vs.forc_tke_surface = update( vs.forc_tke_surface, at[2:-2, 2:-2], npx.sqrt( (0.5 * (vs.surface_taux[2:-2, 2:-2] + vs.surface_taux[1:-3, 2:-2])) ** 2 + (0.5 * (vs.surface_tauy[2:-2, 2:-2] + vs.surface_tauy[2:-2, 1:-3])) ** 2 ) ** (1.5), ) @veros_routine def set_forcing(self, state): vs = state.variables vs.forc_temp_surface = vs.t_rest * (vs.t_star - vs.temp[:, :, -1, vs.tau]) @veros_routine def set_diagnostics(self, state): pass @veros_routine def after_timestep(self, state): pass def test_acc_setup(pyom2_lib): pyom_obj = load_pyom(pyom2_lib) setup_pyom( pyom_obj, set_parameter_pyom, set_grid_pyom, set_coriolis_pyom, set_topography_pyom, set_initial_conditions_pyom, set_forcing_pyom, ) sim = ACCSetup() sim.setup() # Veros runs a streamfunction solve during setup allowed_failures = ("p_hydro",) # psin and line_psin don't quite meet the tolerance compare_state(sim.state, pyom_obj, rtol=1e-6, allowed_failures=allowed_failures)