def test_vortex_init(ctx_factory): """ Simple test to check that Vortex2D initializer creates the expected solution field. """ cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) actx = PyOpenCLArrayContext(queue) dim = 2 nel_1d = 4 from meshmode.mesh.generation import generate_regular_rect_mesh mesh = generate_regular_rect_mesh(a=[(0.0, ), (-5.0, )], b=[(10.0, ), (5.0, )], nelements_per_axis=(nel_1d, ) * dim) order = 3 logger.info(f"Number of elements: {mesh.nelements}") discr = EagerDGDiscretization(actx, mesh, order=order) nodes = thaw(actx, discr.nodes()) # Init soln with Vortex vortex = Vortex2D() cv = vortex(nodes) gamma = 1.4 p = 0.4 * (cv.energy - 0.5 * np.dot(cv.momentum, cv.momentum) / cv.mass) exp_p = cv.mass**gamma errmax = discr.norm(p - exp_p, np.inf) logger.info(f"vortex_soln = {cv}") logger.info(f"pressure = {p}") assert errmax < 1e-15
def test_symbolic_evaluation(actx_factory): """ Evaluate a symbolic expression by plugging in numbers and :class:`~meshmode.dof_array.DOFArray`s and compare the result to the equivalent quantity computed explicitly. """ actx = actx_factory() mesh = generate_regular_rect_mesh( a=(-np.pi/2,)*2, b=(np.pi/2,)*2, nelements_per_axis=(4,)*2) from grudge.eager import EagerDGDiscretization discr = EagerDGDiscretization(actx, mesh, order=2) nodes = thaw(actx, discr.nodes()) sym_coords = pmbl.make_sym_vector("x", 2) sym_f = ( pmbl.var("exp")(-pmbl.var("t")) * pmbl.var("cos")(sym_coords[0]) * pmbl.var("sin")(sym_coords[1])) t = 0.5 f = sym.EvaluationMapper({"t": t, "x": nodes})(sym_f) expected_f = np.exp(-t) * actx.np.cos(nodes[0]) * actx.np.sin(nodes[1]) assert actx.to_numpy(discr.norm(f - expected_f)/discr.norm(expected_f)) < 1e-12
def test_wave_stability(actx_factory, problem, timestep_scale, order, visualize=False): """Checks stability of the wave operator for a given problem setup. Adjust *timestep_scale* to get timestep close to stability limit. """ actx = actx_factory() p = problem sym_u, sym_v, sym_f, sym_rhs = sym_wave(p.dim, p.sym_phi) mesh = p.mesh_factory(8) from grudge.eager import EagerDGDiscretization discr = EagerDGDiscretization(actx, mesh, order=order) nodes = thaw(actx, discr.nodes()) def sym_eval(expr, t): return sym.EvaluationMapper({"c": p.c, "x": nodes, "t": t})(expr) def get_rhs(t, w): result = wave_operator(discr, c=p.c, w=w) result[0] += sym_eval(sym_f, t) return result t = 0. u = sym_eval(sym_u, t) v = sym_eval(sym_v, t) fields = flat_obj_array(u, v) from mirgecom.integrators import rk4_step dt = timestep_scale / order**2 for istep in range(10): fields = rk4_step(fields, t, dt, get_rhs) t += dt expected_u = sym_eval(sym_u, 10 * dt) expected_v = sym_eval(sym_v, 10 * dt) expected_fields = flat_obj_array(expected_u, expected_v) if visualize: from grudge.shortcuts import make_visualizer vis = make_visualizer(discr, discr.order) vis.write_vtk_file("wave_stability.vtu", [ ("u", fields[0]), ("v", fields[1:]), ("u_expected", expected_fields[0]), ("v_expected", expected_fields[1:]), ]) err = discr.norm(fields - expected_fields, np.inf) max_err = discr.norm(expected_fields, np.inf) assert err < max_err
def test_pulse(ctx_factory, dim): """ Test of Gaussian pulse generator. If it looks, walks, and quacks like a Gaussian, then ... """ cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) actx = PyOpenCLArrayContext(queue) nel_1d = 10 from meshmode.mesh.generation import generate_regular_rect_mesh mesh = generate_regular_rect_mesh(a=(-0.5, ) * dim, b=(0.5, ) * dim, nelements_per_axis=(nel_1d, ) * dim) order = 1 print(f"Number of elements: {mesh.nelements}") discr = EagerDGDiscretization(actx, mesh, order=order) nodes = thaw(actx, discr.nodes()) print(f"DIM = {dim}, {len(nodes)}") print(f"Nodes={nodes}") tol = 1e-15 from mirgecom.initializers import make_pulse amp = 1.0 w = .1 rms2 = w * w r0 = np.zeros(dim) r2 = np.dot(nodes, nodes) / rms2 pulse = make_pulse(amp=amp, r0=r0, w=w, r=nodes) print(f"Pulse = {pulse}") # does it return the expected exponential? pulse_check = actx.np.exp(-.5 * r2) print(f"exact: {pulse_check}") pulse_resid = pulse - pulse_check print(f"pulse residual: {pulse_resid}") assert (discr.norm(pulse_resid, np.inf) < tol) # proper scaling with amplitude? amp = 2.0 pulse = 0 pulse = make_pulse(amp=amp, r0=r0, w=w, r=nodes) pulse_resid = pulse - (pulse_check + pulse_check) assert (discr.norm(pulse_resid, np.inf) < tol) # proper scaling with r? amp = 1.0 rcheck = np.sqrt(2.0) * nodes pulse = make_pulse(amp=amp, r0=r0, w=w, r=rcheck) assert (discr.norm(pulse - (pulse_check * pulse_check), np.inf) < tol) # proper scaling with w? w = w / np.sqrt(2.0) pulse = make_pulse(amp=amp, r0=r0, w=w, r=nodes) assert (discr.norm(pulse - (pulse_check * pulse_check), np.inf) < tol)
def test_inviscid_flux_components(actx_factory, dim): """Test uniform pressure case. Checks that the Euler-internal inviscid flux routine :func:`mirgecom.inviscid.inviscid_flux` returns exactly the expected result with a constant pressure and no flow. Expected inviscid flux is: F(q) = <rhoV, (E+p)V, rho(V.x.V) + pI> Checks that only diagonal terms of the momentum flux: [ rho(V.x.V) + pI ] are non-zero and return the correctly calculated p. """ actx = actx_factory() eos = IdealSingleGas() p0 = 1.0 nel_1d = 4 from meshmode.mesh.generation import generate_regular_rect_mesh mesh = generate_regular_rect_mesh(a=(-0.5, ) * dim, b=(0.5, ) * dim, nelements_per_axis=(nel_1d, ) * dim) order = 3 discr = EagerDGDiscretization(actx, mesh, order=order) eos = IdealSingleGas() logger.info(f"Number of {dim}d elems: {mesh.nelements}") # === this next block tests 1,2,3 dimensions, # with single and multiple nodes/states. The # purpose of this block is to ensure that when # all components of V = 0, the flux recovers # the expected values (and p0 within tolerance) # === with V = 0, fixed P = p0 tolerance = 1e-15 nodes = thaw(actx, discr.nodes()) mass = discr.zeros(actx) + np.dot(nodes, nodes) + 1.0 mom = make_obj_array([discr.zeros(actx) for _ in range(dim)]) p_exact = discr.zeros(actx) + p0 energy = p_exact / 0.4 + 0.5 * np.dot(mom, mom) / mass cv = make_conserved(dim, mass=mass, energy=energy, momentum=mom) p = eos.pressure(cv) flux = inviscid_flux(discr, eos, cv) assert discr.norm(p - p_exact, np.inf) < tolerance logger.info(f"{dim}d flux = {flux}") # for velocity zero, these components should be == zero assert discr.norm(flux.mass, 2) == 0.0 assert discr.norm(flux.energy, 2) == 0.0 # The momentum diagonal should be p # Off-diagonal should be identically 0 assert discr.norm(flux.momentum - p0 * np.identity(dim), np.inf) < tolerance
def test_wave_accuracy(actx_factory, problem, order, visualize=False): """Checks accuracy of the wave operator for a given problem setup. """ actx = actx_factory() p = problem sym_u, sym_v, sym_f, sym_rhs = sym_wave(p.dim, p.sym_phi) from pytools.convergence import EOCRecorder eoc_rec = EOCRecorder() for n in [8, 10, 12] if p.dim == 3 else [8, 12, 16]: mesh = p.mesh_factory(n) from grudge.eager import EagerDGDiscretization discr = EagerDGDiscretization(actx, mesh, order=order) nodes = thaw(actx, discr.nodes()) def sym_eval(expr, t): return sym.EvaluationMapper({"c": p.c, "x": nodes, "t": t})(expr) t_check = 1.23456789 u = sym_eval(sym_u, t_check) v = sym_eval(sym_v, t_check) fields = flat_obj_array(u, v) rhs = wave_operator(discr, c=p.c, w=fields) rhs[0] = rhs[0] + sym_eval(sym_f, t_check) expected_rhs = sym_eval(sym_rhs, t_check) rel_linf_err = actx.to_numpy( discr.norm(rhs - expected_rhs, np.inf) / discr.norm(expected_rhs, np.inf)) eoc_rec.add_data_point(1. / n, rel_linf_err) if visualize: from grudge.shortcuts import make_visualizer vis = make_visualizer(discr, discr.order) vis.write_vtk_file( "wave_accuracy_{order}_{n}.vtu".format(order=order, n=n), [ ("u", fields[0]), ("v", fields[1:]), ("rhs_u_actual", rhs[0]), ("rhs_v_actual", rhs[1:]), ("rhs_u_expected", expected_rhs[0]), ("rhs_v_expected", expected_rhs[1:]), ]) print("Approximation error:") print(eoc_rec) assert (eoc_rec.order_estimate() >= order - 0.5 or eoc_rec.max_error() < 1e-11)
def test_lump_rhs(actx_factory, dim, order): """Test the inviscid rhs using the non-trivial mass lump case. The case is tested against the analytic expressions of the RHS. Checks several different orders and refinement levels to check error behavior. """ actx = actx_factory() tolerance = 1e-10 maxxerr = 0.0 from pytools.convergence import EOCRecorder eoc_rec = EOCRecorder() for nel_1d in [4, 8, 12]: from meshmode.mesh.generation import ( generate_regular_rect_mesh, ) mesh = generate_regular_rect_mesh( a=(-5, ) * dim, b=(5, ) * dim, nelements_per_axis=(nel_1d, ) * dim, ) logger.info(f"Number of elements: {mesh.nelements}") discr = EagerDGDiscretization(actx, mesh, order=order) nodes = thaw(actx, discr.nodes()) # Init soln with Lump and expected RHS = 0 center = np.zeros(shape=(dim, )) velocity = np.zeros(shape=(dim, )) lump = Lump(dim=dim, center=center, velocity=velocity) lump_soln = lump(nodes) boundaries = { BTAG_ALL: PrescribedInviscidBoundary(fluid_solution_func=lump) } inviscid_rhs = euler_operator(discr, eos=IdealSingleGas(), boundaries=boundaries, cv=lump_soln, time=0.0) expected_rhs = lump.exact_rhs(discr, cv=lump_soln, time=0) err_max = discr.norm((inviscid_rhs - expected_rhs).join(), np.inf) if err_max > maxxerr: maxxerr = err_max eoc_rec.add_data_point(1.0 / nel_1d, err_max) logger.info(f"Max error: {maxxerr}") logger.info(f"Error for (dim,order) = ({dim},{order}):\n" f"{eoc_rec}") assert (eoc_rec.order_estimate() >= order - 0.5 or eoc_rec.max_error() < tolerance)
def test_velocity_gradient_eoc(actx_factory, dim): """Test that the velocity gradient converges at the proper rate.""" from mirgecom.fluid import velocity_gradient actx = actx_factory() order = 3 from pytools.convergence import EOCRecorder eoc = EOCRecorder() nel_1d_0 = 4 for hn1 in [1, 2, 3, 4]: nel_1d = hn1 * nel_1d_0 h = 1/nel_1d from meshmode.mesh.generation import generate_regular_rect_mesh mesh = generate_regular_rect_mesh( a=(1.0,) * dim, b=(2.0,) * dim, nelements_per_axis=(nel_1d,) * dim ) discr = EagerDGDiscretization(actx, mesh, order=order) nodes = thaw(actx, discr.nodes()) zeros = discr.zeros(actx) energy = zeros + 2.5 mass = nodes[dim-1]*nodes[dim-1] velocity = make_obj_array([actx.np.cos(nodes[i]) for i in range(dim)]) mom = mass*velocity q = join_conserved(dim, mass=mass, energy=energy, momentum=mom) cv = split_conserved(dim, q) grad_q = obj_array_vectorize(discr.grad, q) grad_cv = split_conserved(dim, grad_q) grad_v = velocity_gradient(discr, cv, grad_cv) def exact_grad_row(xdata, gdim, dim): exact_grad_row = make_obj_array([zeros for _ in range(dim)]) exact_grad_row[gdim] = -actx.np.sin(xdata) return exact_grad_row comp_err = make_obj_array([ discr.norm(grad_v[i] - exact_grad_row(nodes[i], i, dim), np.inf) for i in range(dim)]) err_max = comp_err.max() eoc.add_data_point(h, err_max) logger.info(eoc) assert ( eoc.order_estimate() >= order - 0.5 or eoc.max_error() < 1e-9 )
def test_viscous_stress_tensor(actx_factory, transport_model): """Test tau data structure and values against exact.""" actx = actx_factory() dim = 3 nel_1d = 4 from meshmode.mesh.generation import generate_regular_rect_mesh mesh = generate_regular_rect_mesh(a=(1.0, ) * dim, b=(2.0, ) * dim, nelements_per_axis=(nel_1d, ) * dim) order = 1 discr = EagerDGDiscretization(actx, mesh, order=order) nodes = thaw(actx, discr.nodes()) zeros = discr.zeros(actx) ones = zeros + 1.0 # assemble velocities for simple, unique grad components velocity_x = nodes[0] + 2 * nodes[1] + 3 * nodes[2] velocity_y = 4 * nodes[0] + 5 * nodes[1] + 6 * nodes[2] velocity_z = 7 * nodes[0] + 8 * nodes[1] + 9 * nodes[2] velocity = make_obj_array([velocity_x, velocity_y, velocity_z]) mass = 2 * ones energy = zeros + 2.5 mom = mass * velocity cv = make_conserved(dim, mass=mass, energy=energy, momentum=mom) grad_cv = op.local_grad(discr, cv) if transport_model: tv_model = SimpleTransport(bulk_viscosity=1.0, viscosity=0.5) else: tv_model = PowerLawTransport() eos = IdealSingleGas() gas_model = GasModel(eos=eos, transport=tv_model) fluid_state = make_fluid_state(cv, gas_model) mu = tv_model.viscosity(eos, cv) lam = tv_model.volume_viscosity(eos, cv) # Exact answer for tau exp_grad_v = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) exp_grad_v_t = np.array([[1, 4, 7], [2, 5, 8], [3, 6, 9]]) exp_div_v = 15 exp_tau = (mu * (exp_grad_v + exp_grad_v_t) + lam * exp_div_v * np.eye(3)) from mirgecom.viscous import viscous_stress_tensor tau = viscous_stress_tensor(fluid_state, grad_cv) # The errors come from grad_v assert actx.to_numpy(discr.norm(tau - exp_tau, np.inf)) < 1e-12
def test_inviscid_mom_flux_components(actx_factory, dim, livedim): r"""Test components of the momentum flux with constant pressure, V != 0. Checks that the flux terms are returned in the proper order by running only 1 non-zero velocity component at-a-time. """ actx = actx_factory() eos = IdealSingleGas() p0 = 1.0 nel_1d = 4 from meshmode.mesh.generation import generate_regular_rect_mesh mesh = generate_regular_rect_mesh(a=(-0.5, ) * dim, b=(0.5, ) * dim, nelements_per_axis=(nel_1d, ) * dim) order = 3 discr = EagerDGDiscretization(actx, mesh, order=order) nodes = thaw(actx, discr.nodes()) tolerance = 1e-15 for livedim in range(dim): mass = discr.zeros(actx) + 1.0 + np.dot(nodes, nodes) mom = make_obj_array([discr.zeros(actx) for _ in range(dim)]) mom[livedim] = mass p_exact = discr.zeros(actx) + p0 energy = (p_exact / (eos.gamma() - 1.0) + 0.5 * np.dot(mom, mom) / mass) cv = make_conserved(dim, mass=mass, energy=energy, momentum=mom) p = eos.pressure(cv) from mirgecom.gas_model import GasModel, make_fluid_state state = make_fluid_state(cv, GasModel(eos=eos)) def inf_norm(x): return actx.to_numpy(discr.norm(x, np.inf)) assert inf_norm(p - p_exact) < tolerance flux = inviscid_flux(state) logger.info(f"{dim}d flux = {flux}") vel_exact = mom / mass # first two components should be nonzero in livedim only assert inf_norm(flux.mass - mom) == 0 eflux_exact = (energy + p_exact) * vel_exact assert inf_norm(flux.energy - eflux_exact) == 0 logger.info("Testing momentum") xpmomflux = mass * np.outer(vel_exact, vel_exact) + p_exact * np.identity(dim) assert inf_norm(flux.momentum - xpmomflux) < tolerance
def test_species_mass_gradient(actx_factory, dim): """Test gradY structure and values against exact.""" actx = actx_factory() nel_1d = 4 from meshmode.mesh.generation import generate_regular_rect_mesh mesh = generate_regular_rect_mesh( a=(1.0,) * dim, b=(2.0,) * dim, nelements_per_axis=(nel_1d,) * dim ) order = 1 discr = EagerDGDiscretization(actx, mesh, order=order) nodes = thaw(actx, discr.nodes()) zeros = discr.zeros(actx) ones = zeros + 1 nspecies = 2*dim mass = 2*ones # make mass != 1 energy = zeros + 2.5 velocity = make_obj_array([ones for _ in range(dim)]) mom = mass * velocity # assemble y so that each one has simple, but unique grad components y = make_obj_array([ones for _ in range(nspecies)]) for idim in range(dim): ispec = 2*idim y[ispec] = ispec*(idim*dim+1)*sum([(iidim+1)*nodes[iidim] for iidim in range(dim)]) y[ispec+1] = -y[ispec] species_mass = mass*y cv = make_conserved(dim, mass=mass, energy=energy, momentum=mom, species_mass=species_mass) from grudge.op import local_grad grad_cv = local_grad(discr, cv) from mirgecom.fluid import species_mass_fraction_gradient grad_y = species_mass_fraction_gradient(cv, grad_cv) assert grad_y.shape == (nspecies, dim) from meshmode.dof_array import DOFArray assert type(grad_y[0, 0]) == DOFArray def inf_norm(x): return actx.to_numpy(discr.norm(x, np.inf)) tol = 1e-11 for idim in range(dim): ispec = 2*idim exact_grad = np.array([(ispec*(idim*dim+1))*(iidim+1) for iidim in range(dim)]) assert inf_norm(grad_y[ispec] - exact_grad) < tol assert inf_norm(grad_y[ispec+1] + exact_grad) < tol
def test_idealsingle_lump(ctx_factory): """Test EOS with mass lump. Tests that the IdealSingleGas EOS returns the correct (uniform) pressure for the Lump solution field. """ cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) actx = PyOpenCLArrayContext(queue) dim = 2 nel_1d = 4 from meshmode.mesh.generation import generate_regular_rect_mesh mesh = generate_regular_rect_mesh(a=[(0.0, ), (-5.0, )], b=[(10.0, ), (5.0, )], n=(nel_1d, ) * dim) order = 3 logger.info(f"Number of elements {mesh.nelements}") discr = EagerDGDiscretization(actx, mesh, order=order) nodes = thaw(actx, discr.nodes()) # Init soln with Vortex center = np.zeros(shape=(dim, )) velocity = np.zeros(shape=(dim, )) center[0] = 5 velocity[0] = 1 lump = Lump(center=center, velocity=velocity) eos = IdealSingleGas() lump_soln = lump(0, nodes) cv = split_conserved(dim, lump_soln) p = eos.pressure(cv) exp_p = 1.0 errmax = discr.norm(p - exp_p, np.inf) exp_ke = 0.5 * cv.mass ke = eos.kinetic_energy(cv) kerr = discr.norm(ke - exp_ke, np.inf) te = eos.total_energy(cv, p) terr = discr.norm(te - cv.energy, np.inf) logger.info(f"lump_soln = {lump_soln}") logger.info(f"pressure = {p}") assert errmax < 1e-15 assert kerr < 1e-15 assert terr < 1e-15
def test_idealsingle_lump(ctx_factory, dim): """Test IdealSingle EOS with mass lump. Tests that the IdealSingleGas EOS returns the correct (uniform) pressure for the Lump solution field. """ cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) actx = PyOpenCLArrayContext(queue) nel_1d = 4 from meshmode.mesh.generation import generate_regular_rect_mesh mesh = generate_regular_rect_mesh(a=(-0.5, ) * dim, b=(0.5, ) * dim, nelements_per_axis=(nel_1d, ) * dim) order = 3 logger.info(f"Number of elements {mesh.nelements}") discr = EagerDGDiscretization(actx, mesh, order=order) from meshmode.dof_array import thaw nodes = thaw(actx, discr.nodes()) # Init soln with Vortex center = np.zeros(shape=(dim, )) velocity = np.zeros(shape=(dim, )) velocity[0] = 1 lump = Lump(dim=dim, center=center, velocity=velocity) eos = IdealSingleGas() cv = lump(nodes) def inf_norm(x): return actx.to_numpy(discr.norm(x, np.inf)) p = eos.pressure(cv) exp_p = 1.0 errmax = inf_norm(p - exp_p) exp_ke = 0.5 * cv.mass ke = eos.kinetic_energy(cv) kerr = inf_norm(ke - exp_ke) te = eos.total_energy(cv, p) terr = inf_norm(te - cv.energy) logger.info(f"lump_soln = {cv}") logger.info(f"pressure = {p}") assert errmax < 1e-15 assert kerr < 1e-15 assert terr < 1e-15
def test_uniform_init(ctx_factory, dim, nspecies): """Test the uniform flow initializer. Simple test to check that uniform initializer creates the expected solution field. """ cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) actx = PyOpenCLArrayContext(queue) nel_1d = 4 from meshmode.mesh.generation import generate_regular_rect_mesh mesh = generate_regular_rect_mesh(a=[(0.0, ), (-5.0, )], b=[(10.0, ), (5.0, )], nelements_per_axis=(nel_1d, ) * dim) order = 3 logger.info(f"Number of elements: {mesh.nelements}") discr = EagerDGDiscretization(actx, mesh, order=order) nodes = thaw(actx, discr.nodes()) velocity = np.ones(shape=(dim, )) from mirgecom.initializers import Uniform mass_fracs = np.array([float(ispec + 1) for ispec in range(nspecies)]) initializer = Uniform(dim=dim, mass_fracs=mass_fracs, velocity=velocity) cv = initializer(nodes) def inf_norm(data): if len(data) > 0: return discr.norm(data, np.inf) else: return 0.0 p = 0.4 * (cv.energy - 0.5 * np.dot(cv.momentum, cv.momentum) / cv.mass) exp_p = 1.0 perrmax = inf_norm(p - exp_p) exp_mass = 1.0 merrmax = inf_norm(cv.mass - exp_mass) exp_energy = 2.5 + .5 * dim eerrmax = inf_norm(cv.energy - exp_energy) exp_species_mass = exp_mass * mass_fracs mferrmax = inf_norm(cv.species_mass - exp_species_mass) assert perrmax < 1e-15 assert merrmax < 1e-15 assert eerrmax < 1e-15 assert mferrmax < 1e-15
def test_idealsingle_vortex(ctx_factory): r"""Test EOS with isentropic vortex. Tests that the IdealSingleGas EOS returns the correct pressure (p) for the Vortex2D solution field (i.e. $p = \rho^{\gamma}$). """ cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) actx = PyOpenCLArrayContext(queue) dim = 2 nel_1d = 4 from meshmode.mesh.generation import generate_regular_rect_mesh mesh = generate_regular_rect_mesh(a=[(0.0, ), (-5.0, )], b=[(10.0, ), (5.0, )], nelements_per_axis=(nel_1d, ) * dim) order = 3 logger.info(f"Number of elements {mesh.nelements}") discr = EagerDGDiscretization(actx, mesh, order=order) from meshmode.dof_array import thaw nodes = thaw(actx, discr.nodes()) eos = IdealSingleGas() # Init soln with Vortex vortex = Vortex2D() cv = vortex(nodes) def inf_norm(x): return actx.to_numpy(discr.norm(x, np.inf)) gamma = eos.gamma() p = eos.pressure(cv) exp_p = cv.mass**gamma errmax = inf_norm(p - exp_p) exp_ke = 0.5 * np.dot(cv.momentum, cv.momentum) / cv.mass ke = eos.kinetic_energy(cv) kerr = inf_norm(ke - exp_ke) te = eos.total_energy(cv, p) terr = inf_norm(te - cv.energy) logger.info(f"vortex_soln = {cv}") logger.info(f"pressure = {p}") assert errmax < 1e-15 assert kerr < 1e-15 assert terr < 1e-15
def test_local_max_species_diffusivity(actx_factory, dim, array_valued): """Test the local maximum species diffusivity.""" actx = actx_factory() nel_1d = 4 from meshmode.mesh.generation import generate_regular_rect_mesh mesh = generate_regular_rect_mesh(a=(1.0, ) * dim, b=(2.0, ) * dim, nelements_per_axis=(nel_1d, ) * dim) order = 1 discr = EagerDGDiscretization(actx, mesh, order=order) nodes = thaw(actx, discr.nodes()) zeros = discr.zeros(actx) ones = zeros + 1.0 vel = .32 velocity = make_obj_array([zeros + vel for _ in range(dim)]) massval = 1 mass = massval * ones energy = zeros + 1.0 / (1.4 * .4) mom = mass * velocity species_mass = np.array([1., 2., 3.], dtype=object) cv = make_conserved(dim, mass=mass, energy=energy, momentum=mom, species_mass=species_mass) d_alpha_input = np.array([.1, .2, .3]) if array_valued: f = 1 + 0.1 * actx.np.sin(nodes[0]) d_alpha_input *= f tv_model = SimpleTransport(species_diffusivity=d_alpha_input) eos = IdealSingleGas() d_alpha = tv_model.species_diffusivity(eos, cv) from mirgecom.viscous import get_local_max_species_diffusivity expected = .3 * ones if array_valued: expected *= f calculated = get_local_max_species_diffusivity(actx, d_alpha) assert actx.to_numpy(discr.norm(calculated - expected, np.inf)) == 0
def test_velocity_gradient_structure(actx_factory): """Test gradv data structure, verifying usability with other helper routines.""" from mirgecom.fluid import velocity_gradient actx = actx_factory() dim = 3 nel_1d = 4 from meshmode.mesh.generation import generate_regular_rect_mesh mesh = generate_regular_rect_mesh( a=(1.0,) * dim, b=(2.0,) * dim, nelements_per_axis=(nel_1d,) * dim ) order = 1 discr = EagerDGDiscretization(actx, mesh, order=order) nodes = thaw(actx, discr.nodes()) zeros = discr.zeros(actx) ones = zeros + 1.0 mass = 2*ones energy = zeros + 2.5 velocity_x = nodes[0] + 2*nodes[1] + 3*nodes[2] velocity_y = 4*nodes[0] + 5*nodes[1] + 6*nodes[2] velocity_z = 7*nodes[0] + 8*nodes[1] + 9*nodes[2] velocity = make_obj_array([velocity_x, velocity_y, velocity_z]) mom = mass * velocity cv = make_conserved(dim, mass=mass, energy=energy, momentum=mom) from grudge.op import local_grad grad_cv = local_grad(discr, cv) grad_v = velocity_gradient(cv, grad_cv) tol = 1e-11 exp_result = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] exp_trans = [[1, 4, 7], [2, 5, 8], [3, 6, 9]] exp_trace = 15 assert grad_v.shape == (dim, dim) from meshmode.dof_array import DOFArray assert type(grad_v[0, 0]) == DOFArray def inf_norm(x): return actx.to_numpy(discr.norm(x, np.inf)) assert inf_norm(grad_v - exp_result) < tol assert inf_norm(grad_v.T - exp_trans) < tol assert inf_norm(np.trace(grad_v) - exp_trace) < tol
def test_vortex_rhs(actx_factory, order): """Tests the inviscid rhs using the non-trivial 2D isentropic vortex case configured to yield rhs = 0. Checks several different orders and refinement levels to check error behavior. """ actx = actx_factory() dim = 2 from pytools.convergence import EOCRecorder eoc_rec = EOCRecorder() from meshmode.mesh.generation import generate_regular_rect_mesh for nel_1d in [16, 32, 64]: mesh = generate_regular_rect_mesh( a=(-5, ) * dim, b=(5, ) * dim, n=(nel_1d, ) * dim, ) logger.info(f"Number of {dim}d elements: {mesh.nelements}") discr = EagerDGDiscretization(actx, mesh, order=order) nodes = thaw(actx, discr.nodes()) # Init soln with Vortex and expected RHS = 0 vortex = Vortex2D(center=[0, 0], velocity=[0, 0]) vortex_soln = vortex(0, nodes) boundaries = {BTAG_ALL: PrescribedBoundary(vortex)} inviscid_rhs = inviscid_operator(discr, eos=IdealSingleGas(), boundaries=boundaries, q=vortex_soln, t=0.0) err_max = discr.norm(inviscid_rhs, np.inf) eoc_rec.add_data_point(1.0 / nel_1d, err_max) message = (f"Error for (dim,order) = ({dim},{order}):\n" f"{eoc_rec}") logger.info(message) assert (eoc_rec.order_estimate() >= order - 0.5 or eoc_rec.max_error() < 1e-11)
def test_uniform(ctx_factory, dim): """ Simple test to check that Uniform initializer creates the expected solution field. """ cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) actx = PyOpenCLArrayContext(queue) nel_1d = 2 from meshmode.mesh.generation import generate_regular_rect_mesh mesh = generate_regular_rect_mesh(a=(-0.5, ) * dim, b=(0.5, ) * dim, nelements_per_axis=(nel_1d, ) * dim) order = 1 print(f"Number of elements: {mesh.nelements}") discr = EagerDGDiscretization(actx, mesh, order=order) nodes = thaw(actx, discr.nodes()) print(f"DIM = {dim}, {len(nodes)}") print(f"Nodes={nodes}") from mirgecom.initializers import Uniform initr = Uniform(dim=dim) initsoln = initr(time=0.0, x_vec=nodes) tol = 1e-15 def inf_norm(x): return actx.to_numpy(discr.norm(x, np.inf)) assert inf_norm(initsoln.mass - 1.0) < tol assert inf_norm(initsoln.energy - 2.5) < tol print(f"Uniform Soln:{initsoln}") eos = IdealSingleGas() p = eos.pressure(initsoln) print(f"Press:{p}") assert inf_norm(p - 1.0) < tol
def test_restart_cv(actx_factory, nspecies): """Test that restart can read a CV array container.""" actx = actx_factory() nel_1d = 4 dim = 3 from meshmode.mesh.generation import generate_regular_rect_mesh mesh = generate_regular_rect_mesh(a=(-0.5, ) * dim, b=(0.5, ) * dim, nelements_per_axis=(nel_1d, ) * dim) order = 3 discr = EagerDGDiscretization(actx, mesh, order=order) from meshmode.dof_array import thaw nodes = thaw(actx, discr.nodes()) mass = nodes[0] energy = nodes[1] mom = make_obj_array([nodes[2] * (i + 3) for i in range(dim)]) species_mass = None if nspecies > 0: mass_fractions = make_obj_array( [i * nodes[0] for i in range(nspecies)]) species_mass = mass * mass_fractions rst_filename = f"test_{nspecies}.pkl" from mirgecom.fluid import make_conserved test_state = make_conserved(dim, mass=mass, energy=energy, momentum=mom, species_mass=species_mass) rst_data = {"state": test_state} from mirgecom.restart import write_restart_file write_restart_file(actx, rst_data, rst_filename) from mirgecom.restart import read_restart_data restart_data = read_restart_data(actx, rst_filename) resid = test_state - restart_data["state"] assert discr.norm(resid.join(), np.inf) == 0
def test_velocity_gradient_sanity(actx_factory, dim, mass_exp, vel_fac): """Test that the grad(v) returns {0, I} for v={constant, r_xyz}.""" from mirgecom.fluid import velocity_gradient actx = actx_factory() nel_1d = 16 from meshmode.mesh.generation import generate_regular_rect_mesh mesh = generate_regular_rect_mesh(a=(1.0, ) * dim, b=(2.0, ) * dim, nelements_per_axis=(nel_1d, ) * dim) order = 3 discr = EagerDGDiscretization(actx, mesh, order=order) nodes = thaw(actx, discr.nodes()) zeros = discr.zeros(actx) ones = zeros + 1.0 mass = 1 * ones for i in range(mass_exp): mass *= (mass + i) energy = zeros + 2.5 velocity = vel_fac * nodes mom = mass * velocity cv = make_conserved(dim, mass=mass, energy=energy, momentum=mom) from grudge.op import local_grad grad_cv = make_conserved(dim, q=local_grad(discr, cv.join())) grad_v = velocity_gradient(discr, cv, grad_cv) tol = 1e-11 exp_result = vel_fac * np.eye(dim) * ones grad_v_err = [ discr.norm(grad_v[i] - exp_result[i], np.inf) for i in range(dim) ] assert max(grad_v_err) < tol
def test_lump_init(ctx_factory): """ Simple test to check that Lump initializer creates the expected solution field. """ cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) actx = PyOpenCLArrayContext(queue) dim = 2 nel_1d = 4 from meshmode.mesh.generation import generate_regular_rect_mesh mesh = generate_regular_rect_mesh(a=[(0.0, ), (-5.0, )], b=[(10.0, ), (5.0, )], n=(nel_1d, ) * dim) order = 3 logger.info(f"Number of elements: {mesh.nelements}") discr = EagerDGDiscretization(actx, mesh, order=order) nodes = thaw(actx, discr.nodes()) # Init soln with Vortex center = np.zeros(shape=(dim, )) velocity = np.zeros(shape=(dim, )) center[0] = 5 velocity[0] = 1 lump = Lump(center=center, velocity=velocity) lump_soln = lump(0, nodes) cv = split_conserved(dim, lump_soln) p = 0.4 * (cv.energy - 0.5 * np.dot(cv.momentum, cv.momentum) / cv.mass) exp_p = 1.0 errmax = discr.norm(p - exp_p, np.inf) logger.info(f"lump_soln = {lump_soln}") logger.info(f"pressure = {p}") assert errmax < 1e-15
def test_analytic_comparison(actx_factory): """Quick test of state comparison routine.""" from mirgecom.initializers import Vortex2D from mirgecom.simutil import compare_fluid_solutions, componentwise_norms actx = actx_factory() nel_1d = 4 dim = 2 from meshmode.mesh.generation import generate_regular_rect_mesh mesh = generate_regular_rect_mesh(a=(1.0, ) * dim, b=(2.0, ) * dim, nelements_per_axis=(nel_1d, ) * dim) order = 2 discr = EagerDGDiscretization(actx, mesh, order=order) nodes = thaw(discr.nodes(), actx) zeros = discr.zeros(actx) ones = zeros + 1.0 mass = ones energy = ones velocity = 2 * nodes mom = mass * velocity vortex_init = Vortex2D() vortex_soln = vortex_init(x_vec=nodes, eos=IdealSingleGas()) cv = make_conserved(dim, mass=mass, energy=energy, momentum=mom) resid = vortex_soln - cv expected_errors = actx.to_numpy( flatten(componentwise_norms(discr, resid, order=np.inf), actx)).tolist() errors = compare_fluid_solutions(discr, cv, cv) assert max(errors) == 0 errors = compare_fluid_solutions(discr, cv, vortex_soln) assert errors == expected_errors
def test_shock_init(ctx_factory): """ Simple test to check that Shock1D initializer creates the expected solution field. """ cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) actx = PyOpenCLArrayContext(queue) nel_1d = 10 dim = 2 from meshmode.mesh.generation import generate_regular_rect_mesh mesh = generate_regular_rect_mesh(a=[(0.0, ), (1.0, )], b=[(-0.5, ), (0.5, )], nelements_per_axis=(nel_1d, ) * dim) order = 3 print(f"Number of elements: {mesh.nelements}") discr = EagerDGDiscretization(actx, mesh, order=order) nodes = thaw(actx, discr.nodes()) initr = SodShock1D() initsoln = initr(time=0.0, x_vec=nodes) print("Sod Soln:", initsoln) xpl = 1.0 xpr = 0.1 tol = 1e-15 nodes_x = nodes[0] eos = IdealSingleGas() p = eos.pressure(initsoln) assert actx.to_numpy( discr.norm(actx.np.where(nodes_x < 0.5, p - xpl, p - xpr), np.inf)) < tol
def test_idealsingle_vortex(ctx_factory): r""" Tests that the IdealSingleGas EOS returns the correct pressure (p) for the Vortex2D solution field (i.e. :math:'p = \rho^{\gamma}'). """ cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) actx = PyOpenCLArrayContext(queue) dim = 2 nel_1d = 4 from meshmode.mesh.generation import generate_regular_rect_mesh mesh = generate_regular_rect_mesh(a=[(0.0, ), (-5.0, )], b=[(10.0, ), (5.0, )], n=(nel_1d, ) * dim) order = 3 logger.info(f"Number of elements {mesh.nelements}") discr = EagerDGDiscretization(actx, mesh, order=order) nodes = thaw(actx, discr.nodes()) eos = IdealSingleGas() # Init soln with Vortex vortex = Vortex2D() vortex_soln = vortex(0, nodes) cv = split_conserved(dim, vortex_soln) gamma = eos.gamma() p = eos.pressure(cv) exp_p = cv.mass**gamma errmax = discr.norm(p - exp_p, np.inf) logger.info(f"vortex_soln = {vortex_soln}") logger.info(f"pressure = {p}") assert errmax < 1e-15
def main(ctx_factory=cl.create_some_context, use_logmgr=True, use_leap=False, use_profiling=False, casename=None, rst_filename=None, actx_class=PyOpenCLArrayContext): """Run the example.""" cl_ctx = cl.create_some_context() queue = cl.CommandQueue(cl_ctx) from mpi4py import MPI comm = MPI.COMM_WORLD num_parts = comm.Get_size() logmgr = initialize_logmgr(use_logmgr, filename="heat-source.sqlite", mode="wu", mpi_comm=comm) if use_profiling: queue = cl.CommandQueue( cl_ctx, properties=cl.command_queue_properties.PROFILING_ENABLE) else: queue = cl.CommandQueue(cl_ctx) actx = actx_class( queue, allocator=cl_tools.MemoryPool(cl_tools.ImmediateAllocator(queue))) from meshmode.distributed import MPIMeshDistributor, get_partition_by_pymetis mesh_dist = MPIMeshDistributor(comm) dim = 2 nel_1d = 16 t = 0 t_final = 0.0002 istep = 0 if mesh_dist.is_mananger_rank(): from meshmode.mesh.generation import generate_regular_rect_mesh mesh = generate_regular_rect_mesh( a=(-0.5,)*dim, b=(0.5,)*dim, nelements_per_axis=(nel_1d,)*dim, boundary_tag_to_face={ "dirichlet": ["+x", "-x"], "neumann": ["+y", "-y"] } ) print("%d elements" % mesh.nelements) part_per_element = get_partition_by_pymetis(mesh, num_parts) local_mesh = mesh_dist.send_mesh_parts(mesh, part_per_element, num_parts) del mesh else: local_mesh = mesh_dist.receive_mesh_part() order = 3 discr = EagerDGDiscretization(actx, local_mesh, order=order, mpi_communicator=comm) if dim == 2: # no deep meaning here, just a fudge factor dt = 0.0025/(nel_1d*order**2) else: raise ValueError("don't have a stable time step guesstimate") source_width = 0.2 from arraycontext import thaw nodes = thaw(discr.nodes(), actx) boundaries = { DTAG_BOUNDARY("dirichlet"): DirichletDiffusionBoundary(0.), DTAG_BOUNDARY("neumann"): NeumannDiffusionBoundary(0.) } u = discr.zeros(actx) if logmgr: logmgr_add_device_name(logmgr, queue) logmgr_add_device_memory_usage(logmgr, queue) logmgr.add_watches(["step.max", "t_step.max", "t_log.max"]) try: logmgr.add_watches(["memory_usage_python.max", "memory_usage_gpu.max"]) except KeyError: pass if use_profiling: logmgr.add_watches(["multiply_time.max"]) vis_timer = IntervalTimer("t_vis", "Time spent visualizing") logmgr.add_quantity(vis_timer) vis = make_visualizer(discr) def rhs(t, u): return ( diffusion_operator( discr, quad_tag=DISCR_TAG_BASE, alpha=1, boundaries=boundaries, u=u) + actx.np.exp(-np.dot(nodes, nodes)/source_width**2)) compiled_rhs = actx.compile(rhs) rank = comm.Get_rank() while t < t_final: if logmgr: logmgr.tick_before() if istep % 10 == 0: print(istep, t, actx.to_numpy(actx.np.linalg.norm(u[0]))) vis.write_vtk_file("fld-heat-source-mpi-%03d-%04d.vtu" % (rank, istep), [ ("u", u) ], overwrite=True) u = rk4_step(u, t, dt, compiled_rhs) t += dt istep += 1 if logmgr: set_dt(logmgr, dt) logmgr.tick_after() final_answer = discr.norm(u, np.inf) resid = abs(final_answer - 0.00020620711665201585) if resid > 1e-15: raise ValueError(f"Run did not produce the expected result {resid=}")
def main(ctx_factory=cl.create_some_context, snapshot_pattern="y0euler-{step:06d}-{rank:04d}.pkl", restart_step=None, use_profiling=False, use_logmgr=False): """Drive the Y0 example.""" from mpi4py import MPI comm = MPI.COMM_WORLD rank = 0 rank = comm.Get_rank() nparts = comm.Get_size() """logging and profiling""" logmgr = initialize_logmgr(use_logmgr, use_profiling, filename="y0euler.sqlite", mode="wu", mpi_comm=comm) cl_ctx = ctx_factory() if use_profiling: queue = cl.CommandQueue( cl_ctx, properties=cl.command_queue_properties.PROFILING_ENABLE) actx = PyOpenCLProfilingArrayContext( queue, allocator=cl_tools.MemoryPool(cl_tools.ImmediateAllocator(queue)), logmgr=logmgr) else: queue = cl.CommandQueue(cl_ctx) actx = PyOpenCLArrayContext(queue, allocator=cl_tools.MemoryPool( cl_tools.ImmediateAllocator(queue))) #nviz = 500 #nrestart = 500 nviz = 50 nrestart = 10000000 current_dt = 1.0e-7 #t_final = 5.e-7 t_final = 3e-4 dim = 2 order = 1 exittol = 10000000 # do never exit when comparing to exact solution #t_final = 0.001 current_cfl = 1.0 vel_init = np.zeros(shape=(dim, )) vel_inflow = np.zeros(shape=(dim, )) vel_outflow = np.zeros(shape=(dim, )) orig = np.zeros(shape=(dim, )) #vel[0] = 340.0 #vel_inflow[0] = 100.0 # m/s current_t = 0 casename = "y0euler" constant_cfl = False # no internal euler status messages nstatus = 1000000000 checkpoint_t = current_t current_step = 0 # working gas: CO2 # # gamma = 1.289 # MW=44.009 g/mol # cp = 37.135 J/mol-K, # rho= 1.977 kg/m^3 @298K gamma_CO2 = 1.289 R_CO2 = 8314.59 / 44.009 # background # 100 Pa # 298 K # rho = 1.77619667e-3 kg/m^3 # velocity = 0,0,0 rho_bkrnd = 1.77619667e-3 pres_bkrnd = 100 temp_bkrnd = 298 c_bkrnd = math.sqrt(gamma_CO2 * pres_bkrnd / rho_bkrnd) # isentropic shock relations # # lab frame, moving shock # state 1 is behind (downstream) the shock, state 2 is in front (upstream) of the shock mach = 2.0 pressure_ratio = (2. * gamma_CO2 * mach * mach - (gamma_CO2 - 1.)) / (gamma_CO2 + 1.) density_ratio = (gamma_CO2 + 1.) * mach * mach / ( (gamma_CO2 - 1.) * mach * mach + 2.) mach2 = math.sqrt(((gamma_CO2 - 1.) * mach * mach + 2.) / (2. * gamma_CO2 * mach * mach - (gamma_CO2 - 1.))) rho1 = rho_bkrnd pressure1 = pres_bkrnd rho2 = rho1 * density_ratio pressure2 = pressure1 * pressure_ratio velocity1 = 0. velocity2 = -mach * c_bkrnd * (1 / density_ratio - 1) c_shkd = math.sqrt(gamma_CO2 * pressure2 / rho2) vel_inflow[0] = velocity2 timestepper = rk4_step eos = IdealSingleGas(gamma=gamma_CO2, gas_const=R_CO2) bulk_init = Discontinuity(dim=dim, x0=.05, sigma=0.01, rhol=rho2, rhor=rho1, pl=pressure2, pr=pressure1, ul=vel_inflow[0], ur=0.) inflow_init = Lump(dim=dim, rho0=rho2, p0=pressure2, center=orig, velocity=vel_inflow, rhoamp=0.0) outflow_init = Lump(dim=dim, rho0=rho1, p0=pressure1, center=orig, velocity=vel_outflow, rhoamp=0.0) inflow = PrescribedBoundary(inflow_init) outflow = PrescribedBoundary(outflow_init) wall = AdiabaticSlipBoundary() dummy = DummyBoundary() # shock capturing parameters # sonic conditions density_ratio = (gamma_CO2 + 1.) * 1.0 / ((gamma_CO2 - 1.) + 2.) density_star = rho1 * density_ratio shock_thickness = 20 * 0.001 # on the order of 3 elements, should match what is in mesh generator # alpha is ~h/p (spacing/order) #alpha_sc = shock_thickness*abs(velocity1-velocity2)*density_star alpha_sc = 0.1 # sigma is ~p^-4 sigma_sc = -11.0 # kappa is empirical ... kappa_sc = 0.5 print( f"Shock capturing parameters: alpha {alpha_sc}, s0 {sigma_sc}, kappa {kappa_sc}" ) # timestep estimate wave_speed = max(mach2 * c_bkrnd, c_shkd + velocity2) char_len = 0.001 area = char_len * char_len / 2 perimeter = 2 * char_len + math.sqrt(2 * char_len * char_len) h = 2 * area / perimeter dt_est = 1 / (wave_speed * order * order / h) print(f"Time step estimate {dt_est}\n") dt_est_visc = 1 / (wave_speed * order * order / h + alpha_sc * order * order * order * order / h / h) print(f"Viscous timestep estimate {dt_est_visc}\n") from grudge import sym # boundaries = {BTAG_ALL: DummyBoundary} boundaries = { sym.DTAG_BOUNDARY("Inflow"): inflow, sym.DTAG_BOUNDARY("Outflow"): outflow, sym.DTAG_BOUNDARY("Wall"): wall } #local_mesh, global_nelements = create_parallel_grid(comm, #get_pseudo_y0_mesh) # #local_nelements = local_mesh.nelements if restart_step is None: local_mesh, global_nelements = create_parallel_grid(comm, get_mesh) local_nelements = local_mesh.nelements else: # Restart with open(snapshot_pattern.format(step=restart_step, rank=rank), "rb") as f: restart_data = pickle.load(f) local_mesh = restart_data["local_mesh"] local_nelements = local_mesh.nelements global_nelements = restart_data["global_nelements"] assert comm.Get_size() == restart_data["num_parts"] if rank == 0: logging.info("Making discretization") discr = EagerDGDiscretization(actx, local_mesh, order=order, mpi_communicator=comm) nodes = thaw(actx, discr.nodes()) if restart_step is None: if rank == 0: logging.info("Initializing soln.") current_state = bulk_init(0, nodes, eos=eos) else: current_t = restart_data["t"] current_step = restart_step current_state = unflatten( actx, discr.discr_from_dd("vol"), obj_array_vectorize(actx.from_numpy, restart_data["state"])) vis_timer = None if logmgr: logmgr_add_device_name(logmgr, queue) logmgr_add_discretization_quantities(logmgr, discr, eos, dim) #logmgr_add_package_versions(logmgr) logmgr.add_watches([ "step.max", "t_sim.max", "t_step.max", "min_pressure", "max_pressure", "min_temperature", "max_temperature" ]) try: logmgr.add_watches(["memory_usage.max"]) except KeyError: pass if use_profiling: logmgr.add_watches(["pyopencl_array_time.max"]) vis_timer = IntervalTimer("t_vis", "Time spent visualizing") logmgr.add_quantity(vis_timer) #visualizer = make_visualizer(discr, discr.order + 3 #if discr.dim == 2 else discr.order) visualizer = make_visualizer(discr, discr.order) # initname = initializer.__class__.__name__ initname = "pseudoY0" eosname = eos.__class__.__name__ init_message = make_init_message(dim=dim, order=order, nelements=local_nelements, global_nelements=global_nelements, dt=current_dt, t_final=t_final, nstatus=nstatus, nviz=nviz, cfl=current_cfl, constant_cfl=constant_cfl, initname=initname, eosname=eosname, casename=casename) if rank == 0: logger.info(init_message) get_timestep = partial(inviscid_sim_timestep, discr=discr, t=current_t, dt=current_dt, cfl=current_cfl, eos=eos, t_final=t_final, constant_cfl=constant_cfl) def my_rhs(t, state): #return inviscid_operator(discr, eos=eos, boundaries=boundaries, q=state, t=t) return (inviscid_operator( discr, q=state, t=t, boundaries=boundaries, eos=eos) + artificial_viscosity(discr, t=t, r=state, eos=eos, boundaries=boundaries, alpha=alpha_sc, sigma=sigma_sc, kappa=kappa_sc)) def my_checkpoint(step, t, dt, state): write_restart = (check_step(step, nrestart) if step != restart_step else False) if write_restart is True: with open(snapshot_pattern.format(step=step, rank=rank), "wb") as f: pickle.dump( { "local_mesh": local_mesh, "state": obj_array_vectorize(actx.to_numpy, flatten(state)), "t": t, "step": step, "global_nelements": global_nelements, "num_parts": nparts, }, f) #x0=f(time) exact_soln = Discontinuity(dim=dim, x0=.05, sigma=0.00001, rhol=rho2, rhor=rho1, pl=pressure2, pr=pressure1, ul=vel_inflow[0], ur=0., uc=mach * c_bkrnd) return sim_checkpoint(discr=discr, visualizer=visualizer, eos=eos, q=state, vizname=casename, step=step, t=t, dt=dt, nstatus=nstatus, nviz=nviz, exittol=exittol, constant_cfl=constant_cfl, comm=comm, vis_timer=vis_timer, overwrite=True, exact_soln=exact_soln, sigma=sigma_sc, kappa=kappa_sc) if rank == 0: logging.info("Stepping.") (current_step, current_t, current_state) = \ advance_state(rhs=my_rhs, timestepper=timestepper, checkpoint=my_checkpoint, get_timestep=get_timestep, state=current_state, t_final=t_final, t=current_t, istep=current_step, logmgr=logmgr,eos=eos,dim=dim) if rank == 0: logger.info("Checkpointing final state ...") my_checkpoint(current_step, t=current_t, dt=(current_t - checkpoint_t), state=current_state) if current_t - t_final < 0: raise ValueError("Simulation exited abnormally") if logmgr: logmgr.close() elif use_profiling: print(actx.tabulate_profiling_data())
def main(ctx_factory=cl.create_some_context, use_leap=False): """Drive the example.""" cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) actx = PyOpenCLArrayContext(queue, allocator=cl_tools.MemoryPool( cl_tools.ImmediateAllocator(queue))) dim = 1 nel_1d = 24 order = 1 # tolerate large errors; case is unstable exittol = .2 t_final = 0.01 current_cfl = 1.0 current_dt = .0001 current_t = 0 eos = IdealSingleGas() initializer = SodShock1D(dim=dim) casename = "sod1d" boundaries = {BTAG_ALL: PrescribedBoundary(initializer)} constant_cfl = False nstatus = 10 nviz = 10 rank = 0 checkpoint_t = current_t current_step = 0 if use_leap: from leap.rk import RK4MethodBuilder timestepper = RK4MethodBuilder("state") else: timestepper = rk4_step box_ll = -5.0 box_ur = 5.0 from mpi4py import MPI comm = MPI.COMM_WORLD rank = comm.Get_rank() from meshmode.mesh.generation import generate_regular_rect_mesh generate_mesh = partial(generate_regular_rect_mesh, a=(box_ll, ) * dim, b=(box_ur, ) * dim, nelements_per_axis=(nel_1d, ) * dim) local_mesh, global_nelements = generate_and_distribute_mesh( comm, generate_mesh) local_nelements = local_mesh.nelements discr = EagerDGDiscretization(actx, local_mesh, order=order, mpi_communicator=comm) nodes = thaw(actx, discr.nodes()) current_state = initializer(nodes) visualizer = make_visualizer(discr) initname = initializer.__class__.__name__ eosname = eos.__class__.__name__ init_message = make_init_message(dim=dim, order=order, nelements=local_nelements, global_nelements=global_nelements, dt=current_dt, t_final=t_final, nstatus=nstatus, nviz=nviz, cfl=current_cfl, constant_cfl=constant_cfl, initname=initname, eosname=eosname, casename=casename) if rank == 0: logger.info(init_message) get_timestep = partial(inviscid_sim_timestep, discr=discr, t=current_t, dt=current_dt, cfl=current_cfl, eos=eos, t_final=t_final, constant_cfl=constant_cfl) def my_rhs(t, state): return euler_operator(discr, cv=state, t=t, boundaries=boundaries, eos=eos) def my_checkpoint(step, t, dt, state): return sim_checkpoint(discr, visualizer, eos, cv=state, exact_soln=initializer, vizname=casename, step=step, t=t, dt=dt, nstatus=nstatus, nviz=nviz, exittol=exittol, constant_cfl=constant_cfl, comm=comm) try: (current_step, current_t, current_state) = \ advance_state(rhs=my_rhs, timestepper=timestepper, checkpoint=my_checkpoint, get_timestep=get_timestep, state=current_state, t=current_t, t_final=t_final) except ExactSolutionMismatch as ex: current_step = ex.step current_t = ex.t current_state = ex.state # if current_t != checkpoint_t: if rank == 0: logger.info("Checkpointing final state ...") my_checkpoint(current_step, t=current_t, dt=(current_t - checkpoint_t), state=current_state) if current_t - t_final < 0: raise ValueError("Simulation exited abnormally")
def main(ctx_factory=cl.create_some_context, use_logmgr=True, use_leap=False, use_profiling=False, casename=None, rst_filename=None, actx_class=PyOpenCLArrayContext): """Drive example.""" cl_ctx = ctx_factory() if casename is None: casename = "mirgecom" from mpi4py import MPI comm = MPI.COMM_WORLD rank = comm.Get_rank() nparts = comm.Get_size() logmgr = initialize_logmgr(use_logmgr, filename=f"{casename}.sqlite", mode="wu", mpi_comm=comm) if use_profiling: queue = cl.CommandQueue( cl_ctx, properties=cl.command_queue_properties.PROFILING_ENABLE) else: queue = cl.CommandQueue(cl_ctx) actx = actx_class( queue, allocator=cl_tools.MemoryPool(cl_tools.ImmediateAllocator(queue))) # timestepping control current_step = 0 if use_leap: from leap.rk import RK4MethodBuilder timestepper = RK4MethodBuilder("state") else: timestepper = rk4_step t_final = 0.005 current_cfl = 1.0 current_dt = .001 current_t = 0 constant_cfl = False # some i/o frequencies nstatus = 1 nrestart = 5 nviz = 1 nhealth = 1 dim = 2 rst_path = "restart_data/" rst_pattern = ( rst_path + "{cname}-{step:04d}-{rank:04d}.pkl" ) if rst_filename: # read the grid from restart data rst_filename = f"{rst_filename}-{rank:04d}.pkl" from mirgecom.restart import read_restart_data restart_data = read_restart_data(actx, rst_filename) local_mesh = restart_data["local_mesh"] local_nelements = local_mesh.nelements global_nelements = restart_data["global_nelements"] assert restart_data["nparts"] == nparts else: # generate the grid from scratch box_ll = -5.0 box_ur = 5.0 nel_1d = 16 from meshmode.mesh.generation import generate_regular_rect_mesh generate_mesh = partial(generate_regular_rect_mesh, a=(box_ll,)*dim, b=(box_ur,) * dim, nelements_per_axis=(nel_1d,)*dim) local_mesh, global_nelements = generate_and_distribute_mesh(comm, generate_mesh) local_nelements = local_mesh.nelements order = 3 discr = EagerDGDiscretization( actx, local_mesh, order=order, mpi_communicator=comm ) nodes = thaw(actx, discr.nodes()) vis_timer = None if logmgr: logmgr_add_device_name(logmgr, queue) logmgr_add_device_memory_usage(logmgr, queue) logmgr_add_many_discretization_quantities(logmgr, discr, dim, extract_vars_for_logging, units_for_logging) vis_timer = IntervalTimer("t_vis", "Time spent visualizing") logmgr.add_quantity(vis_timer) logmgr.add_watches([ ("step.max", "step = {value}, "), ("t_sim.max", "sim time: {value:1.6e} s\n"), ("min_pressure", "------- P (min, max) (Pa) = ({value:1.9e}, "), ("max_pressure", "{value:1.9e})\n"), ("t_step.max", "------- step walltime: {value:6g} s, "), ("t_log.max", "log walltime: {value:6g} s") ]) # soln setup and init nspecies = 4 centers = make_obj_array([np.zeros(shape=(dim,)) for i in range(nspecies)]) spec_y0s = np.ones(shape=(nspecies,)) spec_amplitudes = np.ones(shape=(nspecies,)) eos = IdealSingleGas() velocity = np.ones(shape=(dim,)) initializer = MulticomponentLump(dim=dim, nspecies=nspecies, spec_centers=centers, velocity=velocity, spec_y0s=spec_y0s, spec_amplitudes=spec_amplitudes) boundaries = { BTAG_ALL: PrescribedInviscidBoundary(fluid_solution_func=initializer) } if rst_filename: current_t = restart_data["t"] current_step = restart_data["step"] current_state = restart_data["state"] if logmgr: from mirgecom.logging_quantities import logmgr_set_time logmgr_set_time(logmgr, current_step, current_t) else: # Set the current state from time 0 current_state = initializer(nodes) visualizer = make_visualizer(discr) initname = initializer.__class__.__name__ eosname = eos.__class__.__name__ init_message = make_init_message(dim=dim, order=order, nelements=local_nelements, global_nelements=global_nelements, dt=current_dt, t_final=t_final, nstatus=nstatus, nviz=nviz, cfl=current_cfl, constant_cfl=constant_cfl, initname=initname, eosname=eosname, casename=casename) if rank == 0: logger.info(init_message) def my_write_status(component_errors): if rank == 0: logger.info( "------- errors=" + ", ".join("%.3g" % en for en in component_errors)) def my_write_viz(step, t, state, dv=None, exact=None, resid=None): if dv is None: dv = eos.dependent_vars(state) if exact is None: exact = initializer(x_vec=nodes, eos=eos, time=t) if resid is None: resid = state - exact viz_fields = [("cv", state), ("dv", dv), ("exact", exact), ("resid", resid)] from mirgecom.simutil import write_visfile write_visfile(discr, viz_fields, visualizer, vizname=casename, step=step, t=t, overwrite=True, vis_timer=vis_timer) def my_write_restart(step, t, state): rst_fname = rst_pattern.format(cname=casename, step=step, rank=rank) if rst_fname != rst_filename: rst_data = { "local_mesh": local_mesh, "state": state, "t": t, "step": step, "order": order, "global_nelements": global_nelements, "num_parts": nparts } from mirgecom.restart import write_restart_file write_restart_file(actx, rst_data, rst_fname, comm) def my_health_check(pressure, component_errors): health_error = False from mirgecom.simutil import check_naninf_local, check_range_local if check_naninf_local(discr, "vol", pressure) \ or check_range_local(discr, "vol", pressure, .99999999, 1.00000001): health_error = True logger.info(f"{rank=}: Invalid pressure data found.") exittol = .09 if max(component_errors) > exittol: health_error = True if rank == 0: logger.info("Solution diverged from exact soln.") return health_error def my_pre_step(step, t, dt, state): try: dv = None exact = None component_errors = None if logmgr: logmgr.tick_before() from mirgecom.simutil import check_step do_viz = check_step(step=step, interval=nviz) do_restart = check_step(step=step, interval=nrestart) do_health = check_step(step=step, interval=nhealth) do_status = check_step(step=step, interval=nstatus) if do_health: dv = eos.dependent_vars(state) exact = initializer(x_vec=nodes, eos=eos, time=t) from mirgecom.simutil import compare_fluid_solutions component_errors = compare_fluid_solutions(discr, state, exact) from mirgecom.simutil import allsync health_errors = allsync( my_health_check(dv.pressure, component_errors), comm, op=MPI.LOR ) if health_errors: if rank == 0: logger.info("Fluid solution failed health check.") raise MyRuntimeError("Failed simulation health check.") if do_restart: my_write_restart(step=step, t=t, state=state) if do_viz: if dv is None: dv = eos.dependent_vars(state) if exact is None: exact = initializer(x_vec=nodes, eos=eos, time=t) resid = state - exact my_write_viz(step=step, t=t, state=state, dv=dv, exact=exact, resid=resid) if do_status: if component_errors is None: if exact is None: exact = initializer(x_vec=nodes, eos=eos, time=t) from mirgecom.simutil import compare_fluid_solutions component_errors = compare_fluid_solutions(discr, state, exact) my_write_status(component_errors) except MyRuntimeError: if rank == 0: logger.info("Errors detected; attempting graceful exit.") my_write_viz(step=step, t=t, state=state) my_write_restart(step=step, t=t, state=state) raise dt = get_sim_timestep(discr, state, t, dt, current_cfl, eos, t_final, constant_cfl) return state, dt def my_post_step(step, t, dt, state): # Logmgr needs to know about EOS, dt, dim? # imo this is a design/scope flaw if logmgr: set_dt(logmgr, dt) set_sim_state(logmgr, dim, state, eos) logmgr.tick_after() return state, dt def my_rhs(t, state): return euler_operator(discr, cv=state, time=t, boundaries=boundaries, eos=eos) current_dt = get_sim_timestep(discr, current_state, current_t, current_dt, current_cfl, eos, t_final, constant_cfl) current_step, current_t, current_state = \ advance_state(rhs=my_rhs, timestepper=timestepper, pre_step_callback=my_pre_step, dt=current_dt, post_step_callback=my_post_step, state=current_state, t=current_t, t_final=t_final) # Dump the final data if rank == 0: logger.info("Checkpointing final state ...") final_dv = eos.dependent_vars(current_state) final_exact = initializer(x_vec=nodes, eos=eos, time=current_t) final_resid = current_state - final_exact my_write_viz(step=current_step, t=current_t, state=current_state, dv=final_dv, exact=final_exact, resid=final_resid) my_write_restart(step=current_step, t=current_t, state=current_state) if logmgr: logmgr.close() elif use_profiling: print(actx.tabulate_profiling_data()) finish_tol = 1e-16 time_err = current_t - t_final if np.abs(time_err) > finish_tol: raise ValueError(f"Simulation did not finish at expected time {time_err=}.")
def main(ctx_factory=cl.create_some_context): cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) actx = PyOpenCLArrayContext(queue, allocator=cl_tools.MemoryPool( cl_tools.ImmediateAllocator(queue))) dim = 3 nel_1d = 16 order = 3 exittol = .09 t_final = 0.01 current_cfl = 1.0 vel = np.zeros(shape=(dim, )) orig = np.zeros(shape=(dim, )) vel[:dim] = 1.0 current_dt = .001 current_t = 0 eos = IdealSingleGas() initializer = Lump(numdim=dim, center=orig, velocity=vel) casename = "lump" boundaries = {BTAG_ALL: PrescribedBoundary(initializer)} constant_cfl = False nstatus = 1 nviz = 1 rank = 0 checkpoint_t = current_t current_step = 0 timestepper = rk4_step box_ll = -5.0 box_ur = 5.0 comm = MPI.COMM_WORLD rank = comm.Get_rank() from meshmode.mesh.generation import generate_regular_rect_mesh generate_grid = partial(generate_regular_rect_mesh, a=(box_ll, ) * dim, b=(box_ur, ) * dim, n=(nel_1d, ) * dim) local_mesh, global_nelements = create_parallel_grid(comm, generate_grid) local_nelements = local_mesh.nelements discr = EagerDGDiscretization(actx, local_mesh, order=order, mpi_communicator=comm) nodes = thaw(actx, discr.nodes()) current_state = initializer(0, nodes) visualizer = make_visualizer( discr, discr.order + 3 if discr.dim == 2 else discr.order) initname = initializer.__class__.__name__ eosname = eos.__class__.__name__ init_message = make_init_message(dim=dim, order=order, nelements=local_nelements, global_nelements=global_nelements, dt=current_dt, t_final=t_final, nstatus=nstatus, nviz=nviz, cfl=current_cfl, constant_cfl=constant_cfl, initname=initname, eosname=eosname, casename=casename) if rank == 0: logger.info(init_message) get_timestep = partial(inviscid_sim_timestep, discr=discr, t=current_t, dt=current_dt, cfl=current_cfl, eos=eos, t_final=t_final, constant_cfl=constant_cfl) def my_rhs(t, state): return inviscid_operator(discr, q=state, t=t, boundaries=boundaries, eos=eos) def my_checkpoint(step, t, dt, state): return sim_checkpoint(discr, visualizer, eos, q=state, exact_soln=initializer, vizname=casename, step=step, t=t, dt=dt, nstatus=nstatus, nviz=nviz, exittol=exittol, constant_cfl=constant_cfl, comm=comm) try: (current_step, current_t, current_state) = \ advance_state(rhs=my_rhs, timestepper=timestepper, checkpoint=my_checkpoint, get_timestep=get_timestep, state=current_state, t=current_t, t_final=t_final) except ExactSolutionMismatch as ex: current_step = ex.step current_t = ex.t current_state = ex.state # if current_t != checkpoint_t: if rank == 0: logger.info("Checkpointing final state ...") my_checkpoint(current_step, t=current_t, dt=(current_t - checkpoint_t), state=current_state) if current_t - t_final < 0: raise ValueError("Simulation exited abnormally")