def sym_operator(self): u = sym.var("u") # boundary conditions ------------------------------------------------- bc_in = self.inflow_u all_faces_dd = sym.DOFDesc(sym.FACE_RESTR_ALL, self.quad_tag) boundary_dd = sym.DOFDesc(sym.BTAG_ALL, self.quad_tag) def flux(pair): return sym.interp(pair.dd, all_faces_dd)( self.flux(pair)) quad_dd = sym.DOFDesc("vol", self.quad_tag) to_quad = sym.interp("vol", quad_dd) stiff_t = sym.stiffness_t(self.ambient_dim, quad_dd, "vol") quad_v = to_quad(self.v) quad_u = to_quad(u) return sym.InverseMassOperator()( (stiff_t[0](quad_u * quad_v[0]) + stiff_t[1](quad_u * quad_v[1])) - sym.FaceMassOperator(all_faces_dd, "vol")( flux(sym.int_tpair(u, self.quad_tag)) + flux(sym.bv_tpair(boundary_dd, u, bc_in)) # FIXME: Add back support for inflow/outflow tags #+ flux(sym.bv_tpair(self.inflow_tag, u, bc_in)) #+ flux(sym.bv_tpair(self.outflow_tag, u, bc_out)) ))
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) boundary_dd = sym.DOFDesc(sym.BTAG_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)) + flux(sym.bv_tpair(boundary_dd, u, self.inflow_u)) # FIXME: Add back support for inflow/outflow tags #+ flux(sym.bv_tpair(self.inflow_tag, u, bc_in)) #+ flux(sym.bv_tpair(self.outflow_tag, u, bc_out)) ))
def sym_operator(self, w=None): """The full operator template - the high level description of the Maxwell operator. Combines the relevant operator templates for spatial derivatives, flux, boundary conditions etc. """ from grudge.tools import count_subset w = sym.make_sym_array("w", count_subset(self.get_eh_subset())) elec_components = count_subset(self.get_eh_subset()[0:3]) mag_components = count_subset(self.get_eh_subset()[3:6]) if self.fixed_material: # need to check this material_divisor = ([self.epsilon] * elec_components + [self.mu] * mag_components) tags_and_bcs = [ (self.pec_tag, self.pec_bc(w)), (self.pmc_tag, self.pmc_bc(w)), (self.absorb_tag, self.absorbing_bc(w)), (self.incident_tag, self.incident_bc(w)), ] def flux(pair): return sym.project(pair.dd, "all_faces")(self.flux(pair)) return (-self.local_derivatives(w) - sym.InverseMassOperator() (sym.FaceMassOperator()(flux(sym.int_tpair(w)) + sum( flux(sym.bv_tpair(tag, w, bc)) for tag, bc in tags_and_bcs)))) / material_divisor
def sym_operator(self): from grudge.dof_desc import DOFDesc, DD_VOLUME, DTAG_VOLUME_ALL from meshmode.discretization.connection import FACE_RESTR_ALL u = sym.var("u") def flux(pair): return sym.project(pair.dd, face_dd)(self.flux(pair)) face_dd = DOFDesc(FACE_RESTR_ALL, self.quad_tag) quad_dd = DOFDesc(DTAG_VOLUME_ALL, self.quad_tag) to_quad = sym.project(DD_VOLUME, quad_dd) stiff_t_op = sym.stiffness_t(self.ambient_dim, dd_in=quad_dd, dd_out=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, DD_VOLUME) (flux(sym.int_tpair(u, self.quad_tag))))
def sym_operator(self): from grudge.dof_desc import DOFDesc, DD_VOLUME, DTAG_VOLUME_ALL from meshmode.mesh import BTAG_ALL from meshmode.discretization.connection import FACE_RESTR_ALL u = sym.var("u") def flux(pair): return sym.project(pair.dd, face_dd)(self.flux(pair)) face_dd = DOFDesc(FACE_RESTR_ALL, self.quad_tag) boundary_dd = DOFDesc(BTAG_ALL, self.quad_tag) quad_dd = DOFDesc(DTAG_VOLUME_ALL, self.quad_tag) to_quad = sym.project(DD_VOLUME, quad_dd) stiff_t_op = sym.stiffness_t(self.ambient_dim, dd_in=quad_dd, dd_out=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, DD_VOLUME)( flux(sym.int_tpair(u, self.quad_tag)) + flux(sym.bv_tpair(boundary_dd, u, self.inflow_u)) # FIXME: Add back support for inflow/outflow tags #+ flux(sym.bv_tpair(self.inflow_tag, u, bc_in)) #+ flux(sym.bv_tpair(self.outflow_tag, u, bc_out)) ))
def sym_operator(self): d = self.ambient_dim w = sym.make_sym_array("w", d + 1) u = w[0] v = w[1:] # boundary conditions ------------------------------------------------- # dirichlet BCs ------------------------------------------------------- dir_u = sym.cse(sym.interp("vol", self.dirichlet_tag)(u)) dir_v = sym.cse(sym.interp("vol", self.dirichlet_tag)(v)) if self.dirichlet_bc_f: # FIXME from warnings import warn warn("Inhomogeneous Dirichlet conditions on the wave equation " "are still having issues.") dir_g = sym.Field("dir_bc_u") dir_bc = join_fields(2 * dir_g - dir_u, dir_v) else: dir_bc = join_fields(-dir_u, dir_v) dir_bc = sym.cse(dir_bc, "dir_bc") # neumann BCs --------------------------------------------------------- neu_u = sym.cse(sym.interp("vol", self.neumann_tag)(u)) neu_v = sym.cse(sym.interp("vol", self.neumann_tag)(v)) neu_bc = sym.cse(join_fields(neu_u, -neu_v), "neu_bc") # radiation BCs ------------------------------------------------------- rad_normal = sym.normal(self.radiation_tag, d) rad_u = sym.cse(sym.interp("vol", self.radiation_tag)(u)) rad_v = sym.cse(sym.interp("vol", self.radiation_tag)(v)) rad_bc = sym.cse( join_fields( 0.5 * (rad_u - self.sign * np.dot(rad_normal, rad_v)), 0.5 * rad_normal * (np.dot(rad_normal, rad_v) - self.sign * rad_u)), "rad_bc") # entire operator ----------------------------------------------------- def flux(pair): return sym.interp(pair.dd, "all_faces")(self.flux(pair)) result = sym.InverseMassOperator()( join_fields(-self.c * np.dot(sym.stiffness_t(self.ambient_dim), v), -self.c * (sym.stiffness_t(self.ambient_dim) * u)) - sym.FaceMassOperator() (flux(sym.int_tpair(w)) + flux(sym.bv_tpair(self.dirichlet_tag, w, dir_bc)) + flux(sym.bv_tpair(self.neumann_tag, w, neu_bc)) + flux(sym.bv_tpair(self.radiation_tag, w, rad_bc)))) result[0] += self.source_f return result
def get_strong_wave_op_with_discr_direct(cl_ctx, dims=2, order=4): from meshmode.mesh.generation import generate_regular_rect_mesh mesh = generate_regular_rect_mesh(a=(-0.5, ) * dims, b=(0.5, ) * dims, n=(16, ) * dims) logger.debug("%d elements", mesh.nelements) discr = DGDiscretizationWithBoundaries(cl_ctx, mesh, order=order) source_center = np.array([0.1, 0.22, 0.33])[:dims] source_width = 0.05 source_omega = 3 sym_x = sym.nodes(mesh.dim) sym_source_center_dist = sym_x - source_center sym_t = sym.ScalarVariable("t") from meshmode.mesh import BTAG_ALL c = -0.1 sign = -1 w = sym.make_sym_array("w", dims + 1) u = w[0] v = w[1:] source_f = ( sym.sin(source_omega * sym_t) * sym.exp(-np.dot(sym_source_center_dist, sym_source_center_dist) / source_width**2)) rad_normal = sym.normal(BTAG_ALL, dims) rad_u = sym.cse(sym.interp("vol", BTAG_ALL)(u)) rad_v = sym.cse(sym.interp("vol", BTAG_ALL)(v)) rad_bc = sym.cse( sym.join_fields( 0.5 * (rad_u - sign * np.dot(rad_normal, rad_v)), 0.5 * rad_normal * (np.dot(rad_normal, rad_v) - sign * rad_u)), "rad_bc") sym_operator = ( -sym.join_fields(-c * np.dot(sym.nabla(dims), v) - source_f, -c * (sym.nabla(dims) * u)) + sym.InverseMassOperator()( sym.FaceMassOperator() (dg_flux(c, sym.int_tpair(w)) + dg_flux(c, sym.bv_tpair(BTAG_ALL, w, rad_bc))))) return (sym_operator, discr)
def sym_operator(self): u = sym.var("u") def flux(pair): return sym.project(pair.dd, "all_faces")(self.flux(pair)) return (-self.v.dot( sym.nabla(self.ambient_dim) * u ) + sym.InverseMassOperator()( sym.FaceMassOperator()( flux(sym.int_tpair(u)) + flux(sym.bv_tpair(sym.BTAG_ALL, u, self.inflow_u)) # FIXME: Add back support for inflow/outflow tags #+ flux(sym.bv_tpair(self.inflow_tag, u, bc_in)) #+ flux(sym.bv_tpair(self.outflow_tag, u, bc_out)) )))
def sym_operator(self): u = sym.var("u") def flux(pair): return sym.project(pair.dd, "all_faces")(self.flux(pair)) bc_in = self.inflow_u # bc_out = sym.project(sym.DD_VOLUME, self.outflow_tag)(u) return sym.InverseMassOperator()( np.dot(self.v, sym.stiffness_t(self.ambient_dim) * u) - sym.FaceMassOperator()( flux(sym.int_tpair(u)) + flux(sym.bv_tpair(sym.BTAG_ALL, u, bc_in)) # FIXME: Add back support for inflow/outflow tags #+ flux(sym.bv_tpair(self.inflow_tag, u, bc_in)) #+ flux(sym.bv_tpair(self.outflow_tag, u, bc_out)) ))
def sym_operator(self): u = sym.var("u") # boundary conditions ------------------------------------------------- bc_in = self.inflow_u # bc_out = sym.interp("vol", self.outflow_tag)(u) def flux(pair): return sym.interp(pair.dd, "all_faces")( self.flux(pair)) return sym.InverseMassOperator()( np.dot( self.v, sym.stiffness_t(self.ambient_dim)*u) - sym.FaceMassOperator()( flux(sym.int_tpair(u)) + flux(sym.bv_tpair(sym.BTAG_ALL, u, bc_in)) # FIXME: Add back support for inflow/outflow tags #+ flux(sym.bv_tpair(self.inflow_tag, u, bc_in)) #+ flux(sym.bv_tpair(self.outflow_tag, u, bc_out)) ))
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 test_surface_divergence_theorem(actx_factory, mesh_name, visualize=False): r"""Check the surface divergence theorem. .. math:: \int_Sigma \phi \nabla_i f_i = \int_\Sigma \nabla_i \phi f_i + \int_\Sigma \kappa \phi f_i n_i + \int_{\partial \Sigma} \phi f_i m_i where :math:`n_i` is the surface normal and :class:`m_i` is the face normal (which should be orthogonal to both the surface normal and the face tangent). """ actx = actx_factory() # {{{ cases if mesh_name == "2-1-ellipse": from mesh_data import EllipseMeshBuilder builder = EllipseMeshBuilder(radius=3.1, aspect_ratio=2.0) elif mesh_name == "spheroid": from mesh_data import SpheroidMeshBuilder builder = SpheroidMeshBuilder() elif mesh_name == "circle": from mesh_data import EllipseMeshBuilder builder = EllipseMeshBuilder(radius=1.0, aspect_ratio=1.0) elif mesh_name == "starfish": from mesh_data import StarfishMeshBuilder builder = StarfishMeshBuilder() elif mesh_name == "sphere": from mesh_data import SphereMeshBuilder builder = SphereMeshBuilder(radius=1.0, mesh_order=16) else: raise ValueError("unknown mesh name: %s" % mesh_name) # }}} # {{{ convergene def f(x): return flat_obj_array( sym.sin(3 * x[1]) + sym.cos(3 * x[0]) + 1.0, sym.sin(2 * x[0]) + sym.cos(x[1]), 3.0 * sym.cos(x[0] / 2) + sym.cos(x[1]), )[:ambient_dim] from pytools.convergence import EOCRecorder eoc_global = EOCRecorder() eoc_local = EOCRecorder() theta = np.pi / 3.33 ambient_dim = builder.ambient_dim if ambient_dim == 2: mesh_rotation = np.array([ [np.cos(theta), -np.sin(theta)], [np.sin(theta), np.cos(theta)], ]) else: mesh_rotation = np.array([ [1.0, 0.0, 0.0], [0.0, np.cos(theta), -np.sin(theta)], [0.0, np.sin(theta), np.cos(theta)], ]) mesh_offset = np.array([0.33, -0.21, 0.0])[:ambient_dim] for i, resolution in enumerate(builder.resolutions): from meshmode.mesh.processing import affine_map from meshmode.discretization.connection import FACE_RESTR_ALL mesh = builder.get_mesh(resolution, builder.mesh_order) mesh = affine_map(mesh, A=mesh_rotation, b=mesh_offset) from meshmode.discretization.poly_element import \ QuadratureSimplexGroupFactory discr = DiscretizationCollection(actx, mesh, order=builder.order, discr_tag_to_group_factory={ "product": QuadratureSimplexGroupFactory( 2 * builder.order) }) volume = discr.discr_from_dd(dof_desc.DD_VOLUME) logger.info("ndofs: %d", volume.ndofs) logger.info("nelements: %d", volume.mesh.nelements) dd = dof_desc.DD_VOLUME dq = dd.with_discr_tag("product") df = dof_desc.as_dofdesc(FACE_RESTR_ALL) ambient_dim = discr.ambient_dim dim = discr.dim # variables sym_f = f(sym.nodes(ambient_dim, dd=dd)) sym_f_quad = f(sym.nodes(ambient_dim, dd=dq)) sym_kappa = sym.summed_curvature(ambient_dim, dim=dim, dd=dq) sym_normal = sym.surface_normal(ambient_dim, dim=dim, dd=dq).as_vector() sym_face_normal = sym.normal(df, ambient_dim, dim=dim - 1) sym_face_f = sym.project(dd, df)(sym_f) # operators sym_stiff = sum( sym.StiffnessOperator(d)(f) for d, f in enumerate(sym_f)) sym_stiff_t = sum( sym.StiffnessTOperator(d)(f) for d, f in enumerate(sym_f)) sym_k = sym.MassOperator(dq, dd)(sym_kappa * sym_f_quad.dot(sym_normal)) sym_flux = sym.FaceMassOperator()(sym_face_f.dot(sym_face_normal)) # sum everything up sym_op_global = sym.NodalSum(dd)(sym_stiff - (sym_stiff_t + sym_k)) sym_op_local = sym.ElementwiseSumOperator(dd)(sym_stiff - (sym_stiff_t + sym_k + sym_flux)) # evaluate op_global = bind(discr, sym_op_global)(actx) op_local = bind(discr, sym_op_local)(actx) err_global = abs(op_global) err_local = bind(discr, sym.norm(np.inf, sym.var("x")))(actx, x=op_local) logger.info("errors: global %.5e local %.5e", err_global, err_local) # compute max element size h_max = bind( discr, sym.h_max_from_volume(discr.ambient_dim, dim=discr.dim, dd=dd))(actx) eoc_global.add_data_point(h_max, err_global) eoc_local.add_data_point(h_max, err_local) if visualize: from grudge.shortcuts import make_visualizer vis = make_visualizer(discr, vis_order=builder.order) filename = f"surface_divergence_theorem_{mesh_name}_{i:04d}.vtu" vis.write_vtk_file(filename, [("r", actx.np.log10(op_local))], overwrite=True) # }}} order = min(builder.order, builder.mesh_order) - 0.5 logger.info("\n%s", str(eoc_global)) logger.info("\n%s", str(eoc_local)) assert eoc_global.max_error() < 1.0e-12 \ or eoc_global.order_estimate() > order - 0.5 assert eoc_local.max_error() < 1.0e-12 \ or eoc_local.order_estimate() > order - 0.5
def _bound_face_mass(dcoll, dd): u = sym.Variable("u", dd=dd) return bind(dcoll, sym.FaceMassOperator(dd_in=dd)(u), local_only=True)