|
DNDSR 0.2.1
Distributed Numeric Data Structure for CFV
|
Branch: dev/harry Baseline commit: cd065ad (on dev/harry) Date: 2026-04-21
The Geom mesh module (~11,600 lines across 14 files) centres on two structs:
Implementation files:
| File | Lines | Responsibility |
|---|---|---|
| Mesh.cpp | 2,585 | Topology building, ghost, face interpolation, reordering, serialization, bnd mesh |
| Mesh_Elevation.cpp | 1,120 | O1→O2 elevation, O2→O1 bisection, boundary smooth |
| Mesh_Elevation_SmoothSolver.cpp | 1,782 | 4 smooth solver variants (dispatch, V1Old, V1, V2) |
| Mesh_Plts.cpp | 1,685 | VTK/PLT/CGNS/VTKHDF output |
| Mesh_Serial_ReadFromCGNS.cpp | 726 | CGNS + OpenFOAM readers |
| Mesh_Serial_BuildCell2Cell.cpp | 702 | Cell2Cell, Deduplicate1to1Periodic |
| Mesh_ReadSerializeDistributed.cpp | 512 | Distributed H5 read + ParMetis repartition |
| Mesh_Serial_Partition.cpp | 414 | Metis partitioning |
| Mesh_WallDist.cpp | 224 | Wall distance (CGAL-based) |
ConvertAdjEntries, PermuteRows — done.ReadFromCGNSSerial, InterpolateFace, ReorderLocalCells, PrintSerialPartVTKDataArray — done.BuildNodeWallDist, broke Geom→Solver dependency, removed auto mesh = this, deleted dead code — done.| Function | File | Lines | Issue |
|---|---|---|---|
ElevatedNodesSolveInternalSmoothV2 | SmoothSolver.cpp | 864 | Monolithic: assertions, setup, FEM assembly, limiters, GMRES, apply — all in one |
PrintSerialPartVTKDataArray | Plts.cpp | 560 | Repeated binary/ascii encoding pattern for every data array |
BuildO2FromO1Elevation | Elevation.cpp | 450 | 5 distinct phases jammed together |
ElevatedNodesSolveInternalSmoothV1 | SmoothSolver.cpp | 448 | Similar structure to V2 but RBF-based |
ElevatedNodesGetBoundarySmooth | Elevation.cpp | 427 | Extended-face building, normals, Hermite interp |
Deduplicate1to1Periodic | BuildCell2Cell.cpp | 312 | 5 distinct phases |
RecoverCell2CellAndBnd2Cell | Mesh.cpp | 258 | Non-periodic (simple) + periodic (complex) fused |
nodesBoundInterpolated identification loop (identical)boundInterpCoo/boundInterpVal init + populate + ghost pull (identical)ConvertAdjEntries used for Primary/PrimaryForBnd/C2CFace but NOT for Facial, C2F, N2CB (which use manual OMP-parallel loops or lambdas).V1Old (235 lines) has raw std::cout << iN debug prints, serial-only solve, and is superseded by V1.#if 0 / commented-out "fem
method" and "spring method" blocks inside the AssembleRHSMat lambda.Goal: Ensure elevation/bisection + smooth solver correctness is testable before touching any code.
Tests to add (in test/cpp/Geom/test_MeshPipeline.cpp or new file):
| Test | Assertion |
|---|---|
| Elevation + BoundarySmooth: node movement count | nTotalMoved > 0 for a curved mesh |
| Elevation + BoundarySmooth: all O2 nodes valid coords | No NaN/Inf in coords after smooth |
| Elevation + InternalSmooth(V2): Jacobian positive | All cell Jacobians remain > 0 |
| Elevation + InternalSmooth(V2): coords change | coords differ from pre-smooth state |
| Bisection preserves O2 node positions | Bisected mesh nodes are a subset of O2 coords |
Build and run:
Goal: Deduplicate the ~80-line setup block copied across 3 smooth solver variants.
Implementation: Extract a file-local helper struct + function in Mesh_Elevation_SmoothSolver.cpp:
Lines saved: ~160 (80 lines × 3 copies → 1 copy + 3 calls).
| # | Item | Lines Saved |
|---|---|---|
| 1.1 | State assertions + early exit | ~30 |
| 1.2 | nodesBoundInterpolated identification | ~20 |
| 1.3 | boundInterpCoo/boundInterpVal init + populate + ghost pull | ~90 |
| 1.4 | KD-tree construction (V1Old, V1 only) | ~20 |
Goal: Break the monolithic function into focused helpers.
| # | Extract | Current Lines | Description |
|---|---|---|---|
| 2.1 | BuildNode2NodeFromCells() | ~15 | Build node-to-node connectivity from cell2node |
| 2.2 | BuildSmoothDOFVectors() | ~40 | Initialize DOF arrays (dispO2, bO2, etc.) |
| 2.3 | AssembleElasticityRHSAndMatrix() | ~200 | The active "bisect fem method" block from the AssembleRHSMat lambda |
| 2.4 | ApplyDirichletBCToSparse() | ~40 | The "find nDiag, set identity row" pattern (repeated 3× → 1×) |
| 2.5 | LimitDisplacementIncrement() | ~95 | The LimitDisp lambda |
| 2.6 | BuildSparsePreconditioner() | ~40 | Eigen sparse matrix construction from block matrix A |
| 2.7 | Delete disabled code blocks | ~175 | Remove #if 0 "fem method" (lines 1054-1228) and "spring method" (lines 1427-1488) |
After extraction, ElevatedNodesSolveInternalSmoothV2 becomes a ~150-line orchestrator calling the helpers in sequence.
Goal: Reduce boilerplate in the 12 Adj methods without losing OMP parallelism.
| # | Item | Description | Lines Saved |
|---|---|---|---|
| 3.1 | Add FaceIndexGlobal2Local / FaceIndexLocal2Global wrappers | Analogous to Cell/Node/Bnd wrappers, using face2node as the pair | 0 (enables 3.2-3.3) |
| 3.2 | Add OMP variant: ConvertAdjEntriesOMP | Same as ConvertAdjEntries with #pragma omp parallel for | ~5 |
| 3.3 | Unify C2F pair with ConvertAdjEntries | Replace inline lambda + loops with 2 calls each | ~40 |
| 3.4 | Unify N2CB pair with ConvertAdjEntriesOMP | Replace manual loops with 2 calls each | ~20 |
Not unified (deliberate):
face2bnd conversion + stricter assertions. Splitting would regress cache locality and require assertion wrappers for no readability gain.bnd2cell in Primary:** Per-entry assertion depends on row+column index, which ConvertAdjEntries's index(index) signature cannot express.Goal: Separate the simple non-periodic logic from the complex periodic filter.
| # | Extract | Lines | Description |
|---|---|---|---|
| 4.1 | FindCellsSharingFace() | ~30 | Node-intersection algorithm to find cells sharing a face |
| 4.2 | FilterPeriodicBnd2Cell() | ~120 | Ghost-pull candidate cells, pbi-match filter, donor/receiver classification |
The main function becomes: (1) setup, (2) build cell2cell via node neighbors, (3) build bnd2cell via FindCellsSharingFace, (4) if periodic, call FilterPeriodicBnd2Cell.
| # | Item | Lines Removed | Risk |
|---|---|---|---|
| 5.1 | Deprecate ElevatedNodesSolveInternalSmoothV1Old | 235 | Add [[deprecated]] attribute; later delete |
| 5.2 | Remove debug prints from V1 (HEre0, HEre1, etc.) | ~10 | Trivial |
| 5.3 | Remove debug prints from V1Old (std::cout << iN) | ~5 | Trivial |
| 5.4 | Remove commented-out InterpolateTopology (Mesh.cpp:26-42) | ~17 | Dead code |
| 5.5 | Remove dead code after continue in BuildCell2Cell.cpp:416-438 | ~22 | Dead code |
| 5.6 | Deduplicate OMP progress reporting in BuildCell2Cell | ~20 | Extract ReportProgress lambda |
This is the TODO.md Phase 4, deferred to a future pass because it requires touching the public API (Python bindings, downstream Euler/EulerP/CFV code):
MeshTopology**: adjacency arrays + state flags + state transitionsMeshIO**: serialization, VTK/PLT/CGNS/VTKHDF outputMeshElevation**: O1→O2 elevation, bisection, smooth solversMeshReordering**: local cell reordering, partition start trackingUnstructuredMesh becomes a thin facadeif (isPeriodic) appears 58 times across Mesh files. Consider extracting periodic-specific behavior into a policy so non-periodic paths stay clean. Deferred because the branching is in hot paths where template specialization may be needed for performance.
| Phase | Risk | Effort | Prerequisite | Lines Changed |
|---|---|---|---|---|
| Phase 0: Tests | None | 0.5 day | — | +150 test |
| Phase 1: Shared Setup | Low | 0.5 day | Phase 0 | ~160 removed |
| Phase 2: V2 Decomposition | Medium | 1-2 days | Phase 0, 1 | ~175 removed (dead), ~400 restructured |
| Phase 3: Adj Unification | Low | 0.5 day | Phase 0 | ~60 removed |
| Phase 4: RecoverC2C Decomposition | Medium | 0.5 day | Phase 0 | ~150 restructured |
| Phase 5: Cleanup | Trivial | 0.5 day | Any time | ~310 removed |
| Phase 6: Components | High | 3-5 days | All above | Major restructure |
| Phase 7: Periodic Policy | High | 2-3 days | Phase 6 | 58 sites |
Each phase must pass the full geom test suite after completion:
| Variant | Lines | Method | Status | Recommendation |
|---|---|---|---|---|
ElevatedNodesSolveInternalSmooth | 139 | Dispatch to V1Old/V1/V2 | Active | Keep as dispatcher |
ElevatedNodesSolveInternalSmoothV1Old | 235 | Serial RBF direct solve | Deprecated | [[deprecated]], then delete |
ElevatedNodesSolveInternalSmoothV1 | 448 | Distributed RBF GMRES | Intermediate | Keep (used by some configs) |
ElevatedNodesSolveInternalSmoothV2 | 864 | FEM elasticity GMRES | Active/Default | Decompose in Phase 2 |
Evidence V1Old is deprecated:
std::cout << iN debug prints in production codenIter=0 (V1Old path) in productionAfter Phase 3:
| Pair | Before | After |
|---|---|---|
| Primary | ConvertAdjEntries (3/4 arrays) + manual bnd2cell | Unchanged (bnd2cell needs positional assertion) |
| PrimaryForBnd | ConvertAdjEntries | Unchanged |
| C2F | Inline lambda + manual loops | ConvertAdjEntries + FaceIndex wrappers |
| N2CB | Manual OMP loops | ConvertAdjEntriesOMP |
| Facial | Fused OMP loop | Unchanged (fused loop is intentional optimization) |
| C2CFace | ConvertAdjEntries | Unchanged |
Existing tests that guard correctness:
| Test File | Tests | np | Coverage |
|---|---|---|---|
test_MeshPipeline.cpp | 50 test cases | 1,2,4,8 | Full pipeline, Adj round-trips, elevation, bisection |
test_MeshIndexConversion.cpp | 24 test cases | 1,2,4,8 | All 12 named wrappers |
test_MeshDistributedRead.cpp | 18 test cases | 1,2,4,8 | ReadSerializeAndDistribute |
New tests added in Phase 0 specifically guard elevation + smooth solver paths.