Grid Topology and Validation
Purpose and implementation status
This page describes the topology contracts that should remain explicit as OneFLOW’s mesh implementation evolves. It combines the topology-specific material from the repository’s engineering note with the broader OneFLOW Mesh Architecture overview.
The details below distinguish observed implementation behavior from architectural limitations. In particular, rebuilding derived connectivity is currently an explicit call-path responsibility; it must not be mistaken for automatic cache invalidation.
Topological meaning by dimension
One dimension
For a simple chain of two interval cells, there are three nodes, three point-faces, and two cells:
node: 0 1 2
o-----------o-----------o
face: f0 f1 f2
cell: c0 c1
face owner cell neighbour / boundary
f0 c0 boundary / ghost
f1 c0 c1
f2 c1 boundary / ghost
A 1D face is a point, not a line segment. A cell is the interval between nodes. A specialized compact representation is acceptable if it can be converted losslessly to a common face/owner/neighbour interface.
Two dimensions
A 2D mesh requires node, edge/face, and cell connectivity. Shared edges must identify their adjacent cells consistently; boundary edges need a valid boundary classification. Edge-node ordering and owner/neighbour ordering must agree with the normal convention used by numerical fluxes.
Three dimensions
For a general polyhedral finite-volume mesh, each face is an ordered loop of nodes, and cells are bounded by faces. A cell’s vertex list can support cell-oriented algorithms, but it does not replace oriented face connectivity when surface normals and flux traversal matter. Explicit edge storage is only required when algorithms consume it directly.
Across all dimensions, keep geometric dimension, element type, physical boundary, ghost entity, and partition interface as separate concepts. A single ambiguous integer convention must not represent all of them.
Cell dimension, face dimension, and element roles
Use topological cell dimension to determine what a face means. Do not infer a
universal node-count rule from the name face:
In a 1D mesh, a cell is a segment and a face is a 0D point, represented by one node.
In a 2D mesh, a cell may be a triangle, quadrilateral, or polygon with at least three vertices. Its codimension-one faces are 1D edges, represented by two endpoint nodes for a linear mesh.
In a 3D mesh, a cell may be a tetrahedron, hexahedron, or general polyhedron. Its codimension-one faces are 2D surfaces represented by ordered node loops, with at least three nodes.
This is based on cell dimension, not coordinate storage dimension. A 2D
surface mesh embedded in three physical coordinates still has 2D cells and 1D
edge faces. CGNS distinguishes CellDimension from PhysicalDimension.
CGNS element type must be interpreted together with the base cell dimension and
the other sections in the zone. For a 3D polyhedral volume mesh, NGON_n
records the polygonal faces and NFACE_n records each cell as signed face
references. An NGON_n section by itself is not sufficient evidence that the
zone uses 3D polyhedral cells: it may represent 2D polygonal elements. In that
2D case its polygons are cells; their edges must be derived as the OneFLOW
faces. See the CGNS SIDS grid and element definitions.
Current implementation boundary: IsBasicCellElementType is the
dimension-aware gate for OneFLOW cell elements; it currently reads the global
Dim::dimension. Its former Volume name was
misleading because it also accepts 1D line cells and 2D area cells.
FaceSolver::ScanElementFace derives
2-node edge faces from supported 2D fixed-node cells such as TRI_3. The
polyhedral staging function handles face node loops and requires at least three
references, which is a 3D polyhedral-face contract. However,
CgnsZsection::HasPolygonSection currently selects the polyhedron path for
both NGON_n and NFACE_n. CgnsSection::SetElementTypeAndNode only
stages supported fixed-node cell element types, so a 2D polygonal NGON_n cell
is not currently imported as a cell. Treat this as an explicit format-adapter
gap; do not relax the 3D polyhedral-face invariant to mask it. Future routing
must classify by cell dimension and section roles, and add tests for 2D NGON
cell-to-edge conversion before claiming that input form is supported.
Canonical and derived topology
——————————
The intended roles are:
Node coordinates describe canonical geometry.
Cell type and cell-to-node connectivity are canonical for cell-centric algorithms and interchange formats.
Ordered face-to-node connectivity, owner/left cell, neighbour/right cell, and boundary classification are canonical for the current face-based finite-volume model.
Boundary labels and face-to-region mappings preserve physical meaning; they are not visualization colors.
Cell-to-face and cell-to-cell adjacency are derived structures when they can be regenerated from canonical connectivity.
Ghost cells and partition/interface mappings belong to execution and distribution semantics, not to the physical input mesh.
See OneFLOW Mesh Architecture for the mesh information levels, snapshot boundary, and format-adapter design.
Boundary normalization contract
UnsGrid::NormalizeBc() converts imported boundary-face neighbour placeholders into local ghost-cell IDs. For a boundary face with index iFace, the normalized right-cell ID is:
ghostCellId = nCells + iFace
The implementation validates real-cell references before changing boundary neighbour IDs. Tests should preserve this failure atomicity: invalid input must not leave only part of the boundary topology normalized.
The formula is an implementation convention, not a portable interchange-format convention. Exported data must document identifier base and scope explicitly.
Derived adjacency and cache refresh
The current model has distinct source and derived data:
FaceTopo owns face-to-node and face-to-cell source connectivity plus boundary classification.
CellTopo stores cell-oriented source data and derived c2f and c2c adjacency arrays.
CellTopo::CalcC2f() and FaceTopo::CalcC2C() rebuild derived connectivity from the current source arrays.
Relevant call paths explicitly rebuild adjacency before reading it, including solver initialization and the METIS partition graph path.
These are explicit rebuild operations, not automatic invalidation. Source containers remain publicly mutable, so editing connectivity and then reading old adjacency without rebuilding it can produce stale results. A revision counter alone would not solve this while callers can mutate arrays through exposed references.
The current targeted rebuild behavior includes:
CellMesh::CalcCellSpan() rebuilds c2f and recalculates spans instead of returning early merely because a span array is non-empty.
ScalarGrid::CalcC2C(EList&) reconstructs adjacency from current face connectivity rather than trusting a non-empty caller-provided list.
FaceTopo::ReorderLink() applies one permutation to populated face-indexed arrays, including face types, face-position arrays, and flags, and clears c2f because it stores face IDs. Boundary-condition records remain in their original boundary-face order because the permutation preserves the relative order of boundary faces.
These targeted fixes do not establish repository-wide automatic cache coherence. Callers must still rebuild derived data after changing source connectivity. Broader invalidation should follow an audit of actual direct reads and a clear boundary around topology mutation, rather than adding a revision counter that cannot observe direct edits.
Validation invariants
Use small hand-constructed meshes with known expected connectivity. At minimum, verify:
Counts agree with the arrays they describe; all node and cell references are in range.
An internal face connects two distinct real cells.
Before ghost normalization, a boundary face references exactly one real cell and has a valid boundary classification.
Face-node ordering and owner/neighbour ordering obey the solver’s orientation convention.
Face-indexed metadata stays aligned after reordering.
Interface maps refer to valid faces and zones and use documented ID scopes.
A read-normalize-write-read round trip preserves counts, coordinates, face connectivity, internal owner/neighbour pairs, boundary labels, and interface mappings when the format claims to preserve them.
Tiny 1D and higher-dimensional fixtures verify adjacency, orientation, positive cell measures where defined, and shared-face traversal suitable for conservation checks.
A useful test ladder is local connectivity tests, boundary-normalization tests, derived-data rebuild tests, reorder consistency tests, round-trip conversion tests, and finally full CFD regressions. Solver output comparisons complement these tests; they do not replace geometry and topology validation.
Known limitation and next audit
The topology containers still expose mutable source arrays. The implementation therefore relies on explicit rebuild sequencing. Before introducing a general invalidation API, audit direct reads of c2f, c2c, and related derived values outside the known solver-initialization and partition paths. Any change should be driven by a demonstrated call path and covered by a focused regression test.