def test_block_builder(ctx_factory, ambient_dim, block_builder_type, index_sparsity_factor, op_type, visualize=False): """Test that block builders and full matrix builders actually match.""" ctx = ctx_factory() queue = cl.CommandQueue(ctx) actx = PyOpenCLArrayContext(queue) # prevent cache explosion from sympy.core.cache import clear_cache clear_cache() if ambient_dim == 2: case = extra.CurveTestCase( name="ellipse", target_order=7, index_sparsity_factor=index_sparsity_factor, op_type=op_type, resolutions=[32], curve_fn=partial(ellipse, 3.0), )
for i in range(indices.nblocks): isrc = indices.block_indices(i) marker[isrc] = 10.0 * (i + 1.0) from meshmode.dof_array import unflatten marker = unflatten(actx, discr, actx.from_numpy(marker)) vis = make_visualizer(actx, discr, 10) filename = "test_partition_{0}_{1}_{3}d_{2}.vtu".format(*args) vis.write_vtk_file(filename, [("marker", marker)]) PROXY_TEST_CASES = [ extra.CurveTestCase(name="ellipse", target_order=7, curve_fn=partial(ellipse, 3.0)), extra.TorusTestCase(target_order=2, resolutions=[0]) ] @pytest.mark.skip(reason="only useful with visualize=True") @pytest.mark.parametrize("tree_kind", ['adaptive', None]) @pytest.mark.parametrize("case", PROXY_TEST_CASES) def test_partition_points(ctx_factory, tree_kind, case, visualize=False): """Tests that the points are correctly partitioned (by visualization).""" ctx = ctx_factory() queue = cl.CommandQueue(ctx) actx = PyOpenCLArrayContext(queue)
mesh = merge_disjoint_meshes([mesh, discr.mesh]) discr = Discretization( actx, mesh, InterpolatoryQuadratureSimplexGroupFactory(4)) vis = make_visualizer(actx, discr) filename = f"test_proxy_geometry_{suffix}_block_{i:04d}.vtu" vis.write_vtk_file(filename, [], overwrite=False) else: raise ValueError # }}} PROXY_TEST_CASES = [ extra.CurveTestCase(name="ellipse", target_order=7, curve_fn=partial(ellipse, 3.0)), extra.CurveTestCase(name="starfish", target_order=4, curve_fn=NArmedStarfish(5, 0.25), resolutions=[32]), extra.TorusTestCase(target_order=2, resolutions=[0]) ] # {{{ test_partition_points @pytest.mark.parametrize("tree_kind", ["adaptive", None]) @pytest.mark.parametrize("case", PROXY_TEST_CASES) def test_partition_points(actx_factory, tree_kind, case, visualize=False): """Tests that the points are correctly partitioned."""
def test_build_matrix(ctx_factory, k, curve_fn, op_type, visualize=False): """Checks that the matrix built with `symbolic.execution.build_matrix` gives the same (to tolerance) answer as a direct evaluation. """ cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) actx = PyOpenCLArrayContext(queue) # prevent cache 'splosion from sympy.core.cache import clear_cache clear_cache() case = extra.CurveTestCase(name="curve", knl_class_or_helmholtz_k=k, curve_fn=curve_fn, op_type=op_type, target_order=7, qbx_order=4, resolutions=[30]) logger.info("\n%s", case) # {{{ geometry qbx = case.get_layer_potential(actx, case.resolutions[-1], case.target_order) from pytential.qbx.refinement import refine_geometry_collection places = GeometryCollection(qbx, auto_where=case.name) places = refine_geometry_collection(places, kernel_length_scale=(5 / k if k else None)) dd = places.auto_source.to_stage1() density_discr = places.get_discretization(dd.geometry) logger.info("nelements: %d", density_discr.mesh.nelements) logger.info("ndofs: %d", density_discr.ndofs) # }}} # {{{ symbolic sym_u, sym_op = case.get_operator(places.ambient_dim) bound_op = bind(places, sym_op) # }}} # {{{ dense matrix from pytential.symbolic.execution import build_matrix mat = actx.to_numpy( build_matrix(actx, places, sym_op, sym_u, context=case.knl_concrete_kwargs)) if visualize: try: import matplotlib.pyplot as pt except ImportError: visualize = False if visualize: from sumpy.tools import build_matrix as build_matrix_via_matvec mat2 = bound_op.scipy_op(actx, "u", dtype=mat.dtype, **case.knl_concrete_kwargs) mat2 = build_matrix_via_matvec(mat2) logger.info( "real %.5e imag %.5e", la.norm((mat - mat2).real, "fro") / la.norm(mat2.real, "fro"), la.norm((mat - mat2).imag, "fro") / la.norm(mat2.imag, "fro")) pt.subplot(121) pt.imshow(np.log10(np.abs(1.0e-20 + (mat - mat2).real))) pt.colorbar() pt.subplot(122) pt.imshow(np.log10(np.abs(1.0e-20 + (mat - mat2).imag))) pt.colorbar() pt.show() pt.clf() if visualize: pt.subplot(121) pt.imshow(mat.real) pt.colorbar() pt.subplot(122) pt.imshow(mat.imag) pt.colorbar() pt.show() pt.clf() # }}} # {{{ check from pytential.utils import unflatten_from_numpy, flatten_to_numpy np.random.seed(12) for i in range(5): if isinstance(sym_u, np.ndarray): u = make_obj_array([ np.random.randn(density_discr.ndofs) for _ in range(len(sym_u)) ]) else: u = np.random.randn(density_discr.ndofs) u_dev = unflatten_from_numpy(actx, density_discr, u) res_matvec = np.hstack( flatten_to_numpy( actx, bound_op(actx, u=u_dev, **case.knl_concrete_kwargs))) res_mat = mat.dot(np.hstack(u)) abs_err = la.norm(res_mat - res_matvec, np.inf) rel_err = abs_err / la.norm(res_matvec, np.inf) logger.info("AbsErr {:.5e} RelErr {:.5e}".format(abs_err, rel_err)) assert rel_err < 1.0e-13, 'iteration: {}'.format(i)
def test_build_matrix_fixed_stage(ctx_factory, source_discr_stage, target_discr_stage, visualize=False): """Checks that the block builders match for difference stages.""" ctx = ctx_factory() queue = cl.CommandQueue(ctx) actx = PyOpenCLArrayContext(queue) # prevent cache explosion from sympy.core.cache import clear_cache clear_cache() case = extra.CurveTestCase( name="starfish", curve_fn=NArmedStarfish(5, 0.25), target_order=4, resolutions=[32], index_sparsity_factor=0.6, op_type="scalar", tree_kind=None, ) logger.info("\n%s", case) # {{{ geometry dd = sym.DOFDescriptor(case.name) qbx = case.get_layer_potential(actx, case.resolutions[-1], case.target_order) places = GeometryCollection( {case.name: qbx}, auto_where=(dd.copy(discr_stage=source_discr_stage), dd.copy(discr_stage=target_discr_stage))) dd = places.auto_source density_discr = places.get_discretization(dd.geometry, dd.discr_stage) # }}} # {{{ symbolic if source_discr_stage is target_discr_stage: qbx_forced_limit = -1 else: qbx_forced_limit = None sym_u, sym_op = case.get_operator(places.ambient_dim, qbx_forced_limit) from pytential.symbolic.execution import _prepare_expr sym_prep_op = _prepare_expr(places, sym_op) # }}} # {{{ check source_discr = places.get_discretization(case.name, source_discr_stage) target_discr = places.get_discretization(case.name, target_discr_stage) logger.info("nelements: %d", density_discr.mesh.nelements) logger.info("ndofs: %d", source_discr.ndofs) logger.info("ndofs: %d", target_discr.ndofs) icols = case.get_block_indices(actx, source_discr, matrix_indices=False) irows = case.get_block_indices(actx, target_discr, matrix_indices=False) index_set = MatrixBlockIndexRanges(actx.context, icols, irows) kwargs = dict( dep_expr=sym_u, other_dep_exprs=[], dep_source=places.get_geometry(case.name), dep_discr=density_discr, places=places, context=case.knl_concrete_kwargs, ) # qbx from pytential.symbolic import matrix mat = matrix.MatrixBuilder(actx, **kwargs)(sym_prep_op) blk = matrix.NearFieldBlockBuilder(actx, index_set=index_set, **kwargs)(sym_prep_op) assert mat.shape == (target_discr.ndofs, source_discr.ndofs) assert extra.max_block_error(mat, blk, index_set.get(queue)) < 1.0e-14 # p2p mat = matrix.P2PMatrixBuilder(actx, exclude_self=True, **kwargs)(sym_prep_op) blk = matrix.FarFieldBlockBuilder(actx, index_set=index_set, exclude_self=True, **kwargs)(sym_prep_op) assert mat.shape == (target_discr.ndofs, source_discr.ndofs) assert extra.max_block_error(mat, blk, index_set.get(queue)) < 1.0e-14
def test_build_matrix_conditioning(actx_factory, side, op_type, visualize=False): """Checks that :math:`I + K`, where :math:`K` is compact gives a well-conditioned operator when it should. For example, the exterior Laplace problem has a nullspace, so we check that and remove it. """ actx = actx_factory() # prevent cache explosion from sympy.core.cache import clear_cache clear_cache() case = extra.CurveTestCase( name="ellipse", curve_fn=lambda t: ellipse(3.0, t), target_order=16, source_ovsmp=1, qbx_order=4, resolutions=[64], op_type=op_type, side=side, ) logger.info("\n%s", case) # {{{ geometry qbx = case.get_layer_potential(actx, case.resolutions[-1], case.target_order) from pytential.qbx.refinement import refine_geometry_collection places = GeometryCollection(qbx, auto_where=case.name) places = refine_geometry_collection( places, refine_discr_stage=sym.QBX_SOURCE_QUAD_STAGE2) dd = places.auto_source.to_stage1() density_discr = places.get_discretization(dd.geometry) logger.info("nelements: %d", density_discr.mesh.nelements) logger.info("ndofs: %d", density_discr.ndofs) # }}} # {{{ check matrix from pytential.symbolic.execution import build_matrix sym_u, sym_op = case.get_operator(places.ambient_dim, qbx_forced_limit="avg") mat = actx.to_numpy( build_matrix(actx, places, sym_op, sym_u, context=case.knl_concrete_kwargs)) kappa = la.cond(mat) _, sigma, _ = la.svd(mat) logger.info("cond: %.5e sigma_max %.5e", kappa, sigma[0]) # NOTE: exterior Laplace has a nullspace if side == +1 and op_type == "double": assert kappa > 1.0e+9 assert sigma[-1] < 1.0e-9 else: assert kappa < 1.0e+1 assert sigma[-1] > 1.0e-2 # remove the nullspace and check that it worked if side == +1 and op_type == "double": # NOTE: this adds the "mean" to remove the nullspace for the operator # See `pytential.symbolic.pde.scalar` for the equivalent formulation w = actx.to_numpy( flatten( bind(places, sym.sqrt_jac_q_weight(places.ambient_dim)**2)(actx), actx)) w = np.tile(w.reshape(-1, 1), w.size).T kappa = la.cond(mat + w) assert kappa < 1.0e+2 # }}} # {{{ plot if not visualize: return side = "int" if side == -1 else "ext" import matplotlib.pyplot as plt plt.imshow(mat) plt.colorbar() plt.title(fr"$\kappa(A) = {kappa:.5e}$") plt.savefig(f"test_cond_{op_type}_{side}_mat") plt.clf() plt.plot(sigma) plt.ylabel(r"$\sigma$") plt.grid() plt.savefig(f"test_cond_{op_type}_{side}_svd") plt.clf()