def __call__(self, x_vec, *, t=0, eos=None): """ Create a uniform flow solution 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.IdealSingleGas` Equation of state class with method to supply gas *gamma*. """ if eos is None: eos = IdealSingleGas() gamma = eos.gamma() mass = 0.0 * x_vec[0] + self._rho mom = self._velocity * mass energy = (self._p / (gamma - 1.0)) + np.dot(mom, mom) / (2.0 * mass) species_mass = self._mass_fracs * mass return make_conserved(dim=self._dim, mass=mass, energy=energy, momentum=mom, species_mass=species_mass)
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_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 __call__(self, x_vec, *, t=0, eos=None): """ Create a multi-component lump solution at time *t* and locations *x_vec*. The solution at time *t* is created by advecting the component mass lump at the user-specified constant, uniform velocity (``MulticomponentLump._velocity``). Parameters ---------- t: float Current time at which the solution is desired x_vec: numpy.ndarray Nodal coordinates eos: :class:`mirgecom.eos.IdealSingleGas` Equation of state class with method to supply gas *gamma*. """ if eos is None: eos = IdealSingleGas() if x_vec.shape != (self._dim, ): print(f"len(x_vec) = {len(x_vec)}") print(f"self._dim = {self._dim}") raise ValueError(f"Expected {self._dim}-dimensional inputs.") actx = x_vec[0].array_context loc_update = t * self._velocity gamma = eos.gamma() mass = 0 * x_vec[0] + self._rho0 mom = self._velocity * mass energy = (self._p0 / (gamma - 1.0)) + np.dot(mom, mom) / (2.0 * mass) # process the species components independently species_mass = np.empty((self._nspecies, ), dtype=object) for i in range(self._nspecies): lump_loc = self._spec_centers[i] + loc_update rel_pos = x_vec - lump_loc r2 = np.dot(rel_pos, rel_pos) expterm = self._spec_amplitudes[i] * actx.np.exp(-r2) species_mass[i] = self._rho0 * (self._spec_y0s[i] + expterm) return make_conserved(dim=self._dim, mass=mass, energy=energy, momentum=mom, species_mass=species_mass)
def __call__(self, x_vec, *, t=0, eos=None): """ Create the isentropic vortex solution at time *t* at locations *x_vec*. The solution at time *t* is created by advecting the vortex under the assumption of user-supplied constant, uniform velocity (``Vortex2D._velocity``). Parameters ---------- t: float Current time at which the solution is desired. x_vec: numpy.ndarray Nodal coordinates eos: mirgecom.eos.IdealSingleGas Equation of state class to supply method for gas *gamma*. """ if eos is None: eos = IdealSingleGas() vortex_loc = self._center + t * self._velocity # coordinates relative to vortex center x_rel = x_vec[0] - vortex_loc[0] y_rel = x_vec[1] - vortex_loc[1] actx = x_vec[0].array_context gamma = eos.gamma() r = actx.np.sqrt(x_rel**2 + y_rel**2) expterm = self._beta * actx.np.exp(1 - r**2) u = self._velocity[0] - expterm * y_rel / (2 * np.pi) v = self._velocity[1] + expterm * x_rel / (2 * np.pi) velocity = make_obj_array([u, v]) mass = (1 - (gamma - 1) / (16 * gamma * np.pi**2) * expterm**2)**(1 / (gamma - 1)) momentum = mass * velocity p = mass**gamma energy = p / (gamma - 1) + mass / 2 * (u**2 + v**2) return make_conserved(dim=2, mass=mass, energy=energy, momentum=momentum)
def __call__(self, x_vec, *, t=0, eos=None): """ Create the lump-of-mass solution at time *t* and locations *x_vec*. The solution at time *t* is created by advecting the mass lump under the assumption of constant, uniform velocity (``Lump._velocity``). Parameters ---------- t: float Current time at which the solution is desired x_vec: numpy.ndarray Nodal coordinates eos: :class:`mirgecom.eos.IdealSingleGas` Equation of state class with method to supply gas *gamma*. """ if eos is None: eos = IdealSingleGas() if x_vec.shape != (self._dim, ): raise ValueError(f"Position vector has unexpected dimensionality," f" expected {self._dim}.") amplitude = self._rhoamp lump_loc = self._center + t * self._velocity # coordinates relative to lump center rel_center = make_obj_array( [x_vec[i] - lump_loc[i] for i in range(self._dim)]) actx = x_vec[0].array_context r = actx.np.sqrt(np.dot(rel_center, rel_center)) expterm = amplitude * actx.np.exp(1 - r**2) mass = expterm + self._rho0 gamma = eos.gamma() mom = self._velocity * mass energy = (self._p0 / (gamma - 1.0)) + np.dot(mom, mom) / (2.0 * mass) return make_conserved(dim=self._dim, mass=mass, energy=energy, momentum=mom)
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 __call__(self, x_vec, *, eos=None, **kwargs): """ Create the 1D Sod's shock solution at locations *x_vec*. Parameters ---------- x_vec: numpy.ndarray Nodal coordinates eos: :class:`mirgecom.eos.IdealSingleGas` Equation of state class with method to supply gas *gamma*. """ if eos is None: eos = IdealSingleGas() gm1 = eos.gamma() - 1.0 gmn1 = 1.0 / gm1 x_rel = x_vec[self._xdir] actx = x_rel.array_context zeros = 0*x_rel rhor = zeros + self._rhor rhol = zeros + self._rhol x0 = zeros + self._x0 energyl = zeros + gmn1 * self._energyl energyr = zeros + gmn1 * self._energyr yesno = actx.np.greater(x_rel, x0) mass = actx.np.where(yesno, rhor, rhol) energy = actx.np.where(yesno, energyr, energyl) mom = make_obj_array( [ 0*x_rel for i in range(self._dim) ] ) return make_conserved(dim=self._dim, mass=mass, energy=energy, momentum=mom)
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_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