def sym_wave(dim, sym_phi): """Return symbolic expressions for the wave equation system given a desired solution. (Note: In order to support manufactured solutions, we modify the wave equation to add a source term (f). If the solution is exact, this term should be 0.) """ sym_c = pmbl.var("c") sym_coords = prim.make_sym_vector("x", dim) sym_t = pmbl.var("t") # f = phi_tt - c^2 * div(grad(phi)) sym_f = sym.diff(sym_t)(sym.diff(sym_t)(sym_phi)) - sym_c**2\ * sym.div(sym.grad(dim, sym_phi)) # u = phi_t sym_u = sym.diff(sym_t)(sym_phi) # v = c*grad(phi) sym_v = [sym_c * sym.diff(sym_coords[i])(sym_phi) for i in range(dim)] # rhs(u part) = c*div(v) + f # rhs(v part) = c*grad(u) sym_rhs = flat_obj_array(sym_c * sym.div(sym_v) + sym_f, make_obj_array([sym_c]) * sym.grad(dim, sym_u)) return sym_u, sym_v, sym_f, sym_rhs
def test_symbolic_diff(sym_f, expected_sym_df): """ Compute the symbolic derivative of an expression and compare it to an expected result. """ sym_df = sym.diff(pmbl.var("x"))(sym_f) assert sym_df == expected_sym_df
def test_diffusion_accuracy(actx_factory, problem, nsteps, dt, scales, order, visualize=False): """ Checks the accuracy of the diffusion operator by solving the heat equation for a given problem setup. """ actx = actx_factory() p = problem sym_diffusion_u = sym_diffusion(p.dim, p.sym_alpha, p.sym_u) # In order to support manufactured solutions, we modify the heat equation # to add a source term f. If the solution is exact, this term should be 0. sym_t = pmbl.var("t") sym_f = sym.diff(sym_t)(p.sym_u) - sym_diffusion_u from pytools.convergence import EOCRecorder eoc_rec = EOCRecorder() for n in scales: mesh = p.get_mesh(n) from grudge.eager import EagerDGDiscretization from meshmode.discretization.poly_element import \ QuadratureSimplexGroupFactory, \ PolynomialWarpAndBlendGroupFactory discr = EagerDGDiscretization( actx, mesh, discr_tag_to_group_factory={ DISCR_TAG_BASE: PolynomialWarpAndBlendGroupFactory(order), DISCR_TAG_QUAD: QuadratureSimplexGroupFactory(3 * order), }) nodes = thaw(actx, discr.nodes()) def sym_eval(expr, t): return sym.EvaluationMapper({"x": nodes, "t": t})(expr) alpha = sym_eval(p.sym_alpha, 0.) if isinstance(alpha, DOFArray): discr_tag = DISCR_TAG_QUAD else: discr_tag = DISCR_TAG_BASE def get_rhs(t, u): return ( diffusion_operator(discr, quad_tag=discr_tag, alpha=alpha, boundaries=p.get_boundaries(discr, actx, t), u=u) + sym_eval(sym_f, t)) t = 0. u = sym_eval(p.sym_u, t) from mirgecom.integrators import rk4_step for _ in range(nsteps): u = rk4_step(u, t, dt, get_rhs) t += dt expected_u = sym_eval(p.sym_u, t) rel_linf_err = (discr.norm(u - expected_u, np.inf) / discr.norm(expected_u, 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 + 3) vis.write_vtk_file( "diffusion_accuracy_{order}_{n}.vtu".format(order=order, n=n), [ ("u", u), ("expected_u", expected_u), ]) print("L^inf error:") print(eoc_rec) # Expected convergence rates from Hesthaven/Warburton book expected_order = order + 1 if order % 2 == 0 else order assert (eoc_rec.order_estimate() >= expected_order - 0.5 or eoc_rec.max_error() < 1e-11)