def face_mass(self, *args): if len(args) == 1: vec, = args dd = sym.DOFDesc("all_faces", sym.QTAG_NONE) elif len(args) == 2: dd, vec = args else: raise TypeError("invalid number of arguments") if isinstance(vec, np.ndarray): return obj_array_vectorize(lambda el: self.face_mass(dd, el), vec) return self._bound_face_mass(dd)(u=vec)
def sym_operator(self): u = sym.var("u") def flux(pair): return sym.project(pair.dd, face_dd)(self.flux(pair)) face_dd = sym.DOFDesc(sym.FACE_RESTR_ALL, self.quad_tag) quad_dd = sym.DOFDesc(sym.DTAG_VOLUME_ALL, self.quad_tag) to_quad = sym.project(sym.DD_VOLUME, quad_dd) stiff_t_op = sym.stiffness_t(self.ambient_dim, dd_in=quad_dd, dd_out=sym.DD_VOLUME) quad_v = to_quad(self.v) quad_u = to_quad(u) return sym.InverseMassOperator()( sum(stiff_t_op[n](quad_u * quad_v[n]) for n in range(self.ambient_dim)) - sym.FaceMassOperator(face_dd, sym.DD_VOLUME) (flux(sym.int_tpair(u, self.quad_tag))))
def __init__(self, dd_in=None, dd_out=None, unique_id=None): sym = _sym() if dd_in is None: dd_in = sym.DOFDesc(sym.FACE_RESTR_INTERIOR, None) if dd_out is None: dd_out = dd_in super(OppositeInteriorFaceSwap, self).__init__(dd_in, dd_out) if self.dd_in.domain_tag is not sym.FACE_RESTR_INTERIOR: raise ValueError("dd_in must be an interior faces domain") if self.dd_out != self.dd_in: raise ValueError("dd_out and dd_in must be identical") assert unique_id is None or isinstance(unique_id, int) self.unique_id = unique_id
def weak_d_dx(self, *args): r"""Return the derivative along axis *xyz_axis* of the volume function represented by *vec*. May be called with ``(xyz_axis, vecs)`` or ``(dd, xyz_axis, vecs)``. :arg xyz_axis: an integer indicating the axis along which the derivative is taken :arg vec: a :class:`~meshmode.dof_array.DOFArray` :returns: a :class:`~meshmode.dof_array.DOFArray`\ s """ if len(args) == 2: xyz_axis, vec = args dd = sym.DOFDesc("vol", sym.QTAG_NONE) elif len(args) == 3: dd, xyz_axis, vec = args else: raise TypeError("invalid number of arguments") return self._bound_weak_d_dx(dd, xyz_axis)(u=vec)
def weak_grad(self, *args): r"""Return the "weak gradient" of the volume function represented by *vec*. May be called with ``(vecs)`` or ``(dd, vecs)``. :arg dd: a :class:`~grudge.sym.DOFDesc`, or a value convertible to one. Defaults to the base volume discretization if not provided. :arg vec: a :class:`~meshmode.dof_array.DOFArray` :returns: an object array of :class:`~meshmode.dof_array.DOFArray`\ s """ if len(args) == 1: vec, = args dd = sym.DOFDesc("vol", sym.QTAG_NONE) elif len(args) == 2: dd, vec = args else: raise TypeError("invalid number of arguments") return self._bound_weak_grad(dd)(u=vec)
def map_signed_face_ones(self, expr): assert expr.dd.is_trace() face_discr = self.discrwb.discr_from_dd(expr.dd) assert face_discr.dim == 0 # NOTE: ignore quadrature_tags on expr.dd, since we only care about # the face_id here all_faces_conn = self.discrwb.connection_from_dds( sym.DD_VOLUME, sym.DOFDesc(expr.dd.domain_tag)) field = face_discr.empty(self.array_context, dtype=self.discrwb.real_dtype) for grp_ary in field: grp_ary.fill(1) for igrp, grp in enumerate(all_faces_conn.groups): for batch in grp.batches: i = self.array_context.thaw(batch.to_element_indices) grp_field = field[igrp].reshape(-1) grp_field[i] = \ (2.0 * (batch.to_element_face % 2) - 1.0) * grp_field[i] return field
def weak_div(self, *args): r"""Return the "weak divergence" of the vector volume function represented by *vecs*. May be called with ``(vecs)`` or ``(dd, vecs)``. :arg dd: a :class:`~grudge.sym.DOFDesc`, or a value convertible to one. Defaults to the base volume discretization if not provided. :arg vec: a object array of a :class:`~meshmode.dof_array.DOFArray`\ s, where the last axis of the array must have length matching the volume dimension. :returns: a :class:`~meshmode.dof_array.DOFArray` """ if len(args) == 1: vecs, = args dd = sym.DOFDesc("vol", sym.QTAG_NONE) elif len(args) == 2: dd, vecs = args else: raise TypeError("invalid number of arguments") return self._div_helper( lambda i, subvec: self.weak_d_dx(dd, i, subvec), vecs)
def test_mass_mat_trig(ctx_factory, ambient_dim, quad_tag): """Check the integral of some trig functions on an interval using the mass matrix. """ cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) actx = PyOpenCLArrayContext(queue) nelements = 17 order = 4 a = -4.0 * np.pi b = +9.0 * np.pi true_integral = 13 * np.pi / 2 * (b - a)**(ambient_dim - 1) from meshmode.discretization.poly_element import QuadratureSimplexGroupFactory dd_quad = sym.DOFDesc(sym.DTAG_VOLUME_ALL, quad_tag) if quad_tag is sym.QTAG_NONE: quad_tag_to_group_factory = {} else: quad_tag_to_group_factory = { quad_tag: QuadratureSimplexGroupFactory(order=2 * order) } from meshmode.mesh.generation import generate_regular_rect_mesh mesh = generate_regular_rect_mesh(a=(a, ) * ambient_dim, b=(b, ) * ambient_dim, n=(nelements, ) * ambient_dim, order=1) discr = DGDiscretizationWithBoundaries( actx, mesh, order=order, quad_tag_to_group_factory=quad_tag_to_group_factory) def _get_variables_on(dd): sym_f = sym.var("f", dd=dd) sym_x = sym.nodes(ambient_dim, dd=dd) sym_ones = sym.Ones(dd) return sym_f, sym_x, sym_ones sym_f, sym_x, sym_ones = _get_variables_on(sym.DD_VOLUME) f_volm = actx.to_numpy(flatten(bind(discr, sym.cos(sym_x[0])**2)(actx))) ones_volm = actx.to_numpy(flatten(bind(discr, sym_ones)(actx))) sym_f, sym_x, sym_ones = _get_variables_on(dd_quad) f_quad = bind(discr, sym.cos(sym_x[0])**2)(actx) ones_quad = bind(discr, sym_ones)(actx) mass_op = bind(discr, sym.MassOperator(dd_quad, sym.DD_VOLUME)(sym_f)) num_integral_1 = np.dot(ones_volm, actx.to_numpy(flatten(mass_op(f=f_quad)))) err_1 = abs(num_integral_1 - true_integral) assert err_1 < 1e-9, err_1 num_integral_2 = np.dot(f_volm, actx.to_numpy(flatten(mass_op(f=ones_quad)))) err_2 = abs(num_integral_2 - true_integral) assert err_2 < 1.0e-9, err_2 if quad_tag is sym.QTAG_NONE: # NOTE: `integral` always makes a square mass matrix and # `QuadratureSimplexGroupFactory` does not have a `mass_matrix` method. num_integral_3 = bind(discr, sym.integral(sym_f, dd=dd_quad))(f=f_quad) err_3 = abs(num_integral_3 - true_integral) assert err_3 < 5.0e-10, err_3
def main(ctx_factory, dim=2, order=4, product_tag=None, visualize=False): cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) actx = PyOpenCLArrayContext(queue) # {{{ parameters # sphere radius radius = 1.0 # sphere resolution resolution = 64 if dim == 2 else 1 # cfl dt_factor = 2.0 # final time final_time = np.pi # velocity field sym_x = sym.nodes(dim) c = make_obj_array([-sym_x[1], sym_x[0], 0.0])[:dim] # flux flux_type = "lf" # }}} # {{{ discretization if dim == 2: from meshmode.mesh.generation import make_curve_mesh, ellipse mesh = make_curve_mesh(lambda t: radius * ellipse(1.0, t), np.linspace(0.0, 1.0, resolution + 1), order) elif dim == 3: from meshmode.mesh.generation import generate_icosphere mesh = generate_icosphere(radius, order=4 * order, uniform_refinement_rounds=resolution) else: raise ValueError("unsupported dimension") quad_tag_to_group_factory = {} if product_tag == "none": product_tag = None from meshmode.discretization.poly_element import \ PolynomialWarpAndBlendGroupFactory, \ QuadratureSimplexGroupFactory quad_tag_to_group_factory[sym.QTAG_NONE] = \ PolynomialWarpAndBlendGroupFactory(order) if product_tag: quad_tag_to_group_factory[product_tag] = \ QuadratureSimplexGroupFactory(order=4*order) from grudge import DGDiscretizationWithBoundaries discr = DGDiscretizationWithBoundaries( actx, mesh, quad_tag_to_group_factory=quad_tag_to_group_factory) volume_discr = discr.discr_from_dd(sym.DD_VOLUME) logger.info("ndofs: %d", volume_discr.ndofs) logger.info("nelements: %d", volume_discr.mesh.nelements) # }}} # {{{ symbolic operators def f_initial_condition(x): return x[0] from grudge.models.advection import SurfaceAdvectionOperator op = SurfaceAdvectionOperator(c, flux_type=flux_type, quad_tag=product_tag) bound_op = bind(discr, op.sym_operator()) u0 = bind(discr, f_initial_condition(sym_x))(actx, t=0) def rhs(t, u): return bound_op(actx, t=t, u=u) # check velocity is tangential sym_normal = sym.surface_normal(dim, dim=dim - 1, dd=sym.DD_VOLUME).as_vector() error = bind(discr, sym.norm(2, c.dot(sym_normal)))(actx) logger.info("u_dot_n: %.5e", error) # }}} # {{{ time stepping # compute time step h_min = bind(discr, sym.h_max_from_volume(discr.ambient_dim, dim=discr.dim))(actx) dt = dt_factor * h_min / order**2 nsteps = int(final_time // dt) + 1 dt = final_time / nsteps + 1.0e-15 logger.info("dt: %.5e", dt) logger.info("nsteps: %d", nsteps) from grudge.shortcuts import set_up_rk4 dt_stepper = set_up_rk4("u", dt, u0, rhs) plot = Plotter(actx, discr, order, visualize=visualize) norm = bind(discr, sym.norm(2, sym.var("u"))) norm_u = norm(actx, u=u0) step = 0 event = dt_stepper.StateComputed(0.0, 0, 0, u0) plot(event, "fld-surface-%04d" % 0) if visualize and dim == 3: from grudge.shortcuts import make_visualizer vis = make_visualizer(discr, vis_order=order) vis.write_vtk_file("fld-surface-velocity.vtu", [("u", bind(discr, c)(actx)), ("n", bind(discr, sym_normal)(actx))], overwrite=True) df = sym.DOFDesc(sym.FACE_RESTR_INTERIOR) face_discr = discr.connection_from_dds(sym.DD_VOLUME, df).to_discr face_normal = bind( discr, sym.normal(df, face_discr.ambient_dim, dim=face_discr.dim))(actx) from meshmode.discretization.visualization import make_visualizer vis = make_visualizer(actx, face_discr, vis_order=order) vis.write_vtk_file("fld-surface-face-normals.vtu", [("n", face_normal)], overwrite=True) for event in dt_stepper.run(t_end=final_time, max_steps=nsteps + 1): if not isinstance(event, dt_stepper.StateComputed): continue step += 1 if step % 10 == 0: norm_u = norm(actx, u=event.state_component) plot(event, "fld-surface-%04d" % step) logger.info("[%04d] t = %.5f |u| = %.5e", step, event.t, norm_u) plot(event, "fld-surface-%04d" % step)
def connection_from_dds(self, from_dd, to_dd): from_dd = sym.as_dofdesc(from_dd) to_dd = sym.as_dofdesc(to_dd) to_qtag = to_dd.quadrature_tag if (not from_dd.is_volume() and from_dd.quadrature_tag == to_dd.quadrature_tag and to_dd.domain_tag is sym.FACE_RESTR_ALL): faces_conn = self.connection_from_dds( sym.DOFDesc("vol"), sym.DOFDesc(from_dd.domain_tag)) from meshmode.discretization.connection import \ make_face_to_all_faces_embedding return make_face_to_all_faces_embedding( faces_conn, self.discr_from_dd(to_dd), self.discr_from_dd(from_dd)) # {{{ simplify domain + qtag change into chained if (from_dd.domain_tag != to_dd.domain_tag and from_dd.quadrature_tag is sym.QTAG_NONE and to_dd.quadrature_tag is not sym.QTAG_NONE): from meshmode.discretization.connection import \ ChainedDiscretizationConnection intermediate_dd = sym.DOFDesc(to_dd.domain_tag) return ChainedDiscretizationConnection([ # first change domain self.connection_from_dds(from_dd, intermediate_dd), # then go to quad grid self.connection_from_dds(intermediate_dd, to_dd) ]) # }}} # {{{ generic to-quad if (from_dd.domain_tag == to_dd.domain_tag and from_dd.quadrature_tag is sym.QTAG_NONE and to_dd.quadrature_tag is not sym.QTAG_NONE): from meshmode.discretization.connection.same_mesh import \ make_same_mesh_connection return make_same_mesh_connection(self.discr_from_dd(to_dd), self.discr_from_dd(from_dd)) # }}} if from_dd.quadrature_tag is not sym.QTAG_NONE: raise ValueError("cannot interpolate *from* a " "(non-interpolatory) quadrature grid") assert to_qtag is sym.QTAG_NONE if from_dd.is_volume(): if to_dd.domain_tag is sym.FACE_RESTR_ALL: return self._all_faces_volume_connection() if to_dd.domain_tag is sym.FACE_RESTR_INTERIOR: return self._interior_faces_connection() elif to_dd.is_boundary(): assert from_dd.quadrature_tag is sym.QTAG_NONE return self._boundary_connection(to_dd.domain_tag) elif to_dd.is_volume(): from meshmode.discretization.connection import \ make_same_mesh_connection to_discr = self._quad_volume_discr(to_dd.quadrature_tag) from_discr = self._volume_discr return make_same_mesh_connection(to_discr, from_discr) else: raise ValueError("cannot interpolate from volume to: " + str(to_dd)) else: raise ValueError("cannot interpolate from: " + str(from_dd))