# 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 Lattice**: 2D nine-velocity lattice implementation
- **MRT Collision Model**: Multiple-Relaxation-Time collision operator for improved stability
- **Immersed Boundary Method (IBM)**: Support for complex geometries (cylinders, arbitrary shapes)
- **Flexible Boundary Conditions**: Periodic, velocity inlet, pressure outlet
- **Real-time Sensors**: Monitor flow properties at specific locations during simulation
- **Vortex Initialization**: Built-in support for Lamb, Oseen, and Taylor vortices
- **Dynamic Compilation**: Runtime CUDA kernel compilation with configurable parameters

## 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

### Basic Flow Simulation

```python
from CelerisLab import FlowField, utils

# Load configurations
config_cuda = utils.load_cuda_config()  # Uses default or CELERISLAB_CONFIG_DIR
config_field = utils.load_flow_field_config()

# Initialize flow field
flow = FlowField(
    config_cuda=config_cuda,
    config_field=config_field,
    device_id=0
)

# Add a cylinder obstacle
flow.add_cylinder(
    center=(50, 50, 0),
    radius=10,
    velocity=(0, 0, 0),
    use_IBM=True
)

# Add sensors to monitor flow
flow.add_sensor(position=(70, 50, 0))

# Run simulation
for step in range(10000):
    flow.run(1)
    
    # Read sensor data every 100 steps
    if step % 100 == 0:
        sensor_data = flow.read_sensor()
        print(f"Step {step}: Velocity = {sensor_data[0]}")
```

### Configuration

CelerisLab searches for configuration files in the following order:

1. **Explicit path**: Passed to `load_*_config(config_path)`
2. **Environment variable**: `CELERISLAB_CONFIG_DIR` environment variable
3. **Current directory**: `./configs/` in current working directory
4. **Package default**: Bundled `CelerisLab/configs/` directory

#### Configuration Files

**config_cuda.json**: CUDA execution parameters
```json
{
  "multi_gpu": false,
  "gpu_connection": "NVLINK",
  "required_cuda_capability": "6.0",
  "threads_per_block": 256,
  "X_1U": 16,
  "Y_1U": 16,
  "Z_1U": 1
}
```

**config_flowfield.json**: Flow physics parameters
```json
{
  "data_type": "FP32",
  "dimensionality": 2,
  "lattice": 9,
  "field_dim_in_U": [100, 100, 1],
  "viscosity": 0.01,
  "velocity": 0.1,
  "boundary_conditions": {
    "x": ["periodic", "periodic"],
    "y": ["periodic", "periodic"],
    "z": ["periodic", "periodic"]
  }
}
```

## API Reference

### FlowField Class

Main interface for running LBM simulations.

#### Constructor
```python
FlowField(config_cuda, config_field, device_id=0)
```

#### Methods

- `add_cylinder(center, radius, velocity, use_IBM=False)`: Add cylindrical obstacle
- `add_sensor(position)`: Add flow monitoring sensor
- `add_vortex(center, circulation, core_radius, vortex_type='Lamb')`: Initialize vortex
- `run(n_steps)`: Execute simulation steps
- `read_sensor()`: Read current sensor values
- `get_ddf()`: Get distribution function data
- `apply_ddf(ddf)`: Set distribution function data

### Utility Functions

- `load_cuda_config(config_path=None)`: Load CUDA configuration
- `load_flow_field_config(config_path=None)`: Load flow field configuration
- `check_cuda_device_availability(device_id=0)`: Verify CUDA device
- `get_device_info(device_id=0)`: Query GPU properties
- `estimate_memory_consumption(config_field, num_objects, num_sensors)`: Calculate memory usage

## Advanced Usage

### Custom Geometry with IBM

```python
# IBM enables smooth treatment of curved boundaries
flow.add_cylinder(
    center=(grid_x//2, grid_y//2, 0),
    radius=20,
    velocity=(0.0, 0.0, 0.0),
    use_IBM=True  # Enables immersed boundary method
)
```

### Multiple Sensors

```python
# Add sensors in a line downstream of obstacle
for i in range(5):
    flow.add_sensor(position=(100 + i*10, 50, 0))

# Read all sensors at once
sensor_data = flow.read_sensor()  # Returns array of shape (n_sensors, 3)
```

### Vortex Initialization

```python
# Initialize Lamb-Oseen vortex
flow.add_vortex(
    center=(50, 50, 0),
    circulation=1.0,
    core_radius=10.0,
    vortex_type='Lamb'
)
```

## Environment Variables

- `CELERISLAB_CONFIG_DIR`: Directory containing configuration JSON files
- `OMP_NUM_THREADS`: OpenMP thread count (recommend setting to 1 for GPU workflows)
- `MKL_NUM_THREADS`: Intel MKL thread count (recommend setting to 1)

## Performance Tips

1. **Grid Size**: Choose dimensions that are multiples of `unit_dimensions` in config_cuda.json
2. **Thread Block Size**: 256 threads/block works well for most GPUs
3. **Memory**: Estimate memory with `utils.estimate_memory_consumption()` before large runs
4. **Single-threaded Python**: Set `OMP_NUM_THREADS=1` to avoid CPU interference with GPU

## 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

## Contributing

Contributions are welcome! Please feel free to submit issues and pull requests.

## Acknowledgments

- Built with PyCUDA by Andreas Klöckner
- Inspired by the palabos C++ LBM library
