Fluid Simulation¶
Overview¶
NovaPhy implements Position Based Fluids (PBF) following Macklin & Muller 2013, with two-way rigid-fluid coupling via the Akinci et al. 2012 boundary particle method.
PBF Solver¶
The PBF pipeline:
- Predict — Apply gravity, predict particle positions
- Neighbor Search — Spatial hash grid for O(1) average neighbor lookup
- Density Constraint — Iteratively project particles to satisfy rest density
- Corrections — XSPH viscosity and vorticity confinement
- Update — Finalize positions and velocities
Creating a Fluid Simulation¶
import numpy as np
import novaphy
builder = novaphy.ModelBuilder()
builder.add_ground_plane(y=0.0)
# Define a fluid block
fluid_block = novaphy.FluidBlockDef()
fluid_block.lower = np.array([0.0, 0.0, 0.0])
fluid_block.upper = np.array([1.0, 1.0, 1.0])
fluid_block.particle_spacing = 0.05
positions = novaphy.generate_fluid_block(fluid_block)
mass = fluid_block.rest_density * fluid_block.particle_spacing ** 3
radius = fluid_block.particle_spacing * 0.5
builder.add_particles(
positions,
[fluid_block.initial_velocity] * len(positions),
[mass] * len(positions),
[radius] * len(positions),
)
# Configure PBF solver
pbf_config = novaphy.solvers.SolverPBF.Config()
pbf_config.kernel_radius = 4.0 * 0.05 # Kernel radius = 4x spacing
pbf_config.solver_iterations = 4
model = builder.finalize()
# SolverPBF is a SolverBase subclass.
solver = novaphy.solvers.SolverPBF(model, pbf_config)
state = model.state()
solver.initialize_state(state)
collision_pipeline = novaphy.CollisionPipeline(model)
contacts = collision_pipeline.contacts()
for _ in range(500):
collision_pipeline.collide(state, contacts)
solver.step(state, state, None, contacts, 1.0 / 120.0)
SPH Kernels¶
NovaPhy provides standard SPH kernel functions:
- Poly6 kernel — for density estimation
- Spiky kernel gradient — for pressure forces
- Viscosity kernel Laplacian — for viscosity forces
Rigid-Fluid Coupling (Akinci)¶
Two-way coupling between rigid bodies and fluid particles:
- Rigid body surfaces are sampled with boundary particles
- Boundary particles contribute to fluid density computation
- Particle / rigid contacts are generated into the unified
Contacts.soft_contact_*channel and applied to rigid bodies / articulation links during the fluid step
# Two-stage Newton-style chain: SolverPBF (fluid) -> SolverSemiImplicit (rigid).
# Coupling forces flow through the shared Contacts aggregate.
pbf_solver = novaphy.solvers.SolverPBF(model, pbf_config)
rigid_solver = novaphy.solvers.SolverSemiImplicit(model, novaphy.solvers.SolverSemiImplicit.Config())
state = model.state()
pbf_solver.initialize_state(state)
control = model.control()
collision_pipeline = novaphy.CollisionPipeline(model)
contacts = collision_pipeline.contacts()
for _ in range(500):
collision_pipeline.collide(state, contacts)
pbf_solver.step(state, state, None, contacts, 1.0 / 120.0)
collision_pipeline.collide(state, contacts)
rigid_solver.step(state, state, control, contacts, 1.0 / 120.0)
Demos¶
| Demo | Description |
|---|---|
demo_dam_break.py |
Rectangular fluid block collapse |
demo_fluid_box.py |
~8000 particles sloshing in a moving box |
demo_ball_in_water.py |
Ball dropping into water |
demo_fluid_coupling.py |
Boxes and spheres splashing into water |