Python reimplementation of a Bayesian STE framework for joint source localisation in 2D atmospheric dispersion. M.Tech research, IIT Bombay (Aerospace Dynamics and Control).
A continuous point source releases tracer gas into a wind-driven diffusive field. The system recovers the source location (x_s, y_s) from noisy point-concentration measurements using a Sequential Importance Resampling (SIR) particle filter. The analytical Gaussian plume formula serves as the per-particle likelihood forward model; the full 2D advection-diffusion PDE generates the synthetic ground truth.
A controlled experiment tested six fixed sensor placement strategies over 100 sequential Bayesian updates. The sensor with the highest Fisher Information trace (off-axis 1σ plume boundary, trace(FIM)=23.4) did not outperform the near-source sensor (trace(FIM)=3.2) on the λ_min localisation metric. The near-source sensor achieved λ_min≈0 through sample impoverishment — not genuine posterior contraction — while the boundary sensor retained meaningful particle diversity. This empirically demonstrates that a sharp entropy drop is not evidence of localisation, and motivates a mobile-sensor (UAV) strategy that repositions before diversity is exhausted.
Particle-cloud evolution for the two contrasting sensors. The near-source core sensor (S1) collapses to λ_min≈0 through sample impoverishment, while the 1σ boundary sensor (S4a) retains particle diversity — the empirical basis for the key result above.
| Near-source core sensor (S1) | 1σ boundary sensor (S4a) |
|---|---|
![]() |
![]() |
| Module | File | Role |
|---|---|---|
| 1 | src/forward_model.py | 2D advection-diffusion PDE solver (upwind, forward Euler, absorbing BC) |
| 2 | src/analytical_plume.py | Steady-state Gaussian plume — grid and point evaluation |
| 3 | src/sensor.py | Bilinear-interpolation point sensor with additive Gaussian noise |
| 4 | src/particle_filter.py | SIR particle filter — vectorised update, systematic resampling, covariance |
| 5 | src/level_set.py | Marching-squares plume boundary extraction (scikit-image) |
| 6 | scripts/validation.py | PDE vs analytical L2 validation gate (result: 4.42% at dx=1m) |
| 7 | scripts/experiment.py | Six-strategy sensor placement experiment with Fisher Information pre-analysis |
| 8 | scripts/plot_results.py | Particle-weight evolution figures with shared global colorbar |
- Domain: 50×40 m, dx=dy=1 m, dt=0.1 s
- Wind: u=1.0 m/s (+x), diffusivity D=0.5 m²/s
- Source: (15, 20) m, Q=1.0 kg/s (fixed, known)
- Sensor noise: σ=0.01 kg/m²
- Particle filter: N=1000, 100 update steps
pip install -r requirements.txt
# Validation gate
python scripts/validation.py
# Full experiment
python scripts/experiment.py
# Particle evolution plots
python scripts/plot_results.pypytest tests/ -v # 37 tests across 5 modules- State Estimation Benchmarking Dashboard — KF/EKF/UKF/PF on the UNGM nonlinear benchmark (Project 1)
Aryan Prabhu — M.Tech Aerospace Engineering (Dynamics and Control), IIT Bombay.

