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_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_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_viscous_timestep(actx_factory, dim, mu, vel): """Test timestep size.""" 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) zeros = discr.zeros(actx) ones = zeros + 1.0 velocity = make_obj_array([zeros + vel for _ in range(dim)]) massval = 1 mass = massval * ones # I *think* this energy should yield c=1.0 energy = zeros + 1.0 / (1.4 * .4) mom = mass * velocity species_mass = None cv = make_conserved(dim, mass=mass, energy=energy, momentum=mom, species_mass=species_mass) from grudge.dt_utils import characteristic_lengthscales chlen = characteristic_lengthscales(actx, discr) from grudge.op import nodal_min chlen_min = nodal_min(discr, "vol", chlen) mu = mu * chlen_min if mu < 0: mu = 0 tv_model = None else: tv_model = SimpleTransport(viscosity=mu) eos = IdealSingleGas(transport_model=tv_model) from mirgecom.viscous import get_viscous_timestep dt_field = get_viscous_timestep(discr, eos, cv) speed_total = actx.np.sqrt(np.dot(velocity, velocity)) + eos.sound_speed(cv) dt_expected = chlen / (speed_total + (mu / chlen)) error = (dt_expected - dt_field) / dt_expected assert discr.norm(error, np.inf) == 0
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_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 __call__(self, t, x_vec, eos=IdealSingleGas()): """ Create the lump-of-mass solution at time *t* and locations *x_vec*. Note that *t* is used to advect the mass lump under the assumption of constant, and uniform velocity. Parameters ---------- t: float Current time at which the solution is desired x_vec: numpy.ndarray Nodal coordinates eos: :class:`mirgecom.eos.GasEOS` Equation of state class to be used in construction of soln (if needed) """ lump_loc = self._center + t * self._velocity assert len(x_vec) == self._dim # 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)) gamma = eos.gamma() expterm = self._rhoamp * actx.np.exp(1 - r ** 2) mass = expterm + self._rho0 mom = self._velocity * mass energy = (self._p0 / (gamma - 1.0)) + np.dot(mom, mom) / (2.0 * mass) return flat_obj_array(mass, energy, mom)
def __call__(self, t, x_vec, eos=IdealSingleGas()): """ Create the isentropic vortex solution at time *t* at locations *x_vec*. Parameters ---------- t: float Current time at which the solution is desired. x_vec: numpy.ndarray Nodal coordinates eos: :class:`mirgecom.eos.GasEOS` Equation of state class to be used in construction of soln (if needed) """ 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) mass = (1 - (gamma - 1) / (16 * gamma * np.pi ** 2) * expterm ** 2) ** (1 / (gamma - 1)) p = mass ** gamma e = p / (gamma - 1) + mass / 2 * (u ** 2 + v ** 2) return flat_obj_array(mass, e, mass * u, mass * v)
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 test_isentropic_vortex(actx_factory, order): """Advance the 2D isentropic vortex case in time with non-zero velocities using an RK4 timestepping scheme. Check the advanced field values against the exact/analytic expressions. This tests all parts of the Euler module working together, with results converging at the expected rates vs. the order. """ actx = actx_factory() dim = 2 from pytools.convergence import EOCRecorder eoc_rec = EOCRecorder() for nel_1d in [16, 32, 64]: from meshmode.mesh.generation import ( generate_regular_rect_mesh, ) mesh = generate_regular_rect_mesh(a=(-5.0, ) * dim, b=(5.0, ) * dim, nelements_per_axis=(nel_1d, ) * dim) exittol = 1.0 t_final = 0.001 cfl = 1.0 vel = np.zeros(shape=(dim, )) orig = np.zeros(shape=(dim, )) vel[:dim] = 1.0 dt = .0001 initializer = Vortex2D(center=orig, velocity=vel) casename = "Vortex" boundaries = {BTAG_ALL: PrescribedBoundary(initializer)} eos = IdealSingleGas() t = 0 flowparams = { "dim": dim, "dt": dt, "order": order, "time": t, "boundaries": boundaries, "initializer": initializer, "eos": eos, "casename": casename, "mesh": mesh, "tfinal": t_final, "exittol": exittol, "cfl": cfl, "constantcfl": False, "nstatus": 0 } maxerr = _euler_flow_stepper(actx, flowparams) eoc_rec.add_data_point(1.0 / nel_1d, maxerr) 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() < 1e-11)
def boundary_pair(self, discr, q, t=0.0, btag=BTAG_ALL, eos=IdealSingleGas()): """Get the interior and exterior solution on the boundary.""" dir_soln = discr.project("vol", btag, q) return TracePair(btag, interior=dir_soln, exterior=dir_soln)
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 __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 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_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 __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_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_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 boundary_pair(self, discr, q, t=0.0, btag=BTAG_ALL, eos=IdealSingleGas()): """Get the interior and exterior solution on the boundary.""" actx = q[0].array_context boundary_discr = discr.discr_from_dd(btag) nodes = thaw(actx, boundary_discr.nodes()) ext_soln = self._userfunc(t, nodes) int_soln = discr.project("vol", btag, q) return TracePair(btag, interior=int_soln, exterior=ext_soln)
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_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 __call__(self, t, x_vec, eos=IdealSingleGas()): """ 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.GasEOS` Equation of state class to be used in construction of soln (unused) """ return _make_uniform_flow(x_vec=x_vec, mass=self._rho, pressure=self._p, energy=self._e, velocity=self._velocity, eos=eos)
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 _get_scalar_lump(): from mirgecom.eos import IdealSingleGas eos = IdealSingleGas() from mirgecom.initializers import MulticomponentLump init = MulticomponentLump(dim=2, nspecies=3, velocity=np.ones(2), spec_y0s=np.ones(3), spec_amplitudes=np.ones(3)) from meshmode.mesh import BTAG_ALL from mirgecom.boundary import PrescribedInviscidBoundary boundaries = { BTAG_ALL: PrescribedInviscidBoundary(fluid_solution_func=init) } return eos, init, boundaries, 1e-12
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, t, x_vec, eos=IdealSingleGas()): """ 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.GasEOS` Equation of state class to be used in construction of soln (unused) """ gamma = eos.gamma() mass = x_vec[0].copy() mom = self._velocity * make_obj_array([mass]) energy = (self._p / (gamma - 1.0)) + np.dot(mom, mom) / (2.0 * mass) return flat_obj_array(mass, energy, mom)
def _get_pulse(): from mirgecom.eos import IdealSingleGas from mirgecom.gas_model import GasModel gas_model = GasModel(eos=IdealSingleGas()) from mirgecom.initializers import Uniform, AcousticPulse uniform_init = Uniform(dim=2) pulse_init = AcousticPulse(dim=2, center=np.zeros(2), amplitude=1.0, width=.1) def init(nodes): return pulse_init(x_vec=nodes, cv=uniform_init(nodes), eos=gas_model.eos) from meshmode.mesh import BTAG_ALL from mirgecom.boundary import AdiabaticSlipBoundary boundaries = { BTAG_ALL: AdiabaticSlipBoundary() } return gas_model, init, boundaries, 3e-12
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()