def test_function_symbol_array(ctx_factory, array_type): ctx = ctx_factory() queue = cl.CommandQueue(ctx) actx = PyOpenCLArrayContext(queue) from meshmode.mesh.generation import generate_regular_rect_mesh dim = 2 mesh = generate_regular_rect_mesh(a=(-0.5, ) * dim, b=(0.5, ) * dim, n=(8, ) * dim, order=4) discr = DGDiscretizationWithBoundaries(actx, mesh, order=4) volume_discr = discr.discr_from_dd(sym.DD_VOLUME) ndofs = sum(grp.ndofs for grp in volume_discr.groups) import pyopencl.clrandom # noqa: F401 if array_type == "scalar": sym_x = sym.var("x") x = unflatten(actx, volume_discr, cl.clrandom.rand(queue, ndofs, dtype=np.float)) elif array_type == "vector": sym_x = sym.make_sym_array("x", dim) x = make_obj_array([ unflatten(actx, volume_discr, cl.clrandom.rand(queue, ndofs, dtype=np.float)) for _ in range(dim) ]) else: raise ValueError("unknown array type") norm = bind(discr, sym.norm(2, sym_x))(x=x) assert isinstance(norm, float)
def test_bessel(actx_factory): actx = actx_factory() dims = 2 mesh = mgen.generate_regular_rect_mesh(a=(0.1, ) * dims, b=(1.0, ) * dims, nelements_per_axis=(8, ) * dims) dcoll = DiscretizationCollection(actx, mesh, order=3) nodes = thaw(dcoll.nodes(), actx) r = actx.np.sqrt(nodes[0]**2 + nodes[1]**2) # FIXME: Bessel functions need to brought out of the symbolic # layer. Related issue: https://github.com/inducer/grudge/issues/93 def bessel_j(actx, n, r): from grudge import sym, bind return bind(dcoll, sym.bessel_j(n, sym.var("r")))(actx, r=r) # https://dlmf.nist.gov/10.6.1 n = 3 bessel_zero = (bessel_j(actx, n + 1, r) + bessel_j(actx, n - 1, r) - 2 * n / r * bessel_j(actx, n, r)) z = op.norm(dcoll, bessel_zero, 2) assert z < 1e-15
def test_nonequal_rect_mesh_generation(actx_factory, dim, mesh_type, visualize=False): """Test that ``generate_regular_rect_mesh`` works with non-equal arguments across axes. """ actx = actx_factory() mesh = mgen.generate_regular_rect_mesh(a=(0, ) * dim, b=(5, 3, 4)[:dim], npoints_per_axis=(10, 6, 7)[:dim], order=3, mesh_type=mesh_type) from meshmode.discretization import Discretization from meshmode.discretization.poly_element import \ PolynomialWarpAndBlendGroupFactory as GroupFactory discr = Discretization(actx, mesh, GroupFactory(3)) if visualize: from meshmode.discretization.visualization import make_visualizer vis = make_visualizer(actx, discr, 3) vis.write_vtk_file("nonuniform.vtu", [], overwrite=True)
def test_inverse_metric(actx_factory, dim): actx = actx_factory() mesh = mgen.generate_regular_rect_mesh(a=(-0.5, ) * dim, b=(0.5, ) * dim, nelements_per_axis=(6, ) * dim, order=4) def m(x): result = np.empty_like(x) result[0] = (1.5 * x[0] + np.cos(x[0]) + 0.1 * np.sin(10 * x[1])) result[1] = (0.05 * np.cos(10 * x[0]) + 1.3 * x[1] + np.sin(x[1])) if len(x) == 3: result[2] = x[2] return result from meshmode.mesh.processing import map_mesh mesh = map_mesh(mesh, m) dcoll = DiscretizationCollection(actx, mesh, order=4) from grudge.geometry import \ forward_metric_derivative_mat, inverse_metric_derivative_mat mat = forward_metric_derivative_mat(actx, dcoll).dot( inverse_metric_derivative_mat(actx, dcoll)) for i in range(mesh.dim): for j in range(mesh.dim): tgt = 1 if i == j else 0 err = flat_norm(mat[i, j] - tgt, ord=np.inf) logger.info("error[%d, %d]: %.5e", i, j, err) assert err < 1.0e-12, (i, j, err)
def test_norm_obj_array(actx_factory, p): """Test :func:`grudge.op.norm` for object arrays.""" actx = actx_factory() dim = 2 mesh = mgen.generate_regular_rect_mesh(a=(-0.5, ) * dim, b=(0.5, ) * dim, nelements_per_axis=(8, ) * dim, order=1) dcoll = DiscretizationCollection(actx, mesh, order=4) w = make_obj_array([1.0, 2.0, 3.0])[:dim] # {{ scalar norm = op.norm(dcoll, w[0], p) norm_exact = w[0] logger.info("norm: %.5e %.5e", norm, norm_exact) assert abs(norm - norm_exact) < 1.0e-14 # }}} # {{{ vector norm = op.norm(dcoll, w, p) norm_exact = np.sqrt(np.sum(w**2)) if p == 2 else np.max(w) logger.info("norm: %.5e %.5e", norm, norm_exact) assert abs(norm - norm_exact) < 1.0e-14
def test_nodal_dg_interop(actx_factory, dim): pytest.importorskip("oct2py") actx = actx_factory() download_nodal_dg_if_not_present() order = 4 from meshmode.mesh.generation import generate_regular_rect_mesh mesh = generate_regular_rect_mesh(a=(-0.5, ) * dim, b=(0.5, ) * dim, n=(8, ) * dim, order=order) from meshmode.interop.nodal_dg import NodalDGContext with NodalDGContext("./nodal-dg/Codes1.1") as ndgctx: ndgctx.set_mesh(mesh, order=order) discr = ndgctx.get_discr(actx) for ax in range(dim): x_ax = ndgctx.pull_dof_array(actx, ndgctx.AXES[ax]) err = flat_norm(x_ax - discr.nodes()[ax], np.inf) assert err < 1e-15 n0 = thaw(actx, discr.nodes()[0]) ndgctx.push_dof_array("n0", n0) n0_2 = ndgctx.pull_dof_array(actx, "n0") assert flat_norm(n0 - n0_2, np.inf) < 1e-15
def test_external_call(ctx_factory): cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) actx = PyOpenCLArrayContext(queue) def double(queue, x): return 2 * x from meshmode.mesh.generation import generate_regular_rect_mesh dims = 2 mesh = generate_regular_rect_mesh(a=(0, ) * dims, b=(1, ) * dims, n=(4, ) * dims) discr = DGDiscretizationWithBoundaries(actx, mesh, order=1) ones = sym.Ones(sym.DD_VOLUME) op = (ones * 3 + sym.FunctionSymbol("double")(ones)) from grudge.function_registry import (base_function_registry, register_external_function) freg = register_external_function(base_function_registry, "double", implementation=double, dd=sym.DD_VOLUME) bound_op = bind(discr, op, function_registry=freg) result = bound_op(actx, double=double) assert actx.to_numpy(flatten(result) == 5).all()
def test_inverse_metric(actx_factory, dim): actx = actx_factory() mesh = mgen.generate_regular_rect_mesh(a=(-0.5, ) * dim, b=(0.5, ) * dim, nelements_per_axis=(6, ) * dim, order=4) def m(x): result = np.empty_like(x) result[0] = (1.5 * x[0] + np.cos(x[0]) + 0.1 * np.sin(10 * x[1])) result[1] = (0.05 * np.cos(10 * x[0]) + 1.3 * x[1] + np.sin(x[1])) if len(x) == 3: result[2] = x[2] return result from meshmode.mesh.processing import map_mesh mesh = map_mesh(mesh, m) discr = DiscretizationCollection(actx, mesh, order=4) sym_op = (sym.forward_metric_derivative_mat(mesh.dim).dot( sym.inverse_metric_derivative_mat(mesh.dim)).reshape(-1)) op = bind(discr, sym_op) mat = op(actx).reshape(mesh.dim, mesh.dim) for i in range(mesh.dim): for j in range(mesh.dim): tgt = 1 if i == j else 0 err = actx.np.linalg.norm(mat[i, j] - tgt, ord=np.inf) logger.info("error[%d, %d]: %.5e", i, j, err) assert err < 1.0e-12, (i, j, err)
def simple_mpi_communication_entrypoint(): cl_ctx = cl.create_some_context() queue = cl.CommandQueue(cl_ctx) actx = PyOpenCLArrayContext(queue) from meshmode.distributed import MPIMeshDistributor, get_partition_by_pymetis from meshmode.mesh import BTAG_ALL from mpi4py import MPI comm = MPI.COMM_WORLD num_parts = comm.Get_size() mesh_dist = MPIMeshDistributor(comm) if mesh_dist.is_mananger_rank(): from meshmode.mesh.generation import generate_regular_rect_mesh mesh = generate_regular_rect_mesh(a=(-1, ) * 2, b=(1, ) * 2, nelements_per_axis=(2, ) * 2) part_per_element = get_partition_by_pymetis(mesh, num_parts) local_mesh = mesh_dist.send_mesh_parts(mesh, part_per_element, num_parts) else: local_mesh = mesh_dist.receive_mesh_part() vol_discr = DiscretizationCollection(actx, local_mesh, order=5, mpi_communicator=comm) sym_x = sym.nodes(local_mesh.dim) myfunc_symb = sym.sin(np.dot(sym_x, [2, 3])) myfunc = bind(vol_discr, myfunc_symb)(actx) sym_all_faces_func = sym.cse( sym.project("vol", "all_faces")(sym.var("myfunc"))) sym_int_faces_func = sym.cse( sym.project("vol", "int_faces")(sym.var("myfunc"))) sym_bdry_faces_func = sym.cse( sym.project(BTAG_ALL, "all_faces")(sym.project("vol", BTAG_ALL)(sym.var("myfunc")))) bound_face_swap = bind( vol_discr, sym.project("int_faces", "all_faces")( sym.OppositeInteriorFaceSwap("int_faces")(sym_int_faces_func)) - (sym_all_faces_func - sym_bdry_faces_func)) hopefully_zero = bound_face_swap(myfunc=myfunc) error = actx.np.linalg.norm(hopefully_zero, ord=np.inf) print(__file__) with np.printoptions(threshold=100000000, suppress=True): logger.debug(hopefully_zero) logger.info("error: %.5e", error) assert error < 1e-14
def simple_mpi_communication_entrypoint(): cl_ctx = cl.create_some_context() queue = cl.CommandQueue(cl_ctx) actx = PyOpenCLArrayContext(queue, force_device_scalars=True) from meshmode.distributed import MPIMeshDistributor, get_partition_by_pymetis from meshmode.mesh import BTAG_ALL from mpi4py import MPI comm = MPI.COMM_WORLD num_parts = comm.Get_size() mesh_dist = MPIMeshDistributor(comm) if mesh_dist.is_mananger_rank(): from meshmode.mesh.generation import generate_regular_rect_mesh mesh = generate_regular_rect_mesh(a=(-1,)*2, b=(1,)*2, nelements_per_axis=(2,)*2) part_per_element = get_partition_by_pymetis(mesh, num_parts) local_mesh = mesh_dist.send_mesh_parts(mesh, part_per_element, num_parts) else: local_mesh = mesh_dist.receive_mesh_part() dcoll = DiscretizationCollection(actx, local_mesh, order=5, mpi_communicator=comm) x = thaw(dcoll.nodes(), actx) myfunc = actx.np.sin(np.dot(x, [2, 3])) from grudge.dof_desc import as_dofdesc dd_int = as_dofdesc("int_faces") dd_vol = as_dofdesc("vol") dd_af = as_dofdesc("all_faces") all_faces_func = op.project(dcoll, dd_vol, dd_af, myfunc) int_faces_func = op.project(dcoll, dd_vol, dd_int, myfunc) bdry_faces_func = op.project(dcoll, BTAG_ALL, dd_af, op.project(dcoll, dd_vol, BTAG_ALL, myfunc)) hopefully_zero = ( op.project( dcoll, "int_faces", "all_faces", dcoll.opposite_face_connection()(int_faces_func) ) + sum(op.project(dcoll, tpair.dd, "all_faces", tpair.int) for tpair in op.cross_rank_trace_pairs(dcoll, myfunc)) ) - (all_faces_func - bdry_faces_func) error = actx.to_numpy(flat_norm(hopefully_zero, ord=np.inf)) print(__file__) with np.printoptions(threshold=100000000, suppress=True): logger.debug(hopefully_zero) logger.info("error: %.5e", error) assert error < 1e-14
def get_mesh(): """Import a grid using `gmsh`. Input required: data/mesh.msh (read existing mesh) This routine will generate a new grid if it does not find the grid file (data/mesh.msh). """ from meshmode.mesh.io import (read_gmsh, generate_gmsh, ScriptWithFilesSource) import os if os.path.exists("data/mesh.msh") is False: char_len = 0.001 box_ll = (0.0, 0.0) box_ur = (0.25, 0.01) num_elements = (int((box_ur[0] - box_ll[0]) / char_len), int((box_ur[1] - box_ll[1]) / char_len)) from meshmode.mesh.generation import generate_regular_rect_mesh mesh = generate_regular_rect_mesh(a=box_ll, b=box_ur, n=num_elements, boundary_tag_to_face={ "Inflow": ["-x"], "Outflow": ["+x"], "Wall": ["+y", "-y"] }) print("%d elements" % mesh.nelements) else: mesh = read_gmsh("data/mesh.msh") return mesh
def simple_mpi_communication_entrypoint(): cl_ctx = cl.create_some_context() queue = cl.CommandQueue(cl_ctx) from meshmode.distributed import MPIMeshDistributor, get_partition_by_pymetis from mpi4py import MPI comm = MPI.COMM_WORLD num_parts = comm.Get_size() mesh_dist = MPIMeshDistributor(comm) if mesh_dist.is_mananger_rank(): from meshmode.mesh.generation import generate_regular_rect_mesh mesh = generate_regular_rect_mesh(a=(-1, ) * 2, b=(1, ) * 2, n=(3, ) * 2) part_per_element = get_partition_by_pymetis(mesh, num_parts) local_mesh = mesh_dist.send_mesh_parts(mesh, part_per_element, num_parts) else: local_mesh = mesh_dist.receive_mesh_part() vol_discr = DGDiscretizationWithBoundaries(cl_ctx, local_mesh, order=5, mpi_communicator=comm) sym_x = sym.nodes(local_mesh.dim) myfunc_symb = sym.sin(np.dot(sym_x, [2, 3])) myfunc = bind(vol_discr, myfunc_symb)(queue) sym_all_faces_func = sym.cse( sym.interp("vol", "all_faces")(sym.var("myfunc"))) sym_int_faces_func = sym.cse( sym.interp("vol", "int_faces")(sym.var("myfunc"))) sym_bdry_faces_func = sym.cse( sym.interp(sym.BTAG_ALL, "all_faces")(sym.interp("vol", sym.BTAG_ALL)(sym.var("myfunc")))) bound_face_swap = bind( vol_discr, sym.interp("int_faces", "all_faces")( sym.OppositeInteriorFaceSwap("int_faces")(sym_int_faces_func)) - (sym_all_faces_func - sym_bdry_faces_func)) # print(bound_face_swap) # 1/0 hopefully_zero = bound_face_swap(queue, myfunc=myfunc) import numpy.linalg as la error = la.norm(hopefully_zero.get()) np.set_printoptions(threshold=100000000, suppress=True) print(hopefully_zero) print(error) assert error < 1e-14
def get_strong_wave_op_with_discr(cl_ctx, dims=2, order=4): from meshmode.mesh.generation import generate_regular_rect_mesh mesh = generate_regular_rect_mesh(a=(-0.5, ) * dims, b=(0.5, ) * dims, n=(16, ) * dims) logger.debug("%d elements", mesh.nelements) discr = DGDiscretizationWithBoundaries(cl_ctx, mesh, order=order) source_center = np.array([0.1, 0.22, 0.33])[:dims] source_width = 0.05 source_omega = 3 sym_x = sym.nodes(mesh.dim) sym_source_center_dist = sym_x - source_center sym_t = sym.ScalarVariable("t") from grudge.models.wave import StrongWaveOperator from meshmode.mesh import BTAG_ALL, BTAG_NONE op = StrongWaveOperator( -0.1, dims, source_f=( sym.sin(source_omega * sym_t) * sym.exp(-np.dot(sym_source_center_dist, sym_source_center_dist) / source_width**2)), dirichlet_tag=BTAG_NONE, neumann_tag=BTAG_NONE, radiation_tag=BTAG_ALL, flux_type="upwind") op.check_bc_coverage(mesh) return (op.sym_operator(), discr)
def test_bessel(ctx_factory): cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) actx = PyOpenCLArrayContext(queue) dims = 2 from meshmode.mesh.generation import generate_regular_rect_mesh mesh = generate_regular_rect_mesh(a=(0.1, ) * dims, b=(1.0, ) * dims, n=(8, ) * dims) discr = DGDiscretizationWithBoundaries(actx, mesh, order=3) nodes = sym.nodes(dims) r = sym.cse(sym.sqrt(nodes[0]**2 + nodes[1]**2)) # https://dlmf.nist.gov/10.6.1 n = 3 bessel_zero = (sym.bessel_j(n + 1, r) + sym.bessel_j(n - 1, r) - 2 * n / r * sym.bessel_j(n, r)) z = bind(discr, sym.norm(2, bessel_zero))(actx) assert z < 1e-15
def test_trace_pair(actx_factory): """Simple smoke test for :class:`grudge.trace_pair.TracePair`.""" actx = actx_factory() dim = 3 order = 1 n = 4 mesh = mgen.generate_regular_rect_mesh( a=(-1,)*dim, b=(1,)*dim, nelements_per_axis=(n,)*dim) dcoll = DiscretizationCollection(actx, mesh, order=order) def rand(): return DOFArray( actx, tuple(actx.from_numpy( np.random.rand(grp.nelements, grp.nunit_dofs)) for grp in dcoll.discr_from_dd("vol").groups)) interior = rand() exterior = rand() tpair = TracePair("vol", interior=interior, exterior=exterior) import grudge.op as op assert op.norm(dcoll, tpair.avg - 0.5*(exterior + interior), np.inf) == 0 assert op.norm(dcoll, tpair.diff - (exterior - interior), np.inf) == 0 assert op.norm(dcoll, tpair.int - interior, np.inf) == 0 assert op.norm(dcoll, tpair.ext - exterior, np.inf) == 0
def test_symbolic_evaluation(actx_factory): """ Evaluate a symbolic expression by plugging in numbers and :class:`~meshmode.dof_array.DOFArray`s and compare the result to the equivalent quantity computed explicitly. """ actx = actx_factory() mesh = generate_regular_rect_mesh( a=(-np.pi/2,)*2, b=(np.pi/2,)*2, nelements_per_axis=(4,)*2) from grudge.eager import EagerDGDiscretization discr = EagerDGDiscretization(actx, mesh, order=2) nodes = thaw(actx, discr.nodes()) sym_coords = pmbl.make_sym_vector("x", 2) sym_f = ( pmbl.var("exp")(-pmbl.var("t")) * pmbl.var("cos")(sym_coords[0]) * pmbl.var("sin")(sym_coords[1])) t = 0.5 f = sym.EvaluationMapper({"t": t, "x": nodes})(sym_f) expected_f = np.exp(-t) * actx.np.cos(nodes[0]) * actx.np.sin(nodes[1]) assert actx.to_numpy(discr.norm(f - expected_f)/discr.norm(expected_f)) < 1e-12
def test_1d_mass_mat_trig(ctx_factory): """Check the integral of some trig functions on an interval using the mass matrix """ cl_ctx = cl.create_some_context() queue = cl.CommandQueue(cl_ctx) from meshmode.mesh.generation import generate_regular_rect_mesh mesh = generate_regular_rect_mesh(a=(-4 * np.pi, ), b=(9 * np.pi, ), n=(17, ), order=1) discr = DGDiscretizationWithBoundaries(cl_ctx, mesh, order=8) x = sym.nodes(1) f = bind(discr, sym.cos(x[0])**2)(queue) ones = bind(discr, sym.Ones(sym.DD_VOLUME))(queue) mass_op = bind(discr, sym.MassOperator()(sym.var("f"))) num_integral_1 = np.dot(ones.get(), mass_op(queue, f=f)) num_integral_2 = np.dot(f.get(), mass_op(queue, f=ones)) num_integral_3 = bind(discr, sym.integral(sym.var("f")))(queue, f=f) true_integral = 13 * np.pi / 2 err_1 = abs(num_integral_1 - true_integral) err_2 = abs(num_integral_2 - true_integral) err_3 = abs(num_integral_3 - true_integral) assert err_1 < 1e-10 assert err_2 < 1e-10 assert err_3 < 1e-10
def test_norm_obj_array(actx_factory, p): """Test :func:`grudge.symbolic.operators.norm` for object arrays.""" actx = actx_factory() dim = 2 mesh = mgen.generate_regular_rect_mesh(a=(-0.5, ) * dim, b=(0.5, ) * dim, nelements_per_axis=(8, ) * dim, order=1) discr = DiscretizationCollection(actx, mesh, order=4) w = make_obj_array([1.0, 2.0, 3.0])[:dim] # {{ scalar sym_w = sym.var("w") norm = bind(discr, sym.norm(p, sym_w))(actx, w=w[0]) norm_exact = w[0] logger.info("norm: %.5e %.5e", norm, norm_exact) assert abs(norm - norm_exact) < 1.0e-14 # }}} # {{{ vector sym_w = sym.make_sym_array("w", dim) norm = bind(discr, sym.norm(p, sym_w))(actx, w=w) norm_exact = np.sqrt(np.sum(w**2)) if p == 2 else np.max(w) logger.info("norm: %.5e %.5e", norm, norm_exact) assert abs(norm - norm_exact) < 1.0e-14
def test_operator_compiler_overwrite(actx_factory): """Tests that the same expression in ``eval_code`` and ``discr_code`` does not confuse the OperatorCompiler in grudge/symbolic/compiler.py. """ actx = actx_factory() ambient_dim = 2 target_order = 4 from meshmode.mesh.generation import generate_regular_rect_mesh mesh = generate_regular_rect_mesh(a=(-0.5, ) * ambient_dim, b=(0.5, ) * ambient_dim, n=(8, ) * ambient_dim, order=1) discr = DiscretizationCollection(actx, mesh, order=target_order) # {{{ test sym_u = sym.nodes(ambient_dim) sym_div_u = sum(d(u) for d, u in zip(sym.nabla(ambient_dim), sym_u)) div_u = bind(discr, sym_div_u)(actx) error = bind(discr, sym.norm(2, sym.var("x")))(actx, x=div_u - discr.dim) logger.info("error: %.5e", error)
def test_external_call(actx_factory): actx = actx_factory() def double(queue, x): return 2 * x dims = 2 mesh = mgen.generate_regular_rect_mesh(a=(0, ) * dims, b=(1, ) * dims, nelements_per_axis=(4, ) * dims) discr = DiscretizationCollection(actx, mesh, order=1) ones = sym.Ones(dof_desc.DD_VOLUME) op = (ones * 3 + sym.FunctionSymbol("double")(ones)) from grudge.function_registry import (base_function_registry, register_external_function) freg = register_external_function(base_function_registry, "double", implementation=double, dd=dof_desc.DD_VOLUME) bound_op = bind(discr, op, function_registry=freg) result = bound_op(actx, double=double) assert actx.to_numpy(flatten(result) == 5).all()
def test_flatten_unflatten(actx_factory): actx = actx_factory() ambient_dim = 2 from meshmode.mesh.generation import generate_regular_rect_mesh mesh = generate_regular_rect_mesh(a=(-0.5, ) * ambient_dim, b=(+0.5, ) * ambient_dim, n=(3, ) * ambient_dim, order=1) discr = Discretization(actx, mesh, PolynomialWarpAndBlendGroupFactory(3)) a = np.random.randn(discr.ndofs) from meshmode.dof_array import flatten, unflatten a_round_trip = actx.to_numpy( flatten(unflatten(actx, discr, actx.from_numpy(a)))) assert np.array_equal(a, a_round_trip) from meshmode.dof_array import flatten_to_numpy, unflatten_from_numpy a_round_trip = flatten_to_numpy(actx, unflatten_from_numpy(actx, discr, a)) assert np.array_equal(a, a_round_trip) x = thaw(discr.nodes(), actx) avg_mass = DOFArray( actx, tuple([(np.pi + actx.zeros((grp.nelements, 1), a.dtype)) for grp in discr.groups])) c = MyContainer(name="flatten", mass=avg_mass, momentum=make_obj_array([x, x, x]), enthalpy=x) from meshmode.dof_array import unflatten_like c_round_trip = unflatten_like(actx, flatten(c), c) assert flat_norm(c - c_round_trip) < 1.0e-8
def test_vortex_init(ctx_factory): """ Simple test to check that Vortex2D initializer creates the expected solution field. """ cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) actx = PyOpenCLArrayContext(queue) dim = 2 nel_1d = 4 from meshmode.mesh.generation import generate_regular_rect_mesh mesh = generate_regular_rect_mesh(a=[(0.0, ), (-5.0, )], b=[(10.0, ), (5.0, )], nelements_per_axis=(nel_1d, ) * dim) order = 3 logger.info(f"Number of elements: {mesh.nelements}") discr = EagerDGDiscretization(actx, mesh, order=order) nodes = thaw(actx, discr.nodes()) # Init soln with Vortex vortex = Vortex2D() cv = vortex(nodes) gamma = 1.4 p = 0.4 * (cv.energy - 0.5 * np.dot(cv.momentum, cv.momentum) / cv.mass) exp_p = cv.mass**gamma errmax = discr.norm(p - exp_p, np.inf) logger.info(f"vortex_soln = {cv}") logger.info(f"pressure = {p}") assert errmax < 1e-15
def test_pulse(ctx_factory, dim): """ Test of Gaussian pulse generator. If it looks, walks, and quacks like a Gaussian, then ... """ cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) actx = PyOpenCLArrayContext(queue) nel_1d = 10 from meshmode.mesh.generation import generate_regular_rect_mesh mesh = generate_regular_rect_mesh(a=(-0.5, ) * dim, b=(0.5, ) * dim, nelements_per_axis=(nel_1d, ) * dim) order = 1 print(f"Number of elements: {mesh.nelements}") discr = EagerDGDiscretization(actx, mesh, order=order) nodes = thaw(actx, discr.nodes()) print(f"DIM = {dim}, {len(nodes)}") print(f"Nodes={nodes}") tol = 1e-15 from mirgecom.initializers import make_pulse amp = 1.0 w = .1 rms2 = w * w r0 = np.zeros(dim) r2 = np.dot(nodes, nodes) / rms2 pulse = make_pulse(amp=amp, r0=r0, w=w, r=nodes) print(f"Pulse = {pulse}") # does it return the expected exponential? pulse_check = actx.np.exp(-.5 * r2) print(f"exact: {pulse_check}") pulse_resid = pulse - pulse_check print(f"pulse residual: {pulse_resid}") assert (discr.norm(pulse_resid, np.inf) < tol) # proper scaling with amplitude? amp = 2.0 pulse = 0 pulse = make_pulse(amp=amp, r0=r0, w=w, r=nodes) pulse_resid = pulse - (pulse_check + pulse_check) assert (discr.norm(pulse_resid, np.inf) < tol) # proper scaling with r? amp = 1.0 rcheck = np.sqrt(2.0) * nodes pulse = make_pulse(amp=amp, r0=r0, w=w, r=rcheck) assert (discr.norm(pulse - (pulse_check * pulse_check), np.inf) < tol) # proper scaling with w? w = w / np.sqrt(2.0) pulse = make_pulse(amp=amp, r0=r0, w=w, r=nodes) assert (discr.norm(pulse - (pulse_check * pulse_check), np.inf) < tol)
def test_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 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_rect_mesh(visualize=False): mesh = mgen.generate_regular_rect_mesh(nelements_per_axis=(4, 4)) if visualize: from meshmode.mesh.visualization import draw_2d_mesh draw_2d_mesh(mesh, fill=None, draw_nodal_adjacency=True) import matplotlib.pyplot as pt pt.show()
def get_mesh(self, resolution, mesh_order): if not isinstance(resolution, (list, tuple)): resolution = (resolution, ) * self.ambient_dim return mgen.generate_regular_rect_mesh(a=self.a, b=self.b, nelements_per_axis=resolution, order=mesh_order)
def get_box_mesh(dim, a, b, n): dim_names = ["x", "y", "z"] boundary_tag_to_face = {} for i in range(dim): boundary_tag_to_face["-"+str(i)] = ["-"+dim_names[i]] boundary_tag_to_face["+"+str(i)] = ["+"+dim_names[i]] from meshmode.mesh.generation import generate_regular_rect_mesh return generate_regular_rect_mesh(a=(a,)*dim, b=(b,)*dim, nelements_per_axis=(n,)*dim, boundary_tag_to_face=boundary_tag_to_face)
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_rect_mesh(do_plot=False): from meshmode.mesh.generation import generate_regular_rect_mesh mesh = generate_regular_rect_mesh() if do_plot: from meshmode.mesh.visualization import draw_2d_mesh draw_2d_mesh(mesh, fill=None, draw_connectivity=True) import matplotlib.pyplot as pt pt.show()
def test_rect_mesh(do_plot=False): from meshmode.mesh.generation import generate_regular_rect_mesh mesh = generate_regular_rect_mesh() if do_plot: from meshmode.mesh.visualization import draw_2d_mesh draw_2d_mesh(mesh, fill=None, draw_nodal_adjacency=True) import matplotlib.pyplot as pt pt.show()
def main(): import logging logging.basicConfig(level=logging.INFO) ctx = cl.create_some_context() queue = cl.CommandQueue(ctx) if 1: ext = 0.5 mesh = generate_regular_rect_mesh( a=(-ext/2, -ext/2), b=(ext/2, ext/2), n=(int(ext/h), int(ext/h))) else: mesh = generate_gmsh( FileSource("circle.step"), 2, order=mesh_order, force_ambient_dim=2, other_options=["-string", "Mesh.CharacteristicLengthMax = %g;" % h] ) logger.info("%d elements" % mesh.nelements) # {{{ discretizations and connections vol_discr = Discretization(ctx, mesh, InterpolatoryQuadratureSimplexGroupFactory(vol_quad_order)) ovsmp_vol_discr = Discretization(ctx, mesh, InterpolatoryQuadratureSimplexGroupFactory(vol_ovsmp_quad_order)) from meshmode.mesh import BTAG_ALL from meshmode.discretization.connection import ( make_face_restriction, make_same_mesh_connection) bdry_connection = make_face_restriction( vol_discr, InterpolatoryQuadratureSimplexGroupFactory(bdry_quad_order), BTAG_ALL) bdry_discr = bdry_connection.to_discr vol_to_ovsmp_vol = make_same_mesh_connection(ovsmp_vol_discr, vol_discr) # }}} # {{{ visualizers vol_vis = make_visualizer(queue, vol_discr, 20) bdry_vis = make_visualizer(queue, bdry_discr, 20) # }}} vol_x = vol_discr.nodes().with_queue(queue) ovsmp_vol_x = ovsmp_vol_discr.nodes().with_queue(queue) rhs = rhs_func(vol_x[0], vol_x[1]) poisson_true_sol = sol_func(vol_x[0], vol_x[1]) vol_vis.write_vtk_file("volume.vtu", [("f", rhs)]) bdry_normals = bind( bdry_discr, p.normal(mesh.ambient_dim))(queue).as_vector(dtype=object) bdry_vis.write_vtk_file("boundary.vtu", [ ("normals", bdry_normals) ]) bdry_nodes = bdry_discr.nodes().with_queue(queue) bdry_f = rhs_func(bdry_nodes[0], bdry_nodes[1]) bdry_f_2 = bdry_connection(queue, rhs) bdry_vis.write_vtk_file("y.vtu", [("f", bdry_f_2)]) if 0: vol_vis.show_scalar_in_mayavi(rhs, do_show=False) bdry_vis.show_scalar_in_mayavi(bdry_f - bdry_f_2, line_width=10, do_show=False) import mayavi.mlab as mlab mlab.colorbar() mlab.show() # {{{ compute volume potential from sumpy.qbx import LayerPotential from sumpy.expansion.local import LineTaylorLocalExpansion def get_kernel(): from sumpy.symbolic import pymbolic_real_norm_2 from pymbolic.primitives import make_sym_vector from pymbolic import var d = make_sym_vector("d", 3) r = pymbolic_real_norm_2(d[:-1]) # r3d = pymbolic_real_norm_2(d) #expr = var("log")(r3d) log = var("log") sqrt = var("sqrt") a = d[-1] expr = log(r) expr = log(sqrt(r**2 + a**2)) expr = log(sqrt(r + a**2)) #expr = log(sqrt(r**2 + a**2))-a**2/2/(r**2+a**2) #expr = 2*log(sqrt(r**2 + a**2)) scaling = 1/(2*var("pi")) from sumpy.kernel import ExpressionKernel return ExpressionKernel( dim=3, expression=expr, global_scaling_const=scaling, is_complex_valued=False) laplace_2d_in_3d_kernel = get_kernel() layer_pot = LayerPotential(ctx, [ LineTaylorLocalExpansion(laplace_2d_in_3d_kernel, order=0)]) targets = cl.array.zeros(queue, (3,) + vol_x.shape[1:], vol_x.dtype) targets[:2] = vol_x center_dist = 0.125*np.min( cl.clmath.sqrt( bind(vol_discr, p.area_element(mesh.ambient_dim, mesh.dim)) (queue)).get()) centers = make_obj_array([ci.copy().reshape(vol_discr.nnodes) for ci in targets]) centers[2][:] = center_dist print(center_dist) sources = cl.array.zeros(queue, (3,) + ovsmp_vol_x.shape[1:], ovsmp_vol_x.dtype) sources[:2] = ovsmp_vol_x ovsmp_rhs = vol_to_ovsmp_vol(queue, rhs) ovsmp_vol_weights = bind(ovsmp_vol_discr, p.area_element(mesh.ambient_dim, mesh.dim) * p.QWeight() )(queue) print("volume: %d source nodes, %d target nodes" % ( ovsmp_vol_discr.nnodes, vol_discr.nnodes)) evt, (vol_pot,) = layer_pot( queue, targets=targets.reshape(3, vol_discr.nnodes), centers=centers, sources=sources.reshape(3, ovsmp_vol_discr.nnodes), strengths=( (ovsmp_vol_weights*ovsmp_rhs).reshape(ovsmp_vol_discr.nnodes),), expansion_radii=np.zeros(vol_discr.nnodes), ) vol_pot_bdry = bdry_connection(queue, vol_pot) # }}} # {{{ solve bvp from sumpy.kernel import LaplaceKernel from pytential.symbolic.pde.scalar import DirichletOperator op = DirichletOperator(LaplaceKernel(2), -1, use_l2_weighting=True) sym_sigma = sym.var("sigma") op_sigma = op.operator(sym_sigma) from pytential.qbx import QBXLayerPotentialSource qbx = QBXLayerPotentialSource( bdry_discr, fine_order=bdry_ovsmp_quad_order, qbx_order=qbx_order, fmm_order=fmm_order, ) bound_op = bind(qbx, op_sigma) poisson_bc = poisson_bc_func(bdry_nodes[0], bdry_nodes[1]) bvp_bc = poisson_bc - vol_pot_bdry bdry_f = rhs_func(bdry_nodes[0], bdry_nodes[1]) bvp_rhs = bind(bdry_discr, op.prepare_rhs(sym.var("bc")))(queue, bc=bvp_bc) from pytential.solve import gmres gmres_result = gmres( bound_op.scipy_op(queue, "sigma", dtype=np.float64), bvp_rhs, tol=1e-14, progress=True, hard_failure=False) sigma = gmres_result.solution print("gmres state:", gmres_result.state) # }}} bvp_sol = bind( (qbx, vol_discr), op.representation(sym_sigma))(queue, sigma=sigma) poisson_sol = bvp_sol + vol_pot poisson_err = poisson_sol-poisson_true_sol rel_err = ( norm(vol_discr, queue, poisson_err) / norm(vol_discr, queue, poisson_true_sol)) bdry_vis.write_vtk_file("poisson-boundary.vtu", [ ("vol_pot_bdry", vol_pot_bdry), ("sigma", sigma), ]) vol_vis.write_vtk_file("poisson-volume.vtu", [ ("bvp_sol", bvp_sol), ("poisson_sol", poisson_sol), ("poisson_true_sol", poisson_true_sol), ("poisson_err", poisson_err), ("vol_pot", vol_pot), ("rhs", rhs), ]) print("h = %s" % h) print("mesh_order = %s" % mesh_order) print("vol_quad_order = %s" % vol_quad_order) print("vol_ovsmp_quad_order = %s" % vol_ovsmp_quad_order) print("bdry_quad_order = %s" % bdry_quad_order) print("bdry_ovsmp_quad_order = %s" % bdry_ovsmp_quad_order) print("qbx_order = %s" % qbx_order) #print("vol_qbx_order = %s" % vol_qbx_order) print("fmm_order = %s" % fmm_order) print() print("rel err: %g" % rel_err)