def __call__(self, x_vec, *, t=0, eos): if self._p_fun is not None: P0 = self._p_fun(t) else: P0 = self._P0 T0 = self._T0 gamma = eos.gamma() gas_const = eos.gas_const() pressure = getIsentropicPressure(mach=self._mach, P0=P0, gamma=gamma) temperature = getIsentropicTemperature(mach=self._mach, T0=T0, gamma=gamma) rho = pressure/temperature/gas_const #print(f'ramp Mach number {self._mach}') #print(f'ramp stagnation pressure {P0}') #print(f'ramp stagnation temperature {T0}') #print(f'ramp pressure {pressure}') #print(f'ramp temperature {temperature}') velocity = np.zeros(shape=(self._dim,)) velocity[self._direc] = self._mach*math.sqrt(gamma*pressure/rho) mass = 0.0*x_vec[0] + rho mom = velocity*mass energy = (pressure/(gamma - 1.0)) + np.dot(mom, mom)/(2.0*mass) from mirgecom.euler import join_conserved return join_conserved(dim=self._dim, mass=mass, momentum=mom, energy=energy)
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 _make_uniform_flow(x_vec, mass=1.0, energy=2.5, pressure=1.0, velocity=None, eos=IdealSingleGas()): r"""Construct uniform, constant flow. Construct a uniform, constant flow from mass, energy, pressure, and an EOS object. Parameters ---------- x_vec: np.ndarray Nodal positions mass: float Value to set $\rho$ energy: float Optional value to set $\rho{E}$ pressure: float Value to use for calculating $\rho{E}$ velocity: np.ndarray Optional constant velocity to set $\rho\vec{V}$ Returns ------- q: Object array of DOFArrays Agglomerated object array with the uniform flow """ dim = len(x_vec) if velocity is None: velocity = np.zeros(shape=(dim,)) _rho = mass _p = pressure _velocity = velocity _gamma = eos.gamma() mom0 = _velocity * _rho e0 = _p / (_gamma - 1.0) ke0 = _rho * 0.5 * np.dot(_velocity, _velocity) x_rel = x_vec[0] zeros = 0.0*x_rel ones = zeros + 1.0 mass = zeros + _rho mom = mom0 * ones energy = e0 + ke0 + zeros return join_conserved(dim=dim, mass=mass, energy=energy, momentum=mom)
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_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_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