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_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_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 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_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 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 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 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])
def test_dependent_state(order=3, step_ratio=3): # Solve # f' = f+s # s' = -f+s def true_f(t): return np.exp(t) * np.sin(t) def true_s(t): return np.exp(t) * np.cos(t) method = MultiRateMultiStepMethodBuilder(order, ( ( "dt", "fast", "=", MRHistory(1, "<func>f", ( "two_fast", "slow", )), ), ("dt", "slow", "=", MRHistory(step_ratio, "<func>s", ("fast", "slow"))), ( "two_fast", "=", MRHistory(step_ratio, "<func>twice", ("fast", )), ), ), static_dt=True) code = method.generate() print(code) from pytools.convergence import EOCRecorder eocrec = EOCRecorder() from dagrt.codegen import PythonCodeGenerator codegen = PythonCodeGenerator(class_name="Method") stepper_cls = codegen.get_class(code) for n in range(4, 7): t = 0 dt = 2**(-n) final_t = 10 stepper = stepper_cls( function_map={ "<func>f": lambda t, two_fast, slow: 0.5 * two_fast + slow, "<func>s": lambda t, fast, slow: -fast + slow, "<func>twice": lambda t, fast: 2 * fast, }) stepper.set_up(t_start=t, dt_start=dt, context={ "fast": true_f(t), "slow": true_s(t), }) f_times = [] f_values = [] s_times = [] s_values = [] for event in stepper.run(t_end=final_t): if isinstance(event, stepper_cls.StateComputed): if event.component_id == "fast": f_times.append(event.t) f_values.append(event.state_component) elif event.component_id == "slow": s_times.append(event.t) s_values.append(event.state_component) else: assert False, event.component_id f_times = np.array(f_times) s_times = np.array(s_times) f_values_true = true_f(f_times) s_values_true = true_s(s_times) f_err = f_values - f_values_true s_err = s_values - s_values_true error = ( la.norm(f_err) / la.norm(f_values_true) + # noqa: W504 la.norm(s_err) / la.norm(s_values_true)) eocrec.add_data_point(dt, error) print(eocrec.pretty_print()) orderest = eocrec.estimate_order_of_convergence()[0, 1] assert orderest > 3 * 0.95
def main(show_dag=False, plot_solution=False): component_id = "y" method = ODE23Method(component_id, use_high_order=True) expected_order = 3 # Use "DEBUG" to trace execution logging.basicConfig(level=logging.INFO) code = method.generate() if show_dag: from dagrt.language import show_dependency_graph show_dependency_graph(code) from dagrt.exec_numpy import NumpyInterpreter def rhs(t, y): u, v = y return np.array([v, -u/t**2], dtype=np.float64) def soln(t): inner = np.sqrt(3)/2*np.log(t) return np.sqrt(t)*( 5*np.sqrt(3)/3*np.sin(inner) + np.cos(inner) ) from pytools.convergence import EOCRecorder eocrec = EOCRecorder() for n in range(4, 7): dt = 2**(-n) t = 1 y = np.array([1, 3], dtype=np.float64) final_t = 10 interp = NumpyInterpreter(code, function_map={"<func>" + component_id: rhs}) 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) < 1e-10 times = np.array(times) if plot_solution: import matplotlib.pyplot as pt pt.plot(times, values, label="comp") pt.plot(times, soln(times), label="true") pt.show() error = abs(values[-1]-soln(final_t)) eocrec.add_data_point(dt, error) print("------------------------------------------------------") print("%s: expected order %d" % (method, expected_order)) print("------------------------------------------------------") print(eocrec.pretty_print()) orderest = eocrec.estimate_order_of_convergence()[0, 1] assert orderest > expected_order*0.95
def test_convergence_maxwell(ctx_factory, order): """Test whether 3D Maxwell's actually converges""" cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) actx = PyOpenCLArrayContext(queue) from pytools.convergence import EOCRecorder eoc_rec = EOCRecorder() dims = 3 ns = [4, 6, 8] for n in ns: from meshmode.mesh.generation import generate_regular_rect_mesh mesh = generate_regular_rect_mesh(a=(0.0, ) * dims, b=(1.0, ) * dims, n=(n, ) * dims) discr = DGDiscretizationWithBoundaries(actx, mesh, order=order) epsilon = 1 mu = 1 from grudge.models.em import get_rectangular_cavity_mode sym_mode = get_rectangular_cavity_mode(1, (1, 2, 2)) analytic_sol = bind(discr, sym_mode) fields = analytic_sol(actx, t=0, epsilon=epsilon, mu=mu) from grudge.models.em import MaxwellOperator op = MaxwellOperator(epsilon, mu, flux_type=0.5, dimensions=dims) op.check_bc_coverage(mesh) bound_op = bind(discr, op.sym_operator()) def rhs(t, w): return bound_op(t=t, w=w) dt = 0.002 final_t = dt * 5 nsteps = int(final_t / dt) from grudge.shortcuts import set_up_rk4 dt_stepper = set_up_rk4("w", dt, fields, rhs) logger.info("dt %.5e nsteps %5d", dt, nsteps) norm = bind(discr, sym.norm(2, sym.var("u"))) step = 0 for event in dt_stepper.run(t_end=final_t): if isinstance(event, dt_stepper.StateComputed): assert event.component_id == "w" esc = event.state_component step += 1 logger.debug("[%04d] t = %.5e", step, event.t) sol = analytic_sol(actx, mu=mu, epsilon=epsilon, t=step * dt) vals = [norm(u=(esc[i] - sol[i])) / norm(u=sol[i]) for i in range(5)] # noqa E501 total_error = sum(vals) 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")) assert eoc_rec.order_estimate() > order
def test_convergence_advec(ctx_factory, mesh_name, mesh_pars, op_type, flux_type, order, visualize=False): """Test whether 2D advection actually converges""" cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) actx = PyOpenCLArrayContext(queue) from pytools.convergence import EOCRecorder eoc_rec = EOCRecorder() for mesh_par in mesh_pars: if mesh_name == "segment": from meshmode.mesh.generation import generate_box_mesh mesh = generate_box_mesh([np.linspace(-1.0, 1.0, mesh_par)], order=order) dim = 1 dt_factor = 1.0 elif mesh_name == "disk": pytest.importorskip("meshpy") from meshpy.geometry import make_circle, GeometryBuilder from meshpy.triangle import MeshInfo, build geob = GeometryBuilder() geob.add_geometry(*make_circle(1)) mesh_info = MeshInfo() geob.set(mesh_info) mesh_info = build(mesh_info, max_volume=mesh_par) from meshmode.mesh.io import from_meshpy mesh = from_meshpy(mesh_info, order=1) dim = 2 dt_factor = 4 elif mesh_name.startswith("rect"): dim = int(mesh_name[4:]) from meshmode.mesh.generation import generate_regular_rect_mesh mesh = generate_regular_rect_mesh(a=(-0.5, ) * dim, b=(0.5, ) * dim, n=(mesh_par, ) * dim, order=4) if dim == 2: dt_factor = 4 elif dim == 3: dt_factor = 2 else: raise ValueError("dt_factor not known for %dd" % dim) else: raise ValueError("invalid mesh name: " + mesh_name) v = np.array([0.27, 0.31, 0.1])[:dim] norm_v = la.norm(v) def f(x): return sym.sin(10 * x) def u_analytic(x): return f(-v.dot(x) / norm_v + sym.var("t", sym.DD_SCALAR) * norm_v) from grudge.models.advection import (StrongAdvectionOperator, WeakAdvectionOperator) discr = DGDiscretizationWithBoundaries(actx, mesh, order=order) op_class = { "strong": StrongAdvectionOperator, "weak": WeakAdvectionOperator, }[op_type] op = op_class(v, inflow_u=u_analytic(sym.nodes(dim, sym.BTAG_ALL)), flux_type=flux_type) bound_op = bind(discr, op.sym_operator()) u = bind(discr, u_analytic(sym.nodes(dim)))(actx, t=0) def rhs(t, u): return bound_op(t=t, u=u) if dim == 3: final_time = 0.1 else: final_time = 0.2 h_max = bind(discr, sym.h_max_from_volume(discr.ambient_dim))(actx) dt = dt_factor * h_max / order**2 nsteps = (final_time // dt) + 1 dt = final_time / nsteps + 1e-15 from grudge.shortcuts import set_up_rk4 dt_stepper = set_up_rk4("u", dt, u, rhs) last_u = None from grudge.shortcuts import make_visualizer vis = make_visualizer(discr, vis_order=order) step = 0 for event in dt_stepper.run(t_end=final_time): if isinstance(event, dt_stepper.StateComputed): step += 1 logger.debug("[%04d] t = %.5f", step, event.t) last_t = event.t last_u = event.state_component if visualize: vis.write_vtk_file("fld-%s-%04d.vtu" % (mesh_par, step), [("u", event.state_component)]) error_l2 = bind(discr, sym.norm(2, sym.var("u") - u_analytic(sym.nodes(dim))))( t=last_t, u=last_u) logger.info("h_max %.5e error %.5e", h_max, error_l2) eoc_rec.add_data_point(h_max, error_l2) logger.info( "\n%s", eoc_rec.pretty_print(abscissa_label="h", error_label="L2 Error")) assert eoc_rec.order_estimate() > order
if not visualize: continue from meshmode.discretization.visualization import make_visualizer vis = make_visualizer(actx, density_discr, case.target_order) filename = f"beltrami_{case.name}_{solution.name}_{resolution}.vtu" vis.write_vtk_file(filename, [ ("result", result), ("ref_result", ref_result), ("error", result - ref_result) ], overwrite=True) logger.info("\n%s", eoc.pretty_print( abscissa_format="%.8e", error_format="%.8e", eoc_format="%.2f")) # NOTE: expected order is `order - 2` because the formulation has two # additional target derivatives on the kernel order = min(case.target_order, case.qbx_order) assert eoc.order_estimate() > order - 2.5 # }}} if __name__ == "__main__": import sys if len(sys.argv) > 1: exec(sys.argv[1]) else:
def test_im_euler_accuracy(python_method_impl, show_dag=False, plot_solution=False): component_id = "y" from implicit_euler import ImplicitEulerMethod method = ImplicitEulerMethod(component_id) code = method.generate(solver_hook) expected_order = 1 if show_dag: from dagrt.language import show_dependency_graph show_dependency_graph(code) h = -0.5 y_0 = 1.0 def rhs(t, y): return h * y def soln(t): return y_0 * np.exp(h * t) from pytools.convergence import EOCRecorder eocrec = EOCRecorder() for n in range(1, 5): dt = 2**(-n) t = 0.0 y = y_0 final_t = 1 from functools import partial interp = python_method_impl(code, function_map={ method.rhs_func.name: rhs, "<func>solver": partial(solver, rhs) }) 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) times.append(event.t) assert abs(times[-1] - final_t) < 1e-10 times = np.array(times) if plot_solution: import matplotlib.pyplot as pt pt.plot(times, values, label="comp") pt.plot(times, soln(times), label="true") pt.show() error = abs(values[-1] - soln(final_t)) eocrec.add_data_point(dt, error) print("------------------------------------------------------") print("%s: expected order %d" % (method, expected_order)) print("------------------------------------------------------") print(eocrec.pretty_print()) orderest = eocrec.estimate_order_of_convergence()[0, 1] assert orderest > expected_order * 0.9
# }}} if __name__ == "__main__": grid_sizes = [ (3, 3), (3, 4), (4, 4), (4, 5), (5, 5), (5, 6), (6, 6), (6, 7), (7, 7), (7, 8), (8, 8), (8, 9), (9, 9), (9, 10), (10, 10), ] from pytools.convergence import EOCRecorder eoc = EOCRecorder() for nx, ny in grid_sizes: npoints, t_elapsed = timing_run(nx, ny) eoc.add_data_point(npoints, t_elapsed) print(eoc.pretty_print(abscissa_label="Elements", error_label="Timing (s)"))
def test_adaptive(python_method_impl, problem, method): pytest.importorskip("scipy") t_start = problem.t_start t_end = problem.t_end dt = 1.0e-1 tols = [10.0**(-j) for j in range(1, 5)] from pytools.convergence import EOCRecorder eocrec = EOCRecorder() # Test that tightening the tolerance will decrease the overall error. for atol in tols: generator = method("y", atol=atol) code = generator.generate() #sgen = ScipySolverGenerator(*generator.implicit_expression()) from leap.implicit import replace_AssignImplicit code = replace_AssignImplicit(code, {"solve": solver_hook}) 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": problem.initial()}) times = [] values = [] new_times = [] new_values = [] for event in interp.run(t_end=t_end): clear_flag = False if isinstance(event, interp.StateComputed): assert event.component_id == "y" new_values.append(event.state_component) new_times.append(event.t) elif isinstance(event, interp.StepCompleted): values.extend(new_values) times.extend(new_times) clear_flag = True elif isinstance(event, interp.StepFailed): clear_flag = True if clear_flag: del new_times[:] del new_values[:] times = np.array(times) values = np.array(values) exact = problem.exact(times[-1]) error = np.linalg.norm(values[-1] - exact) eocrec.add_data_point(atol, error) print("Error vs. tolerance") print(eocrec.pretty_print()) order = eocrec.estimate_order_of_convergence()[0, 1] assert order > 0.9
def test_convergence_maxwell(actx_factory, order): """Test whether 3D Maxwell's actually converges""" actx = actx_factory() from pytools.convergence import EOCRecorder eoc_rec = EOCRecorder() dims = 3 ns = [4, 6, 8] for n in ns: mesh = mgen.generate_regular_rect_mesh(a=(0.0, ) * dims, b=(1.0, ) * dims, nelements_per_axis=(n, ) * dims) dcoll = DiscretizationCollection(actx, mesh, order=order) epsilon = 1 mu = 1 from grudge.models.em import get_rectangular_cavity_mode def analytic_sol(x, t=0): return get_rectangular_cavity_mode(actx, x, t, 1, (1, 2, 2)) nodes = thaw(dcoll.nodes(), actx) fields = analytic_sol(nodes, t=0) from grudge.models.em import MaxwellOperator maxwell_operator = MaxwellOperator(dcoll, epsilon, mu, flux_type=0.5, dimensions=dims) maxwell_operator.check_bc_coverage(mesh) def rhs(t, w): return maxwell_operator.operator(t, w) dt = maxwell_operator.estimate_rk4_timestep(actx, dcoll) final_t = dt * 5 nsteps = int(final_t / dt) from grudge.shortcuts import set_up_rk4 dt_stepper = set_up_rk4("w", dt, fields, rhs) logger.info("dt %.5e nsteps %5d", dt, nsteps) step = 0 for event in dt_stepper.run(t_end=final_t): if isinstance(event, dt_stepper.StateComputed): assert event.component_id == "w" esc = event.state_component step += 1 logger.debug("[%04d] t = %.5e", step, event.t) sol = analytic_sol(nodes, t=step * dt) total_error = op.norm(dcoll, esc - sol, 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")) assert eoc_rec.order_estimate() > order
def test_convergence_advec(actx_factory, mesh_name, mesh_pars, op_type, flux_type, order, visualize=False): """Test whether 2D advection actually converges""" actx = actx_factory() from pytools.convergence import EOCRecorder eoc_rec = EOCRecorder() for mesh_par in mesh_pars: if mesh_name == "segment": mesh = mgen.generate_box_mesh([np.linspace(-1.0, 1.0, mesh_par)], order=order) dim = 1 dt_factor = 1.0 elif mesh_name == "disk": pytest.importorskip("meshpy") from meshpy.geometry import make_circle, GeometryBuilder from meshpy.triangle import MeshInfo, build geob = GeometryBuilder() geob.add_geometry(*make_circle(1)) mesh_info = MeshInfo() geob.set(mesh_info) mesh_info = build(mesh_info, max_volume=mesh_par) from meshmode.mesh.io import from_meshpy mesh = from_meshpy(mesh_info, order=1) dim = 2 dt_factor = 4 elif mesh_name.startswith("rect"): dim = int(mesh_name[-1:]) mesh = mgen.generate_regular_rect_mesh( a=(-0.5, ) * dim, b=(0.5, ) * dim, nelements_per_axis=(mesh_par, ) * dim, order=4) if dim == 2: dt_factor = 4 elif dim == 3: dt_factor = 2 else: raise ValueError("dt_factor not known for %dd" % dim) elif mesh_name.startswith("warped"): dim = int(mesh_name[-1:]) mesh = mgen.generate_warped_rect_mesh(dim, order=order, nelements_side=mesh_par) if dim == 2: dt_factor = 4 elif dim == 3: dt_factor = 2 else: raise ValueError("dt_factor not known for %dd" % dim) else: raise ValueError("invalid mesh name: " + mesh_name) v = np.array([0.27, 0.31, 0.1])[:dim] norm_v = la.norm(v) def f(x): return actx.np.sin(10 * x) def u_analytic(x, t=0): return f(-v.dot(x) / norm_v + t * norm_v) from grudge.models.advection import (StrongAdvectionOperator, WeakAdvectionOperator) from meshmode.mesh import BTAG_ALL dcoll = DiscretizationCollection(actx, mesh, order=order) op_class = { "strong": StrongAdvectionOperator, "weak": WeakAdvectionOperator }[op_type] adv_operator = op_class(dcoll, v, inflow_u=lambda t: u_analytic( thaw(dcoll.nodes(dd=BTAG_ALL), actx), t=t), flux_type=flux_type) nodes = thaw(dcoll.nodes(), actx) u = u_analytic(nodes, t=0) def rhs(t, u): return adv_operator.operator(t, u) if dim == 3: final_time = 0.1 else: final_time = 0.2 from grudge.dt_utils import h_max_from_volume h_max = h_max_from_volume(dcoll, dim=dcoll.ambient_dim) dt = dt_factor * h_max / order**2 nsteps = (final_time // dt) + 1 dt = final_time / nsteps + 1e-15 from grudge.shortcuts import set_up_rk4 dt_stepper = set_up_rk4("u", dt, u, rhs) last_u = None from grudge.shortcuts import make_visualizer vis = make_visualizer(dcoll) step = 0 for event in dt_stepper.run(t_end=final_time): if isinstance(event, dt_stepper.StateComputed): step += 1 logger.debug("[%04d] t = %.5f", step, event.t) last_t = event.t last_u = event.state_component if visualize: vis.write_vtk_file("fld-%s-%04d.vtu" % (mesh_par, step), [("u", event.state_component)]) error_l2 = op.norm(dcoll, last_u - u_analytic(nodes, t=last_t), 2) logger.info("h_max %.5e error %.5e", h_max, error_l2) eoc_rec.add_data_point(h_max, actx.to_numpy(error_l2)) logger.info( "\n%s", eoc_rec.pretty_print(abscissa_label="h", error_label="L2 Error")) if mesh_name.startswith("warped"): # NOTE: curvilinear meshes are hard assert eoc_rec.order_estimate() > order - 0.5 else: assert eoc_rec.order_estimate() > order