def send_mesh_parts(self, mesh, part_per_element, num_parts): """ :arg mesh: A :class:`Mesh` to distribute to other ranks. :arg part_per_element: A :class:`numpy.ndarray` containing one integer per element of *mesh* indicating which part of the partitioned mesh the element is to become a part of. :arg num_parts: The number of partitions to divide the mesh into. Sends each partition to a different rank. Returns one partition that was not sent to any other rank. """ mpi_comm = self.mpi_comm rank = mpi_comm.Get_rank() assert num_parts <= mpi_comm.Get_size() assert self.is_mananger_rank() from meshmode.mesh.processing import partition_mesh parts = [partition_mesh(mesh, part_per_element, i)[0] for i in range(num_parts)] local_part = None reqs = [] for r, part in enumerate(parts): if r == self.manager_rank: local_part = part else: reqs.append(mpi_comm.isend(part, dest=r, tag=TAG_DISTRIBUTE_MESHES)) logger.info('rank %d: sent all mesh partitions', rank) for req in reqs: req.wait() return local_part
def test_partition_interpolation(actx_factory, dim, mesh_pars, num_parts, num_groups, part_method): np.random.seed(42) group_factory = PolynomialWarpAndBlendGroupFactory actx = actx_factory() order = 4 def f(x): return 10. * actx.np.sin(50. * x) for n in mesh_pars: from meshmode.mesh.generation import generate_warped_rect_mesh base_mesh = generate_warped_rect_mesh(dim, order=order, n=n) if num_groups > 1: from meshmode.mesh.processing import split_mesh_groups # Group every Nth element element_flags = np.arange( base_mesh.nelements, dtype=base_mesh.element_id_dtype) % num_groups mesh = split_mesh_groups(base_mesh, element_flags) else: mesh = base_mesh if part_method == "random": part_per_element = np.random.randint(num_parts, size=mesh.nelements) else: pytest.importorskip("pymetis") from meshmode.distributed import get_partition_by_pymetis part_per_element = get_partition_by_pymetis( mesh, num_parts, connectivity=part_method) from meshmode.mesh.processing import partition_mesh part_meshes = [ partition_mesh(mesh, part_per_element, i)[0] for i in range(num_parts) ] connected_parts = set() for i_local_part, part_mesh in enumerate(part_meshes): from meshmode.distributed import get_connected_partitions neighbors = get_connected_partitions(part_mesh) for i_remote_part in neighbors: connected_parts.add((i_local_part, i_remote_part)) from meshmode.discretization import Discretization vol_discrs = [ Discretization(actx, part_meshes[i], group_factory(order)) for i in range(num_parts) ] from meshmode.mesh import BTAG_PARTITION from meshmode.discretization.connection import ( make_face_restriction, make_partition_connection, check_connection) for i_local_part, i_remote_part in connected_parts: # Mark faces within local_mesh that are connected to remote_mesh local_bdry_conn = make_face_restriction( actx, vol_discrs[i_local_part], group_factory(order), BTAG_PARTITION(i_remote_part)) # Mark faces within remote_mesh that are connected to local_mesh remote_bdry_conn = make_face_restriction( actx, vol_discrs[i_remote_part], group_factory(order), BTAG_PARTITION(i_local_part)) bdry_nelements = sum(grp.nelements for grp in local_bdry_conn.to_discr.groups) remote_bdry_nelements = sum( grp.nelements for grp in remote_bdry_conn.to_discr.groups) assert bdry_nelements == remote_bdry_nelements, \ "partitions do not have the same number of connected elements" local_bdry = local_bdry_conn.to_discr remote_bdry = remote_bdry_conn.to_discr from meshmode.distributed import make_remote_group_infos remote_to_local_conn = make_partition_connection( actx, local_bdry_conn=local_bdry_conn, i_local_part=i_local_part, remote_bdry_discr=remote_bdry, remote_group_infos=make_remote_group_infos( actx, remote_bdry_conn)) # Connect from local mesh to remote mesh local_to_remote_conn = make_partition_connection( actx, local_bdry_conn=remote_bdry_conn, i_local_part=i_remote_part, remote_bdry_discr=local_bdry, remote_group_infos=make_remote_group_infos( actx, local_bdry_conn)) check_connection(actx, remote_to_local_conn) check_connection(actx, local_to_remote_conn) true_local_points = f(thaw(actx, local_bdry.nodes()[0])) remote_points = local_to_remote_conn(true_local_points) local_points = remote_to_local_conn(remote_points) err = actx.np.linalg.norm(true_local_points - local_points, np.inf) # Can't currently expect exact results due to limitations of # interpolation "snapping" in DirectDiscretizationConnection's # _resample_point_pick_indices assert err < 1e-11
def test_partition_mesh(mesh_size, num_parts, num_groups, dim, scramble_partitions): np.random.seed(42) n = (mesh_size, ) * dim from meshmode.mesh.generation import generate_regular_rect_mesh meshes = [ generate_regular_rect_mesh(a=(0 + i, ) * dim, b=(1 + i, ) * dim, n=n) for i in range(num_groups) ] from meshmode.mesh.processing import merge_disjoint_meshes mesh = merge_disjoint_meshes(meshes) if scramble_partitions: part_per_element = np.random.randint(num_parts, size=mesh.nelements) else: pytest.importorskip("pymetis") from meshmode.distributed import get_partition_by_pymetis part_per_element = get_partition_by_pymetis(mesh, num_parts) from meshmode.mesh.processing import partition_mesh # TODO: The same part_per_element array must be used to partition each mesh. # Maybe the interface should be changed to guarantee this. new_meshes = [ partition_mesh(mesh, part_per_element, i) for i in range(num_parts) ] assert mesh.nelements == np.sum( [new_meshes[i][0].nelements for i in range(num_parts)]), \ "part_mesh has the wrong number of elements" assert count_tags(mesh, BTAG_ALL) == np.sum( [count_tags(new_meshes[i][0], BTAG_ALL) for i in range(num_parts)]), \ "part_mesh has the wrong number of BTAG_ALL boundaries" connected_parts = set() for i_local_part, (part_mesh, _) in enumerate(new_meshes): from meshmode.distributed import get_connected_partitions neighbors = get_connected_partitions(part_mesh) for i_remote_part in neighbors: connected_parts.add((i_local_part, i_remote_part)) from meshmode.mesh import BTAG_PARTITION, InterPartitionAdjacencyGroup from meshmode.mesh.processing import find_group_indices num_tags = np.zeros((num_parts, )) index_lookup_table = dict() for ipart, (m, _) in enumerate(new_meshes): for igrp in range(len(m.groups)): adj = m.facial_adjacency_groups[igrp][None] if not isinstance(adj, InterPartitionAdjacencyGroup): # This group is not connected to another partition. continue for i, (elem, face) in enumerate(zip(adj.elements, adj.element_faces)): index_lookup_table[ipart, igrp, elem, face] = i for part_num in range(num_parts): part, part_to_global = new_meshes[part_num] for grp_num in range(len(part.groups)): adj = part.facial_adjacency_groups[grp_num][None] tags = -part.facial_adjacency_groups[grp_num][None].neighbors assert np.all(tags >= 0) if not isinstance(adj, InterPartitionAdjacencyGroup): # This group is not connected to another partition. continue elem_base = part.groups[grp_num].element_nr_base for idx in range(len(adj.elements)): if adj.partition_neighbors[idx] == -1: continue elem = adj.elements[idx] face = adj.element_faces[idx] n_part_num = adj.neighbor_partitions[idx] n_meshwide_elem = adj.partition_neighbors[idx] n_face = adj.neighbor_faces[idx] num_tags[n_part_num] += 1 n_part, n_part_to_global = new_meshes[n_part_num] # Hack: find_igrps expects a numpy.ndarray and returns # a numpy.ndarray. But if a single integer is fed # into find_igrps, an integer is returned. n_grp_num = int( find_group_indices(n_part.groups, n_meshwide_elem)) n_adj = n_part.facial_adjacency_groups[n_grp_num][None] n_elem_base = n_part.groups[n_grp_num].element_nr_base n_elem = n_meshwide_elem - n_elem_base n_idx = index_lookup_table[n_part_num, n_grp_num, n_elem, n_face] assert (part_num == n_adj.neighbor_partitions[n_idx] and elem + elem_base == n_adj.partition_neighbors[n_idx] and face == n_adj.neighbor_faces[n_idx]),\ "InterPartitionAdjacencyGroup is not consistent" _, n_part_to_global = new_meshes[n_part_num] p_meshwide_elem = part_to_global[elem + elem_base] p_meshwide_n_elem = n_part_to_global[n_elem + n_elem_base] p_grp_num = find_group_indices(mesh.groups, p_meshwide_elem) p_n_grp_num = find_group_indices(mesh.groups, p_meshwide_n_elem) p_elem_base = mesh.groups[p_grp_num].element_nr_base p_n_elem_base = mesh.groups[p_n_grp_num].element_nr_base p_elem = p_meshwide_elem - p_elem_base p_n_elem = p_meshwide_n_elem - p_n_elem_base f_groups = mesh.facial_adjacency_groups[p_grp_num] for p_bnd_adj in f_groups.values(): for idx in range(len(p_bnd_adj.elements)): if (p_elem == p_bnd_adj.elements[idx] and face == p_bnd_adj.element_faces[idx]): assert p_n_elem == p_bnd_adj.neighbors[idx],\ "Tag does not give correct neighbor" assert n_face == p_bnd_adj.neighbor_faces[idx],\ "Tag does not give correct neighbor" for i_remote_part in range(num_parts): tag_sum = 0 for i_local_part, (mesh, _) in enumerate(new_meshes): if (i_local_part, i_remote_part) in connected_parts: tag_sum += count_tags(mesh, BTAG_PARTITION(i_remote_part)) assert num_tags[i_remote_part] == tag_sum,\ "part_mesh has the wrong number of BTAG_PARTITION boundaries"
def test_partition_interpolation(ctx_factory, dim, mesh_pars, num_parts, num_groups, scramble_partitions): np.random.seed(42) group_factory = PolynomialWarpAndBlendGroupFactory cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) order = 4 from pytools.convergence import EOCRecorder eoc_rec = dict() for i in range(num_parts): for j in range(num_parts): if i == j: continue eoc_rec[i, j] = EOCRecorder() def f(x): return 10. * cl.clmath.sin(50. * x) for n in mesh_pars: from meshmode.mesh.generation import generate_warped_rect_mesh meshes = [ generate_warped_rect_mesh(dim, order=order, n=n) for _ in range(num_groups) ] if num_groups > 1: from meshmode.mesh.processing import merge_disjoint_meshes mesh = merge_disjoint_meshes(meshes) else: mesh = meshes[0] if scramble_partitions: part_per_element = np.random.randint(num_parts, size=mesh.nelements) else: from pymetis import part_graph _, p = part_graph( num_parts, xadj=mesh.nodal_adjacency.neighbors_starts.tolist(), adjncy=mesh.nodal_adjacency.neighbors.tolist()) part_per_element = np.array(p) from meshmode.mesh.processing import partition_mesh part_meshes = [ partition_mesh(mesh, part_per_element, i)[0] for i in range(num_parts) ] from meshmode.discretization import Discretization vol_discrs = [ Discretization(cl_ctx, part_meshes[i], group_factory(order)) for i in range(num_parts) ] from meshmode.mesh import BTAG_PARTITION from meshmode.discretization.connection import ( make_face_restriction, make_partition_connection, check_connection) for i_local_part, i_remote_part in eoc_rec.keys(): if eoc_rec[i_local_part, i_remote_part] is None: continue # Mark faces within local_mesh that are connected to remote_mesh local_bdry_conn = make_face_restriction( vol_discrs[i_local_part], group_factory(order), BTAG_PARTITION(i_remote_part)) # If these parts are not connected, don't bother checking the error bdry_nodes = local_bdry_conn.to_discr.nodes() if bdry_nodes.size == 0: eoc_rec[i_local_part, i_remote_part] = None continue # Mark faces within remote_mesh that are connected to local_mesh remote_bdry_conn = make_face_restriction( vol_discrs[i_remote_part], group_factory(order), BTAG_PARTITION(i_local_part)) assert bdry_nodes.size == remote_bdry_conn.to_discr.nodes().size, \ "partitions do not have the same number of connected nodes" # Gather just enough information for the connection local_bdry = local_bdry_conn.to_discr local_mesh = part_meshes[i_local_part] local_adj_groups = [ local_mesh.facial_adjacency_groups[i][None] for i in range(len(local_mesh.groups)) ] local_batches = [ local_bdry_conn.groups[i].batches for i in range(len(local_mesh.groups)) ] local_from_elem_faces = [[ batch.to_element_face for batch in grp_batches ] for grp_batches in local_batches] local_from_elem_indices = [[ batch.to_element_indices.get(queue=queue) for batch in grp_batches ] for grp_batches in local_batches] remote_bdry = remote_bdry_conn.to_discr remote_mesh = part_meshes[i_remote_part] remote_adj_groups = [ remote_mesh.facial_adjacency_groups[i][None] for i in range(len(remote_mesh.groups)) ] remote_batches = [ remote_bdry_conn.groups[i].batches for i in range(len(remote_mesh.groups)) ] remote_from_elem_faces = [[ batch.to_element_face for batch in grp_batches ] for grp_batches in remote_batches] remote_from_elem_indices = [[ batch.to_element_indices.get(queue=queue) for batch in grp_batches ] for grp_batches in remote_batches] # Connect from remote_mesh to local_mesh remote_to_local_conn = make_partition_connection( local_bdry_conn, i_local_part, remote_bdry, remote_adj_groups, remote_from_elem_faces, remote_from_elem_indices) # Connect from local mesh to remote mesh local_to_remote_conn = make_partition_connection( remote_bdry_conn, i_remote_part, local_bdry, local_adj_groups, local_from_elem_faces, local_from_elem_indices) check_connection(remote_to_local_conn) check_connection(local_to_remote_conn) true_local_points = f(local_bdry.nodes()[0].with_queue(queue)) remote_points = local_to_remote_conn(queue, true_local_points) local_points = remote_to_local_conn(queue, remote_points) err = la.norm((true_local_points - local_points).get(), np.inf) eoc_rec[i_local_part, i_remote_part].add_data_point(1. / n, err) for (i, j), e in eoc_rec.items(): if e is not None: print("Error of connection from part %i to part %i." % (i, j)) print(e) assert (e.order_estimate() >= order - 0.5 or e.max_error() < 1e-11)
def test_partition_interpolation(ctx_factory, dim, mesh_pars, num_parts, num_groups, part_method): np.random.seed(42) group_factory = PolynomialWarpAndBlendGroupFactory cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) actx = PyOpenCLArrayContext(queue) order = 4 def f(x): return 10.*actx.np.sin(50.*x) for n in mesh_pars: from meshmode.mesh.generation import generate_warped_rect_mesh base_mesh = generate_warped_rect_mesh(dim, order=order, n=n) if num_groups > 1: from meshmode.mesh.processing import split_mesh_groups # Group every Nth element element_flags = np.arange(base_mesh.nelements, dtype=base_mesh.element_id_dtype) % num_groups mesh = split_mesh_groups(base_mesh, element_flags) else: mesh = base_mesh if part_method == "random": part_per_element = np.random.randint(num_parts, size=mesh.nelements) else: pytest.importorskip('pymetis') from meshmode.distributed import get_partition_by_pymetis part_per_element = get_partition_by_pymetis(mesh, num_parts, connectivity=part_method) from meshmode.mesh.processing import partition_mesh part_meshes = [ partition_mesh(mesh, part_per_element, i)[0] for i in range(num_parts)] connected_parts = set() for i_local_part, part_mesh in enumerate(part_meshes): from meshmode.distributed import get_connected_partitions neighbors = get_connected_partitions(part_mesh) for i_remote_part in neighbors: connected_parts.add((i_local_part, i_remote_part)) from meshmode.discretization import Discretization vol_discrs = [Discretization(actx, part_meshes[i], group_factory(order)) for i in range(num_parts)] from meshmode.mesh import BTAG_PARTITION from meshmode.discretization.connection import (make_face_restriction, make_partition_connection, check_connection) for i_local_part, i_remote_part in connected_parts: # Mark faces within local_mesh that are connected to remote_mesh local_bdry_conn = make_face_restriction(actx, vol_discrs[i_local_part], group_factory(order), BTAG_PARTITION(i_remote_part)) # Mark faces within remote_mesh that are connected to local_mesh remote_bdry_conn = make_face_restriction(actx, vol_discrs[i_remote_part], group_factory(order), BTAG_PARTITION(i_local_part)) bdry_nelements = sum( grp.nelements for grp in local_bdry_conn.to_discr.groups) remote_bdry_nelements = sum( grp.nelements for grp in remote_bdry_conn.to_discr.groups) assert bdry_nelements == remote_bdry_nelements, \ "partitions do not have the same number of connected elements" # Gather just enough information for the connection local_bdry = local_bdry_conn.to_discr local_mesh = part_meshes[i_local_part] local_adj_groups = [local_mesh.facial_adjacency_groups[i][None] for i in range(len(local_mesh.groups))] local_batches = [local_bdry_conn.groups[i].batches for i in range(len(local_mesh.groups))] local_from_elem_faces = [[batch.to_element_face for batch in grp_batches] for grp_batches in local_batches] local_from_elem_indices = [[batch.to_element_indices.get(queue=queue) for batch in grp_batches] for grp_batches in local_batches] remote_bdry = remote_bdry_conn.to_discr remote_mesh = part_meshes[i_remote_part] remote_adj_groups = [remote_mesh.facial_adjacency_groups[i][None] for i in range(len(remote_mesh.groups))] remote_batches = [remote_bdry_conn.groups[i].batches for i in range(len(remote_mesh.groups))] remote_from_elem_faces = [[batch.to_element_face for batch in grp_batches] for grp_batches in remote_batches] remote_from_elem_indices = [[batch.to_element_indices.get(queue=queue) for batch in grp_batches] for grp_batches in remote_batches] # Connect from remote_mesh to local_mesh remote_to_local_conn = make_partition_connection( actx, local_bdry_conn, i_local_part, remote_bdry, remote_adj_groups, remote_from_elem_faces, remote_from_elem_indices) # Connect from local mesh to remote mesh local_to_remote_conn = make_partition_connection( actx, remote_bdry_conn, i_remote_part, local_bdry, local_adj_groups, local_from_elem_faces, local_from_elem_indices) check_connection(actx, remote_to_local_conn) check_connection(actx, local_to_remote_conn) true_local_points = f(thaw(actx, local_bdry.nodes()[0])) remote_points = local_to_remote_conn(true_local_points) local_points = remote_to_local_conn(remote_points) err = flat_norm(true_local_points - local_points, np.inf) # Can't currently expect exact results due to limitations of # interpolation 'snapping' in DirectDiscretizationConnection's # _resample_point_pick_indices assert err < 1e-11