261 lines
9.5 KiB
Markdown
261 lines
9.5 KiB
Markdown
# CelerisLab
|
||
|
||
**GPU-Accelerated Lattice Boltzmann Method (LBM) CFD Solver**
|
||
|
||
CelerisLab is a high-performance computational fluid dynamics (CFD) solver based on the Lattice Boltzmann Method, leveraging NVIDIA CUDA for GPU acceleration. It provides a Python interface for easy integration into scientific workflows while maintaining high computational efficiency through CUDA kernels.
|
||
|
||
## Features
|
||
|
||
- **GPU Acceleration**: CUDA-based kernels for high-performance simulations
|
||
- **D2Q9 / D3Q19 Lattice**: 2D and 3D lattice implementations
|
||
- **Multiple Collision Models**: SRT, TRT, and MRT operators; Smagorinsky LES subgrid model
|
||
- **Dual Streaming Paths**: Standard double-buffer pull and memory-efficient esoteric-pull (EsoPull)
|
||
- **Immersed Boundary Method (IBM)**: Support for complex geometries (cylinders, arbitrary shapes)
|
||
- **Flexible Boundary Conditions**: NEQ-extrapolation pressure outlet, parabolic/uniform velocity inlet, half-way bounce-back walls
|
||
- **Layered Configuration**: Compile-time parameters organized into Global / Method / Case / Debug tiers
|
||
- **High-Re Validated**: Tested up to Re=5000 (2D cylinder); MRT+LES and SRT+LES stable; TRT+LES stable with tuned Lambda and WMAX
|
||
- **Python API**: High-level `Simulation` class for scripting and RL integration
|
||
|
||
## Installation
|
||
|
||
### Prerequisites
|
||
|
||
- Python 3.8 or higher
|
||
- NVIDIA GPU with CUDA Compute Capability 6.0 or higher
|
||
- CUDA Toolkit 11.0 or higher
|
||
- NVIDIA drivers
|
||
|
||
### Install from source
|
||
|
||
```bash
|
||
git clone <repository_url>
|
||
cd CelerisLab
|
||
pip install -e . # Installs from src/ directory
|
||
```
|
||
|
||
### Dependencies
|
||
|
||
- `pycuda>=2020.1`: CUDA Python bindings
|
||
- `numpy>=1.19.0`: Numerical computing
|
||
- `scipy>=1.5.0`: Scientific computing (special functions for vortex initialization)
|
||
|
||
## Quick Start
|
||
|
||
```python
|
||
from CelerisLab import Simulation
|
||
|
||
# Path is optional; see Configuration → paths. Example passes the usual relative name.
|
||
sim = Simulation("configs/config_lbm.json")
|
||
sim.add_cylinder(center=(50, 50), radius=10)
|
||
sim.initialize()
|
||
|
||
for step in range(10000):
|
||
sim.run(1)
|
||
|
||
macro = sim.get_macroscopic() # {"rho": ..., "ux": ..., "uy": ...}
|
||
sim.close()
|
||
```
|
||
|
||
Or as a context manager:
|
||
|
||
```python
|
||
with Simulation("configs/config_lbm.json") as sim:
|
||
sim.add_cylinder(center=(96, 64), radius=12)
|
||
sim.initialize()
|
||
sim.run(5000)
|
||
data = sim.get_macroscopic()
|
||
```
|
||
|
||
## Configuration
|
||
|
||
### Where `config_lbm.json` is loaded from
|
||
|
||
`load_lbm_config()` resolves `config_lbm.json` in this order: an explicit path argument to `Simulation(...)` / `load_lbm_config(path)`, then `$CELERISLAB_CONFIG_DIR/config_lbm.json`, then `./configs/config_lbm.json` under the current working directory, then the copy shipped inside the installed package at `CelerisLab/configs/config_lbm.json`. In a source checkout the same file lives at `src/CelerisLab/configs/config_lbm.json`. There is **no** top-level `configs/` directory at the repository root; from the clone root you can omit the path (`Simulation()`), set `CELERISLAB_CONFIG_DIR`, or create your own `./configs/config_lbm.json`.
|
||
|
||
### `config_lbm.json` shape
|
||
|
||
The on-disk schema matches `src/CelerisLab/configs/config_lbm.json` (nested sections). Example fragment:
|
||
|
||
```json
|
||
{
|
||
"grid": {
|
||
"lattice_model": "D2Q9",
|
||
"nx": 512,
|
||
"ny": 256,
|
||
"nz": 1
|
||
},
|
||
"physics": {
|
||
"data_type": "FP32",
|
||
"viscosity": 0.0035,
|
||
"velocity": 0.03,
|
||
"rho": 1.0
|
||
},
|
||
"method": {
|
||
"collision": "SRT",
|
||
"streaming": "double_buffer",
|
||
"store_precision": "FP32",
|
||
"ddf_shifting": false,
|
||
"les": { "enabled": false, "cs": 0.16, "closed_form": true },
|
||
"trt": { "magic_param": 0.1875 },
|
||
"inlet": { "profile": "parabolic", "scheme": "zou_he_local", "trt_neq_damp": 0.5 },
|
||
"outlet": {
|
||
"mode": "neq_extrap",
|
||
"backflow_clamp": true,
|
||
"blend_alpha": 0.7,
|
||
"srt_neq_damp": 0.5
|
||
},
|
||
"omega_guard": { "min": 0.01, "max": 1.96 }
|
||
},
|
||
"cuda": {
|
||
"threads_per_block": 256,
|
||
"compute_capability": "auto"
|
||
}
|
||
}
|
||
```
|
||
|
||
Lattice size and model come from `grid`; viscosity and scales from `physics`; collision, LES, boundaries, and ω clamps from `method` (ω upper bound is `method.omega_guard.max`, not a top-level `omega_max`). For high-Re runs, keep `method.omega_guard.max` in the `1.90-1.96` window. See `src/CelerisLab/configs/CONFIG.md` for the full parameter tables.
|
||
|
||
### Parameter tiers
|
||
|
||
| Tier | Headers | Examples |
|
||
|---|---|---|
|
||
| Global/Grid | `config_grid.h` | `NX`, `NY`, `NZ`, `LATTICE_MODEL`; `DIM` / `NQ` are **derived** from `LATTICE_MODEL` when `cuda/compiler_v2.py` emits headers (they are not separate keys in JSON) |
|
||
| Global/Physics | `config_physics.h` | VIS, RHO, U0, flag constants |
|
||
| Method | `config_method.h` | COLLISION_MODEL, USE_LES, TRT_MAGIC_PARAM, OMEGA_COLLISION_MAX |
|
||
| Case | `config_objects.h`, `config_obs.h` | `N_OBJS`; packed obs macros `OBS_*` from `generate_config(cfg, n_objects=K)` (`max(N_OBJS,1)` × `DIM` per segment; no extra JSON keys) |
|
||
|
||
Headers are auto-generated by `cuda/compiler_v2.py` from `LBMConfig`; do not edit manually.
|
||
|
||
## API Reference
|
||
|
||
### `Simulation`
|
||
|
||
```python
|
||
sim = Simulation(lbm_config_path=None, body_config_path=None, device_id=0)
|
||
sim.add_cylinder(center, radius) -> int
|
||
sim.add_sensor(center, radius) -> int
|
||
sim.initialize() # recompiles with N_OBJS when bodies were added
|
||
sim.run(steps, checkpoint_interval=0) # wires bodies.action_gpu / bodies.obs_gpu
|
||
sim.step(n=1)
|
||
sim.bodies # ObjectManager: packed buffers + zero_force_segment_async, ...
|
||
sim.get_macroscopic() -> {"rho": ndarray, "ux": ndarray, "uy": ndarray}
|
||
sim.get_ddf() -> ndarray
|
||
sim.get_flags() -> ndarray
|
||
sim.update_runtime_params(omega=..., fx=..., fy=...)
|
||
sim.snapshot() / sim.restore()
|
||
sim.save_checkpoint(path=None) -> str # HDF5; default path if omitted
|
||
sim.load_checkpoint(path) # restores field, step count, bodies
|
||
sim.close()
|
||
```
|
||
|
||
### `LBMStepper` (advanced)
|
||
|
||
```python
|
||
stepper.step(n=1, *, action_gpu, obs_gpu, stream=None)
|
||
```
|
||
|
||
Curved BC / sensor lists live on `field.curved` and `field.sensors` (`CurvedLinkSoA` / `SensorSoA`), filled by `ObjectManager.sync_to_gpu(field)`.
|
||
|
||
### Vortex initialization
|
||
|
||
```python
|
||
from CelerisLab.lbm.initializers import add_vortex
|
||
|
||
# Superimpose a Lamb–Oseen vortex on an existing LBMField
|
||
add_vortex(sim.field, center=(50, 50), radius=10.0, strength=1.0, vortex_type="lamb")
|
||
```
|
||
|
||
## Collision & LES Recommendations
|
||
|
||
| Use case | Recommended config |
|
||
|---|---|
|
||
| Low Re (≤ 500) | SRT or TRT, LES off |
|
||
| Medium Re (500–2000) | MRT or SRT+LES |
|
||
| High Re (2000–5000) | MRT+LES (most robust); SRT+LES; TRT+LES with `method.omega_guard.max` in `1.90-1.96` (default `1.96`) and tuned `method.trt.magic_param` (default `0.1875`) |
|
||
|
||
## Project Layout
|
||
|
||
```
|
||
src/CelerisLab/
|
||
simulation.py High-level API
|
||
config.py LBMConfig / BodyConfig dataclasses
|
||
cuda/
|
||
compiler_v2.py Config header generation + nvcc + PTX load
|
||
context.py CUDA context lifecycle
|
||
lbm/
|
||
field.py GPU memory + ``curved`` / ``sensors`` SoA handles
|
||
curved_links.py CurvedLinkSoA / SensorSoA
|
||
stepper.py Time-step driver (``action_gpu``, ``obs_gpu``)
|
||
initializers.py Vortex superposition
|
||
kernels/
|
||
kernel_v2.cu Kernel entry (thin wrapper)
|
||
config/ Auto-generated headers (``config_grid.h``, …, ``config_obs.h``)
|
||
core/ Descriptors, layout, flags, params
|
||
operators/ Collision, LES, forcing
|
||
boundary/ Inlet, outlet, wall, curved, IBM
|
||
streaming/ Double-buffer & esopull
|
||
step/ Step orchestration
|
||
body/
|
||
objects.py SimObject / Cylinder / Sensor
|
||
manager.py ObjectManager; packed ``obs_gpu`` / ``obs_pinned``, B3 helpers
|
||
common/
|
||
preprocess.py Geometry utilities
|
||
output/
|
||
CelerisLab_stage1_architecture.md Architecture specification (v3)
|
||
refactor_brief_stage1.md Refactoring brief
|
||
high_re_audit_round1.md 8-round audit log
|
||
legacy/ Superseded code (FlowField, compiler v1, macros.h)
|
||
```
|
||
|
||
## Performance
|
||
|
||
Tested on Tesla V100-SXM2-16GB (CUDA 12.4):
|
||
|
||
| Config | Grid | MLUPS |
|
||
|---|---|---|
|
||
| Re100 MRT noLES | 384×192 | ~4200 |
|
||
| Re100 EsoPull SRT | 384×192 | ~3900 |
|
||
| Re3000 MRT+LES | 384×192 | ~4360 |
|
||
|
||
### Performance methodology
|
||
|
||
For a "kernel-dominant" baseline (closest to FluidX3D-style throughput testing),
|
||
use the dedicated script:
|
||
|
||
```bash
|
||
conda run -n pycuda_3_10 python tests/run_perf_baseline.py \
|
||
--lattice-model D2Q9 --nx 384 --ny 192 --nz 1 \
|
||
--steps 4000 --warmup-steps 400 --batch-steps 100
|
||
```
|
||
|
||
This path times GPU stepping (`stepper.step`) and reports MLUPS and batch latency
|
||
percentiles. By default it avoids host readbacks inside the timed loop.
|
||
|
||
APIs that trigger device-to-host transfers (DTOH) and can reduce MLUPS:
|
||
|
||
- `Simulation.get_macroscopic()` / `LBMField.get_macroscopic()` (downloads full DDF)
|
||
- `Simulation.get_ddf()` / `LBMField.download_ddf()`
|
||
- `Simulation.save_checkpoint()` (downloads field/state buffers)
|
||
- Body observation downloads (e.g. `ObjectManager.download_obs_full_async(...)`)
|
||
|
||
Use `tests/run_perf_baseline.py` switches (`--macro-every`, `--ddf-every`,
|
||
`--checkpoint-every`, `--obs-every`) to quantify each overhead path against the
|
||
pure-step baseline.
|
||
|
||
## Citation
|
||
|
||
If you use CelerisLab in your research, please cite:
|
||
|
||
```bibtex
|
||
@software{celerislab2026,
|
||
author = {Frank14f},
|
||
title = {CelerisLab: GPU-Accelerated Lattice Boltzmann Method Solver},
|
||
year = {2026},
|
||
url = {https://github.com/frank14f/CelerisLab}
|
||
}
|
||
```
|
||
|
||
## License
|
||
|
||
MIT License — see LICENSE file for details.
|