Beispiel #1
0
def _build_block_index(discr, nblks=10, factor=1.0):
    nnodes = discr.nnodes
    max_particles_in_box = nnodes // nblks

    from pytential.linalg.proxy import partition_by_nodes
    indices = partition_by_nodes(discr, use_tree=True,
                                 max_nodes_in_box=max_particles_in_box)

    # randomly pick a subset of points
    from sumpy.tools import MatrixBlockIndexRanges, BlockIndexRanges
    if abs(factor - 1.0) > 1.0e-14:
        with cl.CommandQueue(discr.cl_context) as queue:
            indices = indices.get(queue)

            indices_ = np.empty(indices.nblocks, dtype=np.object)
            for i in range(indices.nblocks):
                iidx = indices.block_indices(i)
                isize = int(factor * len(iidx))
                isize = max(1, min(isize, len(iidx)))

                indices_[i] = np.sort(
                        np.random.choice(iidx, size=isize, replace=False))

            ranges_ = cl.array.to_device(queue,
                    np.cumsum([0] + [r.shape[0] for r in indices_]))
            indices_ = cl.array.to_device(queue, np.hstack(indices_))

            indices = BlockIndexRanges(discr.cl_context,
                                       indices_.with_queue(None),
                                       ranges_.with_queue(None))

    indices = MatrixBlockIndexRanges(indices.cl_context,
                                     indices, indices)

    return indices
Beispiel #2
0
    def get_block_indices(self, actx, discr, matrix_indices=True):
        max_particles_in_box = self.max_particles_in_box
        if max_particles_in_box is None:
            max_particles_in_box = discr.ndofs // self.approx_block_count

        from pytential.linalg.proxy import partition_by_nodes
        indices = partition_by_nodes(actx, discr,
                tree_kind=self.tree_kind,
                max_nodes_in_box=max_particles_in_box)

        if abs(self.index_sparsity_factor - 1.0) < 1.0e-14:
            if not matrix_indices:
                return indices
            return MatrixBlockIndexRanges(actx.context, indices, indices)

        # randomly pick a subset of points
        indices = indices.get(actx.queue)

        subset = np.empty(indices.nblocks, dtype=np.object)
        for i in range(indices.nblocks):
            iidx = indices.block_indices(i)
            isize = int(self.index_sparsity_factor * len(iidx))
            isize = max(1, min(isize, len(iidx)))

            subset[i] = np.sort(np.random.choice(iidx, size=isize, replace=False))

        ranges = actx.from_numpy(np.cumsum([0] + [r.shape[0] for r in subset]))
        indices = actx.from_numpy(np.hstack(subset))

        indices = BlockIndexRanges(actx.context,
                actx.freeze(indices), actx.freeze(ranges))

        if not matrix_indices:
            return indices
        return MatrixBlockIndexRanges(actx.context, indices, indices)
Beispiel #3
0
def partition_by_nodes(actx, discr,
        tree_kind="adaptive-level-restricted", max_nodes_in_box=None):
    """Generate equally sized ranges of nodes. The partition is created at the
    lowest level of granularity, i.e. nodes. This results in balanced ranges
    of points, but will split elements across different ranges.

    :arg discr: a :class:`meshmode.discretization.Discretization`.
    :arg tree_kind: if not *None*, it is passed to :class:`boxtree.TreeBuilder`.
    :arg max_nodes_in_box: passed to :class:`boxtree.TreeBuilder`.

    :return: a :class:`sumpy.tools.BlockIndexRanges`.
    """

    if max_nodes_in_box is None:
        # FIXME: this is just an arbitrary value
        max_nodes_in_box = 32

    if tree_kind is not None:
        from boxtree import box_flags_enum
        from boxtree import TreeBuilder

        builder = TreeBuilder(actx.context)

        from meshmode.dof_array import flatten, thaw
        tree, _ = builder(actx.queue,
                flatten(thaw(actx, discr.nodes())),
                max_particles_in_box=max_nodes_in_box,
                kind=tree_kind)

        tree = tree.get(actx.queue)
        leaf_boxes, = (tree.box_flags
                       & box_flags_enum.HAS_CHILDREN == 0).nonzero()

        indices = np.empty(len(leaf_boxes), dtype=np.object)
        for i, ibox in enumerate(leaf_boxes):
            box_start = tree.box_source_starts[ibox]
            box_end = box_start + tree.box_source_counts_cumul[ibox]
            indices[i] = tree.user_source_ids[box_start:box_end]

        ranges = actx.from_numpy(
                np.cumsum([0] + [box.shape[0] for box in indices])
                )
        indices = actx.from_numpy(np.hstack(indices))
    else:
        indices = actx.from_numpy(np.arange(0, discr.ndofs, dtype=np.int))
        ranges = actx.from_numpy(np.arange(
            0,
            discr.ndofs + 1,
            max_nodes_in_box, dtype=np.int))

    assert ranges[-1] == discr.ndofs

    return BlockIndexRanges(actx.context,
        actx.freeze(indices), actx.freeze(ranges))
Beispiel #4
0
def partition_by_nodes(discr, use_tree=True, max_nodes_in_box=None):
    """Generate equally sized ranges of nodes. The partition is created at the
    lowest level of granularity, i.e. nodes. This results in balanced ranges
    of points, but will split elements across different ranges.

    :arg discr: a :class:`meshmode.discretization.Discretization`.
    :arg use_tree: if ``True``, node partitions are generated using a
        :class:`boxtree.TreeBuilder`, which leads to geometrically close
        points to belong to the same partition. If ``False``, a simple linear
        partition is constructed.
    :arg max_nodes_in_box: passed to :class:`boxtree.TreeBuilder`.

    :return: a :class:`sumpy.tools.BlockIndexRanges`.
    """

    if max_nodes_in_box is None:
        # FIXME: this is just an arbitrary value
        max_nodes_in_box = 32

    with cl.CommandQueue(discr.cl_context) as queue:
        if use_tree:
            from boxtree import box_flags_enum
            from boxtree import TreeBuilder

            builder = TreeBuilder(discr.cl_context)

            tree, _ = builder(queue,
                              discr.nodes(),
                              max_particles_in_box=max_nodes_in_box)

            tree = tree.get(queue)
            leaf_boxes, = (tree.box_flags
                           & box_flags_enum.HAS_CHILDREN == 0).nonzero()

            indices = np.empty(len(leaf_boxes), dtype=np.object)
            for i, ibox in enumerate(leaf_boxes):
                box_start = tree.box_source_starts[ibox]
                box_end = box_start + tree.box_source_counts_cumul[ibox]
                indices[i] = tree.user_source_ids[box_start:box_end]

            ranges = to_device(
                queue, np.cumsum([0] + [box.shape[0] for box in indices]))
            indices = to_device(queue, np.hstack(indices))
        else:
            indices = cl.array.arange(queue, 0, discr.nnodes, dtype=np.int)
            ranges = cl.array.arange(queue,
                                     0,
                                     discr.nnodes + 1,
                                     discr.nnodes // max_nodes_in_box,
                                     dtype=np.int)
        assert ranges[-1] == discr.nnodes

        return BlockIndexRanges(discr.cl_context, indices.with_queue(None),
                                ranges.with_queue(None))
Beispiel #5
0
def _build_block_index(queue, nnodes, nblks, factor):
    indices = np.arange(0, nnodes)
    ranges = np.arange(0, nnodes + 1, nnodes // nblks)

    if abs(factor - 1.0) < 1.0e-14:
        ranges_ = ranges
        indices_ = indices
    else:
        indices_ = np.empty(ranges.shape[0] - 1, dtype=np.object)
        for i in range(ranges.shape[0] - 1):
            iidx = indices[np.s_[ranges[i]:ranges[i + 1]]]
            indices_[i] = np.sort(np.random.choice(iidx,
                size=int(factor * len(iidx)), replace=False))

        ranges_ = np.cumsum([0] + [r.shape[0] for r in indices_])
        indices_ = np.hstack(indices_)

    from sumpy.tools import BlockIndexRanges
    return BlockIndexRanges(queue.context,
                            cl.array.to_device(queue, indices_).with_queue(None),
                            cl.array.to_device(queue, ranges_).with_queue(None))
Beispiel #6
0
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))
Beispiel #7
0
def partition_from_coarse(resampler, from_indices):
    """Generate a partition of nodes from an existing partition on a
    coarser discretization. The new partition is generated based on element
    refinement relationships in *resampler*, so the existing partition
    needs to be created using :func:`partition_by_elements`,
    since we assume that each range contains all the nodes in an element.

    The new partition will have the same number of ranges as the old partition.
    The nodes inside each range in the new partition are all the nodes in
    *resampler.to_discr* that were refined from elements in the same
    range from *resampler.from_discr*.

    :arg resampler: a
        :class:`meshmode.discretization.connection.DirectDiscretizationConnection`.
    :arg from_indices: a :class:`sumpy.tools.BlockIndexRanges`.

    :return: a :class:`sumpy.tools.BlockIndexRanges`.
    """

    if not hasattr(resampler, "groups"):
        raise ValueError("resampler must be a DirectDiscretizationConnection.")

    with cl.CommandQueue(resampler.cl_context) as queue:
        from_indices = from_indices.get(queue)

        # construct ranges
        from_discr = resampler.from_discr
        from_grp_ranges = np.cumsum(
            [0] + [grp.nelements for grp in from_discr.mesh.groups])
        from_el_ranges = np.hstack([
            np.arange(grp.node_nr_base, grp.nnodes + 1, grp.nunit_nodes)
            for grp in from_discr.groups
        ])

        # construct coarse element arrays in each from_range
        el_indices = np.empty(from_indices.nblocks, dtype=np.object)
        el_ranges = np.full(from_grp_ranges[-1], -1, dtype=np.int)
        for i in range(from_indices.nblocks):
            ifrom = from_indices.block_indices(i)
            el_indices[i] = np.unique(np.digitize(ifrom, from_el_ranges)) - 1
            el_ranges[el_indices[i]] = i
        el_indices = np.hstack(el_indices)

        # construct lookup table
        to_el_table = [
            np.full(g.nelements, -1, dtype=np.int)
            for g in resampler.to_discr.groups
        ]

        for igrp, grp in enumerate(resampler.groups):
            for batch in grp.batches:
                to_el_table[igrp][batch.to_element_indices.get(queue)] = \
                    from_grp_ranges[igrp] + batch.from_element_indices.get(queue)

        # construct fine node index list
        indices = [
            np.empty(0, dtype=np.int) for _ in range(from_indices.nblocks)
        ]
        for igrp in range(len(resampler.groups)):
            to_element_indices = \
                    np.where(np.isin(to_el_table[igrp], el_indices))[0]

            for i, j in zip(el_ranges[to_el_table[igrp][to_element_indices]],
                            to_element_indices):
                indices[i] = np.hstack([
                    indices[i],
                    _element_node_range(resampler.to_discr.groups[igrp], j)
                ])

        ranges = to_device(queue,
                           np.cumsum([0] + [b.shape[0] for b in indices]))
        indices = to_device(queue, np.hstack(indices))

        return BlockIndexRanges(resampler.cl_context, indices.with_queue(None),
                                ranges.with_queue(None))