lsurf 1.0.0
L-SURF: GPU-accelerated physically-accurate raytracing framework for simulating light-surface interactions
License: BSD-3-Clause-Clear
Requires Python: >=3.13
Keywords: raytracing, gpu, cuda, numba, optics, fresnel, reflection, refraction, waves, ocean, simulation, physics

# L-SURF

<p align="center">
  <img src="lsurf_logo.jpg" alt="L-SURF Logo" width="400">
</p>

<p align="center">
  <a href="https://pypi.org/project/lsurf/"><img src="https://badge.fury.io/py/lsurf.svg" alt="PyPI version"></a>
  <a href="https://www.python.org/downloads/"><img src="https://img.shields.io/badge/python-3.13+-blue.svg" alt="Python 3.13+"></a>
  <a href="https://spdx.org/licenses/BSD-3-Clause-Clear.html"><img src="https://img.shields.io/badge/License-BSD--3--Clause--Clear-blue.svg" alt="License"></a>
  <a href="https://l-surf-b86f2d.gitlab.io/"><img src="https://img.shields.io/badge/docs-GitLab%20Pages-orange.svg" alt="Documentation"></a>
</p>

**L**ight **SURF**ace Reflections - GPU-accelerated physically-accurate raytracing framework for simulating light-surface interactions.

**Documentation:** https://l-surf-b86f2d.gitlab.io

## Features

- **Fresnel reflection and refraction** - Physically accurate polarization-dependent coefficients
- **Multiple scattering** - Multi-bounce ray tracing with intensity splitting
- **Ocean wave surfaces** - Gerstner wave modeling with curved Earth geometry
- **GPU acceleration** - CUDA-accelerated via Numba for high-performance simulations
- **Wavelength-dependent materials** - Sellmeier and Cauchy dispersion models
- **Polarization tracking** - s and p polarization component handling

## Installation

### Prerequisites

#### NVIDIA GPU Support (Recommended)

For GPU-accelerated ray tracing, you need:

1. **NVIDIA GPU** with CUDA Compute Capability 3.5+ (most GPUs from 2012 onwards)
2. **NVIDIA Driver** (version 450+ recommended)
3. **CUDA Toolkit** (version 11.0+ recommended)

**Check your system:**
```bash
# Check NVIDIA driver
nvidia-smi

# Check CUDA version (if installed)
nvcc --version
```

**Installing NVIDIA drivers (if needed):**
```bash
# Ubuntu/Debian
sudo apt update
sudo apt install nvidia-driver-535  # or latest version

# Fedora
sudo dnf install akmod-nvidia
```

**Installing CUDA Toolkit:**
- Download from [NVIDIA CUDA Downloads](https://developer.nvidia.com/cuda-downloads)
- Or use your distribution's package manager

> **Note:** The package works without a GPU (CPU fallback), but simulations will be significantly slower.

#### Python Environment

- Python >= 3.13
- pip, conda, or mamba

### Install from PyPI

```bash
pip install lsurf
```

### Development Installation

```bash
git clone https://github.com/tobi-h/lsurf.git
cd lsurf
pip install -e .
```

### Verifying GPU Setup

After installation, verify everything works:

```python
from numba import cuda

# Check CUDA availability
print(f"CUDA available: {cuda.is_available()}")

if cuda.is_available():
    gpu = cuda.get_current_device()
    print(f"GPU: {gpu.name}")
    print(f"Compute Capability: {gpu.compute_capability}")
    print(f"Memory: {gpu.total_memory / 1e9:.1f} GB")
```

### Troubleshooting

| Issue | Solution |
|-------|----------|
| `CUDA not available` | Install NVIDIA drivers and CUDA toolkit |
| `conda: PackagesNotFoundError` | Update conda: `conda update -n base conda` |
| `numba.cuda import error` | Ensure CUDA_HOME is set and nvcc is in PATH |
| `Out of memory` | Reduce `num_rays` or use batched processing |

## Quick Start

### Using the CLI (Recommended)

The **command-line interface** is the recommended way to run simulations:

```bash
# Build a configuration interactively
lsurf build -o my_simulation.yaml

# Or use a built-in template
lsurf build --template ocean -o ocean_sim.yaml

# Validate the configuration
lsurf run my_simulation.yaml --dry-run

# Run the simulation
lsurf run my_simulation.yaml --progress

# Run with custom parameters
lsurf run my_simulation.yaml --num-rays 100000 --output-dir ./results
```

### Using the GUI

For interactive exploration and visualization, use the **graphical interface**:

```bash
# Launch the GUI
lsurf gui

# Or load an existing configuration
lsurf gui my_simulation.yaml
```

The GUI provides:
- Interactive 3D visualization of geometry
- Configuration editor with live preview
- One-click simulation execution
- Built-in result visualization and analysis

### Python API

For custom workflows and advanced use cases, use the Python API with `GeometryBuilder`:

```python
import lsurf as sr
from lsurf.geometry import GeometryBuilder
from lsurf.simulation import Simulation, SimulationConfig
from lsurf.surfaces import SphereSurface, PlaneSurface, SurfaceRole

# Define constants
EARTH_RADIUS = 6.371e6  # meters

# Create materials
atmosphere = sr.ExponentialAtmosphere(n_sea_level=1.000293)

# Create surfaces
ocean = SphereSurface(
    center=(0, 0, -EARTH_RADIUS),
    radius=EARTH_RADIUS,
    role=SurfaceRole.OPTICAL,
    name="ocean",
)

detector = PlaneSurface(
    point=(0, 0, 35000),
    normal=(0, 0, 1),
    role=SurfaceRole.DETECTOR,
    name="detector_35km",
)

# Build geometry with named media
geometry = (
    GeometryBuilder()
    .register_medium("atmosphere", atmosphere)
    .register_medium("ocean", sr.WATER)
    .set_background("atmosphere")  # Ray propagation medium
    .add_surface(ocean, front="atmosphere", back="ocean")
    .add_detector(detector)
    .build()
)

# Configure simulation
config = SimulationConfig(
    step_size=100.0,           # Max step size in meters
    max_bounces=5,             # Maximum surface interactions
    adaptive_stepping=True,    # Use smaller steps near surfaces
    min_step_size=3e-4,        # 0.3mm for ~1ps time resolution
)

# Create simulation and run
sim = Simulation(geometry, config)

# Generate rays from a source
source = sr.CollimatedBeam(
    center=(0, 0, 1000),
    direction=(0.17, 0, -0.98),  # ~10° grazing angle
    radius=10.0,
    num_rays=10000,
    wavelength=532e-9,
)
rays = source.generate()

# Run simulation
result = sim.run(rays)
print(f"Detected: {result.statistics.rays_detected}")
print(f"Absorbed: {result.statistics.rays_absorbed}")
```

### Simple Surface Interaction

For quick tests without the full simulation framework:

```python
import lsurf as sr

# Create a water surface
surface = sr.PlaneSurface(
    point=(0, 0, 0),
    normal=(0, 0, 1),
    material_front=sr.AIR_STP,
    material_back=sr.WATER,
    role=sr.SurfaceRole.OPTICAL,
    name="water_surface",
)

# Create a collimated beam
source = sr.CollimatedBeam(
    center=(0, 0, 0.1),
    direction=(0.707, 0, -0.707),  # 45° incidence
    radius=0.01,
    num_rays=1000,
    wavelength=532e-9,
)
rays = source.generate()

# Process reflection/refraction
reflected, refracted = sr.process_surface_interaction(
    rays, surface,
    wavelength=532e-9,
    generate_reflected=True,
    generate_refracted=True,
)

print(f"Reflected rays: {reflected.num_rays}")
print(f"Refracted rays: {refracted.num_rays}")
```

## Example Scripts

The `scripts/` directory contains **educational example scripts** that demonstrate specific features and techniques. These are provided as learning resources and references for custom workflows.

> **Note:** For standard simulations, use the CLI (`lsurf run`) or GUI (`lsurf gui`) instead of writing custom scripts. The CLI/GUI handle configuration management, validation, and output automatically.

| Script                                 | Description                                    |
| -------------------------------------- | ---------------------------------------------- |
| `01_basic.py`                          | Basic raytracing setup                         |
| `02_sources.py`                        | Different ray source types                     |
| `03_visualization.py`                  | Visualization capabilities                     |
| `04_glass_reflection.py`               | Fresnel reflection at glass interface          |
| `05_water_brewster.py`                 | Brewster angle validation                      |
| `06_detector_scan.py`                  | Detector position scanning (planar)            |
| `07_detector_scan_waves.py`            | Detector scanning with wave surface            |
| `08_detector_scan_waves_largescale.py` | Large-scale ocean simulation                   |
| `09_full_3d_curved_ocean.py`           | Full 3D ray tracing with curved ocean surface  |
| `10_time_spread_estimate.py`           | Geometric time spread estimation               |

Run an example:

```bash
cd scripts
python 01_basic.py
```

## Dependencies

| Category | Packages |
|----------|----------|
| **Core** | numpy >= 1.24, matplotlib >= 3.7, pydantic >= 2.0 |
| **GPU** | numba >= 0.58, CUDA toolkit >= 11.0 |
| **Optional** | h5py >= 3.8 (HDF5 support), astropy-healpix (spherical analysis) |
| **Dev** | pytest, black, ruff, mypy, pre-commit |

All dependencies are automatically installed via `environment.yml` or `pip install -e ".[dev]"`.

## Project Structure

```
lsurf/
├── src/
│   └── lsurf/                     # Main package
│       ├── geometry/              # GeometryBuilder and Geometry
│       ├── simulation/            # Simulation, SimulationConfig, results
│       ├── propagation/           # Ray propagation engines and GPU kernels
│       ├── materials/             # Material definitions (homogeneous, atmosphere)
│       ├── sources/               # Ray source generators
│       ├── surfaces/              # Surface geometries (cpu/ and gpu/)
│       ├── detectors/             # Detection and analysis
│       ├── utilities/             # Ray data, fresnel, recording
│       ├── visualization/         # Plotting functions
│       └── interactions.py        # Surface interaction processing
├── scripts/                       # Example scripts
├── tests/                         # Test suite
├── docs/                          # Sphinx documentation
├── pyproject.toml                 # Python packaging config
└── environment.yml                # Development environment
```

## Testing

```bash
pytest tests/ -v
```

## License

Clear BSD License - see [LICENSE](LICENSE) for details.

Copyright (c) 2026 Tobias Heibges. All rights reserved.