// undine docs
Solver Architecture
Undine is designed both as a production-ready tool and as a platform for continued research and experimentation in fluid simulation techniques.
This chapter gathers the architectural material that matters most to technical users, developers, and researchers.
System Overview
The Undine system is intentionally layered so that the Blender-facing experience remains separate from the simulation core.
This separation keeps the user interface flexible while preserving the numerical integrity of the solver.
Undine is organized as a layered system that connects Blender scene setup to a dedicated simulation core.
graph TD
Blender["Blender Scene"]
Addon["Undine Blender Addon"]
UIPanels["UI Panels"]
SimConfig["Simulation Configuration"]
SceneExport["Scene Export"]
SimControl["Simulation Control"]
Interface["Simulation Interface Layer"]
ConfigFiles["Configuration Files"]
CommandExec["Command Execution"]
DataExchange["Data Exchange"]
Core["Undine Simulation Core (C++)"]
ParticleSystem["Particle System"]
Neighbor["Neighbor Search"]
Pipeline["Solver Pipeline"]
Advection["Advection"]
Boundary["Boundary Handling"]
Viscosity["Viscosity"]
Pressure["Pressure Solver"]
Redist["Particle Redistribution"]
Output["Output System"]
Cache["Particle Cache"]
MeshGen["Mesh Generation"]
Blender --> Addon
Addon --> UIPanels
Addon --> SimConfig
Addon --> SceneExport
Addon --> SimControl
Addon --> Interface
Interface --> ConfigFiles
Interface --> CommandExec
Interface --> DataExchange
Interface --> Core
Core --> ParticleSystem
Core --> Neighbor
Core --> Pipeline
Pipeline --> Advection
Pipeline --> Boundary
Pipeline --> Viscosity
Pipeline --> Pressure
Pipeline --> Redist
Core --> Output
Output --> Cache
Output --> MeshGen
Blender Scene
|
+-- Undine Blender Addon
| +-- UI Panels
| +-- Simulation Configuration
| +-- Scene Export
| +-- Simulation Control
|
+-- Simulation Interface Layer
| +-- Configuration Files
| +-- Command Execution
| +-- Data Exchange
|
+-- Undine Simulation Core (C++)
+-- Particle System
+-- Neighbor Search
+-- Solver Pipeline
| +-- Advection
| +-- Boundary Handling
| +-- Viscosity
| +-- Pressure Solver
| +-- Particle Redistribution
|
+-- Output System
+-- Particle Cache
+-- Mesh Generation
Philosophy of the Solver
Fluid simulation systems are extremely sensitive to numerical consistency.
Even small mismatches between solver stages can produce visible artifacts, and those issues are often caused by subtle inconsistencies between multiple subsystems rather than by a single isolated algorithm.
For this reason, Undine approaches fluid simulation as a coherent numerical system rather than a collection of isolated features.
Architectural consequences
This philosophy influences the modular stage design, the strict separation between simulation logic and user interface, and the careful validation of numerical changes.
Consistency Across Execution Paths
In many fluid systems, CPU and GPU execution paths can behave slightly differently.
Perfect numerical equivalence is rarely possible in practice, but maintaining comparable solver behavior remains an important design objective because it improves predictability and reproducibility across systems and configurations.
Extensibility and Long-Term Evolution
Fluid simulation research continues to evolve rapidly. New numerical methods, acceleration techniques, and meshing algorithms are regularly developed by the simulation and computer graphics communities.
Because the simulation pipeline is modular, individual solver stages can be replaced or upgraded as new approaches become available.
This design allows Undine to grow over time while maintaining a stable foundation for existing workflows.
Orchestration layer
The orchestration layer sits between the solver core and the addon. It owns the frame loop, the substep loop, route resolution, retry coordination, and stage scheduling — the parts of the system that decide which kernel runs, in what order, on which backend, with which fallback.
Keeping orchestration separate from algorithms is what allows the retry chain (PCG → FP64 → MG → CPU) to exist without leaking into individual stages. Each stage just does its job; the orchestration layer decides whether to escalate.
Pipeline stages
- Emission — particle creation from emitters, with temporal interpolation across substeps
- Advection / Forces — gravity, drag, surface tension contribution
- P2G — particle-to-grid transfer (APIC, MLS-MPM stress fold)
- Pressure — projection (PCG, optional V-cycle multigrid, density correction)
- Viscosity — when enabled (Simple / Implicit), physically motivated viscosity solve
- G2P — grid-to-particle transfer with FLIP/PIC blend
- Collisions — SDF lookup, normal damping, tangential friction, no-slip, contact refill
- Surface tension — when enabled, on the free-surface band
- Particle redistribution — reseeding, separation, mass-aware coverage
- Streamflow publish — frame committed to the shared cache
- Output write — disk persistence of expensive points and (optionally) mesh
Bricks subsystem
The bricks subsystem is the sparse-grid layer. It tracks active simulation regions as bricks with a halo wide enough for the pressure stencil, validates halo sufficiency / G2P coverage / extrapolation coverage, and decides authority transitions (dense ↔ velocity-pages) based on observed coverage and topology stability.
Authority demotions are logged with reasons (ParticleCore, ParticleHalo, TemporalEmission, ColliderBand, PressureBand, ExtrapolationBand, G2PSampling, PredictedMotion, RetainedTtl). When a sim is demoting often, the reason mask points at the cause.
GPU multigrid pressure
The PCG path optionally uses a V-cycle multigrid as preconditioner. The smoother is selectable: Jacobi (default, simple, cache-friendly) or symmetric red-black Gauss-Seidel (sym_rbgs) — single-sweep RBGS is intentionally not exposed because it breaks the SPD invariant PCG requires.
MG levels, V-cycles, smoother iterations, and a 'safe retry' policy with target-levels cap are tunable. The retry path uses weighted Jacobi at the coarse level when MG converges poorly on adversarial scenes.
FP32 reductions in the inner PCG loop are computed via warp-shuffle two-stage reductions (no shared-memory bank conflicts), with FP64 accumulation for determinism within a block.
Numerical safety net
Undine treats numerical failure as a predictable event, not an edge case. Three mechanisms protect long bakes:
FP32 tolerance floor
Below ~√N · ε_f32, the FP32 PCG residual is fighting roundoff, not the actual problem. Requested tolerances are clamped from below by a configurable safety factor (default 4.0). The floor is logged.
True-residual refresh
The CPU PCG path periodically recomputes b - A·x to reset the recurrence residual against drift. This kills the 'silent non-convergence' on long, ill-conditioned solves.
Retry chain
On breakdown (max iters, NaN, FP32 plateau): retry → FP64 → multigrid V-cycle → CPU. Each step is logged with code, iters, rel_res, and chosen backend.