PICurv 0.1.0
A Parallel Particle-In-Cell Solver for Curvilinear LES
 
Loading...
Searching...
No Matches
C Runtime Execution Map

This page is a contributor-oriented map of how the C solver executes from process start to timestep loop. Use it as a practical companion to architecture and methods pages when modifying solver behavior.

1. Solver Startup Order (<tt>src/simulator.c</tt>)

High-level startup sequence:

  1. PetscInitialize
  2. CreateSimulationContext
  3. SetupSimulationEnvironment
  4. SetupGridAndSolvers
  5. SetupBoundaryConditions
  6. SetupDomainRankInfo
  7. InitializeEulerianState
  8. InitializeParticleSwarm (if np > 0)
  9. DisplayBanner
  10. initial settlement/restart finalization
  11. AdvanceSimulation

Startup branch details:

2. Python-to-C Configuration Boundary

picurv_cli/core.py is the control-plane generator. It writes normalized runtime artifacts under <run.config>/ and launches C binaries with -control_file.

Core generated files consumed by C:

  • *.control (solver flags),
  • bcs.run (boundary face/type/handler + params),
  • whitelist.run and profile.run,
  • grid.run (for file/grid_gen paths),
  • post.run (postprocessor path).

Physical-solution monitoring is normalized by picurv and written directly into *.control as -solution_convergence_* options. There is no separate observation-plan sidecar. Scientific field-statistics ingress will only be added when the runtime, checkpoint, and postprocessor implementation is ready as one usable feature.

3. Core Runtime Structs

Most solver-wide state flows through:

  • SimCtx: global run configuration, solver controls, pointers to hierarchy and shared runtime services.
  • UserCtx: per-block/per-level field ownership, DM/Vec handles, boundary configs, and local coupling context.
  • BoundaryFaceConfig: per-face mathematical type + handler + param list.
  • swarm particle records and DMSwarm fields (position, DMSwarm_CellID, status fields, etc.).

Related ownership note:

  • most numerics run at finest level simCtx->usermg.mgctx[simCtx->usermg.mglevels-1].user[*]

4. Initialization Branches

Eulerian:

Lagrangian:

5. Timestep Loop (<tt>AdvanceSimulation</tt>)

Per-step sequence:

  1. update step and time,
  2. reset particle statuses (if particles enabled),
  3. Eulerian step:
    • FlowSolver for solve mode, or load/analytical branch,
  4. Lagrangian step:
  5. write the configured physical-solution convergence observation through the existing logger (unless disabled),
  6. update history vectors,
  7. commit a checkpoint bundle on configured cadence.

The loop calls WriteCheckpointBundle. That thin coordinator delegates payload I/O to:

ProfilingLogTimestepSummary and all other persistent logging remain separate loop services and are not checkpoint payloads.

Loop-time branch notes:

  • Eulerian source mode is selected once per step (solve, load, or analytical), then particle coupling follows.
  • particle migration is iterative with global settlement passes and explicit lost-particle handling.
  • periodic particle console snapshots are controlled by monitor/profiling settings and rank-aware logging helpers.

6. Boundary System Runtime Hooks

Boundary lifecycle is object-style (function pointers per handler):

  1. parse bcs.run,
  2. create handler objects via factory,
  3. run Initialize once,
  4. on each step, run handler phases (PreStep, Apply, optional PostStep) in priority order.

Useful entry points:

6a. Residual-Purity Invariant (Matrix-Free Solvers)

Any field read by the momentum residual or its boundary handlers must be reconstructed or synchronized from the current SNES trial vector before it can influence F(X). A matrix-free residual (MomentumNewtonKrylov_FormResidual) that reads stale state is not a deterministic function of X, which breaks the finite-difference Jacobian action and the line search.

Concretely, the Cartesian velocity state must be seeded in dependency order at the top of each residual evaluation, before ApplyBoundaryConditions runs (the conservation-outlet handler reads lUcat on its first pass):

X / Ucont -> lUcont -> Ucat -> periodic Ucat finalization -> lUcat -> boundary application

Contra2Cart() alone is insufficient (it does not refresh lUcat), and the reconstruction inside ApplyBoundaryConditions() runs after the first handler sweep, so it cannot prepare that sweep. When adding a new field read by the residual or a boundary handler, seed it from X here or expect residual-purity regressions. See Newton–Krylov Momentum Solver, Section 5.

7. Safe Extension Workflow (C Side)

When adding or changing physics behavior:

  1. identify owning module (solvers.c, rhs.c, poisson.c, Particle*, Boundaries*),
  2. add/modify fields in SimCtx or UserCtx only when ownership is clear,
  3. keep history-vector and ghost-update contracts intact,
  4. update logging labels and diagnostics,
  5. update Python ingestion path so YAML and C flags remain consistent.

Cross-layer reminder:

8. Debugging Entry Points

High-value checks during development:

  • DisplayBanner summary (BCs, modes, solver selection),
  • per-step profile summaries (ProfilingLogTimestepSummary),
  • particle metrics and location/migration counters,
  • strict YAML validation through picurv validate before solver execution.

Additional fast diagnostics:

  • compare manifest.json + generated <run.config>/*.control against expected YAML role settings
  • run picurv run --dry-run --format json to verify launch-mode/rank assumptions before expensive runs
  • for restart issues, verify --restart-from / --continue resolution and the generated -restart_dir in control artifacts

9. Related Pages