def map_call(self, expr): arg, = expr.parameters rec_arg = self.rec(arg) if isinstance(rec_arg, np.ndarray) and self.is_kind_matrix(rec_arg): raise RuntimeError("expression is nonlinear in variable") from numbers import Number if isinstance(rec_arg, Number): return getattr(np, expr.function.name)(rec_arg) else: rec_arg = unflatten_from_numpy(self.array_context, None, rec_arg) result = getattr(self.array_context.np, expr.function.name)(rec_arg) return flatten_to_numpy(self.array_context, result)
def plot_partition_indices(actx, discr, indices, **kwargs): try: import matplotlib.pyplot as pt except ImportError: return indices = indices.get(actx.queue) args = [ kwargs.get("tree_kind", "linear").replace("-", "_"), kwargs.get("discr_stage", "stage1"), discr.ambient_dim ] pt.figure(figsize=(10, 8), dpi=300) pt.plot(np.diff(indices.ranges)) pt.savefig("test_partition_{1}_{3}d_ranges_{2}.png".format(*args)) pt.clf() from pytential.utils import flatten_to_numpy if discr.ambient_dim == 2: sources = flatten_to_numpy(actx, discr.nodes()) pt.figure(figsize=(10, 8), dpi=300) if indices.indices.shape[0] != discr.ndofs: pt.plot(sources[0], sources[1], 'ko', alpha=0.5) for i in range(indices.nblocks): isrc = indices.block_indices(i) pt.plot(sources[0][isrc], sources[1][isrc], 'o') pt.xlim([-1.5, 1.5]) pt.ylim([-1.5, 1.5]) pt.savefig("test_partition_{1}_{3}d_{2}.png".format(*args)) pt.clf() elif discr.ambient_dim == 3: from meshmode.discretization.visualization import make_visualizer marker = -42.0 * np.ones(discr.ndofs) for i in range(indices.nblocks): isrc = indices.block_indices(i) marker[isrc] = 10.0 * (i + 1.0) from meshmode.dof_array import unflatten marker = unflatten(actx, discr, actx.from_numpy(marker)) vis = make_visualizer(actx, discr, 10) filename = "test_partition_{0}_{1}_{3}d_{2}.vtu".format(*args) vis.write_vtk_file(filename, [("marker", marker)])
def map_interpolation(self, expr): from pytential import sym if expr.to_dd.discr_stage != sym.QBX_SOURCE_QUAD_STAGE2: raise RuntimeError( "can only interpolate to QBX_SOURCE_QUAD_STAGE2") operand = self.rec(expr.operand) actx = self.array_context if isinstance(operand, (int, float, complex, np.number)): return operand elif isinstance(operand, np.ndarray) and operand.ndim == 1: conn = self.places.get_connection(expr.from_dd, expr.to_dd) discr = self.places.get_discretization(expr.from_dd.geometry, expr.from_dd.discr_stage) operand = unflatten_from_numpy(actx, discr, operand) return flatten_to_numpy(actx, conn(operand)) elif isinstance(operand, np.ndarray) and operand.ndim == 2: cache = self.places._get_cache("direct_resampler") key = (expr.from_dd.geometry, expr.from_dd.discr_stage, expr.to_dd.discr_stage) try: mat = cache[key] except KeyError: from meshmode.discretization.connection import \ flatten_chained_connection conn = self.places.get_connection(expr.from_dd, expr.to_dd) conn = flatten_chained_connection(actx, conn) mat = actx.to_numpy(conn.full_resample_matrix(actx)) # FIXME: the resample matrix is slow to compute and very big # to store, so caching it may not be the best idea cache[key] = mat return mat.dot(operand) else: raise RuntimeError("unknown operand type: {}".format( type(operand)))
def map_num_reference_derivative(self, expr): from pytential import bind, sym rec_operand = self.rec(expr.operand) assert isinstance(rec_operand, np.ndarray) if self.is_kind_matrix(rec_operand): raise NotImplementedError("derivatives") dofdesc = expr.dofdesc op = sym.NumReferenceDerivative(ref_axes=expr.ref_axes, operand=sym.var("u"), dofdesc=dofdesc) discr = self.places.get_discretization(dofdesc.geometry, dofdesc.discr_stage) rec_operand = unflatten_from_numpy(self.array_context, discr, rec_operand) return flatten_to_numpy( self.array_context, bind(self.places, op)(self.array_context, u=rec_operand))
def main(curve_fn=starfish, visualize=True): import logging logging.basicConfig(level=logging.WARNING) # INFO for more progress info cl_ctx = cl.create_some_context() queue = cl.CommandQueue(cl_ctx) from meshmode.mesh.generation import make_curve_mesh mesh = make_curve_mesh( curve_fn, np.linspace(0, 1, nelements+1), target_order) from pytential.qbx import QBXLayerPotentialSource from meshmode.array_context import PyOpenCLArrayContext from meshmode.discretization import Discretization from meshmode.discretization.poly_element import \ InterpolatoryQuadratureSimplexGroupFactory actx = PyOpenCLArrayContext(queue) pre_density_discr = Discretization( actx, mesh, InterpolatoryQuadratureSimplexGroupFactory(target_order)) qbx = QBXLayerPotentialSource(pre_density_discr, 4*target_order, qbx_order, fmm_order=qbx_order+3, target_association_tolerance=0.005) from pytential.target import PointsTarget fplot = FieldPlotter(np.zeros(2), extent=5, npoints=1000) targets_dev = cl.array.to_device(queue, fplot.points) from pytential import GeometryCollection places = GeometryCollection({ "qbx": qbx, "targets": PointsTarget(targets_dev), }, auto_where="qbx") density_discr = places.get_discretization("qbx") from meshmode.dof_array import thaw nodes = thaw(actx, density_discr.nodes()) angle = actx.np.arctan2(nodes[1], nodes[0]) if k: kernel = HelmholtzKernel(2) kernel_kwargs = {"k": sym.var("k")} else: kernel = LaplaceKernel(2) kernel_kwargs = {} def op(**kwargs): kwargs.update(kernel_kwargs) #op = sym.d_dx(sym.S(kernel, sym.var("sigma"), **kwargs)) return sym.D(kernel, sym.var("sigma"), **kwargs) #op = sym.S(kernel, sym.var("sigma"), qbx_forced_limit=None, **kwargs) sigma = actx.np.cos(mode_nr*angle) if 0: from meshmode.dof_array import flatten, unflatten sigma = flatten(0 * angle) from random import randrange for i in range(5): sigma[randrange(len(sigma))] = 1 sigma = unflatten(actx, density_discr, sigma) if isinstance(kernel, HelmholtzKernel): for i, elem in np.ndenumerate(sigma): sigma[i] = elem.astype(np.complex128) bound_bdry_op = bind(places, op()) if visualize: fld_in_vol = actx.to_numpy( bind(places, op( source="qbx", target="targets", qbx_forced_limit=None))(actx, sigma=sigma, k=k)) if enable_mayavi: fplot.show_scalar_in_mayavi(fld_in_vol.real, max_val=5) else: fplot.write_vtk_file("layerpot-potential.vts", [ ("potential", fld_in_vol) ]) if 0: apply_op = bound_bdry_op.scipy_op(actx, "sigma", np.float64, k=k) from sumpy.tools import build_matrix mat = build_matrix(apply_op) import matplotlib.pyplot as pt pt.imshow(mat) pt.colorbar() pt.show() if enable_mayavi: # {{{ plot boundary field from pytential.utils import flatten_to_numpy fld_on_bdry = flatten_to_numpy( actx, bound_bdry_op(actx, sigma=sigma, k=k)) nodes_host = flatten_to_numpy(actx, density_discr.nodes()) mlab.points3d(nodes_host[0], nodes_host[1], fld_on_bdry.real, scale_factor=0.03) mlab.colorbar() mlab.show()
r = la.norm(proxies[:, ipxy] - pxycenters[:, i].reshape(-1, 1), axis=0) assert np.allclose(r - pxyradii[i], 0.0, atol=1.0e-14) # }}} # {{{ visualization srcindices = srcindices.get(queue) if visualize: ambient_dim = places.ambient_dim if ambient_dim == 2: import matplotlib.pyplot as pt from pytential.utils import flatten_to_numpy density_nodes = np.vstack( flatten_to_numpy(actx, density_discr.nodes())) ci = bind(places, sym.expansion_centers(ambient_dim, -1))(actx) ci = np.vstack(flatten_to_numpy(actx, ci)) ce = bind(places, sym.expansion_centers(ambient_dim, +1))(actx) ce = np.vstack(flatten_to_numpy(actx, ce)) r = bind(places, sym.expansion_radii(ambient_dim))(actx) r = flatten_to_numpy(actx, r) for i in range(srcindices.nblocks): isrc = srcindices.block_indices(i) ipxy = np.s_[pxyranges[i]:pxyranges[i + 1]] pt.figure(figsize=(10, 8)) axis = pt.gca() for j in isrc: c = pt.Circle(ci[:, j], r[j], color='k', alpha=0.1)
def gather_block_neighbor_points(actx, discr, indices, pxycenters, pxyradii, max_nodes_in_box=None): """Generate a set of neighboring points for each range of points in *discr*. Neighboring points of a range :math:`i` are defined as all the points inside the proxy ball :math:`i` that do not also belong to the range itself. :arg discr: a :class:`meshmode.discretization.Discretization`. :arg indices: a :class:`sumpy.tools.BlockIndexRanges`. :arg pxycenters: an array containing the center of each proxy ball. :arg pxyradii: an array containing the radius of each proxy ball. :return: a :class:`sumpy.tools.BlockIndexRanges`. """ if max_nodes_in_box is None: # FIXME: this is a fairly arbitrary value max_nodes_in_box = 32 indices = indices.get(actx.queue) # NOTE: this is constructed for multiple reasons: # * TreeBuilder takes object arrays # * `srcindices` can be a small subset of nodes, so this will save # some work # * `srcindices` may reorder the array returned by nodes(), so this # makes sure that we have the same order in tree.user_source_ids # and friends from pytential.utils import flatten_to_numpy sources = flatten_to_numpy(actx, discr.nodes()) sources = make_obj_array([ actx.from_numpy(sources[idim][indices.indices]) for idim in range(discr.ambient_dim)]) # construct tree from boxtree import TreeBuilder builder = TreeBuilder(actx.context) tree, _ = builder(actx.queue, sources, max_particles_in_box=max_nodes_in_box) from boxtree.area_query import AreaQueryBuilder builder = AreaQueryBuilder(actx.context) query, _ = builder(actx.queue, tree, pxycenters, pxyradii) # find nodes inside each proxy ball tree = tree.get(actx.queue) query = query.get(actx.queue) pxycenters = np.vstack([ actx.to_numpy(pxycenters[idim]) for idim in range(discr.ambient_dim) ]) pxyradii = actx.to_numpy(pxyradii) nbrindices = np.empty(indices.nblocks, dtype=np.object) for iproxy in range(indices.nblocks): # get list of boxes intersecting the current ball istart = query.leaves_near_ball_starts[iproxy] iend = query.leaves_near_ball_starts[iproxy + 1] iboxes = query.leaves_near_ball_lists[istart:iend] # get nodes inside the boxes istart = tree.box_source_starts[iboxes] iend = istart + tree.box_source_counts_cumul[iboxes] isources = np.hstack([np.arange(s, e) for s, e in zip(istart, iend)]) nodes = np.vstack([tree.sources[idim][isources] for idim in range(discr.ambient_dim)]) isources = tree.user_source_ids[isources] # get nodes inside the ball but outside the current range center = pxycenters[:, iproxy].reshape(-1, 1) radius = pxyradii[iproxy] mask = ((la.norm(nodes - center, axis=0) < radius) & ((isources < indices.ranges[iproxy]) | (indices.ranges[iproxy + 1] <= isources))) nbrindices[iproxy] = indices.indices[isources[mask]] nbrranges = actx.from_numpy(np.cumsum([0] + [n.shape[0] for n in nbrindices])) nbrindices = actx.from_numpy(np.hstack(nbrindices)) return BlockIndexRanges(actx.context, actx.freeze(nbrindices), actx.freeze(nbrranges))
def __call__(self, actx, source_dd, indices, **kwargs): """Generate proxy points for each given range of source points in the discretization in *source_dd*. :arg actx: a :class:`~meshmode.array_context.ArrayContext`. :arg source_dd: a :class:`~pytential.symbolic.primitives.DOFDescriptor` for the discretization on which the proxy points are to be generated. :arg indices: a :class:`sumpy.tools.BlockIndexRanges`. :return: a tuple of ``(proxies, pxyranges, pxycenters, pxyranges)``, where each element is a :class:`pyopencl.array.Array`. The sizes of the arrays are as follows: ``pxycenters`` is of size ``(2, nranges)``, ``pxyradii`` is of size ``(nranges,)``, ``pxyranges`` is of size ``(nranges + 1,)`` and ``proxies`` is of size ``(2, nranges * nproxy)``. The proxy points in a range :math:`i` can be obtained by a slice ``proxies[pxyranges[i]:pxyranges[i + 1]]`` and are all at a distance ``pxyradii[i]`` from the range center ``pxycenters[i]``. """ def _affine_map(v, A, b): return np.dot(A, v) + b from pytential import bind, sym source_dd = sym.as_dofdesc(source_dd) discr = self.places.get_discretization( source_dd.geometry, source_dd.discr_stage) radii = bind(self.places, sym.expansion_radii( self.ambient_dim, dofdesc=source_dd))(actx) center_int = bind(self.places, sym.expansion_centers( self.ambient_dim, -1, dofdesc=source_dd))(actx) center_ext = bind(self.places, sym.expansion_centers( self.ambient_dim, +1, dofdesc=source_dd))(actx) from meshmode.dof_array import flatten, thaw knl = self.get_kernel() _, (centers_dev, radii_dev,) = knl(actx.queue, sources=flatten(thaw(actx, discr.nodes())), center_int=flatten(center_int), center_ext=flatten(center_ext), expansion_radii=flatten(radii), srcindices=indices.indices, srcranges=indices.ranges, **kwargs) from pytential.utils import flatten_to_numpy centers = flatten_to_numpy(actx, centers_dev) radii = flatten_to_numpy(actx, radii_dev) proxies = np.empty(indices.nblocks, dtype=np.object) for i in range(indices.nblocks): proxies[i] = _affine_map(self.ref_points, A=(radii[i] * np.eye(self.ambient_dim)), b=centers[:, i].reshape(-1, 1)) pxyranges = actx.from_numpy(np.arange( 0, proxies.shape[0] * proxies[0].shape[1] + 1, proxies[0].shape[1], dtype=indices.ranges.dtype)) proxies = make_obj_array([ actx.freeze(actx.from_numpy(np.hstack([p[idim] for p in proxies]))) for idim in range(self.ambient_dim) ]) centers = make_obj_array([ actx.freeze(centers_dev[idim]) for idim in range(self.ambient_dim) ]) assert pxyranges[-1] == proxies[0].shape[0] return proxies, actx.freeze(pxyranges), centers, actx.freeze(radii_dev)
def map_node_coordinate_component(self, expr): from pytential import bind, sym op = sym.NodeCoordinateComponent(expr.ambient_axis, dofdesc=expr.dofdesc) return flatten_to_numpy(self.array_context, bind(self.places, op)(self.array_context))
# }}} # {{{ plot geometry if visualize and ambient_dim == 2: try: import matplotlib.pyplot as pt except ImportError: visualize = False if visualize: normals = bind(places, sym.normal(ambient_dim).as_vector())(actx) # show geometry, centers, normals if ambient_dim == 2: nodes = flatten_to_numpy(actx, density_discr.nodes()) normals = flatten_to_numpy(actx, normals) pt.plot(nodes[0], nodes[1], "x-") pt.quiver(nodes[0], nodes[1], normals[0], normals[1]) pt.gca().set_aspect("equal") pt.savefig(f"pre-solve-source-{resolution}", dpi=300) elif ambient_dim == 3: bdry_vis = make_visualizer(actx, density_discr, case.target_order + 3) bdry_vis.write_vtk_file(f"pre-solve-source-{resolution}.vtu", [ ("normals", normals), ]) else: raise ValueError("invalid mesh dim") # }}}