def test_b_mesh_mapping(celltype):
    """
    Creates a boundary mesh and checks that the geometrical entities
    are mapped to the correct cells.
    """
    mesh = dolfinx.UnitCubeMesh(MPI.COMM_WORLD, 2, 2, 2, cell_type=celltype)

    b_mesh, bndry_to_mesh = dolfinx.plotting.create_boundary_mesh(
        mesh, MPI.COMM_SELF)

    # Compute map from boundary mesh topology to boundary mesh geometry
    b_mesh.topology.create_connectivity(b_mesh.topology.dim,
                                        b_mesh.topology.dim)
    b_imap = b_mesh.topology.index_map(b_mesh.topology.dim)
    tdim_entities = np.arange(b_imap.size_local, dtype=np.int32)
    boundary_geometry = cmesh.entities_to_geometry(b_mesh, b_mesh.topology.dim,
                                                   tdim_entities, False)

    # Compare geometry maps
    for i in range(boundary_geometry.shape[0]):
        assert (np.allclose(b_mesh.geometry.x[boundary_geometry[i]],
                            mesh.geometry.x[bndry_to_mesh[i]]))

    # Check that boundary mesh integrated has the correct area
    b_volume = mesh.mpi_comm().allreduce(dolfinx.fem.assemble_scalar(
        dolfinx.Constant(b_mesh, 1) * ufl.dx),
                                         op=MPI.SUM)
    mesh_surface = mesh.mpi_comm().allreduce(dolfinx.fem.assemble_scalar(
        dolfinx.Constant(mesh, 1) * ufl.ds),
                                             op=MPI.SUM)
    assert (np.isclose(b_volume, mesh_surface))
Exemple #2
0
def boundary_grid_from_fenics_mesh(fenics_mesh):
    """
    Create a Bempp boundary grid from a FEniCS Mesh.

    Return the Bempp grid and a map from the node numberings of the FEniCS
    mesh to the node numbers of the boundary grid.
    """
    import bempp.api
    import numpy as np
    from dolfinx.cpp.mesh import entities_to_geometry, exterior_facet_indices

    boundary = entities_to_geometry(
        fenics_mesh,
        fenics_mesh.topology.dim - 1,
        exterior_facet_indices(fenics_mesh),
        True,
    )

    bm_nodes = set()
    for tri in boundary:
        for node in tri:
            bm_nodes.add(node)
    bm_nodes = list(bm_nodes)
    bm_cells = np.array([[bm_nodes.index(i) for i in tri] for tri in boundary])
    bm_coords = fenics_mesh.geometry.x[bm_nodes]

    bempp_boundary_grid = bempp.api.Grid(bm_coords.transpose(),
                                         bm_cells.transpose())

    return bempp_boundary_grid, bm_nodes
Exemple #3
0
def bm_from_fenics_mesh(fenics_mesh):
    boundary = entities_to_geometry(
        fenics_mesh,
        fenics_mesh.topology.dim - 1,
        exterior_facet_indices(fenics_mesh),
        True,
    )

    # print("number of facets ", len(exterior_facet_indices(fenics_mesh)))
    bm_nodes = set()
    for tri in boundary:
        for node in tri:
            bm_nodes.add(node)
    bm_nodes = list(bm_nodes)
    # bm_cells - remap cell indices between 0-len(bm_nodes)
    bm_cells = np.array([[bm_nodes.index(i) for i in tri] for tri in boundary])
    bm_coords = fenics_mesh.geometry.x[bm_nodes]

    # print(boundary)
    # print("fenics mesh dofs\n", fm_dofs)
    # # print("type of bm_coords ", type(bm_nodes), len(bm_nodes))
    # print('shape bm_cells ', bm_cells.shape)
    # print('type bm_cells ', type(bm_cells))
    # print('bm_cells \n', bm_cells)
    # print('bm_nodes \n', bm_nodes)
    return bm_nodes, bm_cells, bm_coords
Exemple #4
0
def solve_system(N):
    fenics_mesh = dolfinx.UnitCubeMesh(fenicsx_comm, N, N, N)
    fenics_space = dolfinx.FunctionSpace(fenics_mesh, ("CG", 1))
    u = ufl.TrialFunction(fenics_space)
    v = ufl.TestFunction(fenics_space)
    k = 2
    # print(u*v*ufl.ds)
    form = (ufl.inner(ufl.grad(u), ufl.grad(v)) -
            k**2 * ufl.inner(u, v)) * ufl.dx

    # locate facets on the cube boundary
    facets = locate_entities_boundary(
        fenics_mesh, 2, lambda x: np.logical_or(
            np.logical_or(
                np.logical_or(np.isclose(x[2], 0.0), np.isclose(x[2], 1.0)),
                np.logical_or(np.isclose(x[1], 0.0), np.isclose(x[1], 1.0))),
            np.logical_or(np.isclose(x[0], 0.0), np.isclose(x[0], 1.0))))

    facets.sort()

    # alternative - more general approach
    boundary = entities_to_geometry(
        fenics_mesh,
        fenics_mesh.topology.dim - 1,
        exterior_facet_indices(fenics_mesh),
        True,
    )
    # print(len(facets)
    assert len(facets) == len(exterior_facet_indices(fenics_mesh))

    u0 = fem.Function(fenics_space)

    with u0.vector.localForm() as u0_loc:
        u0_loc.set(0)
    # solution vector
    bc = DirichletBC(u0, locate_dofs_topological(fenics_space, 2, facets))

    A = 1 + 1j
    f = Function(fenics_space)
    f.interpolate(lambda x: A * k**2 * np.cos(k * x[0]) * np.cos(k * x[1]))

    L = ufl.inner(f, v) * ufl.dx
    u0.name = "u"
    problem = fem.LinearProblem(form,
                                L,
                                u=u0,
                                petsc_options={
                                    "ksp_type": "preonly",
                                    "pc_type": "lu"
                                })
    # problem = fem.LinearProblem(form, L, bcs=[bc], u=u0, petsc_options={"ksp_type": "preonly", "pc_type": "lu"})

    start_time = time.time()
    soln = problem.solve()
    if world_rank == 0:
        print("--- fenics solve done in %s seconds ---" %
              (time.time() - start_time))
Exemple #5
0
def submesh_geometry_test(mesh, submesh, geom_map, entity_dim, entities):
    submesh_geom_index_map = submesh.geometry.index_map()
    assert submesh_geom_index_map.size_local + submesh_geom_index_map.num_ghosts == submesh.geometry.x.shape[
        0]

    # Some processes might not own or ghost entities
    if len(entities) > 0:
        assert mesh.geometry.dim == submesh.geometry.dim

        e_to_g = entities_to_geometry(mesh, entity_dim, entities, False)
        for submesh_entity in range(len(entities)):
            submesh_x_dofs = submesh.geometry.dofmap.links(submesh_entity)
            # e_to_g[i] gets the mesh x_dofs of entities[i], which should
            # correspond to the x_dofs of cell i in the submesh
            mesh_x_dofs = e_to_g[submesh_entity]
            for i in range(len(submesh_x_dofs)):
                assert mesh_x_dofs[i] == geom_map[submesh_x_dofs[i]]
                assert np.allclose(mesh.geometry.x[mesh_x_dofs[i]],
                                   submesh.geometry.x[submesh_x_dofs[i]])
def bm_from_fenics_mesh_mpi(fenics_mesh, fenics_space):
    boundary = entities_to_geometry(
        fenics_mesh,
        fenics_mesh.topology.dim - 1,
        exterior_facet_indices(fenics_mesh),
        True,
    )

    dofmap = fenics_space.dofmap.index_map.global_indices(False)
    geom_map = fenics_mesh.geometry.index_map().global_indices(False)
    dofmap_mesh = fenics_mesh.geometry.dofmap

    assert dofmap == geom_map
    # print("dofmap ", dofmap)
    # print("geometry map ", geom_map)
    # print("dofmap mesh", dofmap_mesh)
    # print("number of facets ", len(exterior_facet_indices(fenics_mesh)))
    bm_nodes = set()
    for i, tri in enumerate(boundary):
        for j, node in enumerate(tri):
            # print(node, boundary[i][j])
            glob_geom_node = geom_map[node]
            boundary[i][j] = glob_geom_node
            bm_nodes.add(node)

    bm_nodes_global = [geom_map[i] for i in bm_nodes]
    bm_nodes = list(bm_nodes)
    bm_coords = fenics_mesh.geometry.x[bm_nodes]
    # bm_cells - remap cell indices between 0-len(bm_nodes)
    # bm_cells = np.array([[bm_nodes.index(i) for i in tri] for tri in boundary])
    # print("bm_coords\n", bm_coords)
    # print("bm_nodes\n", bm_nodes)
    # print("boundary\n", boundary)
    # # print("type of bm_coords ", type(bm_nodes), len(bm_nodes))
    # print('shape bm_cells ', bm_cells.shape)
    # print('type bm_cells ', type(bm_cells))
    # print('bm_cells \n', bm_cells)

    # print("dofmap ", fenics_mesh.geometry.dofmap)
    return bm_nodes_global, bm_coords, boundary
def create_boundary_mesh(mesh, comm, orient=False):
    """
    Create a mesh consisting of all exterior facets of a mesh
    Input:
      mesh   - The mesh
      comm   - The MPI communicator
      orient - Boolean flag for reorientation of facets to have
               consistent outwards-pointing normal (default: True)
    Output:
      bmesh - The boundary mesh
      bmesh_to_geometry - Map from cells of the boundary mesh
                          to the geometry of the original mesh
    """
    ext_facets = cmesh.exterior_facet_indices(mesh)
    boundary_geometry = cmesh.entities_to_geometry(mesh, mesh.topology.dim - 1,
                                                   ext_facets, orient)
    facet_type = dolfinx.cpp.mesh.to_string(
        cmesh.cell_entity_type(mesh.topology.cell_type, mesh.topology.dim - 1))
    facet_cell = ufl.Cell(facet_type, geometric_dimension=mesh.geometry.dim)
    degree = mesh.ufl_domain().ufl_coordinate_element().degree()
    ufl_domain = ufl.Mesh(ufl.VectorElement("Lagrange", facet_cell, degree))
    bmesh = dolfinx.mesh.create_mesh(comm, boundary_geometry, mesh.geometry.x,
                                     ufl_domain)
    return bmesh, boundary_geometry
Exemple #8
0
def mesh_3D_dolfin(theta=0,
                   ct=CellType.tetrahedron,
                   ext="tetrahedron",
                   num_refinements=0,
                   N0=5):
    timer = Timer("Create mesh")

    def find_plane_function(p0, p1, p2):
        """
        Find plane function given three points:
        http://www.nabla.hr/CG-LinesPlanesIn3DA3.htm
        """
        v1 = np.array(p1) - np.array(p0)
        v2 = np.array(p2) - np.array(p0)

        n = np.cross(v1, v2)
        D = -(n[0] * p0[0] + n[1] * p0[1] + n[2] * p0[2])
        return lambda x: np.isclose(0, np.dot(n, x) + D)

    def over_plane(p0, p1, p2):
        """
        Returns function that checks if a point is over a plane defined
        by the points p0, p1 and p2.
        """
        v1 = np.array(p1) - np.array(p0)
        v2 = np.array(p2) - np.array(p0)

        n = np.cross(v1, v2)
        D = -(n[0] * p0[0] + n[1] * p0[1] + n[2] * p0[2])
        return lambda x: n[0] * x[0] + n[1] * x[1] + D > -n[2] * x[2]

    tmp_mesh_name = "tmp_mesh.xdmf"
    r_matrix = rotation_matrix([1 / np.sqrt(2), 1 / np.sqrt(2), 0], -theta)

    if MPI.COMM_WORLD.rank == 0:
        # Create two coarse meshes and merge them
        mesh0 = create_unit_cube(MPI.COMM_SELF, N0, N0, N0, ct)
        mesh0.geometry.x[:, 2] += 1
        mesh1 = create_unit_cube(MPI.COMM_SELF, 2 * N0, 2 * N0, 2 * N0, ct)

        tdim0 = mesh0.topology.dim
        num_cells0 = mesh0.topology.index_map(tdim0).size_local
        cells0 = entities_to_geometry(
            mesh0, tdim0,
            np.arange(num_cells0, dtype=np.int32).reshape((-1, 1)), False)
        tdim1 = mesh1.topology.dim
        num_cells1 = mesh1.topology.index_map(tdim1).size_local
        cells1 = entities_to_geometry(
            mesh1, tdim1,
            np.arange(num_cells1, dtype=np.int32).reshape((-1, 1)), False)
        cells1 += mesh0.geometry.x.shape[0]

        # Concatenate points and cells
        points = np.vstack([mesh0.geometry.x, mesh1.geometry.x])
        cells = np.vstack([cells0, cells1])
        cell = Cell(ext, geometric_dimension=points.shape[1])
        domain = Mesh(VectorElement("Lagrange", cell, 1))
        # Rotate mesh
        points = np.dot(r_matrix, points.T).T

        mesh = create_mesh(MPI.COMM_SELF, cells, points, domain)
        with XDMFFile(MPI.COMM_SELF, tmp_mesh_name, "w") as xdmf:
            xdmf.write_mesh(mesh)

    MPI.COMM_WORLD.barrier()
    with XDMFFile(MPI.COMM_WORLD, tmp_mesh_name, "r") as xdmf:
        mesh = xdmf.read_mesh()

    # Refine coarse mesh
    for i in range(num_refinements):
        mesh.topology.create_entities(mesh.topology.dim - 2)
        mesh = refine(mesh, redistribute=True)

    tdim = mesh.topology.dim
    fdim = tdim - 1
    # Find information about facets to be used in meshtags
    bottom_points = np.dot(
        r_matrix,
        np.array([[0, 0, 0], [1, 0, 0], [0, 1, 0], [1, 1, 0]]).T)
    bottom = find_plane_function(bottom_points[:, 0], bottom_points[:, 1],
                                 bottom_points[:, 2])
    bottom_facets = locate_entities_boundary(mesh, fdim, bottom)
    top_points = np.dot(
        r_matrix,
        np.array([[0, 0, 2], [1, 0, 2], [0, 1, 2], [1, 1, 2]]).T)
    top = find_plane_function(top_points[:, 0], top_points[:, 1],
                              top_points[:, 2])
    top_facets = locate_entities_boundary(mesh, fdim, top)

    # Determine interface facets
    if_points = np.dot(
        r_matrix,
        np.array([[0, 0, 1], [1, 0, 1], [0, 1, 1], [1, 1, 1]]).T)
    interface = find_plane_function(if_points[:, 0], if_points[:, 1],
                                    if_points[:, 2])
    i_facets = locate_entities_boundary(mesh, fdim, interface)
    mesh.topology.create_connectivity(fdim, tdim)
    top_interface = []
    bottom_interface = []
    facet_to_cell = mesh.topology.connectivity(fdim, tdim)
    num_cells = mesh.topology.index_map(tdim).size_local

    # Find top and bottom interface facets
    cell_midpoints = compute_midpoints(mesh, tdim, range(num_cells))
    top_cube = over_plane(if_points[:, 0], if_points[:, 1], if_points[:, 2])
    for facet in i_facets:
        i_cells = facet_to_cell.links(facet)
        assert (len(i_cells == 1))
        i_cell = i_cells[0]
        if top_cube(cell_midpoints[i_cell]):
            top_interface.append(facet)
        else:
            bottom_interface.append(facet)

    # Create cell tags
    num_cells = mesh.topology.index_map(tdim).size_local
    cell_midpoints = compute_midpoints(mesh, tdim, range(num_cells))
    top_cube_marker = 2
    indices = []
    values = []
    for cell_index in range(num_cells):
        if top_cube(cell_midpoints[cell_index]):
            indices.append(cell_index)
            values.append(top_cube_marker)
    ct = meshtags(mesh, tdim, np.array(indices, dtype=np.intc),
                  np.array(values, dtype=np.intc))

    # Create meshtags for facet data
    markers = {
        3: top_facets,
        4: bottom_interface,
        9: top_interface,
        5: bottom_facets
    }  # , 6: left_facets, 7: right_facets}
    indices = np.array([], dtype=np.intc)
    values = np.array([], dtype=np.intc)
    for key in markers.keys():
        indices = np.append(indices, markers[key])
        values = np.append(values,
                           np.full(len(markers[key]), key, dtype=np.intc))
    sorted_indices = np.argsort(indices)
    mt = meshtags(mesh, fdim, indices[sorted_indices], values[sorted_indices])
    mt.name = "facet_tags"
    fname = f"meshes/mesh_{ext}_{theta:.2f}.xdmf"

    with XDMFFile(MPI.COMM_WORLD, fname, "w") as o_f:
        o_f.write_mesh(mesh)
        o_f.write_meshtags(ct)
        o_f.write_meshtags(mt)
    timer.stop()
Exemple #9
0
def get_num_bdry_dofs(fenics_mesh):
    exterior_facets = exterior_facet_indices(fenics_mesh)
    boundary = entities_to_geometry(
        fenics_mesh,
        fenics_mesh.topology.dim - 1,
        exterior_facet_indices(fenics_mesh),
        True,
    )
    bm_nodes = set()
    fenics_space = dolfinx.FunctionSpace(fenics_mesh, ("CG", 1))
    global_dofs_map = fenics_space.dofmap.index_map.global_indices(False)

    # print(fenics_space.dofmap.index_map.ghosts)
    mapping, dof_map = vertex_dofmap(fenics_mesh)
    # value of 9
    vertex_err_val = 9

    if vertex_err_val in mapping.keys():
        print("value of corresponding dof is ", mapping[9])

    for i, tri in enumerate(boundary):
        for j, node in enumerate(tri):
            # print(node, boundary[i][j])
            glob_geom_node = global_dofs_map[node]
            # glob_geom_node = geom_map[node]
            boundary[i][j] = glob_geom_node
            bm_nodes.add(node)
    bm_nodes_global = [global_dofs_map[i] for i in bm_nodes]
    ghosts = fenics_space.dofmap.index_map.ghosts
    ghosts_list = list(ghosts)
    ghost_owner = fenics_space.dofmap.index_map.ghost_owner_rank()
    ownership = np.ones_like(bm_nodes_global, dtype=np.int32) * comm.rank
    for i in range(len(bm_nodes_global)):
        if bm_nodes_global[i] in ghosts_list:
            index = ghosts_list.index(bm_nodes_global[i])
            ownership[i] = ghost_owner[index]

    print(ownership)
    print(bm_nodes_global)
    print("RANK {} \n".format(comm.Get_rank()))
    root = 0
    rank = comm.Get_rank()
    sendbuf = np.array(bm_nodes_global)
    # print("sendbuf ", sendbuf)
    sendcounts = np.array(comm.gather(len(sendbuf), root))

    print("sendcounts ", sendcounts)

    if rank == root:
        print("sendcounts: {}, total: {}".format(sendcounts, sum(sendcounts)))
        recvbuf = np.empty(sum(sendcounts), dtype=np.int64)
    else:
        recvbuf = None

    comm.Gatherv(sendbuf=sendbuf, recvbuf=(recvbuf, sendcounts), root=root)
    if rank == root:
        bm_nodes_unique = sorted(np.unique(recvbuf))
        print("Gathered array: {}, unique length: {}".format(
            bm_nodes_unique, len(bm_nodes_unique)))
        return len(bm_nodes_unique)
    return 0
Exemple #10
0
def play(fenics_mesh):
    exterior_facets = exterior_facet_indices(fenics_mesh)
    num_fenics_vertices = fenics_mesh.topology.connectivity(0, 0).num_nodes
    tets = fenics_mesh.topology.connectivity(3, 0)
    fenics_mesh.topology.create_connectivity(2, 0)
    tris = fenics_mesh.topology.connectivity(2, 0)
    fenics_mesh.topology.create_connectivity(2, 3)
    tri_to_tet = fenics_mesh.topology.connectivity(2, 3)
    exterior_nodes = set()
    local2global_nodes = fenics_mesh.topology.index_map(0).global_indices(
        False)

    # from exterior facets get all nodes (global) on bdry
    # is the bdry triangle index the same as in tris?
    for i in exterior_facets:
        for j in tris.links(i):
            exterior_nodes.add(local2global_nodes[j])

    # print("ghost owner rank ", fenics_mesh.topology.index_map(0))
    # print("index_map ", dir(fenics_mesh.topology.index_map(0)))
    ghosts = fenics_mesh.topology.index_map(0).ghosts
    # print("l2g ", local2global_nodes)
    # ghosts_global = [i for i in ghosts]
    print("ghosts", fenics_mesh.topology.index_map(0).ghosts)
    # print("ghosts_global", fenics_mesh.topology.index_map(0).ghosts)
    print("ghost owner rank ",
          fenics_mesh.topology.index_map(0).ghost_owner_rank())
    print("all global indices on this process",
          fenics_mesh.topology.index_map(0).indices(True))
    # print(dir(fenics_mesh.topology.index_map(0)))
    size_local = fenics_mesh.topology.index_map(0).size_local
    size_global = fenics_mesh.topology.index_map(0).size_global
    # shared = fenics_mesh.topology.index_map(0).shared_indices
    print("size local ", size_local, " size global ", size_global)
    print("exterior nodes (global)", exterior_nodes)
    print("exterior nodes length (global)", len(exterior_nodes))
    sendbuf_nodes = np.asarray(list(exterior_nodes), dtype=np.int64)
    print("length of sendbuf ", len(sendbuf_nodes))
    # print("exterior facets ", exterior_facets)
    # print("exterior facet 0", [ local2global_nodes[i] for i in tris.links(0)])
    print("RANK ", comm.Get_rank())
    # print(local2global_nodes)
    boundary = entities_to_geometry(
        fenics_mesh,
        fenics_mesh.topology.dim - 1,
        exterior_facet_indices(fenics_mesh),
        True,
    )
    bm_nodes = set()
    # this map is wrong:
    geom_map = fenics_mesh.geometry.index_map().global_indices(False)
    # use this instead:
    fenics_space = dolfinx.FunctionSpace(fenics_mesh, ("CG", 1))
    global_dofs_map = fenics_space.dofmap.index_map.global_indices(False)
    mapping_space = space_dofmap(fenics_mesh, fenics_space)
    # print("mapping space ", mapping_space)
    # print(mapping)
    exterior_dofs = []
    for i in exterior_nodes:
        exterior_dofs.append(mapping_space[i])

    print("exterior dofs from vertices", exterior_dofs)
    for i, tri in enumerate(boundary):
        for j, node in enumerate(tri):
            # print(node, boundary[i][j])
            glob_geom_node = global_dofs_map[node]
            # glob_geom_node = geom_map[node]
            boundary[i][j] = glob_geom_node
            bm_nodes.add(node)
    bm_nodes_global = [geom_map[i] for i in bm_nodes]
    print("bm nodes ", bm_nodes_global)
    mapping, dof_map = vertex_dofmap(fenics_mesh)
    # print("geom nodes     ", bm_nodes_global)
    # print(exterior_facets)
    # get mapping between dofs and vertices
    mesh_dofmap = fenics_mesh.geometry.dofmap
    # mapping = vertex_dofmap(fenics_mesh)
    # print(mapping[13])
    mesh_dofs = []
    for i in exterior_nodes:
        # print(i)
        mesh_dofs.append(mapping[i])
    # print("mapping     ", mapping)
    # print("dof_map     ", dof_map)
    ma = [dof_map[i] for i in bm_nodes_global]
    print(ma)
    print("\n")

    # print("bm_nodes global ", sorted(bm_nodes_global))

    print("do a gather")

    recvbuf_nodes = None
    if comm.rank == 0:
        info = MPI.Status()
        recvbuf_nodes = np.empty(comm.Get_size() * len(exterior_nodes),
                                 dtype=np.int64)
    comm.Gather(sendbuf_nodes, recvbuf_nodes, root=0)

    print("received nodes ", len(np.unique(recvbuf_nodes)))
    exit(0)
Exemple #11
0
def bm_from_fenics_mesh(comm, fenics_comm, fenics_mesh, fenics_space):
    """
    Create a Bempp boundary grid from a FEniCS Mesh.
    Return the Bempp grid and a map from the node numberings of the FEniCS
    mesh to the node numbers of the boundary grid.
    """
    from dolfinx.cpp.mesh import entities_to_geometry, exterior_facet_indices

    boundary = entities_to_geometry(
        fenics_mesh,
        fenics_mesh.topology.dim - 1,
        exterior_facet_indices(fenics_mesh),
        True,
    )

    dofmap = fenics_space.dofmap.index_map.global_indices(False)
    geom_map = fenics_mesh.geometry.index_map().global_indices(False)
    dofmap_mesh = fenics_mesh.geometry.dofmap

    # assert dofmap == geom_map
    # print("number of facets ", len(exterior_facet_indices(fenics_mesh)))
    bm_nodes = set()
    for i, tri in enumerate(boundary):
        for j, node in enumerate(tri):
            # print(node, boundary[i][j])
            glob_dof_node = dofmap[node]
            boundary[i][j] = glob_dof_node
            bm_nodes.add(node)

    bm_nodes_global = [dofmap[i] for i in bm_nodes]
    bm_nodes = list(bm_nodes)
    bm_coords = fenics_mesh.geometry.x[bm_nodes]
    # bm_cells - remap cell indices between 0-len(bm_nodes)
    # bm_cells = np.array([[bm_nodes.index(i) for i in tri] for tri in boundary])
    # print('shape bm_cells ', bm_cells.shape)
    # print('bm_cells \n', bm_cells)
    # print('bm_coords len ', len(bm_coords))
    # print('bm_coords type ', type(bm_coords[0][0]))
    # print("RANK ", fenics_comm.rank)
    gathered_bm_coords = gather(fenics_comm, bm_coords, 3, np.float64)
    gathered_bm_tris = gather(fenics_comm, boundary, 3, np.int32)
    gathered_bm_nodes = gather(fenics_comm,
                               np.asarray(bm_nodes_global, np.int32), 1,
                               np.int32)

    global_alldofs = np.asarray(
        fenics_space.dofmap.index_map.global_indices(False), dtype=np.int32)
    gathered_global_alldofs = gather(fenics_comm, global_alldofs, 1, np.int32)

    if fenics_comm.rank == 0:
        all_bm_coords = gathered_bm_coords.reshape(
            int(len(gathered_bm_coords) / 3), 3)
        all_bm_tris = gathered_bm_tris.reshape(int(len(gathered_bm_tris) / 3),
                                               3)
        all_bm_nodes = gathered_bm_nodes

        # sort gathered nodes and remove repetitions (ghosts on bdry)
        sorted_indices = all_bm_nodes.argsort()
        all_bm_nodes_sorted = all_bm_nodes[sorted_indices]
        all_bm_coords_sorted = all_bm_coords[sorted_indices]
        # print("sorted indices, ", sorted_indices)
        all_bm_nodes, unique = np.unique(all_bm_nodes_sorted,
                                         return_index=True)
        all_bm_coords = all_bm_coords_sorted[unique]
        all_bm_nodes_list = list(all_bm_nodes)
        # bm_cells - remap boundary triangle indices between 0-len(bm_nodes) - this can be improved
        all_bm_cells = np.array([[all_bm_nodes_list.index(i) for i in tri]
                                 for tri in all_bm_tris],
                                dtype=np.int32)
        all_bm_nodes = np.asarray(all_bm_nodes_list, dtype=np.int32)
        # send to Bempp process
        send(comm, all_bm_coords, MPI.DOUBLE, 100)
        send(comm, all_bm_cells, MPI.INT, 101)
        send(comm, all_bm_nodes, MPI.INT, 102)
        # print("all_bm_cells ", type(all_bm_cells))

        num_fenics_vertices = len(np.unique(np.sort(gathered_global_alldofs)))

        # hack - to change
        comm.send(num_fenics_vertices, dest=0, tag=103)