def write_soln(write_status=True): cv = split_conserved(dim, fields) dv = eos.dependent_vars(cv) expected_result = initializer(t, nodes) result_resid = fields - expected_result maxerr = [ np.max(np.abs(result_resid[i].get())) for i in range(dim + 2) ] mindv = [np.min(dvfld.get()) for dvfld in dv] maxdv = [np.max(dvfld.get()) for dvfld in dv] if write_status is True: statusmsg = (f"Status: Step({istep}) Time({t})\n" f"------ P({mindv[0]},{maxdv[0]})\n" f"------ T({mindv[1]},{maxdv[1]})\n" f"------ dt,cfl = ({dt},{cfl})\n" f"------ Err({maxerr})") logger.info(statusmsg) io_fields = ["cv", split_conserved(dim, fields)] io_fields += eos.split_fields(dim, dv) io_fields.append(("exact_soln", expected_result)) io_fields.append(("residual", result_resid)) nameform = casename + "-{iorank:04d}-{iostep:06d}.vtu" visfilename = nameform.format(iorank=rank, iostep=istep) vis.write_vtk_file(visfilename, io_fields) return maxerr
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) cv = split_conserved(dim, state) tagged_cells = smoothness_indicator(discr, cv.mass, s0=s0_sc, kappa=kappa_sc) viz_fields = [("sponge_sigma", gen_sponge()),("tagged cells", tagged_cells)] 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,s0=s0_sc,kappa=kappa_sc, viz_fields=viz_fields)
def sim_checkpoint(discr, visualizer, eos, q, vizname, exact_soln=None, step=0, t=0, dt=0, cfl=1.0, nstatus=-1, nviz=-1, exittol=1e-16, constant_cfl=False, comm=None, overwrite=False): """Check simulation health, status, viz dumps, and restart.""" # TODO: Add restart do_viz = check_step(step=step, interval=nviz) do_status = check_step(step=step, interval=nstatus) if do_viz is False and do_status is False: return 0 from mirgecom.euler import split_conserved cv = split_conserved(discr.dim, q) dependent_vars = eos.dependent_vars(cv) rank = 0 if comm is not None: rank = comm.Get_rank() maxerr = 0.0 if exact_soln is not None: actx = cv.mass.array_context nodes = thaw(actx, discr.nodes()) expected_state = exact_soln(t=t, x_vec=nodes, eos=eos) exp_resid = q - expected_state err_norms = [discr.norm(v, np.inf) for v in exp_resid] maxerr = max(err_norms) if do_viz: io_fields = [ ("cv", cv), ("dv", dependent_vars) ] if exact_soln is not None: exact_list = [ ("exact_soln", expected_state), ] io_fields.extend(exact_list) from mirgecom.io import make_rank_fname, make_par_fname rank_fn = make_rank_fname(basename=vizname, rank=rank, step=step, t=t) visualizer.write_parallel_vtk_file( comm, rank_fn, io_fields, overwrite=overwrite, par_manifest_filename=make_par_fname(basename=vizname, step=step, t=t)) if do_status is True: # if constant_cfl is False: # current_cfl = get_inviscid_cfl(discr=discr, q=q, # eos=eos, dt=dt) statusmesg = make_status_message(discr=discr, t=t, step=step, dt=dt, cfl=cfl, dependent_vars=dependent_vars) if exact_soln is not None: statusmesg += ( "\n------- errors=" + ", ".join("%.3g" % en for en in err_norms)) if rank == 0: logger.info(statusmesg) if maxerr > exittol: raise ExactSolutionMismatch(step, t=t, state=q)
def my_rhs(t, state): # check for some troublesome output types inf_exists = not np.isfinite(discr.norm(state, np.inf)) if inf_exists: if rank == 0: logging.info( "Non-finite values detected in simulation, exiting...") # dump right now sim_checkpoint(discr=discr, visualizer=visualizer, eos=eos, q=state, vizname=casename, step=999999999, t=t, dt=current_dt, nviz=1, exittol=exittol, constant_cfl=constant_cfl, comm=comm, vis_timer=vis_timer, overwrite=True, s0=s0_sc, kappa=kappa_sc) exit() cv = split_conserved(dim=dim, q=state) return (inviscid_operator( discr, q=state, t=t, boundaries=boundaries, eos=eos) + eos.get_species_source_terms(cv))
def test_inviscid_flux(actx_factory, dim): """Identity test - directly check inviscid flux routine :func:`mirgecom.euler.inviscid_flux` against the exact expected result. This test is designed to fail if the flux routine is broken. The expected inviscid flux is: F(q) = <rhoV, (E+p)V, rho(V.x.V) + pI> """ actx = actx_factory() nel_1d = 16 from meshmode.mesh.generation import generate_regular_rect_mesh # for dim in [1, 2, 3]: mesh = generate_regular_rect_mesh(a=(-0.5, ) * dim, b=(0.5, ) * dim, n=(nel_1d, ) * dim) order = 3 discr = EagerDGDiscretization(actx, mesh, order=order) eos = IdealSingleGas() logger.info(f"Number of {dim}d elems: {mesh.nelements}") mass = cl.clrandom.rand(actx.queue, (mesh.nelements, ), dtype=np.float64) energy = cl.clrandom.rand(actx.queue, (mesh.nelements, ), dtype=np.float64) mom = make_obj_array([ cl.clrandom.rand(actx.queue, (mesh.nelements, ), dtype=np.float64) for i in range(dim) ]) q = join_conserved(dim, mass=mass, energy=energy, momentum=mom) cv = split_conserved(dim, q) # {{{ create the expected result p = eos.pressure(cv) escale = (energy + p) / mass expected_flux = np.zeros((dim + 2, dim), dtype=object) expected_flux[0] = mom expected_flux[1] = mom * make_obj_array([escale]) for i in range(dim): for j in range(dim): expected_flux[2 + i, j] = (mom[i] * mom[j] / mass + (p if i == j else 0)) # }}} from mirgecom.euler import inviscid_flux flux = inviscid_flux(discr, eos, q) flux_resid = flux - expected_flux for i in range(dim + 2, dim): for j in range(dim): assert (la.norm(flux_resid[i, j].get())) == 0.0
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, n=(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(numdim=dim) initsoln = initr(t=0.0, x_vec=nodes) tol = 1e-15 ssoln = split_conserved(dim, initsoln) assert discr.norm(ssoln.mass - 1.0, np.inf) < tol assert discr.norm(ssoln.energy - 2.5, np.inf) < tol print(f"Uniform Soln:{initsoln}") eos = IdealSingleGas() cv = split_conserved(dim, initsoln) p = eos.pressure(cv) print(f"Press:{p}") assert discr.norm(p - 1.0, np.inf) < 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 boundary_pair(self, discr, q, btag, eos, t=0.0): """Get the interior and exterior solution on the boundary. The exterior solution is set such that there will be vanishing flux through the boundary, preserving mass, momentum (magnitude) and energy. rho_plus = rho_minus v_plus = v_minus - 2 * (v_minus . n_hat) * n_hat mom_plus = rho_plus * v_plus E_plus = E_minus """ # Grab some boundary-relevant data dim = discr.dim cv = split_conserved(dim, q) actx = cv.mass.array_context # Grab a unit normal to the boundary nhat = thaw(actx, discr.normal(btag)) # Get the interior/exterior solns int_soln = discr.project("vol", btag, q) int_cv = split_conserved(dim, int_soln) # Subtract out the 2*wall-normal component # of velocity from the velocity at the wall to # induce an equal but opposite wall-normal (reflected) wave # preserving the tangential component mom_normcomp = np.dot(int_cv.momentum, nhat) # wall-normal component wnorm_mom = nhat * mom_normcomp # wall-normal mom vec ext_mom = int_cv.momentum - 2.0 * wnorm_mom # prescribed ext momentum # Form the external boundary solution with the new momentum bndry_soln = join_conserved(dim=dim, mass=int_cv.mass, energy=int_cv.energy, momentum=ext_mom) return TracePair(btag, interior=int_soln, exterior=bndry_soln)
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, )], 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) exp_ke = 0.5 * np.dot(cv.momentum, cv.momentum) / 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"vortex_soln = {vortex_soln}") logger.info(f"pressure = {p}") assert errmax < 1e-15 assert kerr < 1e-15 assert terr < 1e-15
def __call__(self, x_vec, q, eos=IdealSingleGas()): """ Create the acoustic pulse at locations *x_vec*. Parameters ---------- t: float Current time at which the solution is desired (unused) x_vec: numpy.ndarray Nodal coordinates eos: :class:`mirgecom.eos.GasEOS` Equation of state class to be used in construction of soln (unused) """ assert len(x_vec) == self._dim cv = split_conserved(self._dim, q) return cv.replace( energy=cv.energy + _make_pulse( amp=self._amp, w=self._width, r0=self._center, r=x_vec) ).join()
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 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) cv = split_conserved(dim, state) reaction_rates = eos.get_production_rates(cv) viz_fields = [("reaction_rates", reaction_rates)] 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, viz_fields=viz_fields)
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, )], n=(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(t=0.0, x_vec=nodes) print("Sod Soln:", initsoln) xpl = 1.0 xpr = 0.1 tol = 1e-15 nodes_x = nodes[0] eos = IdealSingleGas() cv = split_conserved(dim, initsoln) p = eos.pressure(cv) assert discr.norm(actx.np.where(nodes_x < 0.5, p - xpl, p - xpr), np.inf) < tol
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, )], 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 vortex = Vortex2D() vortex_soln = vortex(0, nodes) gamma = 1.4 cv = split_conserved(dim, vortex_soln) 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 = {vortex_soln}") logger.info(f"pressure = {p}") assert errmax < 1e-15
def test_slipwall_identity(actx_factory, dim): """Identity test - check for the expected boundary solution. Checks that the slipwall implements the expected boundary solution: rho_plus = rho_minus v_plus = v_minus - 2 * (n_hat . v_minus) * n_hat mom_plus = rho_plus * v_plus E_plus = E_minus """ actx = actx_factory() 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, n=(nel_1d,) * dim ) order = 3 discr = EagerDGDiscretization(actx, mesh, order=order) nodes = thaw(actx, discr.nodes()) eos = IdealSingleGas() orig = np.zeros(shape=(dim,)) nhat = thaw(actx, discr.normal(BTAG_ALL)) # normal_mag = actx.np.sqrt(np.dot(normal, normal)) # nhat_mult = 1.0 / normal_mag # nhat = normal * make_obj_array([nhat_mult]) logger.info(f"Number of {dim}d elems: {mesh.nelements}") # for velocity going along each direction for vdir in range(dim): vel = np.zeros(shape=(dim,)) # for velocity directions +1, and -1 for parity in [1.0, -1.0]: vel[vdir] = parity # Check incoming normal initializer = Lump(center=orig, velocity=vel, rhoamp=0.0) wall = AdiabaticSlipBoundary() uniform_state = initializer(0, nodes) from functools import partial bnd_norm = partial(discr.norm, p=np.inf, dd=BTAG_ALL) bnd_pair = wall.boundary_pair(discr, uniform_state, t=0.0, btag=BTAG_ALL, eos=eos) bnd_cv_int = split_conserved(dim, bnd_pair.int) bnd_cv_ext = split_conserved(dim, bnd_pair.ext) # check that mass and energy are preserved mass_resid = bnd_cv_int.mass - bnd_cv_ext.mass mass_err = bnd_norm(mass_resid) assert mass_err == 0.0 energy_resid = bnd_cv_int.energy - bnd_cv_ext.energy energy_err = bnd_norm(energy_resid) assert energy_err == 0.0 # check that exterior momentum term is mom_interior - 2 * mom_normal mom_norm_comp = np.dot(bnd_cv_int.momentum, nhat) mom_norm = nhat * mom_norm_comp expected_mom_ext = bnd_cv_int.momentum - 2.0 * mom_norm mom_resid = bnd_cv_ext.momentum - expected_mom_ext mom_err = bnd_norm(mom_resid) assert mom_err == 0.0
def test_uniform_rhs(actx_factory, dim, order): """Tests the inviscid rhs using a trivial constant/uniform state which should yield rhs = 0 to FP. The test is performed for 1, 2, and 3 dimensions. """ actx = actx_factory() tolerance = 1e-9 maxxerr = 0.0 from pytools.convergence import EOCRecorder eoc_rec0 = EOCRecorder() eoc_rec1 = EOCRecorder() # for nel_1d in [4, 8, 12]: for nel_1d in [4, 8]: from meshmode.mesh.generation import generate_regular_rect_mesh mesh = generate_regular_rect_mesh(a=(-0.5, ) * dim, b=(0.5, ) * dim, n=(nel_1d, ) * dim) logger.info(f"Number of {dim}d elements: {mesh.nelements}") discr = EagerDGDiscretization(actx, mesh, order=order) mass_input = discr.zeros(actx) + 1 energy_input = discr.zeros(actx) + 2.5 mom_input = make_obj_array( [discr.zeros(actx) for i in range(discr.dim)]) fields = join_conserved(dim, mass=mass_input, energy=energy_input, momentum=mom_input) expected_rhs = make_obj_array( [discr.zeros(actx) for i in range(len(fields))]) boundaries = {BTAG_ALL: DummyBoundary()} inviscid_rhs = inviscid_operator(discr, eos=IdealSingleGas(), boundaries=boundaries, q=fields, t=0.0) rhs_resid = inviscid_rhs - expected_rhs resid_split = split_conserved(dim, rhs_resid) rho_resid = resid_split.mass rhoe_resid = resid_split.energy mom_resid = resid_split.momentum rhs_split = split_conserved(dim, inviscid_rhs) rho_rhs = rhs_split.mass rhoe_rhs = rhs_split.energy rhov_rhs = rhs_split.momentum message = (f"rho_rhs = {rho_rhs}\n" f"rhoe_rhs = {rhoe_rhs}\n" f"rhov_rhs = {rhov_rhs}") logger.info(message) assert discr.norm(rho_resid, np.inf) < tolerance assert discr.norm(rhoe_resid, np.inf) < tolerance for i in range(dim): assert discr.norm(mom_resid[i], np.inf) < tolerance err_max = discr.norm(rhs_resid[i], np.inf) eoc_rec0.add_data_point(1.0 / nel_1d, err_max) assert (err_max < tolerance) if err_max > maxxerr: maxxerr = err_max # set a non-zero, but uniform velocity component for i in range(len(mom_input)): mom_input[i] = discr.zeros(actx) + (-1.0)**i boundaries = {BTAG_ALL: DummyBoundary()} inviscid_rhs = inviscid_operator(discr, eos=IdealSingleGas(), boundaries=boundaries, q=fields, t=0.0) rhs_resid = inviscid_rhs - expected_rhs resid_split = split_conserved(dim, rhs_resid) rho_resid = resid_split.mass rhoe_resid = resid_split.energy mom_resid = resid_split.momentum assert discr.norm(rho_resid, np.inf) < tolerance assert discr.norm(rhoe_resid, np.inf) < tolerance for i in range(dim): assert discr.norm(mom_resid[i], np.inf) < tolerance err_max = discr.norm(rhs_resid[i], np.inf) eoc_rec1.add_data_point(1.0 / nel_1d, err_max) assert (err_max < tolerance) if err_max > maxxerr: maxxerr = err_max message = (f"V == 0 Errors:\n{eoc_rec0}" f"V != 0 Errors:\n{eoc_rec1}") print(message) assert (eoc_rec0.order_estimate() >= order - 0.5 or eoc_rec0.max_error() < 1e-9) assert (eoc_rec1.order_estimate() >= order - 0.5 or eoc_rec1.max_error() < 1e-9)
def test_facial_flux(actx_factory, order, dim): """Check the flux across element faces by prescribing states (q) with known fluxes. Only uniform states are tested currently - ensuring that the Lax-Friedrichs flux terms which are proportional to jumps in state data vanish. Since the returned fluxes use state data which has been interpolated to-and-from the element faces, this test is grid-dependent. """ actx = actx_factory() tolerance = 1e-14 p0 = 1.0 from meshmode.mesh.generation import generate_regular_rect_mesh from pytools.convergence import EOCRecorder eoc_rec0 = EOCRecorder() eoc_rec1 = EOCRecorder() for nel_1d in [4, 8, 12]: mesh = generate_regular_rect_mesh(a=(-0.5, ) * dim, b=(0.5, ) * dim, n=(nel_1d, ) * dim) logger.info(f"Number of elements: {mesh.nelements}") discr = EagerDGDiscretization(actx, mesh, order=order) mass_input = discr.zeros(actx) + 1.0 energy_input = discr.zeros(actx) + 2.5 mom_input = flat_obj_array( [discr.zeros(actx) for i in range(discr.dim)]) fields = join_conserved(dim, mass=mass_input, energy=energy_input, momentum=mom_input) from mirgecom.euler import _facial_flux interior_face_flux = _facial_flux(discr, eos=IdealSingleGas(), q_tpair=interior_trace_pair( discr, fields)) from functools import partial fnorm = partial(discr.norm, p=np.inf, dd="all_faces") iff_split = split_conserved(dim, interior_face_flux) assert fnorm(iff_split.mass) < tolerance assert fnorm(iff_split.energy) < tolerance # The expected pressure 1.0 (by design). And the flux diagonal is # [rhov_x*v_x + p] (etc) since we have zero velocities it's just p. # # The off-diagonals are zero. We get a {ndim}-vector for each # dimension, the flux for the x-component of momentum (for example) is: # f_momx = < 1.0, 0 , 0> , then we return f_momx .dot. normal, which # can introduce negative values. # # (Explanation courtesy of Mike Campbell, # https://github.com/illinois-ceesd/mirgecom/pull/44#discussion_r463304292) momerr = fnorm(iff_split.momentum) - p0 assert momerr < tolerance eoc_rec0.add_data_point(1.0 / nel_1d, momerr) # Check the boundary facial fluxes as called on a boundary dir_mass = discr.project("vol", BTAG_ALL, mass_input) dir_e = discr.project("vol", BTAG_ALL, energy_input) dir_mom = discr.project("vol", BTAG_ALL, mom_input) dir_bval = join_conserved(dim, mass=dir_mass, energy=dir_e, momentum=dir_mom) dir_bc = join_conserved(dim, mass=dir_mass, energy=dir_e, momentum=dir_mom) boundary_flux = _facial_flux(discr, eos=IdealSingleGas(), q_tpair=TracePair(BTAG_ALL, interior=dir_bval, exterior=dir_bc)) bf_split = split_conserved(dim, boundary_flux) assert fnorm(bf_split.mass) < tolerance assert fnorm(bf_split.energy) < tolerance momerr = fnorm(bf_split.momentum) - p0 assert momerr < tolerance eoc_rec1.add_data_point(1.0 / nel_1d, momerr) message = (f"standalone Errors:\n{eoc_rec0}" f"boundary Errors:\n{eoc_rec1}") logger.info(message) assert (eoc_rec0.order_estimate() >= order - 0.5 or eoc_rec0.max_error() < 1e-9) assert (eoc_rec1.order_estimate() >= order - 0.5 or eoc_rec1.max_error() < 1e-9)
def test_inviscid_mom_flux_components(actx_factory, dim, livedim): r"""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 from mirgecom.euler import inviscid_flux tolerance = 1e-15 for livedim in range(dim): for ntestnodes in [1, 10]: discr = MyDiscr(dim, ntestnodes) mass = discr.zeros(actx) mass_values = np.empty((1, ntestnodes), dtype=np.float64) mass_values[0] = np.linspace(1., ntestnodes, ntestnodes, dtype=np.float64) mass[0].set(mass_values) mom = make_obj_array([discr.zeros(actx) for _ in range(dim)]) mom[livedim] = mass p = discr.zeros(actx) + p0 energy = (p / (eos.gamma() - 1.0) + 0.5 * np.dot(mom, mom) / mass) q = join_conserved(dim, mass=mass, energy=energy, momentum=mom) cv = split_conserved(dim, q) p = eos.pressure(cv) flux = inviscid_flux(discr, eos, q) logger.info(f"{dim}d flux = {flux}") # first two components should be nonzero in livedim only expected_flux = mom logger.info("Testing continuity") for i in range(dim): assert la.norm((flux[0, i] - expected_flux[i])[0].get()) == 0.0 if i != livedim: assert la.norm(flux[0, i][0].get()) == 0.0 else: assert la.norm(flux[0, i][0].get()) > 0.0 logger.info("Testing energy") expected_flux = mom * (energy + p) / mass for i in range(dim): assert la.norm((flux[1, i] - expected_flux[i])[0].get()) == 0.0 if i != livedim: assert la.norm(flux[1, i][0].get()) == 0.0 else: assert la.norm(flux[1, i][0].get()) > 0.0 logger.info("Testing momentum") xpmomflux = make_obj_array([ (mom[i] * mom[j] / mass + (p if i == j else 0)) for i in range(dim) for j in range(dim) ]) for i in range(dim): expected_flux = xpmomflux[i * dim:(i + 1) * dim] for j in range(dim): assert la.norm( (flux[2 + i, j] - expected_flux[j])[0].get()) == 0 if i == j: if i == livedim: assert (la.norm(flux[2 + i, j][0].get()) > 0.0) else: # just for sanity - make sure the flux recovered the # prescribed value of p0 (within fp tol) assert (np.abs(flux[2 + i, j][0].get() - p0) < tolerance).all() else: assert la.norm(flux[2 + i, j][0].get()) == 0.0
def test_inviscid_flux_components(actx_factory, dim): """Uniform pressure test Checks that the Euler-internal inviscid flux routine :func:`mirgecom.euler.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() from mirgecom.euler import inviscid_flux p0 = 1.0 # === 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 for ntestnodes in [1, 10]: discr = MyDiscr(dim, ntestnodes) mass = discr.zeros(actx) mass_values = np.empty((1, ntestnodes), dtype=np.float64) mass_values[0] = np.linspace(1., ntestnodes, ntestnodes, dtype=np.float64) mass[0].set(mass_values) mom = make_obj_array([discr.zeros(actx) for _ in range(dim)]) p = discr.zeros(actx) + p0 energy = p / 0.4 + 0.5 * np.dot(mom, mom) / mass q = join_conserved(dim, mass=mass, energy=energy, momentum=mom) cv = split_conserved(dim, q) p = eos.pressure(cv) flux = inviscid_flux(discr, eos, q) logger.info(f"{dim}d flux = {flux}") # for velocity zero, these components should be == zero for i in range(2): for j in range(dim): assert (flux[i, j][0].get() == 0.0).all() # The momentum diagonal should be p # Off-diagonal should be identically 0 for i in range(dim): for j in range(dim): print(f"(i,j) = ({i},{j})") if i != j: assert (flux[2 + i, j][0].get() == 0.0).all() else: assert (flux[2 + i, j] == p)[0].get().all() assert (np.abs(flux[2 + i, j][0].get() - p0) < tolerance).all()
def test_slipwall_flux(actx_factory, dim, order): """Check for zero boundary flux. Check for vanishing flux across the slipwall. """ actx = actx_factory() wall = AdiabaticSlipBoundary() eos = IdealSingleGas() from pytools.convergence import EOCRecorder eoc = EOCRecorder() for np1 in [4, 8, 12]: from meshmode.mesh.generation import generate_regular_rect_mesh mesh = generate_regular_rect_mesh( a=(-0.5,) * dim, b=(0.5,) * dim, n=(np1,) * dim ) discr = EagerDGDiscretization(actx, mesh, order=order) nodes = thaw(actx, discr.nodes()) nhat = thaw(actx, discr.normal(BTAG_ALL)) h = 1.0 / np1 from functools import partial bnd_norm = partial(discr.norm, p=np.inf, dd=BTAG_ALL) logger.info(f"Number of {dim}d elems: {mesh.nelements}") # for velocities in each direction err_max = 0.0 for vdir in range(dim): vel = np.zeros(shape=(dim,)) # for velocity directions +1, and -1 for parity in [1.0, -1.0]: vel[vdir] = parity from mirgecom.initializers import Uniform initializer = Uniform(numdim=dim, velocity=vel) uniform_state = initializer(0, nodes) bnd_pair = wall.boundary_pair(discr, uniform_state, t=0.0, btag=BTAG_ALL, eos=eos) # Check the total velocity component normal # to each surface. It should be zero. The # numerical fluxes cannot be zero. avg_state = 0.5*(bnd_pair.int + bnd_pair.ext) acv = split_conserved(dim, avg_state) err_max = max(err_max, bnd_norm(np.dot(acv.momentum, nhat))) from mirgecom.euler import _facial_flux bnd_flux = split_conserved(dim, _facial_flux(discr, eos, bnd_pair, local=True)) err_max = max(err_max, bnd_norm(bnd_flux.mass), bnd_norm(bnd_flux.energy)) eoc.add_data_point(h, err_max) message = (f"EOC:\n{eoc}") logger.info(message) assert ( eoc.order_estimate() >= order - 0.5 or eoc.max_error() < 1e-12 )
def test_inviscid_mom_flux_components(actx_factory, dim, livedim): r"""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. """ queue = actx_factory().queue eos = IdealSingleGas() class MyDiscr: def __init__(self, dim=1): self.dim = dim p0 = 1.0 from mirgecom.euler import inviscid_flux tolerance = 1e-15 for livedim in range(dim): for ntestnodes in [1, 10]: fake_dis = MyDiscr(dim) mass = cl.clrandom.rand(queue, (ntestnodes, ), dtype=np.float64) energy = cl.clrandom.rand(queue, (ntestnodes, ), dtype=np.float64) mom = make_obj_array([ cl.clrandom.rand(queue, (ntestnodes, ), dtype=np.float64) for i in range(dim) ]) p = cl.clrandom.rand(queue, (ntestnodes, ), dtype=np.float64) for i in range(ntestnodes): mass[i] = 1.0 + i p[i] = p0 for j in range(dim): mom[j][i] = 0.0 * mass[i] mom[livedim][i] = mass[i] energy = (p / (eos.gamma() - 1.0) + 0.5 * np.dot(mom, mom) / mass) q = join_conserved(dim, mass=mass, energy=energy, momentum=mom) cv = split_conserved(dim, q) p = eos.pressure(cv) flux = inviscid_flux(fake_dis, eos, q) logger.info(f"{dim}d flux = {flux}") # first two components should be nonzero in livedim only expected_flux = mom logger.info("Testing continuity") for i in range(dim): assert la.norm((flux[0, i] - expected_flux[i]).get()) == 0.0 if i != livedim: assert la.norm(flux[0, i].get()) == 0.0 else: assert la.norm(flux[0, i].get()) > 0.0 logger.info("Testing energy") expected_flux = mom * make_obj_array([(energy + p) / mass]) for i in range(dim): assert la.norm((flux[1, i] - expected_flux[i]).get()) == 0.0 if i != livedim: assert la.norm(flux[1, i].get()) == 0.0 else: assert la.norm(flux[1, i].get()) > 0.0 logger.info("Testing momentum") xpmomflux = make_obj_array([ (mom[i] * mom[j] / mass + (p if i == j else 0)) for i in range(dim) for j in range(dim) ]) for i in range(dim): expected_flux = xpmomflux[i * dim:(i + 1) * dim] for j in range(dim): assert la.norm( (flux[2 + i, j] - expected_flux[j]).get()) == 0 if i == j: if i == livedim: assert (la.norm(flux[2 + i, j].get()) > 0.0) else: # just for sanity - make sure the flux recovered the # prescribed value of p0 (within fp tol) for k in range(ntestnodes): assert np.abs(flux[2 + i, j][k] - p0) < tolerance else: assert la.norm(flux[2 + i, j].get()) == 0.0