def separate_particles_into_groups(s, region_size=<NUM_LIT>, bounds=None):
imtile = (<EOL>s.oshape.translate(-s.pad) if bounds is None else<EOL>util.Tile(bounds[<NUM_LIT:0>], bounds[<NUM_LIT:1>])<EOL>)<EOL>region = util.Tile(region_size, dim=s.dim)<EOL>trange = np.ceil(imtile.shape.astype('<STR_LIT:float>') / region.shape)<EOL>translations = util.Tile(trange).coords(form='<STR_LIT>')<EOL>translations = translations.reshape(-<NUM_LIT:1>, translations.shape[-<NUM_LIT:1>])<EOL>groups = []<EOL>positions = s.obj_get_positions()<EOL>for v in translations:<EOL><INDENT>tmptile = region.copy().translate(region.shape * v - s.pad)<EOL>groups.append(find_particles_in_tile(positions, tmptile))<EOL><DEDENT>return [g for g in groups if len(g) > <NUM_LIT:0>]<EOL>
Given a state, returns a list of groups of particles. Each group of
particles are located near each other in the image. Every particle
located in the desired region is contained in exactly 1 group.

Parameters:
-----------
s : state
    The PERI state to find particles in.

region_size: int or list of ints
    The size of the box. Groups particles into boxes of shape region_size.
    If region_size is a scalar, the box is a cube of length region_size.
    Default is 40.

bounds: 2-element list-like of 3-element lists.
    The sub-region of the image over which to look for particles.
        bounds[0]: The lower-left  corner of the image region.
        bounds[1]: The upper-right corner of the image region.
    Default (None -> ([0,0,0], s.oshape.shape)) is a box of the entire
    image size, i.e. the default places every particle in the image
    somewhere in the groups.

Returns:
-----------
particle_groups: list
    Each element of particle_groups is an int numpy.ndarray of the
    group of nearby particles. Only contains groups with a nonzero
    number of particles, so the elements don't necessarily correspond
    to a given image region.