diff --git a/test/test_meshmode.py b/test/test_meshmode.py index 76d6a69751faa2737153f68a2f3e65b325264de6..9b4be17622ffaf3aa1825350929b055c44239db1 100644 --- a/test/test_meshmode.py +++ b/test/test_meshmode.py @@ -32,6 +32,11 @@ from pyopencl.tools import ( # noqa pytest_generate_tests_for_pyopencl as pytest_generate_tests) +import pytest + +import logging +logger = logging.getLogger(__name__) + def test_boundary_interpolation(ctx_getter): cl_ctx = ctx_getter() @@ -41,9 +46,9 @@ def test_boundary_interpolation(ctx_getter): from meshmode.discretization import Discretization from meshmode.discretization.poly_element import \ QuadratureSimplexGroupFactory - from pytools.convergence import EOCRecorder from meshmode.discretization.connection import make_boundary_restriction + from pytools.convergence import EOCRecorder eoc_rec = EOCRecorder() order = 4 @@ -52,7 +57,7 @@ def test_boundary_interpolation(ctx_getter): print "BEGIN GEN" mesh = generate_gmsh( FileSource("blob-2d.step"), 2, order=order, - force_ambient_dimension=2, + force_ambient_dim=2, other_options=[ "-string", "Mesh.CharacteristicLengthMax = %s;" % h] ) @@ -87,7 +92,7 @@ def test_element_orientation(): mesh = generate_gmsh( FileSource("blob-2d.step"), 2, order=mesh_order, - force_ambient_dimension=2, + force_ambient_dim=2, other_options=["-string", "Mesh.CharacteristicLengthMax = 0.02;"] ) @@ -102,13 +107,209 @@ def test_element_orientation(): for i in range(int(0.3*mesh.nelements)): flippy[randrange(0, mesh.nelements)] = 1 - mesh = perform_flips(mesh, flippy) + mesh = perform_flips(mesh, flippy, skip_tests=True) mesh_orient = find_volume_mesh_element_orientations(mesh) assert ((mesh_orient < 0) == (flippy > 0)).all() +@pytest.mark.parametrize("dim", [2, 3]) +@pytest.mark.parametrize("order", [1, 3]) +def test_sanity_single_element(ctx_getter, dim, order, visualize=False): + pytest.importorskip("pytential") + + cl_ctx = ctx_getter() + queue = cl.CommandQueue(cl_ctx) + + from modepy.tools import UNIT_VERTICES + vertices = UNIT_VERTICES[dim].T.copy() + + center = np.empty(dim, np.float64) + center.fill(-0.5) + + import modepy as mp + from meshmode.mesh import SimplexElementGroup, Mesh + mg = SimplexElementGroup( + order=order, + vertex_indices=np.arange(dim+1).reshape(1, -1), + nodes=mp.warp_and_blend_nodes(dim, order).reshape(dim, 1, -1), + dim=dim) + + mesh = Mesh(vertices, [mg]) + + from meshmode.discretization import Discretization + from meshmode.discretization.poly_element import \ + PolynomialWarpAndBlendGroupFactory + vol_discr = Discretization(cl_ctx, mesh, + PolynomialWarpAndBlendGroupFactory(order+3)) + + # {{{ volume calculation check + + vol_x = vol_discr.nodes().with_queue(queue) + + vol_one = vol_x[0].copy() + vol_one.fill(1) + from pytential import norm, integral # noqa + + from pytools import factorial + true_vol = 1/factorial(dim) * 2**dim + + comp_vol = integral(vol_discr, queue, vol_one) + rel_vol_err = abs(true_vol - comp_vol) / true_vol + + assert rel_vol_err < 1e-12 + + # }}} + + # {{{ boundary discretization + + from meshmode.discretization.connection import make_boundary_restriction + bdry_mesh, bdry_discr, bdry_connection = make_boundary_restriction( + queue, vol_discr, PolynomialWarpAndBlendGroupFactory(order + 3)) + + # }}} + + # {{{ visualizers + + from meshmode.discretization.visualization import make_visualizer + #vol_vis = make_visualizer(queue, vol_discr, 4) + bdry_vis = make_visualizer(queue, bdry_discr, 4) + + # }}} + + from pytential import bind, sym + bdry_normals = bind(bdry_discr, sym.normal())(queue).as_vector(dtype=object) + + if visualize: + bdry_vis.write_vtk_file("boundary.vtu", [ + ("bdry_normals", bdry_normals) + ]) + + from pytential import bind, sym + normal_outward_check = bind(bdry_discr, + sym.normal() + | + (sym.Nodes() + 0.5*sym.ones_vec(dim)), + )(queue).as_scalar() > 0 + + assert normal_outward_check.get().all(), normal_outward_check.get() + + +# python test_meshmode.py 'test_sanity_balls(cl._csc, "disk-radius-1.step", 2, 2, visualize=True)' # noqa +@pytest.mark.parametrize(("src_file", "dim"), [ + ("disk-radius-1.step", 2), + ("ball-radius-1.step", 3), + ]) +@pytest.mark.parametrize("mesh_order", [1, 2]) +def test_sanity_balls(ctx_getter, src_file, dim, mesh_order, + visualize=False): + pytest.importorskip("pytential") + + logging.basicConfig(level=logging.INFO) + + ctx = ctx_getter() + queue = cl.CommandQueue(ctx) + + from pytools.convergence import EOCRecorder + vol_eoc_rec = EOCRecorder() + surf_eoc_rec = EOCRecorder() + + # overkill + quad_order = mesh_order + + from pytential import bind, sym + + for h in [0.2, 0.15, 0.1]: + from meshmode.mesh.io import generate_gmsh, FileSource + mesh = generate_gmsh( + FileSource(src_file), dim, order=mesh_order, + other_options=["-string", "Mesh.CharacteristicLengthMax = %g;" % h], + force_ambient_dim=dim) + + logger.info("%d elements" % mesh.nelements) + + # {{{ discretizations and connections + + from meshmode.discretization import Discretization + from meshmode.discretization.poly_element import \ + QuadratureSimplexGroupFactory + vol_discr = Discretization(ctx, mesh, + QuadratureSimplexGroupFactory(quad_order)) + + from meshmode.discretization.connection import make_boundary_restriction + bdry_mesh, bdry_discr, bdry_connection = make_boundary_restriction( + queue, vol_discr, QuadratureSimplexGroupFactory(quad_order)) + + # }}} + + # {{{ visualizers + + from meshmode.discretization.visualization import make_visualizer + vol_vis = make_visualizer(queue, vol_discr, 20) + bdry_vis = make_visualizer(queue, bdry_discr, 20) + + # }}} + + from math import gamma + true_surf = 2*np.pi**(dim/2)/gamma(dim/2) + true_vol = true_surf/dim + + vol_x = vol_discr.nodes().with_queue(queue) + + vol_one = vol_x[0].copy() + vol_one.fill(1) + from pytential import norm, integral # noqa + + comp_vol = integral(vol_discr, queue, vol_one) + rel_vol_err = abs(true_vol - comp_vol) / true_vol + vol_eoc_rec.add_data_point(h, rel_vol_err) + print "VOL", true_vol, comp_vol + + bdry_x = bdry_discr.nodes().with_queue(queue) + + bdry_one_exact = bdry_x[0].copy() + bdry_one_exact.fill(1) + + bdry_one = bdry_connection(queue, vol_one).with_queue(queue) + intp_err = norm(bdry_discr, queue, bdry_one-bdry_one_exact) + assert intp_err < 1e-14 + + comp_surf = integral(bdry_discr, queue, bdry_one) + rel_surf_err = abs(true_surf - comp_surf) / true_surf + surf_eoc_rec.add_data_point(h, rel_surf_err) + print "SURF", true_surf, comp_surf + + if visualize: + vol_vis.write_vtk_file("volume-h=%g.vtu" % h, [ + ("f", vol_one), + ("area_el", bind(vol_discr, sym.area_element())(queue)), + ]) + bdry_vis.write_vtk_file("boundary-h=%g.vtu" % h, [("f", bdry_one)]) + + # {{{ check normals point outward + + normal_outward_check = bind(bdry_discr, + sym.normal() | sym.Nodes(), + )(queue).as_scalar() > 0 + + assert normal_outward_check.get().all(), normal_outward_check.get() + + # }}} + + print "---------------------------------" + print "VOLUME" + print "---------------------------------" + print vol_eoc_rec + assert vol_eoc_rec.order_estimate() >= mesh_order + + print "---------------------------------" + print "SURFACE" + print "---------------------------------" + print surf_eoc_rec + assert surf_eoc_rec.order_estimate() >= mesh_order + + if __name__ == "__main__": import sys if len(sys.argv) > 1: