Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Gaussian puff — derivation from the 3-D advection-diffusion equation

Derivation of the Gaussian puff solution

This note derives the Gaussian-puff solution used by the forward model in plume_simulation.gauss_puff.puff.puff_concentration. The setup mirrors the steady Gaussian plume derivation in 00_gaussian_plume_derivation.md, but with a different simplification strategy: instead of dropping the time derivative (A3), we keep it and work with an instantaneous release — the continuous-source plume and the repeated-release puff are two projections of the same 3-D advection-diffusion equation. Stockie [1] covers the puff model in §3.5.6 and time-varying wind in §§4.3-4.4; his approach derives the puff by the same Laplace-transform / r-coordinate route he uses for the plume (see Stockie eq. 3.19-3.20). In this note we follow a complementary path — a Galilean transformation into the puff-centre frame — which reduces the PDE to the free-space 3-D heat equation and makes the time-varying-wind extension especially natural. The two routes are equivalent for constant wind; for time-varying wind the Galilean path is the one we implement with diffrax in wind.py.

1. Governing equation

As in the plume case, mass conservation for a single contaminant C(x,t)C(\vec x, t) [kg/m³] at position x=(x,y,z)\vec x = (x, y, z) [m] and time tt [s] reads (Stockie 2.2)

Ct+(Cu)  =  (KC)+S,\frac{\partial C}{\partial t} + \nabla \cdot (C \vec u) \;=\; \nabla \cdot (\mathbf K\, \nabla C) + S,

with wind u\vec u [m/s], eddy diffusivity K=diag(Kx,Ky,Kz)\mathbf K = \operatorname{diag}(K_x, K_y, K_z) [m²/s], and volumetric source SS [kg/m³·s].

2. Assumptions for the puff model

We impose a subset of Stockie’s A1-A7 — different from those chosen for the plume.

Crucially, we keep C/t\partial C / \partial t (unlike A3 in the plume) and we keep the along-wind diffusion term Kx2C/x2K_x\, \partial^2 C / \partial x^2 (unlike A5). A puff released from a point source has non-zero spread in every direction, not just crosswind; dropping KxK_x would force an unphysical zero-width along-wind profile at all times.

Applying (P1)-(P3) to equation gives (ignoring the ground reflection for now — we’ll add it by method of images in §5)

Ct+u(t)Cx+v(t)Cy  =  Kh(2Cx2+2Cy2)+Kz2Cz2+mδ(tt0)δ(xx0),\frac{\partial C}{\partial t} + u(t)\, \frac{\partial C}{\partial x} + v(t)\, \frac{\partial C}{\partial y} \;=\; K_h\, \left(\frac{\partial^2 C}{\partial x^2} + \frac{\partial^2 C}{\partial y^2}\right) + K_z\, \frac{\partial^2 C}{\partial z^2} + m\, \delta(t - t_0)\, \delta(\vec x - \vec x_0),

on R3×R\mathbb{R}^3 \times \mathbb{R}, with C(x,t)0C(\vec x, t) \to 0 as x|\vec x| \to \infty and C(x,t<t0)=0C(\vec x, t < t_0) = 0.

3. Galilean transformation into the puff-centred frame

The advection term can be absorbed by moving into a frame that follows the puff centre. Define the puff-centre trajectory

xc(t)  =  x0+t0tu(τ)dτ,yc(t)  =  y0+t0tv(τ)dτ,x_c(t) \;=\; x_0 + \int_{t_0}^{t} u(\tau)\, \mathrm d\tau, \qquad y_c(t) \;=\; y_0 + \int_{t_0}^{t} v(\tau)\, \mathrm d\tau,

i.e. the trajectory of a passive tracer at x0\vec x_0 under the wind (u,v)(u, v). In the moving coordinates x~=xxc(t)\tilde x = x - x_c(t), y~=yyc(t)\tilde y = y - y_c(t), z~=z\tilde z = z, t~=tt0\tilde t = t - t_0, the chain rule gives

Ctx,y,z  =  Ct~x˙cCx~y˙cCy~,Cxt=Cx~,\frac{\partial C}{\partial t} \bigg|_{x, y, z} \;=\; \frac{\partial C}{\partial \tilde t} - \dot x_c\, \frac{\partial C}{\partial \tilde x} - \dot y_c\, \frac{\partial C}{\partial \tilde y}, \qquad \frac{\partial C}{\partial x} \bigg|_{t} = \frac{\partial C}{\partial \tilde x},

and since x˙c=u(t)\dot x_c = u(t) and y˙c=v(t)\dot y_c = v(t) by construction, equation becomes

Ct~  =  Kh(2Cx~2+2Cy~2)+Kz2Cz~2+mδ(t~)δ(x~)δ(y~)δ(z~H),\frac{\partial C}{\partial \tilde t} \;=\; K_h\, \left(\frac{\partial^2 C}{\partial \tilde x^2} + \frac{\partial^2 C}{\partial \tilde y^2}\right) + K_z\, \frac{\partial^2 C}{\partial \tilde z^2} + m\, \delta(\tilde t)\, \delta(\tilde x)\, \delta(\tilde y)\, \delta(\tilde z - H),

the 3-D heat equation with a delta source at t~=0\tilde t = 0. The Galilean transform has removed the advection completely — this is the same trick that makes plume theory tractable under A2.

4. Fundamental solution (unbounded domain)

Equation moving on R3\mathbb R^3 admits the separable fundamental solution

C(x~,y~,z~,t~)  =  mG2Kht~(x~)G2Kht~(y~)G2Kzt~(z~H),C(\tilde x, \tilde y, \tilde z, \tilde t) \;=\; m\, G_{2 K_h \tilde t}(\tilde x)\, G_{2 K_h \tilde t}(\tilde y)\, G_{2 K_z \tilde t}(\tilde z - H),

where

Gσ2(s)  =  12πσ2exp ⁣(s22σ2)G_{\sigma^2}(s) \;=\; \frac{1}{\sqrt{2 \pi \sigma^2}}\, \exp\!\left(- \frac{s^2}{2 \sigma^2}\right)

is the 1-D normal PDF with variance σ2\sigma^2. Direct substitution confirms this. The identification

σx2(t~)=σy2(t~)=2Kht~,σz2(t~)=2Kzt~\sigma_x^2(\tilde t) = \sigma_y^2(\tilde t) = 2 K_h\, \tilde t, \qquad \sigma_z^2(\tilde t) = 2 K_z\, \tilde t

is the Fickian-diffusion scaling: variances grow linearly in puff age.

5. Ground reflection (method of images)

The Neumann boundary KzC/z~z~=0=0K_z\, \partial C/\partial \tilde z|_{\tilde z = 0} = 0 can be enforced by superposing the free-space solution with an image source at z~=H\tilde z = -H (reflected about z=0z = 0). The full solution on {z~0}\{\tilde z \geq 0\} is then

C(x~,y~,z~,t~)  =  mGσx2(x~)Gσy2(y~)[Gσz2(z~H)  +  Gσz2(z~+H)],C(\tilde x, \tilde y, \tilde z, \tilde t) \;=\; m\, G_{\sigma_x^2}(\tilde x)\, G_{\sigma_y^2}(\tilde y)\, \Big[\, G_{\sigma_z^2}(\tilde z - H) \;+\; G_{\sigma_z^2}(\tilde z + H)\, \Big],

which is the expression implemented in puff_concentration. The image source has the same mass as the real source, so the total contaminant mass in the half-space is preserved: 0(Gσz2(z~H)+Gσz2(z~+H))dz~=1\int_0^\infty (G_{\sigma_z^2}(\tilde z - H) + G_{\sigma_z^2}(\tilde z + H))\, \mathrm d \tilde z = 1 by construction.

6. The σ-parameterisation (Pasquill-Gifford)

As in the plume case, the linear-in-t~\tilde t scaling σ² is a consequence of constant eddy diffusivity — an idealisation. In practice the dispersion is driven by turbulent eddies whose statistics depend on atmospheric stability, surface roughness, and the scale of the eddy relative to the puff age. Empirical fits (Pasquill, 1961; Gifford, 1961) to field data give σ as functions of puff travel distance s=xc(t)x0s = |\vec x_c(t) - \vec x_0| rather than age t~\tilde t, because ss correlates more directly with the turbulent spectrum encountered by the puff.

The classic PG correlations are tabulated curves per stability class A (very unstable) through F (very stable). Convenient log-quadratic fits (Beychok 2005; used in plume_simulation.gauss_puff.dispersion.calculate_pg_dispersion) are

σy(s)=exp ⁣(ay+bylns+cy(lns)2),σz(s)=exp ⁣(az+bzlns+cz(lns)2),\sigma_y(s) = \exp\!\big(a_y + b_y\, \ln s + c_y\, (\ln s)^2\big), \qquad \sigma_z(s) = \exp\!\big(a_z + b_z\, \ln s + c_z\, (\ln s)^2\big),

with stability-class-specific coefficients (ay,by,cy,az,bz,cz)(a_y, b_y, c_y, a_z, b_z, c_z). Horizontal spread is taken isotropic: σx=σy\sigma_x = \sigma_y.

Using σ² we could define an effective time-dependent diffusivity Kh(t~)=σy2(s(t~))/(2t~)K_h(\tilde t) = \sigma_y^2(s(\tilde t)) / (2 \tilde t), but this is a fit, not a first-principles quantity — exactly the eddy-diffusivity / σ-parameterisation inconsistency discussed in Stockie §3.4. The σ-form is more robust empirically, so we adopt it, with the understanding that the “diffusion equation” interpretation is a useful fiction.

plume_simulation.gauss_puff.dispersion also exposes the Briggs power-law parameterisation (calculate_briggs_dispersion_xyz) shared with the plume model, so the puff and plume can be run with the same σ(s)\sigma(s) curves when a consistent comparison is needed.

7. Time-varying wind via cumulative integrals

The wind-dependent part of the solution lives entirely in the puff-centre trajectory centre: once we know xc(t)\vec x_c(t) and the travel distance

s(t)  =  t0tu(τ)dτ,s(t) \;=\; \int_{t_0}^{t} \big|\vec u(\tau)\big|\, \mathrm d\tau,

we have everything needed to evaluate puff solution with σ from PG. The three integrals Iu(t)=t0tudτI_u(t) = \int_{t_0}^t u\, \mathrm d\tau, Iv(t)=t0tvdτI_v(t) = \int_{t_0}^t v\, \mathrm d\tau, s(t)s(t) satisfy the trivial ODE system

ddt(IuIvs)  =  (u(t)v(t)u(t)2+v(t)2),(Iu(t0)Iv(t0)s(t0))=(000),\frac{\mathrm d}{\mathrm d t} \begin{pmatrix} I_u \\ I_v \\ s \end{pmatrix} \;=\; \begin{pmatrix} u(t) \\ v(t) \\ \sqrt{u(t)^2 + v(t)^2} \end{pmatrix}, \qquad \begin{pmatrix} I_u(t_0) \\ I_v(t_0) \\ s(t_0) \end{pmatrix} = \begin{pmatrix} 0 \\ 0 \\ 0 \end{pmatrix},

which is solved once with diffrax.diffeqsolve in plume_simulation.gauss_puff.wind.cumulative_wind_integrals, evaluating at all release times and the current evaluation time in a single adaptive sweep. Puff-ii positions are then elementwise differences: xc(i)(t)=x0+Iu(t)Iu(t0(i))x_c^{(i)}(t) = x_0 + I_u(t) - I_u(t_0^{(i)}), and likewise for yc(i),s(i)y_c^{(i)}, s^{(i)}. This avoids any Python-level per-puff loop and keeps the forward model differentiable for NUTS.

8. Continuous source as a superposition of puffs

A continuous source at rate Q(t)Q(t) [kg/s] releases mi=Q(ti)Δtm_i = Q(t_i) \Delta t every Δt=1/frelease\Delta t = 1 / f_{\text{release}} seconds, starting at t0=0t_0 = 0. By linearity of equation, the total concentration is the superposition

C(x,t)  =  i:titC(i)(x,t),C(i)(x,t)  =  [equation \href with mi,x0=xc(ti),t~=tti].C(\vec x, t) \;=\; \sum_{i : t_i \leq t} C^{(i)}(\vec x, t), \qquad C^{(i)}(\vec x, t) \;=\; [\text{equation}~\href{#eq-puff-solution}{(5)}~\text{with}~m_i, \vec x_0 = \vec x_c(t_i), \tilde t = t - t_i].

simulate_puff_field implements this sum via jax.vmap. In the limit freleasef_{\text{release}} \to \infty at fixed QQ, Δt0\Delta t \to 0 and the sum becomes an integral — this is the formal link to the steady plume. Stockie asks the reader to verify this in his Exercise 8: integrating his puff formula (Stockie Eq. 3.20) over t[0,)t \in [0, \infty) recovers the plume expression (Stockie Eq. 3.8). For finite Δt\Delta t, the puff sum has a jittery structure near the source that the steady plume lacks; smooth-looking fields require Δt\Delta t small compared with the transit time from source to receptor.

Comparison with Stockie’s sub-interval approach (§§4.3-4.4)

For time-varying wind, Stockie §4.3 divides the total window into sub-intervals Δt10\Delta t \sim 10 min, assumes the wind is steady within each sub-interval, and applies the steady plume solution in a rotated coordinate frame aligned with each sub-interval’s wind vector. His §4.4 then sums deposition contributions over the sub-intervals. This is a quasi-steady plume treatment — fast and accurate for slowly-varying winds, but fundamentally limited to timescales long enough for each sub-interval plume to reach steady state.

The puff formulation in this note sidesteps that limitation: because we keep the time derivative in equation, we can handle winds that vary on timescales shorter than the plume transit time — a buffeting wind, a gust, a sudden directional shift. The price is computational: instead of one plume per sub-interval we carry an NpuffsN_\text{puffs}-particle ensemble and integrate wind ODE through it. diffrax makes this ensemble evolution a single jittable pass.

9. Operational pitfalls

Three recurring gotchas in implementing the puff model:

  1. Release cadence vs. receptor distance. The field within the first few puff-widths of the source is dominated by a small number of individual puffs and does not look Gaussian — it looks bumpy. If you are evaluating close to the source, pick Δt\Delta t small enough that several puffs overlap within one σy\sigma_y. A rule of thumb: Δtσy(s)/u\Delta t \lesssim \sigma_y(s) / |\vec u| for the shortest relevant ss.
  2. σ evaluation at s=0s = 0. PG gives σ0\sigma \to 0 at the moment of release, so a freshly-released puff has infinite centreline concentration. calculate_pg_dispersion clamps ss to 1 m to avoid this — physically the puff has a nonzero initial size σ0\sigma_0 set by the source geometry, which is ignored by PG but rarely matters at receptor distances \gtrsim tens of metres.
  3. Calm winds. When u0|\vec u| \to 0, the puff trajectory freezes and puffs pile up at the source. The model in this limit is unphysical — a buoyant source should transition to a puff-rise regime, not a static pile. The puff forward model does not clamp calm winds (unlike the steady plume, where MIN_WIND_SPEED keeps the C ∝ 1/u normalization finite); for inference under a puff forward model, calms should be filtered out upstream or handled via a stability-specific prior on the wind schedule.

These are not bugs in the PG puff model — they are the edges of its domain of validity, inherited from the same idealisations that make it tractable.

Footnotes
  1. Stockie, J. M. (2011). The Mathematics of Atmospheric Dispersion Modelling. SIAM Review, 53(2), 349-372. https://www.sfu.ca/~jstockie/atmos/paper.pdf. Puff solution and its plume-superposition relation: §3.5.6 (Eqs. 3.19-3.20 and Exercise 8). Time-varying wind via sub-interval quasi-steady plumes: §§4.3-4.4.