Skip to content

Commit eece44d

Browse files
committed
Big cleanup of simplex closure ordering code
Ideally this will all get ripped out or moved if #3332 goes anywhere. For now I have simply tried to minimise the footprint of the niche reordering code. This has entailed rewriting a fair bit of the algorithm and it is a bit slower since we have to undo some renumbering and this requires Python, not Cython. Tests appear to be passing with segfaulting.
1 parent cb6fa59 commit eece44d

File tree

2 files changed

+302
-328
lines changed

2 files changed

+302
-328
lines changed

firedrake/cython/dmcommon.pyx

+149-167
Original file line numberDiff line numberDiff line change
@@ -567,186 +567,168 @@ def create_cell_closure(PETSc.DM dm,
567567

568568
@cython.boundscheck(False)
569569
@cython.wraparound(False)
570-
def closure_ordering(mesh,
571-
np.ndarray[PetscInt, ndim=2, mode="c"] closure_data,
572-
PETSc.Section vertex_numbering,
573-
np.ndarray[PetscInt, ndim=1, mode="c"] entity_per_cell):
574-
"""Apply Fenics local numbering to a cell closure.
570+
def closure_ordering(mesh, closure_data, closure_sizes):
571+
"""Apply FEniCS local numbering to a cell closure.
575572
576-
:arg dm: The DM object encapsulating the mesh topology
577-
:arg vertex_numbering: Section describing the universal vertex numbering
578-
:arg cell_numbering: Section describing the global cell numbering
579-
:arg entity_per_cell: List of the number of entity points in each dimension
573+
The reordering is achieved by ordering vertices according to their global
574+
number and by ordering edges and facets according to a lexicographical
575+
order of the non-incident vertices.
576+
577+
Parameters
578+
----------
579+
mesh : MeshTopology
580+
The mesh providing the closures.
581+
closure_data : tuple
582+
Tuple of arrays storing cell closure information per dimension.
583+
closure_sizes : tuple
584+
The number of points in the cell closure per dimension.
580585
581-
Vertices := Ordered according to global/universal
582-
vertex numbering
583-
Edges/faces := Ordered according to lexicographical
584-
ordering of non-incident vertices
585586
"""
586587
cdef:
587-
PetscInt c, cStart, cEnd, v, vStart, vEnd
588-
PetscInt f, fStart, fEnd, e, eStart, eEnd
589-
PetscInt dim, vi, ci, fi, v_per_cell, f_per_cell, cell
590-
PetscInt offset, cell_offset, nfaces, nfacets
591-
PetscInt nclosure, nfacet_closure, nface_vertices
592-
PetscInt *vertices = NULL
593-
PetscInt *v_global = NULL
594-
# PetscInt *closure = NULL
595-
np.ndarray[PetscInt, ndim=1, mode="c"] closure
596-
PetscInt *facets = NULL
597-
PetscInt *faces = NULL
598-
PetscInt *face_indices = NULL
599-
const PetscInt *face_vertices = NULL
600-
PetscInt *facet_vertices = NULL
601-
np.ndarray[PetscInt, ndim=2, mode="c"] cell_closure
602-
603-
PETSc.DM dm = mesh.topology_dm
604-
605-
if vertex_numbering is not None:
606-
raise NotImplementedError("TODO, see comments in mesh._closure_map")
607-
608-
dim = get_topological_dimension(dm)
609-
get_height_stratum(dm.dm, 0, &cStart, &cEnd)
610-
get_height_stratum(dm.dm, 1, &fStart, &fEnd)
611-
get_depth_stratum(dm.dm, 1, &eStart, &eEnd)
612-
get_depth_stratum(dm.dm, 0, &vStart, &vEnd)
613-
614-
v_per_cell = entity_per_cell[0]
615-
if len(entity_per_cell) > 1:
616-
f_per_cell = entity_per_cell[1]
617-
else:
618-
f_per_cell = 0
619-
620-
cell_offset = sum(entity_per_cell) - 1
621-
622-
CHKERR(PetscMalloc1(v_per_cell, &vertices))
623-
CHKERR(PetscMalloc1(v_per_cell, &v_global))
624-
CHKERR(PetscMalloc1(v_per_cell, &facets))
625-
if v_per_cell > 0:
626-
CHKERR(PetscMalloc1(v_per_cell-1, &facet_vertices))
627-
if len(entity_per_cell) > 1:
628-
CHKERR(PetscMalloc1(f_per_cell, &faces))
629-
CHKERR(PetscMalloc1(f_per_cell, &face_indices))
630-
cell_closure = np.empty((cEnd - cStart, sum(entity_per_cell)), dtype=IntType)
588+
PETSc.DM dm
589+
PetscInt tdim, cell
590+
PetscInt nverts_per_cell, nedges_per_cell, nfacets_per_cell
591+
PetscInt *verts=NULL, *edges=NULL, *facets=NULL, *global_verts=NULL
592+
PetscInt *edge_cone=NULL, *edge_verts=NULL, *edge_incident_verts=NULL
593+
PetscInt nfacet_closure, *facet_closure=NULL, *facet_verts=NULL
631594

632-
for c in range(cStart, cEnd):
633-
# renumbering already complete
634-
# CHKERR(PetscSectionGetOffset(cell_numbering.sec, c, &cell))
635-
cell_dim, cell = mesh.points.axis_to_component_number(c)
636-
assert int(cell_dim.label) == dim
637-
638-
# closure already packed
639-
# get_transitive_closure(dm.dm, c, PETSC_TRUE, &nclosure, &closure)
640-
# NOTE: This closure omits orientations, so the factor of 2 can be omitted
641-
closure = closure_data[cell, :]
642-
643-
# Find vertices and translate universal numbers
644-
nverts = entity_per_cell[0]
645-
for i in range(nverts):
646-
vertices[i] = closure[i]
595+
dm = mesh.topology_dm
596+
tdim = mesh.dimension
597+
assert tdim <= 3
598+
599+
nverts_per_cell = closure_sizes[0]
600+
nedges_per_cell = closure_sizes[1] if tdim >= 1 else 0
601+
nfacets_per_cell = closure_sizes[tdim-1] if tdim >= 1 else 0
602+
603+
CHKERR(PetscMalloc1(nverts_per_cell, &verts))
604+
CHKERR(PetscMalloc1(nedges_per_cell, &edges))
605+
CHKERR(PetscMalloc1(nfacets_per_cell, &facets))
606+
CHKERR(PetscMalloc1(nverts_per_cell, &global_verts))
607+
608+
CHKERR(PetscMalloc1(2, &edge_verts))
609+
CHKERR(PetscMalloc1(nedges_per_cell, &edge_incident_verts))
610+
611+
# upper bound
612+
CHKERR(PetscMalloc1(nverts_per_cell, &facet_verts))
613+
614+
closure_data_reord = tuple(np.empty_like(d) for d in closure_data)
615+
for cell in range(mesh.num_cells()):
616+
# 1. Order vertices
617+
for vi, vert in enumerate(closure_data[0][cell]):
618+
verts[vi] = vert
619+
global_verts[vi] = mesh._global_vertex_numbering[vert]
647620

648-
if dm.comm.size > 1:
649-
raise NotImplementedError
650-
# TODO for now assume that we are serial and that the local and
651-
# global numbers match
652-
for i in range(nverts):
653-
v_global[i] = vertices[i]
654-
655-
# Sort vertices by universal number
656-
CHKERR(PetscSortIntWithArray(v_per_cell,v_global,vertices))
657-
for vi in range(v_per_cell):
658-
if dim == 1:
659-
# Correct 1D edge numbering
660-
cell_closure[cell, vi] = vertices[v_per_cell-vi-1]
661-
else:
662-
cell_closure[cell, vi] = vertices[vi]
663-
offset = v_per_cell
664-
665-
# Find all edges (dim=1) (only relevant for `DMPlex`)
666-
if dim > 2:
667-
raise NotImplementedError("think about renumbered cones")
668-
assert isinstance(dm, PETSc.DMPlex)
669-
nfaces = 0
670-
for ci in range(nclosure):
671-
if eStart <= closure[ci] < eEnd:
672-
faces[nfaces] = closure[ci]
673-
674-
CHKERR(DMPlexGetConeSize(dm.dm, closure[ci],
675-
&nface_vertices))
676-
CHKERR(DMPlexGetCone(dm.dm, closure[ci],
677-
&face_vertices))
678-
679-
# Edges in 3D are tricky because we need a
680-
# lexicographical sort with two keys (the local
681-
# numbers of the two non-incident vertices).
682-
683-
# Find non-incident vertices
684-
fi = 0
685-
face_indices[nfaces] = 0
686-
for v in range(v_per_cell):
687-
incident = 0
688-
for vi in range(nface_vertices):
689-
if cell_closure[cell,v] == face_vertices[vi]:
690-
incident = 1
691-
break
692-
if incident == 0:
693-
face_indices[nfaces] += v * 10**(1-fi)
694-
fi += 1
695-
nfaces += 1
621+
# Sort vertices by their global number
622+
CHKERR(PetscSortIntWithArray(nverts_per_cell, global_verts, verts))
623+
624+
# Insert into the closure
625+
for vi in range(nverts_per_cell):
626+
# Correct 1D edge numbering
627+
vert = verts[vi] if tdim != 1 else verts[nverts_per_cell-vi-1]
628+
closure_data_reord[0][cell, vi] = vert
629+
630+
# 2. Order edges (3D only, in 2D facets and edges are equivalent)
631+
if tdim > 2:
632+
# Edges are tricky because we need a lexicographical sort with two
633+
# keys (the local numbers of the two non-incident vertices)
634+
for ei, edge in enumerate(closure_data[1][cell]):
635+
edges[ei] = edge
636+
637+
# Convert the edge, which is in stratum-local, renumbered form
638+
# back to what plex expects
639+
edge_unrenum = mesh.points.applied_to_default_component_number(
640+
mesh.edge_label, edge
641+
)
642+
edge_pt = mesh.points.component_to_axis_number(
643+
mesh.edge_label, edge_unrenum
644+
)
645+
646+
# Collect incident vertices
647+
CHKERR(DMPlexGetCone(dm.dm, edge_pt, &edge_cone))
648+
for i in range(2):
649+
pt = edge_cone[i]
650+
stratum, stratum_pt = mesh.points.axis_to_component_number(pt)
651+
stratum_pt_renum = mesh.points.default_to_applied_component_number(
652+
stratum, stratum_pt
653+
)
654+
edge_verts[i] = stratum_pt_renum
655+
656+
# Find non-incident vertices and store lexicographically
657+
edge_incident_verts[ei] = 0
658+
ptr = 0
659+
for vi in range(nverts_per_cell):
660+
incident = False
661+
for vj in range(2):
662+
if edge_verts[vj] == closure_data[0][cell, vi]:
663+
incident = True
664+
break
665+
if not incident:
666+
edge_incident_verts[ei] += vi * 10**(1-ptr)
667+
ptr += 1
696668

697669
# Sort by local numbers of non-incident vertices
698-
CHKERR(PetscSortIntWithArray(f_per_cell,
699-
face_indices, faces))
700-
for fi in range(nfaces):
701-
cell_closure[cell, offset+fi] = faces[fi]
702-
offset += nfaces
703-
704-
# Find all facets (co-dim=1)
705-
nfacets = entity_per_cell[dim-1]
706-
for i in range(nfacets):
707-
# add nverts to skip those, bad for 3D
708-
facets[i] = closure[nverts+i]
709-
710-
# The cell itself is always the first entry in the Plex closure
711-
cell_closure[cell, cell_offset] = closure[0]
712-
713-
# Now we can deal with facets (only relevant for `DMPlex`)
714-
if dim > 1:
715-
for f in range(nfacets):
716-
# Derive facet vertices from facet_closure
717-
myverts = mesh._closures[dim-1][1][0][facets[f]]
718-
719-
if dim > 2:
720-
# skip tets for now, assume facets are edges
721-
# and therefore that the closure has 3 entries
722-
raise NotImplementedError
723-
724-
facet_vertices[0] = myverts[0]
725-
facet_vertices[1] = myverts[1]
726-
727-
# Find non-incident vertices
728-
for v in range(v_per_cell):
729-
incident = 0
730-
for vi in range(v_per_cell-1):
731-
if cell_closure[cell,v] == facet_vertices[vi]:
732-
incident = 1
670+
CHKERR(PetscSortIntWithArray(nedges_per_cell, edge_incident_verts, edges))
671+
672+
# Insert into the closure
673+
for ei in range(nedges_per_cell):
674+
closure_data_reord[1][cell, ei] = edges[ei]
675+
676+
# 3. Order facets
677+
if tdim > 1:
678+
for fi, facet in enumerate(closure_data[tdim-1][cell]):
679+
# Convert the facet, which is in stratum-local, renumbered form
680+
# back to what plex expects
681+
facet_unrenum = mesh.points.applied_to_default_component_number(
682+
mesh.facet_label, facet
683+
)
684+
facet_pt = mesh.points.component_to_axis_number(
685+
mesh.facet_label, facet_unrenum
686+
)
687+
688+
# Collect vertices that are in the closure of the facet
689+
get_transitive_closure(
690+
dm.dm, facet_pt, PETSC_TRUE, &nfacet_closure, &facet_closure
691+
)
692+
ptr = 0
693+
for i in range(nfacet_closure):
694+
pt = facet_closure[2*i]
695+
stratum, stratum_pt = mesh.points.axis_to_component_number(pt)
696+
stratum_pt_renum = mesh.points.default_to_applied_component_number(
697+
stratum, stratum_pt
698+
)
699+
if stratum.label == mesh.vert_label:
700+
facet_verts[ptr] = stratum_pt_renum
701+
ptr += 1
702+
703+
# Find the non-incident vertex
704+
for vi in range(nverts_per_cell):
705+
incident = False
706+
for vj in range(nverts_per_cell-1):
707+
if facet_verts[vj] == closure_data[0][cell, vi]:
708+
incident = True
733709
break
734-
# Only one non-incident vertex per facet, so
735-
# local facet no. = non-incident vertex no.
736-
if incident == 0:
737-
cell_closure[cell,offset+v] = facets[f]
710+
# Only one non-incident vertex per facet, so the local facet
711+
# number is the same as the non-incident vertex number
712+
if not incident:
713+
closure_data_reord[tdim-1][cell, vi] = facet
738714
break
739715

740-
offset += nfacets
716+
# 4. And finally the cell
717+
assert closure_sizes[tdim] == 1
718+
closure_data_reord[tdim][cell] = closure_data[tdim][cell]
741719

742-
CHKERR(PetscFree(vertices))
743-
CHKERR(PetscFree(v_global))
720+
# Cleanup
721+
CHKERR(PetscFree(verts))
722+
CHKERR(PetscFree(edges))
744723
CHKERR(PetscFree(facets))
745-
CHKERR(PetscFree(facet_vertices))
746-
CHKERR(PetscFree(faces))
747-
CHKERR(PetscFree(face_indices))
748-
749-
return cell_closure
724+
CHKERR(PetscFree(global_verts))
725+
CHKERR(PetscFree(edge_verts))
726+
CHKERR(PetscFree(edge_incident_verts))
727+
CHKERR(PetscFree(facet_verts))
728+
if facet_closure != NULL:
729+
restore_transitive_closure(dm.dm, 0, PETSC_TRUE, &nfacet_closure, &facet_closure)
730+
731+
return closure_data_reord
750732

751733

752734
@cython.boundscheck(False)

0 commit comments

Comments
 (0)