A two-dimensional weakly compressible SPH fluid solver in pure NumPy
SPyH solves the Lagrangian Navier–Stokes equations (momentum + continuity) for a particle-based fluid with the weakly compressible SPH (WCSPH) approach — no framework, no compiled extensions, just vectorized NumPy. The code was inspired by the authors author's master thesis Assessment of a Non-linear Gravity Wave with a Weakly Compressible Smoothed Particle Hydrodynamics Code (Technical University of Munich, 2023).
-
Pure NumPy core — all pairwise interactions evaluated on flat pair arrays (
einsum+bincount), ~100× faster than a naive Python loop -
Exchangeable formulations — every numerical choice is a class you can swap:
Parameter Module Options kernelspyh.kernelsQuinticWendland(default),CubicSpline,Gaussianviscosityspyh.viscosityArtificialViscosity(Monaghan 1983)momentumspyh.momentumSymmetricPressure(default),DensityProductPressuredensity_diffusionspyh.density_diffusionMolteniDiffusion(default),FourtakasDiffusion,Noneeosspyh.eomEoS_pressure(Tait/Cole, default),EoS_linear -
Stability guardrails — configurations a formulation study showed to be unstable (no density diffusion, near-zero viscosity) trigger a
StabilityWarningat construction -
Scene building — compose fluids and boundaries from rectangles and circles (
Draftsmen), discretized onto a regular particle grid -
Output —
.vtusnapshots (ParaView),.mp4/.gifanimations, progress bar +log.txt -
Validated — 60 unit/physics tests (kernel moments, Shepard partition of unity, brute-force neighbor verification, hydrostatic settling, dam-break front timing)
pip install .from spyh import SPH, Draftsmen
from spyh.viz import Animator
draft = Draftsmen()
draft.add_rectangle(x_min=-0.1, x_max=0, y_min=-0.1, y_max=1.0, p_type="boundary")
draft.add_rectangle(x_min=1.0, x_max=1.1, y_min=-0.1, y_max=1.0, p_type="boundary")
draft.add_rectangle(x_min=-0.1, x_max=1.1, y_min=-0.1, y_max=0, p_type="boundary")
draft.add_rectangle(x_min=0, x_max=0.4, y_min=0, y_max=0.6, p_type="fluid")
sim = SPH(draft, dp=0.02)
sim.initialize_hydrostatic_density(y_surface=0.6)
anim = Animator(field="vmag")
sim.run(t_total=2.0, on_output=anim.capture)
anim.save("dam_break.mp4")Each notebook builds a scenario, runs it, embeds the animation, and validates the result against theory or experiment:
| Notebook | Scenario | Validation |
|---|---|---|
dam_break_2d.ipynb |
SPHERIC benchmark dam break | Surge front vs Ritter's solution and Martin & Moyce (1952) |
hydrostatic_tank.ipynb |
Water column at rest | Pressure profile vs analytic p = ρg(H−y) |
sloshing_tank.ipynb |
Resonantly forced sloshing | Response frequency vs ω₁² = gk·tanh(kd) |
Rendered animations: dam break · hydrostatic tank · sloshing tank
pip install -e . --group dev # dependency groups need pip >= 25.1
pytestsrc/spyh/
sph.py # SPH driver: state, step(), run()
draftsmen.py # scenario builder (rectangles, circles -> particles)
kernels.py # Kernel ABC + QuinticWendland, CubicSpline, Gaussian
viscosity.py # ViscosityModel ABC + ArtificialViscosity
momentum.py # MomentumEquation ABC + two pressure gradients
density_diffusion.py # DensityDiffusion ABC + Molteni, Fourtakas delta-SPH
eom.py # Tait/Cole + linear equation of state
nnps.py # KDTree neighbor search -> flat pair arrays
viz.py # Animator (.mp4 / .gif)
notebooks/ # validated example scenarios
docs/thesis/ # theoretical basis (master thesis, TUM 2023)
If you use SPyH in your research, please cite it (see CITATION.cff —
GitHub's Cite this repository button):
@software{spyh,
author = {Schäfer, Faro},
title = {{SPyH}: a two-dimensional weakly compressible {SPH} framework},
url = {https://github.com/zweihuehner/SPyH},
license = {MIT},
}
@mastersthesis{schaefer2023,
author = {Schäfer, Faro},
title = {Assessment of a Non-linear Gravity Wave with a Weakly Compressible
Smoothed Particle Hydrodynamics Code},
school = {Technical University of Munich},
year = {2023},
month = {3},
}