def cross_rank_trace_pairs(discrwb, vec, tag=None): if (isinstance(vec, np.ndarray) and vec.dtype.char == "O" and not isinstance(vec, DOFArray)): n, = vec.shape result = {} for ivec in range(n): for rank_tpair in _cross_rank_trace_pairs_scalar_field( discrwb, vec[ivec]): assert isinstance(rank_tpair.dd.domain_tag, sym.DTAG_BOUNDARY) assert isinstance(rank_tpair.dd.domain_tag.tag, BTAG_PARTITION) result[rank_tpair.dd.domain_tag.tag.part_nr, ivec] = rank_tpair return [ TracePair( dd=sym.as_dofdesc(sym.DTAG_BOUNDARY(BTAG_PARTITION(remote_rank))), interior=make_obj_array([ result[remote_rank, i].int for i in range(n)]), exterior=make_obj_array([ result[remote_rank, i].ext for i in range(n)]) ) for remote_rank in discrwb.connected_ranks()] else: return _cross_rank_trace_pairs_scalar_field(discrwb, vec, tag=tag)
def discr_from_dd(self, dd): dd = sym.as_dofdesc(dd) qtag = dd.quadrature_tag if dd.is_volume(): if qtag is not sym.QTAG_NONE: return self._quad_volume_discr(qtag) return self._volume_discr if qtag is not sym.QTAG_NONE: no_quad_discr = self.discr_from_dd(sym.DOFDesc(dd.domain_tag)) from meshmode.discretization import Discretization return Discretization(self._volume_discr.cl_context, no_quad_discr.mesh, self.group_factory_for_quadrature_tag(qtag)) assert qtag is sym.QTAG_NONE if dd.domain_tag is sym.FACE_RESTR_ALL: return self._all_faces_volume_connection().to_discr elif dd.domain_tag is sym.FACE_RESTR_INTERIOR: return self._interior_faces_connection().to_discr elif dd.is_boundary(): return self._boundary_connection(dd.domain_tag).to_discr else: raise ValueError("DOF desc tag not understood: " + str(dd))
def norm(p, arg, dd=None): """ :arg arg: is assumed to be a vector, i.e. have shape ``(n,)``. """ sym = _sym() if dd is None: dd = sym.DD_VOLUME dd = sym.as_dofdesc(dd) if p == 2: norm_squared = sym.NodalSum(dd_in=dd)( sym.FunctionSymbol("fabs")( arg * sym.MassOperator()(arg))) if isinstance(norm_squared, np.ndarray): norm_squared = norm_squared.sum() return sym.FunctionSymbol("sqrt")(norm_squared) elif p == np.Inf: result = sym.NodalMax(dd_in=dd)(sym.FunctionSymbol("fabs")(arg)) from pymbolic.primitives import Max if isinstance(result, np.ndarray): from functools import reduce result = reduce(Max, result) return result else: raise ValueError("unsupported value of p")
def discr_from_dd(self, dd): dd = sym.as_dofdesc(dd) if dd.quadrature_tag is not sym.QTAG_NONE: raise ValueError("quadrature discretization requested from " "PointsDiscretization") if dd.domain_tag is not sym.DTAG_VOLUME_ALL: raise ValueError("non-volume discretization requested from " "PointsDiscretization") return self
def integral(arg, dd=None): sym = _sym() if dd is None: dd = sym.DD_VOLUME dd = sym.as_dofdesc(dd) return sym.NodalSum(dd)( arg * sym.cse( sym.MassOperator(dd_in=dd)(sym.Ones(dd)), "mass_quad_weights", sym.cse_scope.DISCRETIZATION))
def project(self, src, tgt, vec): """Project from one discretization to another, e.g. from the volume to the boundary, or from the base to the an overintegrated quadrature discretization. :arg src: a :class:`~grudge.sym.DOFDesc`, or a value convertible to one :arg tgt: a :class:`~grudge.sym.DOFDesc`, or a value convertible to one :arg vec: a :class:`~meshmode.dof_array.DOFArray` """ src = sym.as_dofdesc(src) tgt = sym.as_dofdesc(tgt) if src == tgt: return vec if isinstance(vec, np.ndarray): return obj_array_vectorize(lambda el: self.project(src, tgt, el), vec) if isinstance(vec, Number): return vec return self.connection_from_dds(src, tgt)(vec)
def finish(self): self.recv_req.Wait() actx = self.array_context remote_dof_array = unflatten(self.array_context, self.bdry_discr, actx.from_numpy(self.remote_data_host)) bdry_conn = self.discrwb.get_distributed_boundary_swap_connection( sym.as_dofdesc(sym.DTAG_BOUNDARY(self.remote_btag))) swapped_remote_dof_array = bdry_conn(remote_dof_array) self.send_req.Wait() return TracePair(self.remote_btag, self.local_dof_array, swapped_remote_dof_array)
def norm(self, vec, p=2, dd=None): if dd is None: dd = "vol" dd = sym.as_dofdesc(dd) if isinstance(vec, np.ndarray): if p == 2: return sum( self.norm(vec[idx], dd=dd)**2 for idx in np.ndindex(vec.shape))**0.5 elif p == np.inf: return max( self.norm(vec[idx], np.inf, dd=dd) for idx in np.ndindex(vec.shape)) else: raise ValueError("unsupported norm order") return self._norm(p, dd)(arg=vec)
def __init__(self, i_remote_part): from meshmode.discretization.connection import FACE_RESTR_INTERIOR from meshmode.mesh import BTAG_PARTITION self.prev_dd = sym.as_dofdesc(FACE_RESTR_INTERIOR) self.new_dd = sym.as_dofdesc(BTAG_PARTITION(i_remote_part))
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 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 = DGDiscretizationWithBoundaries(actx, mesh, order=builder.order, quad_tag_to_group_factory={ "product": QuadratureSimplexGroupFactory(2 * builder.order) }) volume = discr.discr_from_dd(sym.DD_VOLUME) logger.info("ndofs: %d", volume.ndofs) logger.info("nelements: %d", volume.mesh.nelements) dd = sym.DD_VOLUME dq = dd.with_qtag("product") df = sym.as_dofdesc(sym.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 test_face_normal_surface(actx_factory, mesh_name): """Check that face normals are orthogonal to the surface normal""" actx = actx_factory() # {{{ geometry 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() else: raise ValueError("unknown mesh name: %s" % mesh_name) mesh = builder.get_mesh(builder.resolutions[0], builder.mesh_order) discr = DGDiscretizationWithBoundaries(actx, mesh, order=builder.order) 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 dv = sym.DD_VOLUME df = sym.as_dofdesc(sym.FACE_RESTR_INTERIOR) ambient_dim = mesh.ambient_dim dim = mesh.dim sym_surf_normal = sym.project(dv, df)( sym.surface_normal(ambient_dim, dim=dim, dd=dv).as_vector() ) sym_surf_normal = sym_surf_normal / sym.sqrt(sum(sym_surf_normal**2)) sym_face_normal_i = sym.normal(df, ambient_dim, dim=dim - 1) sym_face_normal_e = sym.OppositeInteriorFaceSwap(df)(sym_face_normal_i) if mesh.ambient_dim == 3: # NOTE: there's only one face tangent in 3d sym_face_tangent = ( sym.pseudoscalar(ambient_dim, dim - 1, dd=df) / sym.area_element(ambient_dim, dim - 1, dd=df)).as_vector() # }}} # {{{ checks def _eval_error(x): return bind(discr, sym.norm(np.inf, sym.var("x", dd=df), dd=df))(actx, x=x) rtol = 1.0e-14 surf_normal = bind(discr, sym_surf_normal)(actx) face_normal_i = bind(discr, sym_face_normal_i)(actx) face_normal_e = bind(discr, sym_face_normal_e)(actx) # check interpolated surface normal is orthogonal to face normal error = _eval_error(surf_normal.dot(face_normal_i)) logger.info("error[n_dot_i]: %.5e", error) assert error < rtol # check angle between two neighboring elements error = _eval_error(face_normal_i.dot(face_normal_e) + 1.0) logger.info("error[i_dot_e]: %.5e", error) assert error > rtol # check orthogonality with face tangent if ambient_dim == 3: face_tangent = bind(discr, sym_face_tangent)(actx) error = _eval_error(face_tangent.dot(face_normal_i)) logger.info("error[t_dot_i]: %.5e", error) assert error < 5 * rtol
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))