Skip to content
A NumPy based implementation of a puff-based odour plume model
Branch: master
Clone or download

PomPy - Puff-based odour plume model in Python

PyPI version Documentation Status DOI

PomPy is a NumPy based implementation of the puff-based odour plume model described in

Farrell, J.A., Murlis, J., Long, X., Li, W. and Cardé, R.T., 2002. Filament-based atmospheric dispersion model to achieve short time-scale structure of odor plumes. Environmental fluid mechanics, 2(1-2), pp.143-169. Index page ( :: PDF (

An example simulated concentration field generated by the package is shown below

Plume model animation

PomPy allows simulation of dynamic 2D odour concentration fields which show some of the key characteristics of real chemical plumes in turbulent flows including short term intermittency, diffusive effects and longer term variations in spatial extent and location, while being cheaper to run than a full fluid dynamics simulation.

Installation and requirements

PomPy requires a Python 2.7 or newer environment. For usage of the pompy.models and pompy.processors modules NumPy and SciPy are required. For the demonstrations in the pompy.demos module Matplotlib is also required. The requirements.txt file lists versions of NumPy, SciPy and Matplotlib known to work with PomPy. A Jupyter notebook server installation will also be required to run the example notebooks locally.

To install the package run

pip install pompy


Documentation of the PomPy API is available at Read the Docs.

Two Jupyter notebooks showing examples of using the package are included in the notebooks directory. The Farrell et al. (2002) example.ipynb notebook illustrates an example of using PomPy to generate an animation of an odour plume using the simulation parameters described in Farrell et al. (2002) and includes keys to match the symbols used to define the parameter in the paper with the relevant PomPy class attributes. The Demonstration.ipynb notebook and accompanying module pompy.demos give several other examples of setting up plume models using PomPy and visualising the simulations.

The below script will generate a 20 second MP4 animation (see above for animated GIF version) of a generated plume with model parameters consistent with those proposed in Farrell et al. (2002).

from pompy import models, processors
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation

# Seed random number generator
seed = 20180517
rng = np.random.RandomState(seed)

# Define wind model simulation region
wind_region = models.Rectangle(x_min=0., x_max=100., y_min=-50., y_max=50.)

# Define wind model parameters
wind_model_params = { 
    'n_x': 21,
    'n_y': 21,
    'u_av': 1.,
    'v_av': 0.,
    'k_x': 10.,
    'k_y': 10.,
    'noise_gain': 20.,
    'noise_damp': 0.1,
    'noise_bandwidth': 0.2,
    'use_original_noise_updates': True

# Create wind model object
wind_model = models.WindModel(wind_region, rng=rng, **wind_model_params)

# Define plume simulation region
# This is a subset of the wind simulation region
sim_region = models.Rectangle(x_min=0., x_max=50., y_min=-12.5, y_max=12.5)

# Define plume model parameters
plume_model_params = {
    'source_pos': (5., 0., 0.),
    'centre_rel_diff_scale': 2.,
    'puff_release_rate': 10,
    'puff_init_rad': 0.001**0.5,
    'puff_spread_rate': 0.001,
    'init_num_puffs': 10,
    'max_num_puffs': 1000,
    'model_z_disp': True,

# Create plume model object
plume_model = models.PlumeModel(
    rng=rng, sim_region=sim_region, wind_model=wind_model, **plume_model_params)

# Define concentration array (image) generator parameters
array_gen_params = {
    'array_z': 0.,
    'n_x': 500,
    'n_y': 250,
    'puff_mol_amount': 8.3e8

# Create concentration array generator object
array_gen = processors.ConcentrationArrayGenerator(
    array_xy_region=sim_region, **array_gen_params)
# Set up figure
fig = plt.figure(figsize=(5, 2.5))
ax = fig.add_axes([0., 0., 1., 1.])

# Display initial concentration field as image
conc_array = array_gen.generate_single_array(plume_model.puff_array)
conc_im = ax.imshow(
    conc_array.T, extent=sim_region, vmin=0., vmax=1e10, cmap='Reds')

# Simulation timestep
dt = 0.01

# Run wind model forward to equilibrate
for k in range(2000):

# Define animation update function
def update(i):
    # Do 10 time steps per frame update
    for k in range(10):
    conc_array = array_gen.generate_single_array(plume_model.puff_array)
    return [conc_im]

# Animate plume concentration and save as MP4
anim = FuncAnimation(fig, update, frames=400, repeat=False)'plume.mp4', dpi=100, fps=20, extra_args=['-vcodec', 'libx264'])
You can’t perform that action at this time.