示例#1
0
def CircleManifoldMesh(ncells, radius=1, comm=COMM_WORLD):
    """Generated a 1D mesh of the circle, immersed in 2D.

    :arg ncells: number of cells the circle should be
         divided into (min 3)
    :kwarg radius: (optional) radius of the circle to approximate
           (defaults to 1).
    :kwarg comm: Optional communicator to build the mesh on (defaults to
        COMM_WORLD).
    """
    if ncells < 3:
        raise ValueError("CircleManifoldMesh must have at least three cells")

    vertices = radius * np.column_stack(
        (np.cos(np.arange(ncells) * (2 * np.pi / ncells)),
         np.sin(np.arange(ncells) * (2 * np.pi / ncells))))

    cells = np.column_stack((np.arange(0, ncells, dtype=np.int32),
                             np.roll(np.arange(0, ncells, dtype=np.int32),
                                     -1)))

    plex = mesh._from_cell_list(1, cells, vertices, comm)
    m = mesh.Mesh(plex, dim=2, reorder=False)
    m._circle_manifold = radius
    return m
def CubedSphereMesh(radius, refinement_level=0, degree=1,
                    reorder=None, comm=COMM_WORLD):
    """Generate an cubed approximation to the surface of the
    sphere.

    :arg radius: The radius of the sphere to approximate.
    :kwarg refinement_level: optional number of refinements (0 is a cube).
    :kwarg degree: polynomial degree of coordinate space (defaults
        to 1: bilinear quads)
    :kwarg reorder: (optional), should the mesh be reordered?
    """
    if refinement_level < 0 or refinement_level % 1:
            raise RuntimeError("Number of refinements must be a non-negative integer")

    if degree < 1:
        raise ValueError("Mesh coordinate degree must be at least 1")

    cells, coords = _cubedsphere_cells_and_coords(radius, refinement_level)
    plex = mesh._from_cell_list(2, cells, coords, comm)

    m = mesh.Mesh(plex, dim=3, reorder=reorder)

    if degree > 1:
        new_coords = function.Function(functionspace.VectorFunctionSpace(m, "Q", degree))
        new_coords.interpolate(ufl.SpatialCoordinate(m))
        # "push out" to sphere
        new_coords.dat.data[:] *= (radius / np.linalg.norm(new_coords.dat.data, axis=1)).reshape(-1, 1)
        m = mesh.Mesh(new_coords)
    m._radius = radius
    return m
示例#3
0
def TorusMesh(nR, nr, R, r, quadrilateral=False, reorder=None):
    """Generate a toroidal mesh

    :arg nR: The number of cells in the major direction (min 3)
    :arg nr: The number of cells in the minor direction (min 3)
    :arg R: The major radius
    :arg r: The minor radius
    :kwarg quadrilateral: (optional), creates quadrilateral mesh, defaults to False
    :kwarg reorder: (optional), should the mesh be reordered
    """
    if nR < 3 or nr < 3:
        raise ValueError("Must have at least 3 cells in each direction")

    # gives an array [[0, 0], [0, 1], ..., [1, 0], [1, 1], ...]
    idx_temp = np.asarray(np.meshgrid(np.arange(nR), np.arange(nr))).swapaxes(0, 2).reshape(-1, 2)

    # vertices - standard formula for (x, y, z), see Wikipedia
    vertices = np.column_stack((
        (R + r*np.cos(idx_temp[:, 1]*(2*np.pi/nr)))*np.cos(idx_temp[:, 0]*(2*np.pi/nR)),
        (R + r*np.cos(idx_temp[:, 1]*(2*np.pi/nr)))*np.sin(idx_temp[:, 0]*(2*np.pi/nR)),
        r*np.sin(idx_temp[:, 1]*(2*np.pi/nr))))

    # cell vertices
    i, j = np.meshgrid(np.arange(nR), np.arange(nr))
    i = i.reshape(-1)  # Miklos's suggestion to make the code
    j = j.reshape(-1)  # less impenetrable
    cells = [i*nr + j, i*nr + (j+1) % nr, ((i+1) % nR)*nr + (j+1) % nr, ((i+1) % nR)*nr + j]
    cells = np.column_stack(cells)
    if not quadrilateral:
        # two cells per cell above...
        cells = cells[:, [0, 1, 3, 1, 2, 3]].reshape(-1, 3)

    plex = mesh._from_cell_list(2, cells, vertices)
    m = mesh.Mesh(plex, dim=3, reorder=reorder)
    return m
示例#4
0
def TorusMesh(nR, nr, R, r, quadrilateral=False, reorder=None):
    """Generate a toroidal mesh

    :arg nR: The number of cells in the major direction (min 3)
    :arg nr: The number of cells in the minor direction (min 3)
    :arg R: The major radius
    :arg r: The minor radius
    :kwarg quadrilateral: (optional), creates quadrilateral mesh, defaults to False
    :kwarg reorder: (optional), should the mesh be reordered
    """
    if nR < 3 or nr < 3:
        raise ValueError("Must have at least 3 cells in each direction")

    # gives an array [[0, 0], [0, 1], ..., [1, 0], [1, 1], ...]
    idx_temp = np.asarray(np.meshgrid(np.arange(nR), np.arange(nr))).swapaxes(0, 2).reshape(-1, 2)

    # vertices - standard formula for (x, y, z), see Wikipedia
    vertices = np.column_stack((
        (R + r*np.cos(idx_temp[:, 1]*(2*np.pi/nr)))*np.cos(idx_temp[:, 0]*(2*np.pi/nR)),
        (R + r*np.cos(idx_temp[:, 1]*(2*np.pi/nr)))*np.sin(idx_temp[:, 0]*(2*np.pi/nR)),
        r*np.sin(idx_temp[:, 1]*(2*np.pi/nr))))

    # cell vertices
    i, j = np.meshgrid(np.arange(nR), np.arange(nr))
    i = i.reshape(-1)  # Miklos's suggestion to make the code
    j = j.reshape(-1)  # less impenetrable
    cells = [i*nr + j, i*nr + (j+1) % nr, ((i+1) % nR)*nr + (j+1) % nr, ((i+1) % nR)*nr + j]
    cells = np.column_stack(cells)
    if not quadrilateral:
        # two cells per cell above...
        cells = cells[:, [0, 1, 3, 1, 2, 3]].reshape(-1, 3)

    plex = mesh._from_cell_list(2, cells, vertices)
    m = mesh.Mesh(plex, dim=3, reorder=reorder)
    return m
示例#5
0
def RectangleMesh(nx, ny, Lx, Ly, quadrilateral=False, reorder=None):
    """Generate a rectangular mesh

    :arg nx: The number of cells in the x direction
    :arg ny: The number of cells in the y direction
    :arg Lx: The extent in the x direction
    :arg Ly: The extent in the y direction
    :kwarg quadrilateral: (optional), creates quadrilateral mesh, defaults to False
    :kwarg reorder: (optional), should the mesh be reordered

    The boundary edges in this mesh are numbered as follows:

    * 1: plane x == 0
    * 2: plane x == Lx
    * 3: plane y == 0
    * 4: plane y == Ly
    """
    if quadrilateral:
        dx = float(Lx) / nx
        dy = float(Ly) / ny
        xcoords = np.arange(0.0, Lx + 0.01 * dx, dx)
        ycoords = np.arange(0.0, Ly + 0.01 * dy, dy)
        coords = np.asarray(np.meshgrid(xcoords, ycoords)).swapaxes(0, 2).reshape(-1, 2)

        # cell vertices
        i, j = np.meshgrid(np.arange(nx), np.arange(ny))
        cells = [i*(ny+1) + j, i*(ny+1) + j+1, (i+1)*(ny+1) + j+1, (i+1)*(ny+1) + j]
        cells = np.asarray(cells).swapaxes(0, 2).reshape(-1, 4)

        plex = mesh._from_cell_list(2, cells, coords)
    else:
        boundary = PETSc.DMPlex().create(MPI.comm)
        boundary.setDimension(1)
        boundary.createSquareBoundary([0., 0.], [float(Lx), float(Ly)], [nx, ny])
        boundary.setTriangleOptions("pqezQYSl")

        plex = PETSc.DMPlex().generate(boundary)

    # mark boundary facets
    plex.createLabel("boundary_ids")
    plex.markBoundaryFaces("boundary_faces")
    coords = plex.getCoordinates()
    coord_sec = plex.getCoordinateSection()
    if plex.getStratumSize("boundary_faces", 1) > 0:
        boundary_faces = plex.getStratumIS("boundary_faces", 1).getIndices()
        xtol = float(Lx)/(2*nx)
        ytol = float(Ly)/(2*ny)
        for face in boundary_faces:
            face_coords = plex.vecGetClosure(coord_sec, coords, face)
            if abs(face_coords[0]) < xtol and abs(face_coords[2]) < xtol:
                plex.setLabelValue("boundary_ids", face, 1)
            if abs(face_coords[0] - Lx) < xtol and abs(face_coords[2] - Lx) < xtol:
                plex.setLabelValue("boundary_ids", face, 2)
            if abs(face_coords[1]) < ytol and abs(face_coords[3]) < ytol:
                plex.setLabelValue("boundary_ids", face, 3)
            if abs(face_coords[1] - Ly) < ytol and abs(face_coords[3] - Ly) < ytol:
                plex.setLabelValue("boundary_ids", face, 4)

    return mesh.Mesh(plex, reorder=reorder)
示例#6
0
def RectangleMesh(nx, ny, Lx, Ly, quadrilateral=False, reorder=None):
    """Generate a rectangular mesh

    :arg nx: The number of cells in the x direction
    :arg ny: The number of cells in the y direction
    :arg Lx: The extent in the x direction
    :arg Ly: The extent in the y direction
    :kwarg quadrilateral: (optional), creates quadrilateral mesh, defaults to False
    :kwarg reorder: (optional), should the mesh be reordered

    The boundary edges in this mesh are numbered as follows:

    * 1: plane x == 0
    * 2: plane x == Lx
    * 3: plane y == 0
    * 4: plane y == Ly
    """
    if quadrilateral:
        dx = float(Lx) / nx
        dy = float(Ly) / ny
        xcoords = np.arange(0.0, Lx + 0.01 * dx, dx)
        ycoords = np.arange(0.0, Ly + 0.01 * dy, dy)
        coords = np.asarray(np.meshgrid(xcoords, ycoords)).swapaxes(0, 2).reshape(-1, 2)

        # cell vertices
        i, j = np.meshgrid(np.arange(nx), np.arange(ny))
        cells = [i*(ny+1) + j, i*(ny+1) + j+1, (i+1)*(ny+1) + j+1, (i+1)*(ny+1) + j]
        cells = np.asarray(cells).swapaxes(0, 2).reshape(-1, 4)

        plex = mesh._from_cell_list(2, cells, coords)
    else:
        boundary = PETSc.DMPlex().create(MPI.comm)
        boundary.setDimension(1)
        boundary.createSquareBoundary([0., 0.], [float(Lx), float(Ly)], [nx, ny])
        boundary.setTriangleOptions("pqezQYSl")

        plex = PETSc.DMPlex().generate(boundary)

    # mark boundary facets
    plex.createLabel("boundary_ids")
    plex.markBoundaryFaces("boundary_faces")
    coords = plex.getCoordinates()
    coord_sec = plex.getCoordinateSection()
    if plex.getStratumSize("boundary_faces", 1) > 0:
        boundary_faces = plex.getStratumIS("boundary_faces", 1).getIndices()
        xtol = float(Lx)/(2*nx)
        ytol = float(Ly)/(2*ny)
        for face in boundary_faces:
            face_coords = plex.vecGetClosure(coord_sec, coords, face)
            if abs(face_coords[0]) < xtol and abs(face_coords[2]) < xtol:
                plex.setLabelValue("boundary_ids", face, 1)
            if abs(face_coords[0] - Lx) < xtol and abs(face_coords[2] - Lx) < xtol:
                plex.setLabelValue("boundary_ids", face, 2)
            if abs(face_coords[1]) < ytol and abs(face_coords[3]) < ytol:
                plex.setLabelValue("boundary_ids", face, 3)
            if abs(face_coords[1] - Ly) < ytol and abs(face_coords[3] - Ly) < ytol:
                plex.setLabelValue("boundary_ids", face, 4)

    return mesh.Mesh(plex, reorder=reorder)
示例#7
0
def IcosahedralSphereMesh(radius, refinement_level=0, degree=1, reorder=None):
    """Generate an icosahedral approximation to the surface of the
    sphere.

    :arg radius: The radius of the sphere to approximate.
         For a radius R the edge length of the underlying
         icosahedron will be.

         .. math::

             a = \\frac{R}{\\sin(2 \\pi / 5)}

    :kwarg refinement_level: optional number of refinements (0 is an
        icosahedron).
    :kwarg degree: polynomial degree of coordinate space (defaults
        to 1: flat triangles)
    :kwarg reorder: (optional), should the mesh be reordered?
    """
    if degree < 1:
        raise ValueError("Mesh coordinate degree must be at least 1")
    from math import sqrt
    phi = (1 + sqrt(5)) / 2
    # vertices of an icosahedron with an edge length of 2
    vertices = np.array([[-1, phi, 0], [1, phi, 0], [-1, -phi,
                                                     0], [1, -phi, 0],
                         [0, -1, phi], [0, 1, phi], [0, -1,
                                                     -phi], [0, 1, -phi],
                         [phi, 0, -1], [phi, 0, 1], [-phi, 0, -1],
                         [-phi, 0, 1]])
    # faces of the base icosahedron
    faces = np.array(
        [[0, 11, 5], [0, 5, 1], [0, 1, 7], [0, 7, 10], [0, 10, 11], [1, 5, 9],
         [5, 11, 4], [11, 10, 2], [10, 7, 6], [7, 1, 8], [3, 9, 4], [3, 4, 2],
         [3, 2, 6], [3, 6, 8], [3, 8, 9], [4, 9, 5], [2, 4, 11], [6, 2, 10],
         [8, 6, 7], [9, 8, 1]],
        dtype=np.int32)

    plex = mesh._from_cell_list(2, faces, vertices)
    plex.setRefinementUniform(True)
    for i in range(refinement_level):
        plex = plex.refine()

    coords = plex.getCoordinatesLocal().array.reshape(-1, 3)
    scale = (radius / np.linalg.norm(coords, axis=1)).reshape(-1, 1)
    coords *= scale
    m = mesh.Mesh(plex, dim=3, reorder=reorder)
    if degree > 1:
        new_coords = function.Function(
            functionspace.VectorFunctionSpace(m, "CG", degree))
        new_coords.interpolate(expression.Expression(("x[0]", "x[1]", "x[2]")))
        # "push out" to sphere
        new_coords.dat.data[:] *= (
            radius / np.linalg.norm(new_coords.dat.data, axis=1)).reshape(
                -1, 1)
        m = mesh.Mesh(new_coords)
    m._icosahedral_sphere = radius
    return m
示例#8
0
def UnitTetrahedronMesh(comm=COMM_WORLD):
    """Generate a mesh of the reference tetrahedron.

    :kwarg comm: Optional communicator to build the mesh on (defaults to
        COMM_WORLD).
    """
    coords = [[0., 0., 0.], [1., 0., 0.], [0., 1., 0.], [0., 0., 1.]]
    cells = [[0, 1, 2, 3]]
    plex = mesh._from_cell_list(3, cells, coords, comm)
    return mesh.Mesh(plex, reorder=False)
def UnitTetrahedronMesh(comm=COMM_WORLD):
    """Generate a mesh of the reference tetrahedron.

    :kwarg comm: Optional communicator to build the mesh on (defaults to
        COMM_WORLD).
    """
    coords = [[0., 0., 0.], [1., 0., 0.], [0., 1., 0.], [0., 0., 1.]]
    cells = [[0, 1, 2, 3]]
    plex = mesh._from_cell_list(3, cells, coords, comm)
    return mesh.Mesh(plex, reorder=False)
示例#10
0
def IntervalMesh(ncells, length_or_left, right=None, comm=COMM_WORLD):
    """
    Generate a uniform mesh of an interval.

    :arg ncells: The number of the cells over the interval.
    :arg length_or_left: The length of the interval (if ``right``
         is not provided) or else the left hand boundary point.
    :arg right: (optional) position of the right
         boundary point (in which case ``length_or_left`` should
         be the left boundary point).
    :kwarg comm: Optional communicator to build the mesh on (defaults to
        COMM_WORLD).

    The left hand boundary point has boundary marker 1,
    while the right hand point has marker 2.
    """
    if right is None:
        left = 0
        right = length_or_left
    else:
        left = length_or_left

    if ncells <= 0 or ncells % 1:
        raise ValueError("Number of cells must be a postive integer")
    length = right - left
    if length < 0:
        raise ValueError("Requested mesh has negative length")
    dx = length / ncells
    # This ensures the rightmost point is actually present.
    coords = np.arange(left, right + 0.01 * dx, dx,
                       dtype=np.double).reshape(-1, 1)
    cells = np.dstack(
        (np.arange(0, len(coords) - 1,
                   dtype=np.int32), np.arange(1, len(coords),
                                              dtype=np.int32))).reshape(-1, 2)
    plex = mesh._from_cell_list(1, cells, coords, comm)
    # Apply boundary IDs
    plex.createLabel("boundary_ids")
    coordinates = plex.getCoordinates()
    coord_sec = plex.getCoordinateSection()
    vStart, vEnd = plex.getDepthStratum(0)  # vertices
    for v in range(vStart, vEnd):
        vcoord = plex.vecGetClosure(coord_sec, coordinates, v)
        if vcoord[0] == coords[0]:
            plex.setLabelValue("boundary_ids", v, 1)
        if vcoord[0] == coords[-1]:
            plex.setLabelValue("boundary_ids", v, 2)

    return mesh.Mesh(plex, reorder=False)
示例#11
0
def IntervalMesh(ncells, length_or_left, right=None, comm=COMM_WORLD):
    """
    Generate a uniform mesh of an interval.

    :arg ncells: The number of the cells over the interval.
    :arg length_or_left: The length of the interval (if ``right``
         is not provided) or else the left hand boundary point.
    :arg right: (optional) position of the right
         boundary point (in which case ``length_or_left`` should
         be the left boundary point).
    :kwarg comm: Optional communicator to build the mesh on (defaults to
        COMM_WORLD).

    The left hand boundary point has boundary marker 1,
    while the right hand point has marker 2.
    """
    if right is None:
        left = 0
        right = length_or_left
    else:
        left = length_or_left

    if ncells <= 0 or ncells % 1:
        raise ValueError("Number of cells must be a postive integer")
    length = right - left
    if length < 0:
        raise ValueError("Requested mesh has negative length")
    dx = float(length) / ncells
    # This ensures the rightmost point is actually present.
    coords = np.arange(left, right + 0.01 * dx, dx).reshape(-1, 1)
    cells = np.dstack((np.arange(0, len(coords) - 1, dtype=np.int32),
                       np.arange(1, len(coords), dtype=np.int32))).reshape(-1, 2)
    plex = mesh._from_cell_list(1, cells, coords, comm)
    # Apply boundary IDs
    plex.createLabel("boundary_ids")
    coordinates = plex.getCoordinates()
    coord_sec = plex.getCoordinateSection()
    vStart, vEnd = plex.getDepthStratum(0)  # vertices
    for v in range(vStart, vEnd):
        vcoord = plex.vecGetClosure(coord_sec, coordinates, v)
        if vcoord[0] == coords[0]:
            plex.setLabelValue("boundary_ids", v, 1)
        if vcoord[0] == coords[-1]:
            plex.setLabelValue("boundary_ids", v, 2)

    return mesh.Mesh(plex, reorder=False)
示例#12
0
def CircleManifoldMesh(ncells, radius=1):
    """Generated a 1D mesh of the circle, immersed in 2D.

    :arg ncells: number of cells the circle should be
         divided into (min 3)
    :kwarg radius: (optional) radius of the circle to approximate
           (defaults to 1).
    """
    if ncells < 3:
        raise ValueError("CircleManifoldMesh must have at least three cells")

    vertices = radius*np.column_stack((np.cos(np.arange(ncells)*(2*np.pi/ncells)),
                                       np.sin(np.arange(ncells)*(2*np.pi/ncells))))

    cells = np.column_stack((np.arange(0, ncells, dtype=np.int32),
                             np.roll(np.arange(0, ncells, dtype=np.int32), -1)))

    plex = mesh._from_cell_list(1, cells, vertices)
    m = mesh.Mesh(plex, dim=2, reorder=False)
    m._circle_manifold = radius
    return m
示例#13
0
def IcosahedralSphereMesh(radius, refinement_level=0, degree=1, reorder=None,
                          comm=COMM_WORLD):
    """Generate an icosahedral approximation to the surface of the
    sphere.

    :arg radius: The radius of the sphere to approximate.
         For a radius R the edge length of the underlying
         icosahedron will be.

         .. math::

             a = \\frac{R}{\\sin(2 \\pi / 5)}

    :kwarg refinement_level: optional number of refinements (0 is an
        icosahedron).
    :kwarg degree: polynomial degree of coordinate space (defaults
        to 1: flat triangles)
    :kwarg reorder: (optional), should the mesh be reordered?
    :kwarg comm: Optional communicator to build the mesh on (defaults to
        COMM_WORLD).
    """
    if refinement_level < 0 or refinement_level % 1:
            raise RuntimeError("Number of refinements must be a non-negative integer")

    if degree < 1:
        raise ValueError("Mesh coordinate degree must be at least 1")
    from math import sqrt
    phi = (1 + sqrt(5)) / 2
    # vertices of an icosahedron with an edge length of 2
    vertices = np.array([[-1, phi, 0],
                         [1, phi, 0],
                         [-1, -phi, 0],
                         [1, -phi, 0],
                         [0, -1, phi],
                         [0, 1, phi],
                         [0, -1, -phi],
                         [0, 1, -phi],
                         [phi, 0, -1],
                         [phi, 0, 1],
                         [-phi, 0, -1],
                         [-phi, 0, 1]])
    # faces of the base icosahedron
    faces = np.array([[0, 11, 5],
                      [0, 5, 1],
                      [0, 1, 7],
                      [0, 7, 10],
                      [0, 10, 11],
                      [1, 5, 9],
                      [5, 11, 4],
                      [11, 10, 2],
                      [10, 7, 6],
                      [7, 1, 8],
                      [3, 9, 4],
                      [3, 4, 2],
                      [3, 2, 6],
                      [3, 6, 8],
                      [3, 8, 9],
                      [4, 9, 5],
                      [2, 4, 11],
                      [6, 2, 10],
                      [8, 6, 7],
                      [9, 8, 1]], dtype=np.int32)

    plex = mesh._from_cell_list(2, faces, vertices, comm)
    plex.setRefinementUniform(True)
    for i in range(refinement_level):
        plex = plex.refine()

    coords = plex.getCoordinatesLocal().array.reshape(-1, 3)
    scale = (radius / np.linalg.norm(coords, axis=1)).reshape(-1, 1)
    coords *= scale
    m = mesh.Mesh(plex, dim=3, reorder=reorder)
    if degree > 1:
        new_coords = function.Function(functionspace.VectorFunctionSpace(m, "CG", degree))
        new_coords.interpolate(expression.Expression(("x[0]", "x[1]", "x[2]")))
        # "push out" to sphere
        new_coords.dat.data[:] *= (radius / np.linalg.norm(new_coords.dat.data, axis=1)).reshape(-1, 1)
        m = mesh.Mesh(new_coords)
    m._icosahedral_sphere = radius
    return m
示例#14
0
def UnitTriangleMesh():
    """Generate a mesh of the reference triangle"""
    coords = [[0., 0.], [1., 0.], [0., 1.]]
    cells = [[0, 1, 2]]
    plex = mesh._from_cell_list(2, cells, coords)
    return mesh.Mesh(plex, reorder=False)
示例#15
0
def UnitTetrahedronMesh():
    """Generate a mesh of the reference tetrahedron"""
    coords = [[0., 0., 0.], [1., 0., 0.], [0., 1., 0.], [0., 0., 1.]]
    cells = [[0, 1, 2, 3]]
    plex = mesh._from_cell_list(3, cells, coords)
    return mesh.Mesh(plex, reorder=False)
示例#16
0
def UnitTriangleMesh():
    """Generate a mesh of the reference triangle"""
    coords = [[0., 0.], [1., 0.], [0., 1.]]
    cells = [[0, 1, 2]]
    plex = mesh._from_cell_list(2, cells, coords)
    return mesh.Mesh(plex, reorder=False)
示例#17
0
def UnitTetrahedronMesh():
    """Generate a mesh of the reference tetrahedron"""
    coords = [[0., 0., 0.], [1., 0., 0.], [0., 1., 0.], [0., 0., 1.]]
    cells = [[0, 1, 2, 3]]
    plex = mesh._from_cell_list(3, cells, coords)
    return mesh.Mesh(plex, reorder=False)
示例#18
0
def CubedSphereMesh(radius, refinement_level=0, degree=1,
                    reorder=None, use_dmplex_refinement=False):
    """Generate an cubed approximation to the surface of the
    sphere.

    :arg radius: The radius of the sphere to approximate.
    :kwarg refinement_level: optional number of refinements (0 is a cube).
    :kwarg degree: polynomial degree of coordinate space (defaults
        to 1: bilinear quads)
    :kwarg reorder: (optional), should the mesh be reordered?
    :kwarg use_dmplex_refinement: (optional), use dmplex to apply
        the refinement.
    """
    if degree < 1:
        raise ValueError("Mesh coordinate degree must be at least 1")

    if use_dmplex_refinement:
        # vertices of a cube with an edge length of 2
        vertices = np.array([[-1., -1., -1.],
                             [1., -1., -1.],
                             [-1., 1., -1.],
                             [1., 1., -1.],
                             [-1., -1., 1.],
                             [1., -1., 1.],
                             [-1., 1., 1.],
                             [1., 1., 1.]])
        # faces of the base cube
        # bottom face viewed from above
        # 2 3
        # 0 1
        # top face viewed from above
        # 6 7
        # 4 5
        faces = np.array([[0, 1, 3, 2],  # bottom
                          [4, 5, 7, 6],  # top
                          [0, 1, 5, 4],
                          [2, 3, 7, 6],
                          [0, 2, 6, 4],
                          [1, 3, 7, 5]], dtype=np.int32)

        plex = mesh._from_cell_list(2, faces, vertices)
        plex.setRefinementUniform(True)
        for i in range(refinement_level):
            plex = plex.refine()

        # rescale points to the sphere
        # this is not the same as the gnonomic transformation
        coords = plex.getCoordinatesLocal().array.reshape(-1, 3)
        scale = (radius / np.linalg.norm(coords, axis=1)).reshape(-1, 1)
        coords *= scale
    else:
        cells, coords = _cubedsphere_cells_and_coords(radius, refinement_level)
        plex = mesh._from_cell_list(2, cells, coords)

    m = mesh.Mesh(plex, dim=3, reorder=reorder)

    if degree > 1:
        new_coords = function.Function(functionspace.VectorFunctionSpace(m, "Q", degree))
        new_coords.interpolate(expression.Expression(("x[0]", "x[1]", "x[2]")))
        # "push out" to sphere
        new_coords.dat.data[:] *= (radius / np.linalg.norm(new_coords.dat.data, axis=1)).reshape(-1, 1)
        m = mesh.Mesh(new_coords)

    return m
示例#19
0
def export_mesh_to_firedrake(mesh, group_nr=None, comm=None):
    r"""
    Create a firedrake mesh corresponding to one
    :class:`~meshmode.mesh.Mesh`'s
    :class:`~meshmode.mesh.SimplexElementGroup`.

    :param mesh: A :class:`~meshmode.mesh.Mesh` to convert with
        at least one :class:`~meshmode.mesh.SimplexElementGroup`.
        'mesh.is_conforming' must evaluate to *True*.
        'mesh' must have vertices supplied, i.e.
        'mesh.vertices' must not be *None*.
    :param group_nr: The group number to be converted into a firedrake
        mesh. The corresponding group must be of type
        :class:`~meshmode.mesh.SimplexElementGroup`. If *None* and
        *mesh* has only one group, that group is used. Otherwise,
        a *ValueError* is raised.
    :param comm: The communicator to build the dmplex mesh on

    :return: A tuple *(fdrake_mesh, fdrake_cell_ordering, perm2cell)*
        where

        * *fdrake_mesh* is a :mod:`firedrake`
          `firedrake.mesh.MeshGeometry` corresponding to
          *mesh*
        * *fdrake_cell_ordering* is a numpy array whose *i*\ th
          element in *mesh* (i.e. the *i*\ th element in
          *mesh.groups[group_nr].vertex_indices*) corresponds to the
          *fdrake_cell_ordering[i]*\ th :mod:`firedrake` cell
        * *perm2cell* is a dictionary, mapping tuples to
          1-D numpy arrays of meshmode element indices.
          Each meshmode element index
          appears in exactly one of these arrays. The corresponding
          tuple describes how firedrake reordered the local vertex
          indices on that cell. In particular, if *c*
          is in the list *perm2cell[p]* for a tuple *p*, then
          the *p[i]*\ th local vertex of the *fdrake_cell_ordering[c]*\ th
          firedrake cell corresponds to the *i*\ th local vertex
          of the *c*\ th meshmode element.

    .. warning::
        Currently, no custom boundary tags are exported along with the mesh.
        :mod:`firedrake` seems to only allow one marker on each facet, whereas
        :mod:`meshmode` allows many.
    """
    if not isinstance(mesh, Mesh):
        raise TypeError("'mesh' must of type meshmode.mesh.Mesh,"
                        " not '%s'." % type(mesh))
    if group_nr is None:
        if len(mesh.groups) != 1:
            raise ValueError("'group_nr' is *None* but 'mesh' has "
                             "more than one group.")
        group_nr = 0
    if not isinstance(group_nr, int):
        raise TypeError("Expecting 'group_nr' to be of type int, not "
                        f"'{type(group_nr)}'")
    if group_nr < 0 or group_nr >= len(mesh.groups):
        raise ValueError("'group_nr' is an invalid group index:"
                         f" '{group_nr}' fails to satisfy "
                         f"0 <= {group_nr} < {len(mesh.groups)}")
    if not isinstance(mesh.groups[group_nr], SimplexElementGroup):
        raise TypeError("Expecting 'mesh.groups[group_nr]' to be of type "
                        "meshmode.mesh.SimplexElementGroup, not "
                        f"'{type(mesh.groups[group_nr])}'")
    if mesh.vertices is None:
        raise ValueError("'mesh' has no vertices "
                         "('mesh.vertices' is *None*)")
    if not mesh.is_conforming:
        raise ValueError(f"'mesh.is_conforming' is {mesh.is_conforming} "
                         "instead of *True*. Converting non-conforming "
                         " meshes to Firedrake is not supported")

    # Get the vertices and vertex indices of the requested group
    with ProcessLogger(logger, "Obtaining vertices from selected group"):
        group = mesh.groups[group_nr]
        fd2mm_indices = np.unique(group.vertex_indices.flatten())
        coords = mesh.vertices[:, fd2mm_indices].T
        mm2fd_indices = dict(zip(fd2mm_indices, np.arange(np.size(fd2mm_indices))))
        cells = np.vectorize(mm2fd_indices.__getitem__)(group.vertex_indices)

    # Get a dmplex object and then a mesh topology
    with ProcessLogger(logger, "Building dmplex object and MeshTopology"):
        if comm is None:
            from pyop2.mpi import COMM_WORLD
            comm = COMM_WORLD
        # FIXME : not sure how to get around the private accesses
        import firedrake.mesh as fd_mesh
        plex = fd_mesh._from_cell_list(group.dim, cells, coords, comm)
        # Nb : One might be tempted to pass reorder=False and thereby save some
        #      hassle in exchange for forcing firedrake to have slightly
        #      less efficient caching. Unfortunately, that only prevents
        #      the cells from being reordered, and does not prevent the
        #      vertices from being (locally) reordered on each cell...
        #      the tl;dr is we don't actually save any hassle
        top = fd_mesh.Mesh(plex, dim=mesh.ambient_dim)  # mesh topology
        top.init()

    # Get new element ordering:
    with ProcessLogger(logger, "Determining permutations applied"
                       " to local vertex numbers"):
        c_start, c_end = top._topology_dm.getHeightStratum(0)
        cell_index_mm2fd = np.vectorize(top._cell_numbering.getOffset)(
            np.arange(c_start, c_end))
        v_start, v_end = top._topology_dm.getDepthStratum(0)

        # Firedrake goes crazy reordering local vertex numbers,
        # we've got to work to figure out what changes they made.
        #
        # *perm2cells* will map permutations of local vertex numbers to
        #              the list of all the meshmode cells
        #              which firedrake reordered according to that permutation
        #
        #              Permutations on *n* vertices are stored as a tuple
        #              containing all of the integers *0*, *1*, *2*, ..., *n-1*
        #              exactly once. A permutation *p*
        #              represents relabeling the *i*\ th local vertex
        #              of a meshmode element as the *p[i]*\ th local vertex
        #              in the corresponding firedrake cell.
        #
        #              *perm2cells[p]* is a list of all the meshmode element indices
        #              for which *p* represents the reordering applied by firedrake
        perm2cells = {}
        for mm_cell_id, dmp_ids in enumerate(top.cell_closure[cell_index_mm2fd]):
            # look at order of vertices in firedrake cell
            vert_dmp_ids = \
                dmp_ids[np.logical_and(v_start <= dmp_ids, dmp_ids < v_end)]
            fdrake_order = vert_dmp_ids - v_start
            # get original order
            mm_order = mesh.groups[group_nr].vertex_indices[mm_cell_id]
            # want permutation p so that mm_order[p] = fdrake_order
            # To do so, look at permutations acting by composition.
            #
            # mm_order \circ argsort(mm_order) =
            #     fdrake_order \circ argsort(fdrake_order)
            # so
            # mm_order \circ argsort(mm_order) \circ inv(argsort(fdrake_order))
            #  = fdrake_order
            #
            # argsort acts as an inverse, so the desired permutation is:
            perm = tuple(np.argsort(mm_order)[np.argsort(np.argsort(fdrake_order))])
            perm2cells.setdefault(perm, [])
            perm2cells[perm].append(mm_cell_id)

        # Make perm2cells map to numpy arrays instead of lists
        perm2cells = {perm: np.array(cells)
                      for perm, cells in perm2cells.items()}

    # Now make a coordinates function
    with ProcessLogger(logger, "Building firedrake function "
                       "space for mesh coordinates"):
        from firedrake import VectorFunctionSpace, Function
        coords_fspace = VectorFunctionSpace(top, "CG", group.order,
                                            dim=mesh.ambient_dim)
        coords = Function(coords_fspace)

    # get firedrake unit nodes and map onto meshmode reference element
    fd_ref_cell_to_mm = get_affine_reference_simplex_mapping(group.dim, True)
    fd_unit_nodes = get_finat_element_unit_nodes(coords_fspace.finat_element)
    fd_unit_nodes = fd_ref_cell_to_mm(fd_unit_nodes)

    basis = simplex_best_available_basis(group.dim, group.order)
    resampling_mat = resampling_matrix(basis,
                                       new_nodes=fd_unit_nodes,
                                       old_nodes=group.unit_nodes)
    # Store the meshmode data resampled to firedrake unit nodes
    # (but still in meshmode order)
    resampled_group_nodes = np.matmul(group.nodes, resampling_mat.T)

    # Now put the nodes in the right local order
    # nodes is shaped *(ambient dim, nelements, nunit nodes)*
    with ProcessLogger(logger, "Storing meshmode mesh coordinates"
                       " in firedrake nodal order"):
        from meshmode.mesh.processing import get_simplex_element_flip_matrix
        for perm, cells in perm2cells.items():
            flip_mat = get_simplex_element_flip_matrix(group.order,
                                                       fd_unit_nodes,
                                                       perm)
            flip_mat = np.rint(flip_mat).astype(np.int32)
            resampled_group_nodes[:, cells, :] = \
                np.matmul(resampled_group_nodes[:, cells, :], flip_mat.T)

    # store resampled data in right cell ordering
    with ProcessLogger(logger, "resampling mesh coordinates to "
                       "firedrake unit nodes"):
        reordered_cell_node_list = coords_fspace.cell_node_list[cell_index_mm2fd]
        coords.dat.data[reordered_cell_node_list, :] = \
            resampled_group_nodes.transpose((1, 2, 0))

    return fd_mesh.Mesh(coords), cell_index_mm2fd, perm2cells
示例#20
0
def OneElementThickMesh(ncells, Lx, Ly, comm=COMM_WORLD):
    """
    Generate a rectangular mesh in the domain with corners [0,0]
    and [Lx, Ly] with ncells, that is periodic in the x-direction.

    :arg ncells: The number of cells in the mesh.
    :arg Lx: The width of the domain in the x-direction.
    :arg Ly: The width of the domain in the y-direction.
    :kwarg comm: Optional communicator to build the mesh on (defaults to
        COMM_WORLD).
    """

    left = np.arange(ncells, dtype='int32')
    right = np.roll(left, -1)
    cells = np.array([left, left, right, right]).T
    dx = Lx/ncells
    X = np.arange(1.0*ncells)*dx
    Y = 0.*X
    coords = np.array([X, Y]).T

    # a line of coordinates, with a looped topology
    plex = mesh._from_cell_list(2, cells, coords, comm)
    mesh1 = mesh.Mesh(plex)
    mesh1.topology.init()
    cell_numbering = mesh1._cell_numbering
    cell_range = plex.getHeightStratum(0)
    cell_closure = np.zeros((cell_range[1], 9), dtype=int)

    # Get the coordinates for this process
    coords = plex.getCoordinatesLocal().array_r
    # get the PETSc section
    coords_sec = plex.getCoordinateSection()

    for e in range(*cell_range):

        closure, orient = plex.getTransitiveClosure(e)

        # get the row for this cell
        row = cell_numbering.getOffset(e)

        # run some checks
        assert(closure[0] == e)
        assert len(closure) == 7, closure
        edge_range = plex.getHeightStratum(1)
        assert(all(closure[1:5] >= edge_range[0]))
        assert(all(closure[1:5] < edge_range[1]))
        vertex_range = plex.getHeightStratum(2)
        assert(all(closure[5:] >= vertex_range[0]))
        assert(all(closure[5:] < vertex_range[1]))

        # enter the cell number
        cell_closure[row][8] = e

        # Get a list of unique edges
        edge_set = list(set(closure[1:5]))

        # there are two vertices in the cell
        cell_vertices = closure[5:]
        cell_X = np.array([0., 0.])
        for i, v in enumerate(cell_vertices):
            cell_X[i] = coords[coords_sec.getOffset(v)]

        # Add in the edges
        for i in range(3):
            # count up how many times each edge is repeated
            repeats = list(closure[1:5]).count(edge_set[i])
            if repeats == 2:
                # we have a y-periodic edge
                cell_closure[row][6] = edge_set[i]
                cell_closure[row][7] = edge_set[i]
            elif repeats == 1:
                # in this code we check if it is a right edge, or a left edge
                # by inspecting the x coordinates of the edge vertex (1)
                # and comparing with the x coordinates of the cell vertices (2)

                # there is only one vertex on the edge in this case
                edge_vertex = plex.getCone(edge_set[i])[0]

                # get X coordinate for this edge
                edge_X = coords[coords_sec.getOffset(edge_vertex)]
                # get X coordinates for this cell
                if(cell_X.min() < dx/2):
                    if cell_X.max() < 3*dx/2:
                        # We are in the first cell
                        if(edge_X.min() < dx/2):
                            # we are on left hand edge
                            cell_closure[row][4] = edge_set[i]
                        else:
                            # we are on right hand edge
                            cell_closure[row][5] = edge_set[i]
                    else:
                        # We are in the last cell
                        if(edge_X.min() < dx/2):
                            # we are on right hand edge
                            cell_closure[row][5] = edge_set[i]
                        else:
                            # we are on left hand edge
                            cell_closure[row][4] = edge_set[i]
                else:
                    if(abs(cell_X.min()-edge_X.min()) < dx/2):
                        # we are on left hand edge
                        cell_closure[row][4] = edge_set[i]
                    else:
                        # we are on right hand edge
                        cell_closure[row][5] = edge_set[i]

        # Add in the vertices
        vertices = closure[5:]
        v1 = vertices[0]
        v2 = vertices[1]
        x1 = coords[coords_sec.getOffset(v1)]
        x2 = coords[coords_sec.getOffset(v2)]
        # Fix orientations
        if(x1 > x2):
            if(x1 - x2 < dx*1.5):
                # we are not on the rightmost cell and need to swap
                v1, v2 = v2, v1
        elif(x2 - x1 > dx*1.5):
            # we are on the rightmost cell and need to swap
            v1, v2 = v2, v1

        cell_closure[row][0:4] = [v1, v1, v2, v2]

    mesh1.topology.cell_closure = np.array(cell_closure, dtype=np.int32)

    mesh1.init()

    Vc = VectorFunctionSpace(mesh1, 'DQ', 1)
    fc = Function(Vc).interpolate(mesh1.coordinates)

    mash = mesh.Mesh(fc)
    topverts = Vc.cell_node_list[:, 1::2].flatten()
    mash.coordinates.dat.data_with_halos[topverts, 1] = Ly

    # search for the last cell
    mcoords_ro = mash.coordinates.dat.data_ro_with_halos
    mcoords = mash.coordinates.dat.data_with_halos
    for e in range(*cell_range):
        cell = cell_numbering.getOffset(e)
        cell_nodes = Vc.cell_node_list[cell, :]
        Xvals = mcoords_ro[cell_nodes, 0]
        if(Xvals.max() - Xvals.min() > Lx/2):
            mcoords[cell_nodes[2:], 0] = Lx
        else:
            mcoords

    local_facet_dat = mash.topology.interior_facets.local_facet_dat
    local_facet_number = mash.topology.interior_facets.local_facet_number

    lfd_ro = local_facet_dat.data_ro
    for i in range(lfd_ro.shape[0]):
        if all(lfd_ro[i, :] == np.array([3, 3])):
            local_facet_dat.data[i, :] = [2, 3]
            local_facet_number[i, :] = [2, 3]

    return mash
示例#21
0
def CylinderMesh(nr, nl, radius=1, depth=1, longitudinal_direction="z",
                 quadrilateral=False, reorder=None, comm=COMM_WORLD):
    """Generates a cylinder mesh.

    :arg nr: number of cells the cylinder circumference should be
         divided into (min 3)
    :arg nl: number of cells along the longitudinal axis of the cylinder
    :kwarg radius: (optional) radius of the cylinder to approximate
         (default 1).
    :kwarg depth: (optional) depth of the cylinder to approximate
         (default 1).
    :kwarg longitudinal_direction: (option) direction for the
         longitudinal axis of the cylinder.
    :kwarg quadrilateral: (optional), creates quadrilateral mesh, defaults to False
    :kwarg comm: Optional communicator to build the mesh on (defaults to
        COMM_WORLD).

    The boundary edges in this mesh are numbered as follows:

    * 1: plane l == 0 (bottom)
    * 2: plane l == depth (top)
    """
    if nr < 3:
        raise ValueError("CylinderMesh must have at least three cells")

    coord_xy = radius*np.column_stack((np.cos(np.arange(nr)*(2*np.pi/nr)),
                                       np.sin(np.arange(nr)*(2*np.pi/nr))))
    coord_z = depth*np.linspace(0.0, 1.0, nl + 1).reshape(-1, 1)
    vertices = np.asarray(np.column_stack((np.tile(coord_xy, (nl + 1, 1)),
                                           np.tile(coord_z, (1, nr)).reshape(-1, 1))),
                          dtype=np.double)

    # intervals on circumference
    ring_cells = np.column_stack((np.arange(0, nr, dtype=np.int32),
                                  np.roll(np.arange(0, nr, dtype=np.int32), -1)))
    # quads in the first layer
    ring_cells = np.column_stack((ring_cells, np.roll(ring_cells, 1, axis=1) + nr))
    offset = np.arange(nl, dtype=np.int32)*nr
    cells = np.row_stack((ring_cells + i for i in offset))
    if not quadrilateral:
        # two cells per cell above...
        cells = cells[:, [0, 1, 3, 1, 2, 3]].reshape(-1, 3)

    if longitudinal_direction == "x":
        rotation = np.asarray([[0, 0, 1],
                               [0, 1, 0],
                               [-1, 0, 0]], dtype=np.double)
        vertices = np.dot(vertices, rotation.T)
    elif longitudinal_direction == "y":
        rotation = np.asarray([[1, 0, 0],
                               [0, 0, 1],
                               [0, -1, 0]], dtype=np.double)
        vertices = np.dot(vertices, rotation.T)
    elif longitudinal_direction != "z":
        raise ValueError("Unknown longitudinal direction '%s'" % longitudinal_direction)
    plex = mesh._from_cell_list(2, cells, vertices, comm)

    plex.createLabel("boundary_ids")
    plex.markBoundaryFaces("boundary_faces")
    coords = plex.getCoordinates()
    coord_sec = plex.getCoordinateSection()
    if plex.getStratumSize("boundary_faces", 1) > 0:
        boundary_faces = plex.getStratumIS("boundary_faces", 1).getIndices()
        eps = depth/(2*nl)
        for face in boundary_faces:
            face_coords = plex.vecGetClosure(coord_sec, coords, face)
            # index of x/y/z coordinates of the face element
            axis_ix = {"x": 0, "y": 1, "z": 2}
            i = axis_ix[longitudinal_direction]
            j = i + 3
            if abs(face_coords[i]) < eps and abs(face_coords[j]) < eps:
                # bottom of cylinder
                plex.setLabelValue("boundary_ids", face, 1)
            if abs(face_coords[i] - depth) < eps and abs(face_coords[j] - depth) < eps:
                # top of cylinder
                plex.setLabelValue("boundary_ids", face, 2)

    m = mesh.Mesh(plex, dim=3, reorder=reorder)
    return m
示例#22
0
def CubedSphereMesh(radius, refinement_level=0, degree=1,
                    reorder=None, use_dmplex_refinement=False,
                    comm=COMM_WORLD):
    """Generate an cubed approximation to the surface of the
    sphere.

    :arg radius: The radius of the sphere to approximate.
    :kwarg refinement_level: optional number of refinements (0 is a cube).
    :kwarg degree: polynomial degree of coordinate space (defaults
        to 1: bilinear quads)
    :kwarg reorder: (optional), should the mesh be reordered?
    :kwarg use_dmplex_refinement: (optional), use dmplex to apply
        the refinement.
    """
    if refinement_level < 0 or refinement_level % 1:
            raise RuntimeError("Number of refinements must be a non-negative integer")

    if degree < 1:
        raise ValueError("Mesh coordinate degree must be at least 1")

    if use_dmplex_refinement:
        # vertices of a cube with an edge length of 2
        vertices = np.array([[-1., -1., -1.],
                             [1., -1., -1.],
                             [-1., 1., -1.],
                             [1., 1., -1.],
                             [-1., -1., 1.],
                             [1., -1., 1.],
                             [-1., 1., 1.],
                             [1., 1., 1.]])
        # faces of the base cube
        # bottom face viewed from above
        # 2 3
        # 0 1
        # top face viewed from above
        # 6 7
        # 4 5
        faces = np.array([[0, 1, 3, 2],  # bottom
                          [4, 5, 7, 6],  # top
                          [0, 1, 5, 4],
                          [2, 3, 7, 6],
                          [0, 2, 6, 4],
                          [1, 3, 7, 5]], dtype=np.int32)

        plex = mesh._from_cell_list(2, faces, vertices, comm)
        plex.setRefinementUniform(True)
        for i in range(refinement_level):
            plex = plex.refine()

        # rescale points to the sphere
        # this is not the same as the gnonomic transformation
        coords = plex.getCoordinatesLocal().array.reshape(-1, 3)
        scale = (radius / np.linalg.norm(coords, axis=1)).reshape(-1, 1)
        coords *= scale
    else:
        cells, coords = _cubedsphere_cells_and_coords(radius, refinement_level)
        plex = mesh._from_cell_list(2, cells, coords, comm)

    m = mesh.Mesh(plex, dim=3, reorder=reorder)

    if degree > 1:
        new_coords = function.Function(functionspace.VectorFunctionSpace(m, "Q", degree))
        new_coords.interpolate(expression.Expression(("x[0]", "x[1]", "x[2]")))
        # "push out" to sphere
        new_coords.dat.data[:] *= (radius / np.linalg.norm(new_coords.dat.data, axis=1)).reshape(-1, 1)
        m = mesh.Mesh(new_coords)

    return m
示例#23
0
def RectangleMesh(nx, ny, Lx, Ly, quadrilateral=False, reorder=None,
                  diagonal="left", comm=COMM_WORLD):
    """Generate a rectangular mesh

    :arg nx: The number of cells in the x direction
    :arg ny: The number of cells in the y direction
    :arg Lx: The extent in the x direction
    :arg Ly: The extent in the y direction
    :kwarg quadrilateral: (optional), creates quadrilateral mesh, defaults to False
    :kwarg reorder: (optional), should the mesh be reordered
    :kwarg comm: Optional communicator to build the mesh on (defaults to
        COMM_WORLD).
    :kwarg diagonal: For triangular meshes, should the diagonal got
        from bottom left to top right (``"right"``), or top left to
        bottom right (``"left"``).

    The boundary edges in this mesh are numbered as follows:

    * 1: plane x == 0
    * 2: plane x == Lx
    * 3: plane y == 0
    * 4: plane y == Ly
    """

    for n in (nx, ny):
        if n <= 0 or n % 1:
            raise ValueError("Number of cells must be a postive integer")

    xcoords = np.linspace(0.0, Lx, nx + 1, dtype=np.double)
    ycoords = np.linspace(0.0, Ly, ny + 1, dtype=np.double)
    coords = np.asarray(np.meshgrid(xcoords, ycoords)).swapaxes(0, 2).reshape(-1, 2)

    # cell vertices
    i, j = np.meshgrid(np.arange(nx, dtype=np.int32), np.arange(ny, dtype=np.int32))
    cells = [i*(ny+1) + j, i*(ny+1) + j+1, (i+1)*(ny+1) + j+1, (i+1)*(ny+1) + j]
    cells = np.asarray(cells).swapaxes(0, 2).reshape(-1, 4)
    if not quadrilateral:
        if diagonal == "left":
            idx = [0, 1, 3, 1, 2, 3]
        elif diagonal == "right":
            idx = [0, 1, 2, 0, 2, 3]
        else:
            raise ValueError("Unrecognised value for diagonal '%r'", diagonal)
        # two cells per cell above...
        cells = cells[:, idx].reshape(-1, 3)

    plex = mesh._from_cell_list(2, cells, coords, comm)

    # mark boundary facets
    plex.createLabel("boundary_ids")
    plex.markBoundaryFaces("boundary_faces")
    coords = plex.getCoordinates()
    coord_sec = plex.getCoordinateSection()
    if plex.getStratumSize("boundary_faces", 1) > 0:
        boundary_faces = plex.getStratumIS("boundary_faces", 1).getIndices()
        xtol = Lx/(2*nx)
        ytol = Ly/(2*ny)
        for face in boundary_faces:
            face_coords = plex.vecGetClosure(coord_sec, coords, face)
            if abs(face_coords[0]) < xtol and abs(face_coords[2]) < xtol:
                plex.setLabelValue("boundary_ids", face, 1)
            if abs(face_coords[0] - Lx) < xtol and abs(face_coords[2] - Lx) < xtol:
                plex.setLabelValue("boundary_ids", face, 2)
            if abs(face_coords[1]) < ytol and abs(face_coords[3]) < ytol:
                plex.setLabelValue("boundary_ids", face, 3)
            if abs(face_coords[1] - Ly) < ytol and abs(face_coords[3] - Ly) < ytol:
                plex.setLabelValue("boundary_ids", face, 4)

    return mesh.Mesh(plex, reorder=reorder)
示例#24
0
def OneElementThickMesh(ncells, Lx, Ly, comm=COMM_WORLD):
    """
    Generate a rectangular mesh in the domain with corners [0,0]
    and [Lx, Ly] with ncells, that is periodic in the x-direction.

    :arg ncells: The number of cells in the mesh.
    :arg Lx: The width of the domain in the x-direction.
    :arg Ly: The width of the domain in the y-direction.
    :kwarg comm: Optional communicator to build the mesh on (defaults to
        COMM_WORLD).
    """

    left = np.arange(ncells, dtype=np.int32)
    right = np.roll(left, -1)
    cells = np.array([left, left, right, right]).T
    dx = Lx/ncells
    X = np.arange(1.0*ncells, dtype=np.double)*dx
    Y = 0.*X
    coords = np.array([X, Y]).T

    # a line of coordinates, with a looped topology
    plex = mesh._from_cell_list(2, cells, coords, comm)
    mesh1 = mesh.Mesh(plex)
    mesh1.topology.init()
    cell_numbering = mesh1._cell_numbering
    cell_range = plex.getHeightStratum(0)
    cell_closure = np.zeros((cell_range[1], 9), dtype=IntType)

    # Get the coordinates for this process
    coords = plex.getCoordinatesLocal().array_r
    # get the PETSc section
    coords_sec = plex.getCoordinateSection()

    for e in range(*cell_range):

        closure, orient = plex.getTransitiveClosure(e)

        # get the row for this cell
        row = cell_numbering.getOffset(e)

        # run some checks
        assert(closure[0] == e)
        assert len(closure) == 7, closure
        edge_range = plex.getHeightStratum(1)
        assert(all(closure[1:5] >= edge_range[0]))
        assert(all(closure[1:5] < edge_range[1]))
        vertex_range = plex.getHeightStratum(2)
        assert(all(closure[5:] >= vertex_range[0]))
        assert(all(closure[5:] < vertex_range[1]))

        # enter the cell number
        cell_closure[row][8] = e

        # Get a list of unique edges
        edge_set = list(set(closure[1:5]))

        # there are two vertices in the cell
        cell_vertices = closure[5:]
        cell_X = np.array([0., 0.])
        for i, v in enumerate(cell_vertices):
            cell_X[i] = coords[coords_sec.getOffset(v)]

        # Add in the edges
        for i in range(3):
            # count up how many times each edge is repeated
            repeats = list(closure[1:5]).count(edge_set[i])
            if repeats == 2:
                # we have a y-periodic edge
                cell_closure[row][6] = edge_set[i]
                cell_closure[row][7] = edge_set[i]
            elif repeats == 1:
                # in this code we check if it is a right edge, or a left edge
                # by inspecting the x coordinates of the edge vertex (1)
                # and comparing with the x coordinates of the cell vertices (2)

                # there is only one vertex on the edge in this case
                edge_vertex = plex.getCone(edge_set[i])[0]

                # get X coordinate for this edge
                edge_X = coords[coords_sec.getOffset(edge_vertex)]
                # get X coordinates for this cell
                if(cell_X.min() < dx/2):
                    if cell_X.max() < 3*dx/2:
                        # We are in the first cell
                        if(edge_X.min() < dx/2):
                            # we are on left hand edge
                            cell_closure[row][4] = edge_set[i]
                        else:
                            # we are on right hand edge
                            cell_closure[row][5] = edge_set[i]
                    else:
                        # We are in the last cell
                        if(edge_X.min() < dx/2):
                            # we are on right hand edge
                            cell_closure[row][5] = edge_set[i]
                        else:
                            # we are on left hand edge
                            cell_closure[row][4] = edge_set[i]
                else:
                    if(abs(cell_X.min()-edge_X.min()) < dx/2):
                        # we are on left hand edge
                        cell_closure[row][4] = edge_set[i]
                    else:
                        # we are on right hand edge
                        cell_closure[row][5] = edge_set[i]

        # Add in the vertices
        vertices = closure[5:]
        v1 = vertices[0]
        v2 = vertices[1]
        x1 = coords[coords_sec.getOffset(v1)]
        x2 = coords[coords_sec.getOffset(v2)]
        # Fix orientations
        if(x1 > x2):
            if(x1 - x2 < dx*1.5):
                # we are not on the rightmost cell and need to swap
                v1, v2 = v2, v1
        elif(x2 - x1 > dx*1.5):
            # we are on the rightmost cell and need to swap
            v1, v2 = v2, v1

        cell_closure[row][0:4] = [v1, v1, v2, v2]

    mesh1.topology.cell_closure = np.array(cell_closure, dtype=IntType)

    mesh1.init()

    Vc = VectorFunctionSpace(mesh1, 'DQ', 1)
    fc = Function(Vc).interpolate(mesh1.coordinates)

    mash = mesh.Mesh(fc)
    topverts = Vc.cell_node_list[:, 1::2].flatten()
    mash.coordinates.dat.data_with_halos[topverts, 1] = Ly

    # search for the last cell
    mcoords_ro = mash.coordinates.dat.data_ro_with_halos
    mcoords = mash.coordinates.dat.data_with_halos
    for e in range(*cell_range):
        cell = cell_numbering.getOffset(e)
        cell_nodes = Vc.cell_node_list[cell, :]
        Xvals = mcoords_ro[cell_nodes, 0]
        if(Xvals.max() - Xvals.min() > Lx/2):
            mcoords[cell_nodes[2:], 0] = Lx
        else:
            mcoords

    local_facet_dat = mash.topology.interior_facets.local_facet_dat
    local_facet_number = mash.topology.interior_facets.local_facet_number

    lfd_ro = local_facet_dat.data_ro
    for i in range(lfd_ro.shape[0]):
        if all(lfd_ro[i, :] == np.array([3, 3])):
            local_facet_dat.data[i, :] = [2, 3]
            local_facet_number[i, :] = [2, 3]

    return mash
示例#25
0
def CylinderMesh(nr, nl, radius=1, depth=1, longitudinal_direction="z",
                 quadrilateral=False, reorder=None, comm=COMM_WORLD):
    """Generates a cylinder mesh.

    :arg nr: number of cells the cylinder circumference should be
         divided into (min 3)
    :arg nl: number of cells along the longitudinal axis of the cylinder
    :kwarg radius: (optional) radius of the cylinder to approximate
         (default 1).
    :kwarg depth: (optional) depth of the cylinder to approximate
         (default 1).
    :kwarg longitudinal_direction: (option) direction for the
         longitudinal axis of the cylinder.
    :kwarg quadrilateral: (optional), creates quadrilateral mesh, defaults to False
    :kwarg comm: Optional communicator to build the mesh on (defaults to
        COMM_WORLD).

    The boundary edges in this mesh are numbered as follows:

    * 1: plane l == 0 (bottom)
    * 2: plane l == depth (top)
    """
    if nr < 3:
        raise ValueError("CylinderMesh must have at least three cells")

    coord_xy = radius*np.column_stack((np.cos(np.arange(nr)*(2*np.pi/nr)),
                                       np.sin(np.arange(nr)*(2*np.pi/nr))))
    coord_z = depth*np.linspace(0.0, 1.0, nl + 1).reshape(-1, 1)
    vertices = np.column_stack((np.tile(coord_xy, (nl + 1, 1)),
                                np.tile(coord_z, (1, nr)).reshape(-1, 1)))

    # intervals on circumference
    ring_cells = np.column_stack((np.arange(0, nr, dtype=np.int32),
                                  np.roll(np.arange(0, nr, dtype=np.int32), -1)))
    # quads in the first layer
    ring_cells = np.column_stack((ring_cells, np.roll(ring_cells, 1, axis=1) + nr))
    offset = np.arange(nl)*nr
    cells = np.row_stack((ring_cells + i for i in offset))
    if not quadrilateral:
        # two cells per cell above...
        cells = cells[:, [0, 1, 3, 1, 2, 3]].reshape(-1, 3)

    if longitudinal_direction == "x":
        rotation = np.asarray([[0, 0, 1],
                               [0, 1, 0],
                               [-1, 0, 0]])
        vertices = np.dot(vertices, rotation.T)
    elif longitudinal_direction == "y":
        rotation = np.asarray([[1, 0, 0],
                               [0, 0, 1],
                               [0, -1, 0]])
        vertices = np.dot(vertices, rotation.T)
    elif longitudinal_direction != "z":
        raise ValueError("Unknown longitudinal direction '%s'" % longitudinal_direction)
    plex = mesh._from_cell_list(2, cells, vertices, comm)

    plex.createLabel("boundary_ids")
    plex.markBoundaryFaces("boundary_faces")
    coords = plex.getCoordinates()
    coord_sec = plex.getCoordinateSection()
    if plex.getStratumSize("boundary_faces", 1) > 0:
        boundary_faces = plex.getStratumIS("boundary_faces", 1).getIndices()
        eps = float(depth)/(2*nl)
        for face in boundary_faces:
            face_coords = plex.vecGetClosure(coord_sec, coords, face)
            # index of x/y/z coordinates of the face element
            axis_ix = {"x": 0, "y": 1, "z": 2}
            i = axis_ix[longitudinal_direction]
            j = i + 3
            if abs(face_coords[i]) < eps and abs(face_coords[j]) < eps:
                # bottom of cylinder
                plex.setLabelValue("boundary_ids", face, 1)
            if abs(face_coords[i] - depth) < eps and abs(face_coords[j] - depth) < eps:
                # top of cylinder
                plex.setLabelValue("boundary_ids", face, 2)

    m = mesh.Mesh(plex, dim=3, reorder=reorder)
    return m
示例#26
0
def BoxMesh(nx, ny, nz, Lx, Ly, Lz, reorder=None, comm=COMM_WORLD):
    """Generate a mesh of a 3D box.

    :arg nx: The number of cells in the x direction
    :arg ny: The number of cells in the y direction
    :arg nz: The number of cells in the z direction
    :arg Lx: The extent in the x direction
    :arg Ly: The extent in the y direction
    :arg Lz: The extent in the z direction
    :kwarg reorder: (optional), should the mesh be reordered?
    :kwarg comm: Optional communicator to build the mesh on (defaults to
        COMM_WORLD).

    The boundary surfaces are numbered as follows:

    * 1: plane x == 0
    * 2: plane x == Lx
    * 3: plane y == 0
    * 4: plane y == Ly
    * 5: plane z == 0
    * 6: plane z == Lz
    """
    for n in (nx, ny, nz):
        if n <= 0 or n % 1:
            raise ValueError("Number of cells must be a postive integer")

    xcoords = np.linspace(0, Lx, nx + 1, dtype=np.double)
    ycoords = np.linspace(0, Ly, ny + 1, dtype=np.double)
    zcoords = np.linspace(0, Lz, nz + 1, dtype=np.double)
    # X moves fastest, then Y, then Z
    coords = np.asarray(np.meshgrid(xcoords, ycoords, zcoords)).swapaxes(0, 3).reshape(-1, 3)
    i, j, k = np.meshgrid(np.arange(nx, dtype=np.int32),
                          np.arange(ny, dtype=np.int32),
                          np.arange(nz, dtype=np.int32))
    v0 = k*(nx + 1)*(ny + 1) + j*(nx + 1) + i
    v1 = v0 + 1
    v2 = v0 + (nx + 1)
    v3 = v1 + (nx + 1)
    v4 = v0 + (nx + 1)*(ny + 1)
    v5 = v1 + (nx + 1)*(ny + 1)
    v6 = v2 + (nx + 1)*(ny + 1)
    v7 = v3 + (nx + 1)*(ny + 1)

    cells = [v0, v1, v3, v7,
             v0, v1, v7, v5,
             v0, v5, v7, v4,
             v0, v3, v2, v7,
             v0, v6, v4, v7,
             v0, v2, v6, v7]
    cells = np.asarray(cells).swapaxes(0, 3).reshape(-1, 4)

    plex = mesh._from_cell_list(3, cells, coords, comm)

    # Apply boundary IDs
    plex.createLabel("boundary_ids")
    plex.markBoundaryFaces("boundary_faces")
    coords = plex.getCoordinates()
    coord_sec = plex.getCoordinateSection()
    if plex.getStratumSize("boundary_faces", 1) > 0:
        boundary_faces = plex.getStratumIS("boundary_faces", 1).getIndices()
        xtol = Lx/(2*nx)
        ytol = Ly/(2*ny)
        ztol = Lz/(2*nz)
        for face in boundary_faces:
            face_coords = plex.vecGetClosure(coord_sec, coords, face)
            if abs(face_coords[0]) < xtol and abs(face_coords[3]) < xtol and abs(face_coords[6]) < xtol:
                plex.setLabelValue("boundary_ids", face, 1)
            if abs(face_coords[0] - Lx) < xtol and abs(face_coords[3] - Lx) < xtol and abs(face_coords[6] - Lx) < xtol:
                plex.setLabelValue("boundary_ids", face, 2)
            if abs(face_coords[1]) < ytol and abs(face_coords[4]) < ytol and abs(face_coords[7]) < ytol:
                plex.setLabelValue("boundary_ids", face, 3)
            if abs(face_coords[1] - Ly) < ytol and abs(face_coords[4] - Ly) < ytol and abs(face_coords[7] - Ly) < ytol:
                plex.setLabelValue("boundary_ids", face, 4)
            if abs(face_coords[2]) < ztol and abs(face_coords[5]) < ztol and abs(face_coords[8]) < ztol:
                plex.setLabelValue("boundary_ids", face, 5)
            if abs(face_coords[2] - Lz) < ztol and abs(face_coords[5] - Lz) < ztol and abs(face_coords[8] - Lz) < ztol:
                plex.setLabelValue("boundary_ids", face, 6)

    return mesh.Mesh(plex, reorder=reorder)