def get_boundaries(self, discr, actx, t): nodes = thaw(actx, discr.nodes()) sym_exact_u = self.get_solution(pmbl.make_sym_vector("x", self.dim), pmbl.var("t")) exact_u = _sym_eval(sym_exact_u, x=nodes, t=t) exact_grad_u = _sym_eval(sym.grad(self.dim, sym_exact_u), x=nodes, t=t) boundaries = {} for i in range(self.dim - 1): lower_btag = DTAG_BOUNDARY("-" + str(i)) upper_btag = DTAG_BOUNDARY("+" + str(i)) upper_grad_u = discr.project("vol", upper_btag, exact_grad_u) normal = thaw(actx, discr.normal(upper_btag)) upper_grad_u_dot_n = np.dot(upper_grad_u, normal) boundaries[lower_btag] = NeumannDiffusionBoundary(0.) boundaries[upper_btag] = NeumannDiffusionBoundary( upper_grad_u_dot_n) lower_btag = DTAG_BOUNDARY("-" + str(self.dim - 1)) upper_btag = DTAG_BOUNDARY("+" + str(self.dim - 1)) upper_u = discr.project("vol", upper_btag, exact_u) boundaries[lower_btag] = DirichletDiffusionBoundary(0.) boundaries[upper_btag] = DirichletDiffusionBoundary(upper_u) return boundaries
def get_static_trig_var_diff(dim): def get_mesh(n): return get_box_mesh(dim, -0.5*np.pi, 0.5*np.pi, n) sym_coords = pmbl.make_sym_vector("x", dim) sym_cos = pmbl.var("cos") sym_sin = pmbl.var("sin") sym_alpha = 1 for i in range(dim): sym_alpha *= sym_cos(3.*sym_coords[i]) sym_alpha = 1 + 0.2*sym_alpha sym_u = 1 for i in range(dim-1): sym_u *= sym_sin(sym_coords[i]) sym_u *= sym_cos(sym_coords[dim-1]) def get_boundaries(discr, actx, t): boundaries = {} for i in range(dim-1): boundaries[DTAG_BOUNDARY("-"+str(i))] = NeumannDiffusionBoundary(0.) boundaries[DTAG_BOUNDARY("+"+str(i))] = NeumannDiffusionBoundary(0.) boundaries[DTAG_BOUNDARY("-"+str(dim-1))] = DirichletDiffusionBoundary(0.) boundaries[DTAG_BOUNDARY("+"+str(dim-1))] = DirichletDiffusionBoundary(0.) return boundaries return HeatProblem(dim, get_mesh, sym_alpha, sym_u, get_boundaries)
def get_decaying_trig(dim, alpha): def get_mesh(n): return get_box_mesh(dim, -0.5*np.pi, 0.5*np.pi, n) sym_coords = pmbl.make_sym_vector("x", dim) sym_t = pmbl.var("t") sym_cos = pmbl.var("cos") sym_sin = pmbl.var("sin") sym_exp = pmbl.var("exp") sym_u = sym_exp(-dim*alpha*sym_t) for i in range(dim-1): sym_u *= sym_sin(sym_coords[i]) sym_u *= sym_cos(sym_coords[dim-1]) def get_boundaries(discr, actx, t): boundaries = {} for i in range(dim-1): boundaries[DTAG_BOUNDARY("-"+str(i))] = NeumannDiffusionBoundary(0.) boundaries[DTAG_BOUNDARY("+"+str(i))] = NeumannDiffusionBoundary(0.) boundaries[DTAG_BOUNDARY("-"+str(dim-1))] = DirichletDiffusionBoundary(0.) boundaries[DTAG_BOUNDARY("+"+str(dim-1))] = DirichletDiffusionBoundary(0.) return boundaries return HeatProblem(dim, get_mesh, alpha, sym_u, get_boundaries)
def test_symbolic_evaluation(actx_factory): """ Evaluate a symbolic expression by plugging in numbers and :class:`~meshmode.dof_array.DOFArray`s and compare the result to the equivalent quantity computed explicitly. """ actx = actx_factory() mesh = generate_regular_rect_mesh( a=(-np.pi/2,)*2, b=(np.pi/2,)*2, nelements_per_axis=(4,)*2) from grudge.eager import EagerDGDiscretization discr = EagerDGDiscretization(actx, mesh, order=2) nodes = thaw(actx, discr.nodes()) sym_coords = pmbl.make_sym_vector("x", 2) sym_f = ( pmbl.var("exp")(-pmbl.var("t")) * pmbl.var("cos")(sym_coords[0]) * pmbl.var("sin")(sym_coords[1])) t = 0.5 f = sym.EvaluationMapper({"t": t, "x": nodes})(sym_f) expected_f = np.exp(-t) * actx.np.cos(nodes[0]) * actx.np.sin(nodes[1]) assert actx.to_numpy(discr.norm(f - expected_f)/discr.norm(expected_f)) < 1e-12
def map_vector_variable(self, expr): from pymbolic import make_sym_vector num_components = expr.num_components if num_components is None: num_components = self.ambient_dim return MultiVector(make_sym_vector(expr.name, num_components))
def test_symbolic_grad(): """ Compute the symbolic gradient of an expression and compare it to an expected result. """ sym_coords = pmbl.make_sym_vector("x", 3) sym_x = sym_coords[0] sym_y = sym_coords[1] sym_f = sym_x**2 * sym_y sym_grad_f = sym.grad(3, sym_f) expected_sym_grad_f = make_obj_array([sym_y * 2 * sym_x, sym_x**2, 0]) assert (sym_grad_f == expected_sym_grad_f).all()
def test_symbolic_div(): """ Compute the symbolic divergence of a vector expression and compare it to an expected result. """ # (Equivalent to make_obj_array([pmbl.var("x")[i] for i in range(3)])) sym_coords = pmbl.make_sym_vector("x", 3) sym_x = sym_coords[0] sym_y = sym_coords[1] sym_f = make_obj_array([sym_x, sym_x * sym_y, sym_y]) sym_div_f = sym.div(sym_f) expected_sym_div_f = 1 + sym_x + 0 assert sym_div_f == expected_sym_div_f
def get_decaying_trig_truncated_domain(dim, alpha): def get_mesh(n): return get_box_mesh(dim, -0.5*np.pi, 0.25*np.pi, n) sym_coords = pmbl.make_sym_vector("x", dim) sym_t = pmbl.var("t") sym_cos = pmbl.var("cos") sym_sin = pmbl.var("sin") sym_exp = pmbl.var("exp") sym_u = sym_exp(-dim*alpha*sym_t) for i in range(dim-1): sym_u *= sym_sin(sym_coords[i]) sym_u *= sym_cos(sym_coords[dim-1]) def get_boundaries(discr, actx, t): nodes = thaw(actx, discr.nodes()) def sym_eval(expr): return sym.EvaluationMapper({"x": nodes, "t": t})(expr) exact_u = sym_eval(sym_u) exact_grad_u = sym_eval(sym.grad(dim, sym_u)) boundaries = {} for i in range(dim-1): lower_btag = DTAG_BOUNDARY("-"+str(i)) upper_btag = DTAG_BOUNDARY("+"+str(i)) upper_grad_u = discr.project("vol", upper_btag, exact_grad_u) normal = thaw(actx, discr.normal(upper_btag)) upper_grad_u_dot_n = np.dot(upper_grad_u, normal) boundaries[lower_btag] = NeumannDiffusionBoundary(0.) boundaries[upper_btag] = NeumannDiffusionBoundary(upper_grad_u_dot_n) lower_btag = DTAG_BOUNDARY("-"+str(dim-1)) upper_btag = DTAG_BOUNDARY("+"+str(dim-1)) upper_u = discr.project("vol", upper_btag, exact_u) boundaries[lower_btag] = DirichletDiffusionBoundary(0.) boundaries[upper_btag] = DirichletDiffusionBoundary(upper_u) return boundaries return HeatProblem(dim, get_mesh, alpha, sym_u, get_boundaries)
def test_diffusion_obj_array_vectorize(actx_factory): """ Checks that the diffusion operator can be called with either scalars or object arrays for `u`. """ actx = actx_factory() p = DecayingTrig(1, 2.) sym_x = pmbl.make_sym_vector("x", p.dim) sym_t = pmbl.var("t") def get_u1(x, t): return p.get_solution(x, t) def get_u2(x, t): return 2 * p.get_solution(x, t) sym_u1 = get_u1(sym_x, sym_t) sym_u2 = get_u2(sym_x, sym_t) sym_alpha1 = p.get_alpha(sym_x, sym_t, sym_u1) sym_alpha2 = p.get_alpha(sym_x, sym_t, sym_u2) assert isinstance(sym_alpha1, Number) assert isinstance(sym_alpha2, Number) alpha = sym_alpha1 sym_diffusion_u1 = sym_diffusion(p.dim, alpha, sym_u1) sym_diffusion_u2 = sym_diffusion(p.dim, alpha, sym_u2) n = 128 mesh = p.get_mesh(n) from grudge.eager import EagerDGDiscretization discr = EagerDGDiscretization(actx, mesh, order=4) nodes = thaw(actx, discr.nodes()) t = 1.23456789 u1 = get_u1(nodes, t) u2 = get_u2(nodes, t) alpha = p.get_alpha(nodes, t, u1) boundaries = p.get_boundaries(discr, actx, t) diffusion_u1 = diffusion_operator(discr, quad_tag=DISCR_TAG_BASE, alpha=alpha, boundaries=boundaries, u=u1) assert isinstance(diffusion_u1, DOFArray) expected_diffusion_u1 = _sym_eval(sym_diffusion_u1, x=nodes, t=t) rel_linf_err = actx.to_numpy( discr.norm(diffusion_u1 - expected_diffusion_u1, np.inf) / discr.norm(expected_diffusion_u1, np.inf)) assert rel_linf_err < 1.e-5 boundaries_vector = [boundaries, boundaries] u_vector = make_obj_array([u1, u2]) diffusion_u_vector = diffusion_operator(discr, quad_tag=DISCR_TAG_BASE, alpha=alpha, boundaries=boundaries_vector, u=u_vector) assert isinstance(diffusion_u_vector, np.ndarray) assert diffusion_u_vector.shape == (2, ) expected_diffusion_u_vector = make_obj_array([ _sym_eval(sym_diffusion_u1, x=nodes, t=t), _sym_eval(sym_diffusion_u2, x=nodes, t=t) ]) rel_linf_err = actx.to_numpy( discr.norm(diffusion_u_vector - expected_diffusion_u_vector, np.inf) / discr.norm(expected_diffusion_u_vector, np.inf)) assert rel_linf_err < 1.e-5
def test_diffusion_compare_to_nodal_dg(actx_factory, problem, order, print_err=False): """Compares diffusion operator to Hesthaven/Warburton Nodal-DG code.""" pytest.importorskip("oct2py") actx = actx_factory() p = problem assert p.dim == 1 sym_x = pmbl.make_sym_vector("x", p.dim) sym_t = pmbl.var("t") sym_u = p.get_solution(sym_x, sym_t) sym_alpha = p.get_alpha(sym_x, sym_t, sym_u) assert sym_alpha == 1 sym_diffusion_u = sym_diffusion(p.dim, sym_alpha, sym_u) from meshmode.interop.nodal_dg import download_nodal_dg_if_not_present download_nodal_dg_if_not_present() for n in [4, 8, 16, 32, 64]: mesh = p.get_mesh(n) from meshmode.interop.nodal_dg import NodalDGContext with NodalDGContext("./nodal-dg/Codes1.1") as ndgctx: ndgctx.set_mesh(mesh, order=order) t = 1.23456789 from grudge.eager import EagerDGDiscretization discr_mirgecom = EagerDGDiscretization(actx, mesh, order=order) nodes_mirgecom = thaw(actx, discr_mirgecom.nodes()) u_mirgecom = p.get_solution(nodes_mirgecom, t) diffusion_u_mirgecom = diffusion_operator( discr_mirgecom, quad_tag=DISCR_TAG_BASE, alpha=discr_mirgecom.zeros(actx) + 1., boundaries=p.get_boundaries(discr_mirgecom, actx, t), u=u_mirgecom) discr_ndg = ndgctx.get_discr(actx) nodes_ndg = thaw(actx, discr_ndg.nodes()) u_ndg = p.get_solution(nodes_ndg, t) ndgctx.push_dof_array("u", u_ndg) ndgctx.octave.push("t", t) ndgctx.octave.eval("[rhs] = HeatCRHS1D(u,t)", verbose=False) diffusion_u_ndg = ndgctx.pull_dof_array(actx, "rhs") def inf_norm(f): return actx.np.linalg.norm(f, np.inf) err = (inf_norm(diffusion_u_mirgecom - diffusion_u_ndg) / inf_norm(diffusion_u_ndg)) if print_err: diffusion_u_exact = _sym_eval(sym_diffusion_u, x=nodes_mirgecom, t=t) err_exact = ( inf_norm(diffusion_u_mirgecom - diffusion_u_exact) / inf_norm(diffusion_u_exact)) print(err, err_exact) assert err < 1e-9
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_x = pmbl.make_sym_vector("x", p.dim) sym_t = pmbl.var("t") sym_u = p.get_solution(sym_x, sym_t) sym_alpha = p.get_alpha(sym_x, sym_t, sym_u) sym_diffusion_u = sym_diffusion(p.dim, sym_alpha, 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_f = sym.diff(sym_t)(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 get_rhs(t, u): alpha = p.get_alpha(nodes, t, u) if isinstance(alpha, DOFArray): discr_tag = DISCR_TAG_QUAD else: discr_tag = DISCR_TAG_BASE return ( diffusion_operator(discr, quad_tag=discr_tag, alpha=alpha, boundaries=p.get_boundaries(discr, actx, t), u=u) + _sym_eval(sym_f, x=nodes, t=t)) t = 0. u = p.get_solution(nodes, t) from mirgecom.integrators import rk4_step for _ in range(nsteps): u = rk4_step(u, t, dt, get_rhs) t += dt expected_u = p.get_solution(nodes, t) rel_linf_err = actx.to_numpy( 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)
def grad(dim, func): """Return the symbolic *dim*-dimensional gradient of *func*.""" coords = pmbl.make_sym_vector("x", dim) return make_obj_array([diff(coords[i])(func) for i in range(dim)])
def div(vector_func): """Return the symbolic divergence of *vector_func*.""" dim = len(vector_func) coords = pmbl.make_sym_vector("x", dim) return sum([diff(coords[i])(vector_func[i]) for i in range(dim)])