def test_eoc(): np = pytest.importorskip("numpy") from pytools.convergence import EOCRecorder eoc = EOCRecorder() # {{{ test pretty_print for i in range(1, 8): eoc.add_data_point(1.0 / i, 10**(-i)) p = eoc.pretty_print() print(p) print() p = eoc.pretty_print(abscissa_format="%.5e", error_format="%.5e", eoc_format="%5.2f") print(p) # }}} # {{{ test merging from pytools.convergence import stringify_eocs p = stringify_eocs(eoc, eoc, eoc, names=("First", "Second", "Third")) print(p) # }}} # {{{ test invalid inputs import numpy as np eoc = EOCRecorder() # scalar inputs are fine eoc.add_data_point(1, 1) eoc.add_data_point(1.0, 1.0) eoc.add_data_point(np.float32(1.0), 1.0) eoc.add_data_point(np.array(3), 1.0) eoc.add_data_point(1.0, np.array(3)) # non-scalar inputs are not fine though with pytest.raises(TypeError): eoc.add_data_point(np.array([3]), 1.0) with pytest.raises(TypeError): eoc.add_data_point(1.0, np.array([3]))
def test_integration_order(integrator, method_order): """Test that time integrators have correct order.""" def exact_soln(t): return np.exp(-t) def rhs(t, state): return -np.exp(-t) from pytools.convergence import EOCRecorder integrator_eoc = EOCRecorder() dt = 1.0 for refine in [1, 2, 4, 8]: dt = dt / refine t = 0 state = exact_soln(t) while t < 4: state = integrator(state, t, dt, rhs) t = t + dt error = np.abs(state - exact_soln(t)) / exact_soln(t) integrator_eoc.add_data_point(dt, error) logger.info(f"Time Integrator EOC:\n = {integrator_eoc}") assert integrator_eoc.order_estimate() >= method_order - .01
def test_strang_splitting(plot_solution=False): from leap.rk import LSRK4Method method1 = LSRK4Method("y", rhs_func_name="<func>y1") method2 = LSRK4Method("y", rhs_func_name="<func>y2") code1 = method1.generate() code2 = method2.generate() from leap.transform import strang_splitting code = strang_splitting(code1, code2, "primary") print(code) from utils import python_method_impl_codegen def exact(t): return np.exp(3j * t) from pytools.convergence import EOCRecorder eocrec = EOCRecorder() for dt in 2**-np.array(range(4, 7), dtype=np.float64): # noqa pylint:disable=invalid-unary-operand-type interp = python_method_impl_codegen(code, function_map={ "<func>y1": lambda t, y: 1j * y, "<func>y2": lambda t, y: 2j * y, }) interp.set_up(t_start=0, dt_start=dt, context={"y": 1}) final_t = 4 times = [] values = [] for event in interp.run(t_end=final_t): if isinstance(event, interp.StateComputed): values.append(event.state_component) times.append(event.t) assert abs(times[-1] - final_t) / final_t < 0.1 times = np.array(times) # Check that the timestep is preserved. assert np.allclose(np.diff(times), dt) if plot_solution: import matplotlib.pyplot as pt pt.plot(times, values, label="comp") pt.plot(times, exact(times), label="true") pt.show() error = abs(values[-1] - exact(final_t)) eocrec.add_data_point(dt, error) print(eocrec.pretty_print()) orderest = eocrec.estimate_order_of_convergence()[0, 1] assert orderest > 2 * 0.9
def test_leapgen_integration_order(method, method_order): """Test that time integrators have correct order.""" def exact_soln(t): return np.exp(-t) def rhs(t, y): return -np.exp(-t) from pytools.convergence import EOCRecorder integrator_eoc = EOCRecorder() dt = 1.0 for refine in [1, 2, 4, 8]: dt = dt / refine t = 0 state = exact_soln(t) t_final = 4 step = 0 (step, t, state) = \ advance_state(rhs=rhs, timestepper=method, dt=dt, state=state, t=t, t_final=t_final, component_id="y") error = np.abs(state - exact_soln(t)) / exact_soln(t) integrator_eoc.add_data_point(dt, error) logger.info(f"Time Integrator EOC:\n = {integrator_eoc}") assert integrator_eoc.order_estimate() >= method_order - .1
def test_dielectric(ctx_factory, qbx_order, op_class, mode, visualize=False): cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) import logging logging.basicConfig(level=logging.INFO) from pytools.convergence import EOCRecorder eoc_rec = EOCRecorder() for nelements in [30, 50, 70]: # prevent sympy cache 'splosion from sympy.core.cache import clear_cache clear_cache() errs = run_dielectric_test(cl_ctx, queue, nelements=nelements, qbx_order=qbx_order, op_class=op_class, mode=mode, visualize=visualize) eoc_rec.add_data_point(1 / nelements, la.norm(list(errs), np.inf)) print(eoc_rec) assert eoc_rec.order_estimate() > qbx_order - 0.5
def get_convergence_data(method_class, problem, dts=_default_dts): from pytools.convergence import EOCRecorder eocrec = EOCRecorder() component_id = "y" for dt in dts: t = problem.t_start y = problem.initial() final_t = problem.t_end interp = method_class(function_map={ "<func>f": problem, }) interp.set_up(t_start=t, dt_start=dt, context={component_id: y}) times = [] values = [] for event in interp.run(t_end=final_t): if isinstance(event, interp.StateComputed): assert event.component_id == component_id values.append(event.state_component[0]) times.append(event.t) assert abs(times[-1] - final_t) / final_t < 0.1 times = np.array(times) error = abs(values[-1] - problem.exact(final_t)[0]) eocrec.add_data_point(dt, error) return eocrec
def test_exterior_stokes(ctx_factory, ambient_dim, visualize=False): if visualize: logging.basicConfig(level=logging.INFO) from pytools.convergence import EOCRecorder eoc = EOCRecorder() target_order = 3 qbx_order = 3 print(ambient_dim) if ambient_dim == 2: resolutions = [20, 35, 50] elif ambient_dim == 3: resolutions = [0, 1, 2] else: raise ValueError(f"unsupported dimension: {ambient_dim}") for resolution in resolutions: h_max, err = run_exterior_stokes(ctx_factory, ambient_dim=ambient_dim, target_order=target_order, qbx_order=qbx_order, resolution=resolution, visualize=visualize) eoc.add_data_point(h_max, err) print(eoc) # This convergence data is not as clean as it could be. See # https://github.com/inducer/pytential/pull/32 # for some discussion. assert eoc.order_estimate() > target_order - 0.5
def test_mass_operator_inverse(actx_factory, name): actx = actx_factory() # {{{ cases import mesh_data if name == "2-1-ellipse": # curve builder = mesh_data.EllipseMeshBuilder(radius=3.1, aspect_ratio=2.0) elif name == "spheroid": # surface builder = mesh_data.SpheroidMeshBuilder() elif name.startswith("warped_rect"): builder = mesh_data.WarpedRectMeshBuilder(dim=int(name[-1])) else: raise ValueError("unknown geometry name: %s" % name) # }}} # {{{ inv(m) @ m == id from pytools.convergence import EOCRecorder eoc = EOCRecorder() for resolution in builder.resolutions: mesh = builder.get_mesh(resolution, builder.mesh_order) dcoll = DiscretizationCollection(actx, mesh, order=builder.order) volume_discr = dcoll.discr_from_dd(dof_desc.DD_VOLUME) logger.info("ndofs: %d", volume_discr.ndofs) logger.info("nelements: %d", volume_discr.mesh.nelements) # {{{ compute inverse mass def f(x): return actx.np.cos(4.0 * x[0]) dd = dof_desc.DD_VOLUME x_volm = thaw(volume_discr.nodes(), actx) f_volm = f(x_volm) f_inv = op.inverse_mass(dcoll, op.mass(dcoll, dd, f_volm)) inv_error = actx.to_numpy( op.norm(dcoll, f_volm - f_inv, 2) / op.norm(dcoll, f_volm, 2)) # }}} # compute max element size from grudge.dt_utils import h_max_from_volume h_max = h_max_from_volume(dcoll) eoc.add_data_point(h_max, inv_error) logger.info("inverse mass error\n%s", str(eoc)) # NOTE: both cases give 1.0e-16-ish at the moment, but just to be on the # safe side, choose a slightly larger tolerance assert eoc.max_error() < 1.0e-14
def test_surface_mass_operator_inverse(actx_factory, name): actx = actx_factory() # {{{ cases if name == "2-1-ellipse": from mesh_data import EllipseMeshBuilder builder = EllipseMeshBuilder(radius=3.1, aspect_ratio=2.0) elif name == "spheroid": from mesh_data import SpheroidMeshBuilder builder = SpheroidMeshBuilder() else: raise ValueError("unknown geometry name: %s" % name) # }}} # {{{ convergence from pytools.convergence import EOCRecorder eoc = EOCRecorder() for resolution in builder.resolutions: mesh = builder.get_mesh(resolution, builder.mesh_order) discr = DiscretizationCollection(actx, mesh, order=builder.order) volume_discr = discr.discr_from_dd(dof_desc.DD_VOLUME) logger.info("ndofs: %d", volume_discr.ndofs) logger.info("nelements: %d", volume_discr.mesh.nelements) # {{{ compute inverse mass dd = dof_desc.DD_VOLUME sym_f = sym.cos(4.0 * sym.nodes(mesh.ambient_dim, dd)[0]) sym_op = sym.InverseMassOperator(dd, dd)(sym.MassOperator(dd, dd)( sym.var("f"))) f = bind(discr, sym_f)(actx) f_inv = bind(discr, sym_op)(actx, f=f) inv_error = bind( discr, sym.norm(2, sym.var("x") - sym.var("y")) / sym.norm(2, sym.var("y")))( actx, x=f_inv, y=f) # }}} h_max = bind( discr, sym.h_max_from_volume(discr.ambient_dim, dim=discr.dim, dd=dd))(actx) eoc.add_data_point(h_max, inv_error) # }}} logger.info("inverse mass error\n%s", str(eoc)) # NOTE: both cases give 1.0e-16-ish at the moment, but just to be on the # safe side, choose a slightly larger tolerance assert eoc.max_error() < 1.0e-14
def test_stresslet_identity(actx_factory, cls, visualize=False): if visualize: logging.basicConfig(level=logging.INFO) source_ovsmp = 4 if cls.func.ambient_dim == 2 else 8 case = cls(fmm_backend=None, target_order=5, qbx_order=3, source_ovsmp=source_ovsmp) identity = StressletIdentity(case.ambient_dim) logger.info("\n%s", str(case)) from pytools.convergence import EOCRecorder eocs = [EOCRecorder() for _ in range(case.ambient_dim)] for resolution in case.resolutions: h_max, errors = run_stokes_identity( actx_factory, case, identity, resolution=resolution, visualize=visualize) for eoc, e in zip(eocs, errors): eoc.add_data_point(h_max, e) for eoc in eocs: print(eoc.pretty_print( abscissa_format="%.8e", error_format="%.8e", eoc_format="%.2f")) for eoc in eocs: order = min(case.target_order, case.qbx_order) assert eoc.order_estimate() > order - 1.0
def test_mean_curvature(ctx_factory, discr_name, resolutions, discr_and_ref_mean_curvature_getter, visualize=False): ctx = ctx_factory() queue = cl.CommandQueue(ctx) actx = PyOpenCLArrayContext(queue) from pytools.convergence import EOCRecorder eoc = EOCRecorder() for r in resolutions: discr, ref_mean_curvature = \ discr_and_ref_mean_curvature_getter(actx, r) mean_curvature = bind(discr, sym.mean_curvature(discr.ambient_dim))(actx) h = 1.0 / r from meshmode.dof_array import flat_norm h_error = flat_norm(mean_curvature - ref_mean_curvature, np.inf) eoc.add_data_point(h, h_error) print(eoc) order = min([g.order for g in discr.groups]) assert eoc.order_estimate() > order - 1.1
def test_reversed_chained_connection(actx_factory, ndim, mesh_name): actx = actx_factory() def run(nelements, order): discr = create_discretization(actx, ndim, nelements=nelements, order=order, mesh_name=mesh_name) threshold = 1.0 connections = [] conn = create_refined_connection(actx, discr, threshold=threshold) connections.append(conn) if ndim == 2: # NOTE: additional refinement makes the 3D meshes explode in size conn = create_refined_connection(actx, conn.to_discr, threshold=threshold) connections.append(conn) conn = create_refined_connection(actx, conn.to_discr, threshold=threshold) connections.append(conn) from meshmode.discretization.connection import \ ChainedDiscretizationConnection chained = ChainedDiscretizationConnection(connections) from meshmode.discretization.connection import \ L2ProjectionInverseDiscretizationConnection reverse = L2ProjectionInverseDiscretizationConnection(chained) # create test vector from_nodes = thaw(chained.from_discr.nodes(), actx) to_nodes = thaw(chained.to_discr.nodes(), actx) from_x = 0 to_x = 0 for d in range(ndim): from_x = from_x + actx.np.cos(from_nodes[d])**(d + 1) to_x = to_x + actx.np.cos(to_nodes[d])**(d + 1) from_interp = reverse(to_x) return (1.0 / nelements, flat_norm(from_interp - from_x, np.inf) / flat_norm(from_x, np.inf)) from pytools.convergence import EOCRecorder eoc = EOCRecorder() order = 4 mesh_sizes = [16, 32, 48, 64, 96, 128] for n in mesh_sizes: h, error = run(n, order) eoc.add_data_point(h, error) print(eoc) assert eoc.order_estimate() > (order + 1 - 0.5)
def test_pde_check_kernels(ctx_factory, knl_info, order=5): dim = knl_info.kernel.dim tctx = t.ToyContext(ctx_factory(), knl_info.kernel, extra_source_kwargs=knl_info.extra_kwargs) pt_src = t.PointSources( tctx, np.random.rand(dim, 50) - 0.5, np.ones(50)) from pytools.convergence import EOCRecorder from sumpy.point_calculus import CalculusPatch eoc_rec = EOCRecorder() for h in [0.1, 0.05, 0.025]: cp = CalculusPatch(np.array([1, 0, 0])[:dim], h=h, order=order) pot = pt_src.eval(cp.points) pde = knl_info.pde_func(cp, pot) err = la.norm(pde) eoc_rec.add_data_point(h, err) print(eoc_rec) assert eoc_rec.order_estimate() > order - knl_info.nderivs + 1 - 0.1
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 check_simple_convergence(method, method_impl, expected_order, problem=DefaultProblem(), dts=_default_dts, show_dag=False, plot_solution=False): component_id = method.component_id code = method.generate() print(code) if show_dag: from dagrt.language import show_dependency_graph show_dependency_graph(code) from pytools.convergence import EOCRecorder eocrec = EOCRecorder() for dt in dts: t = problem.t_start y = problem.initial() final_t = problem.t_end interp = method_impl(code, function_map={ "<func>" + component_id: problem, }) interp.set_up(t_start=t, dt_start=dt, context={component_id: y}) times = [] values = [] for event in interp.run(t_end=final_t): if isinstance(event, interp.StateComputed): assert event.component_id == component_id values.append(event.state_component[0]) times.append(event.t) assert abs(times[-1] - final_t) / final_t < 0.1 times = np.array(times) if plot_solution: import matplotlib.pyplot as pt pt.plot(times, values, label="comp") pt.plot(times, problem.exact(times), label="true") pt.show() error = abs(values[-1] - problem.exact(final_t)) eocrec.add_data_point(dt, error) print("------------------------------------------------------") print("%s: expected order %d" % (method.__class__.__name__, expected_order)) print("------------------------------------------------------") print(eocrec.pretty_print()) orderest = eocrec.estimate_order_of_convergence()[0, 1] assert orderest > expected_order * 0.9
def test_direct(ctx_factory): # This evaluates a single layer potential on a circle. logging.basicConfig(level=logging.INFO) ctx = ctx_factory() queue = cl.CommandQueue(ctx) from sumpy.kernel import LaplaceKernel lknl = LaplaceKernel(2) order = 12 from sumpy.qbx import LayerPotential from sumpy.expansion.local import LineTaylorLocalExpansion lpot = LayerPotential(ctx, [LineTaylorLocalExpansion(lknl, order)]) mode_nr = 25 from pytools.convergence import EOCRecorder eocrec = EOCRecorder() for n in [200, 300, 400]: t = np.linspace(0, 2 * np.pi, n, endpoint=False) unit_circle = np.exp(1j * t) unit_circle = np.array([unit_circle.real, unit_circle.imag]) sigma = np.cos(mode_nr * t) eigval = 1 / (2 * mode_nr) result_ref = eigval * sigma h = 2 * np.pi / n targets = unit_circle sources = unit_circle radius = 7 * h centers = unit_circle * (1 - radius) expansion_radii = np.ones(n) * radius strengths = (sigma * h, ) evt, (result_qbx, ) = lpot(queue, targets, sources, centers, strengths, expansion_radii=expansion_radii) eocrec.add_data_point(h, np.max(np.abs(result_ref - result_qbx))) print(eocrec) slack = 1.5 assert eocrec.order_estimate() > order - slack
def test_wave_accuracy(actx_factory, problem, order, visualize=False): """Checks accuracy of the wave operator for a given problem setup. """ actx = actx_factory() p = problem sym_u, sym_v, sym_f, sym_rhs = sym_wave(p.dim, p.sym_phi) from pytools.convergence import EOCRecorder eoc_rec = EOCRecorder() for n in [8, 10, 12] if p.dim == 3 else [8, 12, 16]: mesh = p.mesh_factory(n) from grudge.eager import EagerDGDiscretization discr = EagerDGDiscretization(actx, mesh, order=order) nodes = thaw(actx, discr.nodes()) def sym_eval(expr, t): return sym.EvaluationMapper({"c": p.c, "x": nodes, "t": t})(expr) t_check = 1.23456789 u = sym_eval(sym_u, t_check) v = sym_eval(sym_v, t_check) fields = flat_obj_array(u, v) rhs = wave_operator(discr, c=p.c, w=fields) rhs[0] = rhs[0] + sym_eval(sym_f, t_check) expected_rhs = sym_eval(sym_rhs, t_check) rel_linf_err = actx.to_numpy( discr.norm(rhs - expected_rhs, np.inf) / discr.norm(expected_rhs, np.inf)) eoc_rec.add_data_point(1. / n, rel_linf_err) if visualize: from grudge.shortcuts import make_visualizer vis = make_visualizer(discr, discr.order) vis.write_vtk_file( "wave_accuracy_{order}_{n}.vtu".format(order=order, n=n), [ ("u", fields[0]), ("v", fields[1:]), ("rhs_u_actual", rhs[0]), ("rhs_v_actual", rhs[1:]), ("rhs_u_expected", expected_rhs[0]), ("rhs_v_expected", expected_rhs[1:]), ]) print("Approximation error:") print(eoc_rec) assert (eoc_rec.order_estimate() >= order - 0.5 or eoc_rec.max_error() < 1e-11)
def test_convergence(python_method_impl, problem, method, expected_order): pytest.importorskip("scipy") code = method.generate() from leap.implicit import replace_AssignImplicit code = replace_AssignImplicit(code, {"solve": solver_hook}) from pytools.convergence import EOCRecorder eocrec = EOCRecorder() for n in range(2, 7): dt = 2**(-n) y_0 = problem.initial() t_start = problem.t_start t_end = problem.t_end from functools import partial interp = python_method_impl(code, function_map={ "<func>expl_y": problem.nonstiff, "<func>impl_y": problem.stiff, "<func>solver": partial(solver, problem.stiff), }) interp.set_up(t_start=t_start, dt_start=dt, context={"y": y_0}) times = [] values = [] for event in interp.run(t_end=t_end): if isinstance(event, interp.StateComputed): values.append(event.state_component) times.append(event.t) times = np.array(times) values = np.array(values) assert abs(times[-1] - t_end) < 1e-10 times = np.array(times) error = np.linalg.norm(values[-1] - problem.exact(t_end)) eocrec.add_data_point(dt, error) print("------------------------------------------------------") print("expected order %d" % expected_order) print("------------------------------------------------------") print(eocrec.pretty_print()) orderest = eocrec.estimate_order_of_convergence()[0, 1] assert orderest > 0.9 * expected_order
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 test_exterior_stokes_2d(ctx_factory, qbx_order=3): from pytools.convergence import EOCRecorder eoc_rec = EOCRecorder() for nelements in [20, 50]: h_max, l2_err = run_exterior_stokes_2d(ctx_factory, nelements) eoc_rec.add_data_point(h_max, l2_err) print(eoc_rec) assert eoc_rec.order_estimate() >= qbx_order - 1
def conv_test(descr, use_quad): logger.info("-" * 75) logger.info(descr) logger.info("-" * 75) eoc_rec = EOCRecorder() if use_quad: qtag = dof_desc.DISCR_TAG_QUAD else: qtag = None ns = [20, 25] for n in ns: mesh = mgen.generate_regular_rect_mesh(a=(-0.5, ) * dims, b=(0.5, ) * dims, nelements_per_axis=(n, ) * dims, order=order) if use_quad: discr_tag_to_group_factory = { qtag: QuadratureSimplexGroupFactory(order=4 * order) } else: discr_tag_to_group_factory = {} dcoll = DiscretizationCollection( actx, mesh, order=order, discr_tag_to_group_factory=discr_tag_to_group_factory) nodes = thaw(dcoll.nodes(), actx) def zero_inflow(dtag, t=0): dd = dof_desc.DOFDesc(dtag, qtag) return dcoll.discr_from_dd(dd).zeros(actx) adv_op = VariableCoefficientAdvectionOperator( dcoll, flat_obj_array(-1 * nodes[1], nodes[0]), inflow_u=lambda t: zero_inflow(BTAG_ALL, t=t), flux_type="upwind", quad_tag=qtag) total_error = op.norm(dcoll, adv_op.operator(0, gaussian_mode(nodes)), 2) eoc_rec.add_data_point(1.0 / n, actx.to_numpy(total_error)) logger.info( "\n%s", eoc_rec.pretty_print(abscissa_label="h", error_label="L2 Error")) return eoc_rec.order_estimate(), np.array( [x[1] for x in eoc_rec.history])
def test_velocity_gradient_eoc(actx_factory, dim): """Test that the velocity gradient converges at the proper rate.""" from mirgecom.fluid import velocity_gradient actx = actx_factory() order = 3 from pytools.convergence import EOCRecorder eoc = EOCRecorder() nel_1d_0 = 4 for hn1 in [1, 2, 3, 4]: nel_1d = hn1 * nel_1d_0 h = 1/nel_1d 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 ) discr = EagerDGDiscretization(actx, mesh, order=order) nodes = thaw(actx, discr.nodes()) zeros = discr.zeros(actx) energy = zeros + 2.5 mass = nodes[dim-1]*nodes[dim-1] velocity = make_obj_array([actx.np.cos(nodes[i]) for i in range(dim)]) mom = mass*velocity q = join_conserved(dim, mass=mass, energy=energy, momentum=mom) cv = split_conserved(dim, q) grad_q = obj_array_vectorize(discr.grad, q) grad_cv = split_conserved(dim, grad_q) grad_v = velocity_gradient(discr, cv, grad_cv) def exact_grad_row(xdata, gdim, dim): exact_grad_row = make_obj_array([zeros for _ in range(dim)]) exact_grad_row[gdim] = -actx.np.sin(xdata) return exact_grad_row comp_err = make_obj_array([ discr.norm(grad_v[i] - exact_grad_row(nodes[i], i, dim), np.inf) for i in range(dim)]) err_max = comp_err.max() eoc.add_data_point(h, err_max) logger.info(eoc) assert ( eoc.order_estimate() >= order - 0.5 or eoc.max_error() < 1e-9 )
def test_eoc(): from pytools.convergence import EOCRecorder eoc = EOCRecorder() for i in range(1, 8): eoc.add_data_point(1.0 / i, 10**(-i)) p = eoc.pretty_print() print(p) print() p = eoc.pretty_print(abscissa_format="%.5e", error_format="%.5e", eoc_format="%5.2f") print(p)
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 test_pde_check(dim, order=4): from sumpy.point_calculus import CalculusPatch from pytools.convergence import EOCRecorder for iaxis in range(dim): eoc_rec = EOCRecorder() for h in [0.1, 0.01, 0.001]: cp = CalculusPatch(np.array([3, 0, 0])[:dim], h=h, order=order) df_num = cp.diff(iaxis, np.sin(10 * cp.points[iaxis])) df_true = 10 * np.cos(10 * cp.points[iaxis]) err = la.norm(df_num - df_true) eoc_rec.add_data_point(h, err) print(eoc_rec) assert eoc_rec.order_estimate() > order - 2 - 0.1
def test_exterior_stokes(actx_factory, ambient_dim, visualize=False): if visualize: logging.basicConfig(level=logging.INFO) from pytools.convergence import EOCRecorder eocs = [EOCRecorder() for _ in range(ambient_dim)] target_order = 5 source_ovsmp = 4 qbx_order = 3 if ambient_dim == 2: resolutions = [20, 35, 50] elif ambient_dim == 3: resolutions = [0, 1, 2] else: raise ValueError(f"unsupported dimension: {ambient_dim}") for resolution in resolutions: h_max, errors = run_exterior_stokes(actx_factory, ambient_dim=ambient_dim, target_order=target_order, qbx_order=qbx_order, source_ovsmp=source_ovsmp, resolution=resolution, visualize=visualize) for eoc, e in zip(eocs, errors): eoc.add_data_point(h_max, e) for eoc in eocs: print(eoc.pretty_print( abscissa_format="%.8e", error_format="%.8e", eoc_format="%.2f")) for eoc in eocs: # This convergence data is not as clean as it could be. See # https://github.com/inducer/pytential/pull/32 # for some discussion. order = min(target_order, qbx_order) assert eoc.order_estimate() > order - 0.5
def conv_test(descr, use_quad): logger.info("-" * 75) logger.info(descr) logger.info("-" * 75) eoc_rec = EOCRecorder() ns = [20, 25] for n in ns: mesh = mgen.generate_regular_rect_mesh(a=(-0.5, ) * dims, b=(0.5, ) * dims, nelements_per_axis=(n, ) * dims, order=order) if use_quad: discr_tag_to_group_factory = { "product": QuadratureSimplexGroupFactory(order=4 * order) } else: discr_tag_to_group_factory = {"product": None} discr = DiscretizationCollection( actx, mesh, order=order, discr_tag_to_group_factory=discr_tag_to_group_factory) bound_op = bind(discr, op.sym_operator()) fields = bind(discr, gaussian_mode())(actx, t=0) norm = bind(discr, sym.norm(2, sym.var("u"))) esc = bound_op(u=fields) total_error = norm(u=esc) eoc_rec.add_data_point(1.0 / n, total_error) logger.info( "\n%s", eoc_rec.pretty_print(abscissa_label="h", error_label="L2 Error")) return eoc_rec.order_estimate(), np.array( [x[1] for x in eoc_rec.history])
def __call__(self): """Run the test and output the estimated the order of convergence.""" from pytools.convergence import EOCRecorder if self.display_dag: self.show_dag() eocrec = EOCRecorder() for n in range(6, 8): dt = 2**(-n) error = self.get_error(dt, "mrab-%d.dat" % self.order) eocrec.add_data_point(dt, error) print("------------------------------------------------------") print("ORDER %d" % self.order) print("------------------------------------------------------") print(eocrec.pretty_print()) orderest = eocrec.estimate_order_of_convergence()[0, 1] assert orderest > self.order * 0.70
def test_tri_diff_mat(ctx_factory, dim, order=4): """Check differentiation matrix along the coordinate axes on a disk Uses sines as the function to differentiate. """ cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) actx = PyOpenCLArrayContext(queue) from meshmode.mesh.generation import generate_regular_rect_mesh from pytools.convergence import EOCRecorder axis_eoc_recs = [EOCRecorder() for axis in range(dim)] for n in [10, 20]: mesh = generate_regular_rect_mesh(a=(-0.5, ) * dim, b=(0.5, ) * dim, n=(n, ) * dim, order=4) discr = DGDiscretizationWithBoundaries(actx, mesh, order=4) nabla = sym.nabla(dim) for axis in range(dim): x = sym.nodes(dim) f = bind(discr, sym.sin(3 * x[axis]))(actx) df = bind(discr, 3 * sym.cos(3 * x[axis]))(actx) sym_op = nabla[axis](sym.var("f")) bound_op = bind(discr, sym_op) df_num = bound_op(f=f) linf_error = flat_norm(df_num - df, np.Inf) axis_eoc_recs[axis].add_data_point(1 / n, linf_error) for axis, eoc_rec in enumerate(axis_eoc_recs): logger.info("axis %d\n%s", axis, eoc_rec) assert eoc_rec.order_estimate() >= order
def conv_test(descr, use_quad): print("-" * 75) print(descr) print("-" * 75) eoc_rec = EOCRecorder() ns = [20, 25] for n in ns: mesh = generate_regular_rect_mesh(a=(-0.5, ) * dims, b=(0.5, ) * dims, n=(n, ) * dims, order=order) if use_quad: quad_tag_to_group_factory = { "product": QuadratureSimplexGroupFactory(order=4 * order) } else: quad_tag_to_group_factory = {"product": None} discr = DGDiscretizationWithBoundaries( cl_ctx, mesh, order=order, quad_tag_to_group_factory=quad_tag_to_group_factory) bound_op = bind(discr, op.sym_operator()) fields = bind(discr, gaussian_mode())(queue, t=0) norm = bind(discr, sym.norm(2, sym.var("u"))) esc = bound_op(queue, u=fields) total_error = norm(queue, u=esc) eoc_rec.add_data_point(1.0 / n, total_error) print( eoc_rec.pretty_print(abscissa_label="h", error_label="LInf Error")) return eoc_rec.order_estimate(), np.array( [x[1] for x in eoc_rec.history])