Recently I added some alternative correlations to GasDispersion.jl, the julia package I put together for basic chemical release modeling, and I thought it would be worthwhile to circle back and look at some of those in more depth.
Typically, when evaluating various release scenarios, key pieces of the model are specified in advance and each scenario uses the same set of assumptions: comparing apples to apples. For a Gaussian plume dispersion model there are really three key correlations used for the model parameters: the wind-speed profile, crosswind dispersion, and vertical dispersion. Correlations for each of these are given in the standard references and there is not, to my mind, any deep reason to prefer one reference over the another. Besides maintaining consistency with other modeling or perhaps with industry practice in a particular area.
This raises the obvious question: how much does it matter which reference you use? Usually one takes the results of a Gaussian plume model with a fair grain of salt, these are "order of magnitude" estimates really. That's what I'm going to look at here.
#hide
# This is some boiler plate to make the notebook stand alone
# Creates a temporary project and adds only the packages used
# in the notebook to the Project.toml
using Pkg
Pkg.activate(; temp=true)
Pkg.add([PackageSpec(name="Plots", version="1.39.0"),
PackageSpec(name="LaTeXStrings", version="1.3.1"),
PackageSpec(name="Measures", version="0.3.2"),
PackageSpec(name="Contour", version="0.6.2"),
PackageSpec(name="GasDispersion", version="0.2.1")])
Pkg.instantiate();
Activating new project at `/tmp/jl_0wnbZC` Resolving package versions... Updating `/tmp/jl_0wnbZC/Project.toml` [d38c429a] + Contour v0.6.2 [ac6af613] + GasDispersion v0.2.1 [b964fa9f] + LaTeXStrings v1.3.1 [442fdcdd] + Measures v0.3.2 [91a5bcdd] + Plots v1.39.0 Updating `/tmp/jl_0wnbZC/Manifest.toml` [d1d4a3ce] + BitFlags v0.1.8 [944b1d66] + CodecZlib v0.7.3 [35d6a980] + ColorSchemes v3.24.0 [3da002f7] + ColorTypes v0.11.4 [c3611d14] + ColorVectorSpace v0.10.0 [5ae59095] + Colors v0.12.10 [34da2185] + Compat v4.12.0 [f0e56b4a] + ConcurrentUtilities v2.3.0 [d38c429a] + Contour v0.6.2 [a8cc5b0e] + Crayons v4.1.1 [9a962f9c] + DataAPI v1.15.0 [82cc6244] + DataInterpolations v4.6.0 [864edb3b] + DataStructures v0.18.16 [e2d170a0] + DataValueInterfaces v1.0.0 [8bb1440f] + DelimitedFiles v1.9.1 [ffbed154] + DocStringExtensions v0.9.3 [460bff9d] + ExceptionUnwrapping v0.1.10 [c87230d0] + FFMPEG v0.4.1 [53c48c17] + FixedPointNumbers v0.8.4 [59287772] + Formatting v0.4.2 ⌅ [28b8d3ca] + GR v0.72.10 [ac6af613] + GasDispersion v0.2.1 [42e2da0e] + Grisu v1.0.2 [cd3eb016] + HTTP v1.10.1 [92d709cd] + IrrationalConstants v0.2.2 [82899510] + IteratorInterfaceExtensions v1.0.0 [1019f520] + JLFzf v0.1.7 [692b3bcd] + JLLWrappers v1.5.0 [682c06a0] + JSON v0.21.4 [b964fa9f] + LaTeXStrings v1.3.1 [23fbe1c1] + Latexify v0.16.1 [2ab3a3ac] + LogExpFunctions v0.3.26 [e6f89c97] + LoggingExtras v1.0.3 [1914dd2f] + MacroTools v0.5.13 [739be429] + MbedTLS v1.1.9 [442fdcdd] + Measures v0.3.2 [e1d29d7a] + Missings v1.1.0 [77ba4419] + NaNMath v1.0.2 [4d8831e6] + OpenSSL v1.4.1 [bac558e1] + OrderedCollections v1.6.3 [69de0a69] + Parsers v2.8.1 [b98c9c47] + Pipe v1.3.0 [ccf2f8ad] + PlotThemes v3.1.0 [995b91a9] + PlotUtils v1.4.0 [91a5bcdd] + Plots v1.39.0 [aea7be01] + PrecompileTools v1.2.0 [21216c6a] + Preferences v1.4.1 [08abe8d2] + PrettyTables v2.3.1 [3cdcf5f2] + RecipesBase v1.3.4 [01d81517] + RecipesPipeline v0.6.12 [189a3867] + Reexport v1.2.2 [05181044] + RelocatableFolders v1.0.1 [ae029012] + Requires v1.3.0 [6c6a2e73] + Scratch v1.2.1 [992d4aef] + Showoff v1.0.3 [777ac1f9] + SimpleBufferStream v1.1.0 [a2af1166] + SortingAlgorithms v1.2.1 [276daf66] + SpecialFunctions v2.3.1 [82ae8749] + StatsAPI v1.7.0 [2913bbd2] + StatsBase v0.34.2 [892a3eda] + StringManipulation v0.3.4 [3783bdb8] + TableTraits v1.0.1 [bd369af6] + Tables v1.11.1 [62fd8b95] + TensorCore v0.1.1 [3bb67fe8] + TranscodingStreams v0.10.2 [5c2747f8] + URIs v1.5.1 [1cfade01] + UnicodeFun v0.4.1 [1986cc42] + Unitful v1.19.0 [45397f5d] + UnitfulLatexify v1.6.3 [41fe7b60] + Unzip v0.2.0 [6e34b625] + Bzip2_jll v1.0.8+1 [83423d85] + Cairo_jll v1.16.1+1 [2702e6a9] + EpollShim_jll v0.0.20230411+0 [2e619515] + Expat_jll v2.5.0+0 [b22a6f82] + FFMPEG_jll v4.4.4+1 [a3f928ae] + Fontconfig_jll v2.13.93+0 [d7e528f0] + FreeType2_jll v2.13.1+0 [559328eb] + FriBidi_jll v1.0.10+0 [0656b61e] + GLFW_jll v3.3.9+0 ⌅ [d2c73de3] + GR_jll v0.72.10+0 [78b55507] + Gettext_jll v0.21.0+0 [7746bdde] + Glib_jll v2.76.5+0 [3b182d85] + Graphite2_jll v1.3.14+0 [2e76f6c2] + HarfBuzz_jll v2.8.1+1 [aacddb02] + JpegTurbo_jll v3.0.1+0 [c1c5ebd0] + LAME_jll v3.100.1+0 [88015f11] + LERC_jll v3.0.0+1 [1d63c593] + LLVMOpenMP_jll v15.0.7+0 [dd4b983a] + LZO_jll v2.10.1+0 ⌅ [e9f186c6] + Libffi_jll v3.2.2+1 [d4300ac3] + Libgcrypt_jll v1.8.7+0 [7e76a0d4] + Libglvnd_jll v1.6.0+0 [7add5ba3] + Libgpg_error_jll v1.42.0+0 [94ce4f54] + Libiconv_jll v1.17.0+0 [4b2f31a3] + Libmount_jll v2.35.0+0 ⌅ [89763e89] + Libtiff_jll v4.5.1+1 [38a345b3] + Libuuid_jll v2.36.0+0 [e7412a2a] + Ogg_jll v1.3.5+1 [458c3c95] + OpenSSL_jll v3.0.12+0 [efe28fd5] + OpenSpecFun_jll v0.5.5+0 [91d4177d] + Opus_jll v1.3.2+0 [30392449] + Pixman_jll v0.42.2+0 [c0090381] + Qt6Base_jll v6.5.3+1 [a44049a8] + Vulkan_Loader_jll v1.3.243+0 [a2964d1f] + Wayland_jll v1.21.0+1 [2381bf8a] + Wayland_protocols_jll v1.31.0+0 [02c8fc9c] + XML2_jll v2.12.2+0 [aed1982a] + XSLT_jll v1.1.34+0 [ffd25f8a] + XZ_jll v5.4.5+0 [f67eecfb] + Xorg_libICE_jll v1.0.10+1 [c834827a] + Xorg_libSM_jll v1.2.3+0 [4f6342f7] + Xorg_libX11_jll v1.8.6+0 [0c0b7dd1] + Xorg_libXau_jll v1.0.11+0 [935fb764] + Xorg_libXcursor_jll v1.2.0+4 [a3789734] + Xorg_libXdmcp_jll v1.1.4+0 [1082639a] + Xorg_libXext_jll v1.3.4+4 [d091e8ba] + Xorg_libXfixes_jll v5.0.3+4 [a51aa0fd] + Xorg_libXi_jll v1.7.10+4 [d1454406] + Xorg_libXinerama_jll v1.1.4+4 [ec84b674] + Xorg_libXrandr_jll v1.5.2+4 [ea2f1a96] + Xorg_libXrender_jll v0.9.10+4 [14d82f49] + Xorg_libpthread_stubs_jll v0.1.1+0 [c7cfdc94] + Xorg_libxcb_jll v1.15.0+0 [cc61e674] + Xorg_libxkbfile_jll v1.1.2+0 [e920d4aa] + Xorg_xcb_util_cursor_jll v0.1.4+0 [12413925] + Xorg_xcb_util_image_jll v0.4.0+1 [2def613f] + Xorg_xcb_util_jll v0.4.0+1 [975044d2] + Xorg_xcb_util_keysyms_jll v0.4.0+1 [0d47668e] + Xorg_xcb_util_renderutil_jll v0.3.9+1 [c22f9ab0] + Xorg_xcb_util_wm_jll v0.4.1+1 [35661453] + Xorg_xkbcomp_jll v1.4.6+0 [33bec58e] + Xorg_xkeyboard_config_jll v2.39.0+0 [c5fb5394] + Xorg_xtrans_jll v1.5.0+0 [3161d3a3] + Zstd_jll v1.5.5+0 [35ca27e7] + eudev_jll v3.2.9+0 [214eeab7] + fzf_jll v0.43.0+0 [1a1c6b14] + gperf_jll v3.1.1+0 [a4ae2306] + libaom_jll v3.4.0+0 [0ac62f75] + libass_jll v0.15.1+0 [2db6ffa8] + libevdev_jll v1.11.0+0 [f638f0a6] + libfdk_aac_jll v2.0.2+0 [36db933b] + libinput_jll v1.18.0+0 [b53b4c65] + libpng_jll v1.6.40+0 [f27f6e37] + libvorbis_jll v1.3.7+1 [009596ad] + mtdev_jll v1.1.6+0 [1270edf5] + x264_jll v2021.5.5+0 [dfaa095f] + x265_jll v3.5.0+0 [d8fb68d0] + xkbcommon_jll v1.4.1+1 [0dad84c5] + ArgTools v1.1.1 [56f22d72] + Artifacts [2a0f44e3] + Base64 [ade2ca70] + Dates [f43a241f] + Downloads v1.6.0 [7b1f6079] + FileWatching [b77e0a4c] + InteractiveUtils [b27032c2] + LibCURL v0.6.4 [76f85450] + LibGit2 [8f399da3] + Libdl [37e2e46d] + LinearAlgebra [56ddb016] + Logging [d6f4376e] + Markdown [a63ad114] + Mmap [ca575930] + NetworkOptions v1.2.0 [44cfe95a] + Pkg v1.10.0 [de0858da] + Printf [3fa0cd96] + REPL [9a3f8284] + Random [ea8e919c] + SHA v0.7.0 [9e88b42a] + Serialization [6462fe0b] + Sockets [2f01184e] + SparseArrays v1.10.0 [10745b16] + Statistics v1.10.0 [fa267f1f] + TOML v1.0.3 [a4e569a6] + Tar v1.10.0 [8dfed614] + Test [cf7118a7] + UUIDs [4ec0a83e] + Unicode [e66e0078] + CompilerSupportLibraries_jll v1.0.5+1 [deac9b47] + LibCURL_jll v8.4.0+0 [e37daf67] + LibGit2_jll v1.6.4+0 [29816b5a] + LibSSH2_jll v1.11.0+1 [c8ffd9c3] + MbedTLS_jll v2.28.2+1 [14a3606d] + MozillaCACerts_jll v2023.1.10 [4536629a] + OpenBLAS_jll v0.3.23+2 [05823500] + OpenLibm_jll v0.8.1+2 [efcefdf7] + PCRE2_jll v10.42.0+1 [bea87d4a] + SuiteSparse_jll v7.2.1+1 [83775a58] + Zlib_jll v1.2.13+1 [8e850b90] + libblastrampoline_jll v5.8.0+1 [8e850ede] + nghttp2_jll v1.52.0+1 [3f19e933] + p7zip_jll v17.4.0+2 Info Packages marked with ⌅ have new versions available but compatibility constraints restrict them from upgrading. To see why use `status --outdated -m`
#hide
using Plots, LaTeXStrings, Measures
using GasDispersion
colours = palette(:Paired_8);
#hide
sets = [ (name="Default", eqn=DefaultSet, color=:black, linestyle=:solid, lw=3),
(name="CCPS Rural", eqn=CCPSRural, color=colours[2], linestyle=:solid, lw=3),
(name="CCPS Urban", eqn=CCPSUrban, color=colours[4], linestyle=:solid, lw=3),
(name="ISC3 Rural", eqn=ISC3Rural, color=colours[1], linestyle=:dash, lw=2),
(name="ISC3 Urban", eqn=ISC3Urban, color=colours[3], linestyle=:dash, lw=2),
(name="TNO", eqn=TNO, color=colours[6], linestyle=:dash, lw=2),
(name="Turner", eqn=Turner, color=colours[7], linestyle=:dash, lw=2) ];
The windspeed correlations I am looking at here are the basic power law
$$ u = u_R \left( z \over z_R \right)^p $$where uR is the known windspeed at a reference height zR and p is a parameter that depends upon the Pasquill stability class. There are more complex models that incorporate the surface roughness, Monin-Obukhov mixing length, and other measures of stability, they are beyond this analysis.
There are three different standard references used in GasDispersion.jl
for windspeed: the default which comes from Spicer and Havens (1989), the correlations used by the EPA Industrial Source Complex (ISC3) dispersion models (EPA 1995), and the correlations given in the various CCPS guidance documents (AIChE/CCPS 1999)
The ISC3 and CCPS correlations are divided into urban and rural terrain and are exactly the same correlations for the unstable classes. They appear to be the correlations given in Hanna et al. (1982). They also bracket the default correlation. Clearly whether or not the terrain is urban is significant, it can lead to a 20-30% difference in estimated windspeed (depending upon elevation).
#hide
u0, z0 = 1.0, 1.0
xs = range(1.0,10.0,100)
function plot_windspeed!(plt,class)
plot!(plt, xlabel=L"z/z_R", ylabel=L"u/u_R", title="$class", legend=false)
for s in sets[1:5]
ys = [ GasDispersion._windspeed(u0,z0,x,class,s.eqn) for x in xs]
plot!(plt,xs,ys, color=s.color, linestyle=s.linestyle, lw=s.lw, label=s.name)
end
end
ws_legend = plot(framestyle=:none, legend=:topleft, foreground_color_legend = nothing);
for s in sets[1:5]
plot!(ws_legend,(1), color=s.color, linestyle=:solid, lw=2, label=s.name);
end
#hide
wsA, wsB, wsC, wsD = plot(), plot(), plot(), plot()
plot_windspeed!(wsA,ClassA)
plot_windspeed!(wsB,ClassB)
plot_windspeed!(wsC,ClassC)
plot_windspeed!(wsD,ClassD)
l1 = @layout [ grid(2,2) b{0.14w} ]
plot(wsA, wsB, wsC, wsD, ws_legend, layout=l1, size=(600,400), margin=2.5mm)
For the stable atmospheres the ISC3 and CCPS rural correlations are the same. However they are very different for urban terrain and they no longer bracket the default correlation. The CCPS urban correlations are the same as Hanna et al. (1982), the ISC3 correlations use the parameter p = 0.30 and no reference is given in the model specification so I don't know why.
For an urban release scenario, whether or not one choses the default, the ISC3 urban, or the CCPS urban correlation can lead to a 300% difference in windspeed (for class F stability, depending on elevation). Which is a pretty large difference.
#hide
wsE, wsF = plot(), plot()
plot_windspeed!(wsE,ClassE)
plot_windspeed!(wsF,ClassF)
l2 = @layout [ grid(1,2) b{0.14w} ]
plot(wsE, wsF, ws_legend, layout=l2, size=(600,200), margin=2.5mm)
The more diverse sets of correlations are for the plume dispersion parameters, the crosswind and vertical dispersion. To some extent this is because the early work (Turner 1970) presented the dispersion parameters graphically and many subsequent authors generated their own curves to fit these plots.
Crosswind dispersion can be divided into the various attempts at fitting the curves presented graphically by Turner (1970) and those based on Briggs' urban and rural correlations (Briggs 1973).
The default correlation is a simple set of correlations of the form
$$ \sigma_y = a x^b $$which attempts to fit the Turner curves.
The CCPS correlations (AIChE/CCPS 1999) are from Briggs (1973) and the ISC3 urban correlations (EPA 1995) are from Briggs as well, the ISC3 rural correlations are something else entirely but I suspect are intended to fit the Turner curves. The correlations from the TNO yellow book (Bakkum et al. 2005) are also a different attempt at fitting the Turner curves. What GasDispersion,jl
gives as "Turner" is the fit to the turner curves given in Lees (1996).
#hide
xs = range(1,1e5,1000)
function plot_cw!(plt,class)
plot!(plt, xaxis=:log10, yaxis=:log10, xlabel=L"x", ylabel=L"\sigma_y", title="$class", legend=false)
for s in sets
ys = [ GasDispersion.crosswind_dispersion(x, Plume, class, s.eqn) for x in xs]
plot!(plt, xs, ys, color=s.color, linestyle=s.linestyle, lw=s.lw, label=s.name)
end
end
cw_legend = plot(framestyle=:none, legend=:topleft, foreground_color_legend = nothing);
for s in sets
plot!(cw_legend,(1), color=s.color, linestyle=:solid, lw=2, label=s.name);
end
#hide
cwA, cwB, cwC, cwD, cwE, cwF = plot(), plot(), plot(), plot(), plot(), plot()
plot_cw!(cwA,ClassA)
plot_cw!(cwB,ClassB)
plot_cw!(cwC,ClassC)
plot_cw!(cwD,ClassD)
plot_cw!(cwE,ClassE)
plot_cw!(cwF,ClassF)
l1 = @layout [ grid(3,2) b{0.14w} ]
plot(cwA, cwB, cwC, cwD, cwE, cwF, cw_legend, layout=l1, size=(600,600), margin=2.5mm)
Zooming in on the class F curves is illustrative of the lot: most of the lines overlap and hew pretty close to the curve-fit for Turner (1970) with the exception of the Briggs' urban/rural correlations. The biggest impact on these model parameters is whether or not a rural/urban terrain is used or not. Note these are log-log plots.
plot(cwF, legend=:topleft)
The vertical dispersion correlations are decidedly more varied. Varied enough that I'm just going to show them all at full scale.
Note the correlations given in the CCPS guidance (AIChE/CCPS 1999) for urban conditions has typos in the class A, B and D correlations, I have corrected them here to match the Briggs correlations on which they are supposed to be based.
#hide
using Logging
Logging.disable_logging(Logging.Warn);
#hide
xs = range(1,1e5,1000)
function plot_vt!(plt,class)
plot!(plt, xaxis=:log10, yaxis=:log10, xlabel=L"x", ylabel=L"\sigma", title="$class", legend=:topleft)
for s in sets
ys = [ GasDispersion.vertical_dispersion(x, Plume, class, s.eqn) for x in xs]
plot!(plt, xs, ys, color=s.color, linestyle=s.linestyle, lw=s.lw, label=s.name)
end
end;
vtA = plot()
plot_vt!(vtA,ClassA)
plot(vtA)
vtB = plot()
plot_vt!(vtB,ClassB)
plot(vtB)
vtC = plot()
plot_vt!(vtC,ClassC)
plot(vtC)
vtD = plot()
plot_vt!(vtD,ClassD)
plot(vtD)
vtE = plot()
plot_vt!(vtE,ClassE)
plot(vtE)
vtF = plot()
plot_vt!(vtF,ClassF)
plot(vtF)
For some of these there is an order of magnitude spread in vertical dispersion, depending on which model happens to be used. Even when looking only at the correlations that are "universal", i.e. are not for either urban or rural terrains. From this alone one would expect that the concentration profiles would vary by a large amount, depending on which set of correlations one used to model a given scenario.
Just to give an example of how this works out, lets look at the emissions from a large stack. I happened to have picked the stack for a large power plant in the Edmonton area: TransAlta's Sundance station. This power plant is on the shores of Lake Wabamun and is pretty rural, it has several stacks but let's consider only Stack 2 and examine the dispersion of SO2 emissions.
From Alberta's AEIR Air Emission Rates dataset we can pull the mass emission rates for SO2 as well as the relevant stack dimensions. Note this dataset is from 2018 and thus may not represent the current operations at Sundance.
# TransAlta Sundance - Stack 2
m = 3200/3600 # mass emission rate: 3200kg/h in kg/s
h = 155.5 # stack height, m
d = 7.3 # stack diameter, m
v = 35.6 # stack exit velocity, m/s
T = 439.7 # stack exit temperature, K
439.7
For the sake of modeling let's assume a class D atmospheric stability with a windspeed at 10m of 2m/s. The atmosphere is otherwise at standard state.
# assumed weather conditions
uᵣ = 2 # windspeed, m/s
zᵣ = 10 # windspeed elevation, m
stability = ClassD
# standard state
Pₛ = 101325 # Pa
Tₛ = 273.15 # K
273.15
We can construct the relevant scenario for GasDispersion.jl
directly.
r = VerticalJet(m, Inf, d, v, h, Pₛ, T, 0.0)
a = SimpleAtmosphere(pressure=Pₛ, temperature=Tₛ, windspeed=uᵣ, windspeed_height=zᵣ, stability=stability)
# a dummy substance, since I know a gaussian plume doesn't require any material
# properties I have just left them as NaNs
SO2 = Substance(name=:SulfurDioxide,molar_weight=0.064066,liquid_density=1,boiling_temp=1,
latent_heat=1,gas_heat_capacity=1,liquid_heat_capacity=1)
scn = Scenario(SO2,r,a)
Substance: SulfurDioxide MW: 0.064066 kg/mol P_v: GasDispersion.Antoine{Float64}(0.007705368698167287, 0.007705368698167287, 0.0) Pa ρ_g: 2.7095140841291006 kg/m^3 ρ_l: 1 kg/m^3 T_ref: 288.15 K P_ref: 101325.0 Pa k: 1.4 T_b: 1.0 K Δh_v: 1 J/kg Cp_g: 1 J/kg/K Cp_l: 1 J/kg/K VerticalJet release: ṁ: 0.8888888888888888 kg/s Δt: Inf s d: 7.3 m u: 35.6 m/s h: 155.5 m P: 101325.0 Pa T: 439.7 K f_l: 0.0 SimpleAtmosphere atmosphere: P: 101325.0 Pa T: 273.15 K u: 2.0 m/s h: 10.0 m rh: 0.0 % stability: ClassD
The Gaussian plume model is then given by the following, neglecting the effect of plume rise.
conc = plume(scn, GaussianPlume; plumerise=false);
Plotted below are the results for every equation set, at near ground level (at basically "my head" level). Clearly the urban/rural choice is quite important, leading to a ~2× greater maximum concentration. The TNO correlations, which uses the default correlation for windspeed and the TNO correlations for the crosswind and vertical dispersion, leads to less dispersion and thus a greater maximum concentration relative to the rest.
#hide
xs = range(1,5e4,1000)
kms = xs./1000
plt = plot()
plot!(plt, xlabel="Downwind distance (km)", ylabel="Concentration SO₂ (ppbv)", title="Downwind concentration at 2m elevation", legend=:topright)
plot!(plt, [0, 50], [172, 172], color=:grey, linestyle=:dot, lw=1, label="1-hr AAQO SO₂")
for s in sets
pl = plume(scn, GaussianPlume, s.eqn; plumerise=false)
plot!(plt, kms, pl.(xs,0,2).*1e9, color=s.color, linestyle=s.linestyle, lw=s.lw, label=s.name)
end
plot(plt)
Plotted below is the 172ppbv isopleth, the 1-hr Ambient Air Quality Objective (AAQO) for SO2 in Alberta. As we would expect, the correlations that lead to a higher maximum concentration correspond to less overall dispersion and the isopleth is quite a bit smaller for the urban versus rural case and the TNO versus the remaining cases. The scale is in kilometers so this is quite a large difference in area.
# hide
using Contour
xs = range(1,2e4,1000)
ys = range(-2e3,2e3,1000)
c = 172e-9
plt = plot()
plot!(plt, xlabel="Downwind distance (km)", ylabel="Crosswind distance (km)", title="172ppbv isopleth at 2m elevation", legend=:topright)
for s in sets
pl = plume(scn, GaussianPlume, s.eqn; plumerise=false)
pl_zs = [pl(xi,yi,2.0) for xi in xs, yi in ys]
ctr = Contour.contour(xs,ys,pl_zs,c)
pl_xs, pl_ys = coordinates(first(lines(ctr)))
plot!(plt, pl_xs.*1e-3, pl_ys.*1e-3, color=s.color, linestyle=s.linestyle, lw=s.lw, label=s.name)
end
plot(plt, xlims=(0,20), ylims=(-1,1))
The above was assuming no plume rise, however the relative differences are much more pronounced when plume rise is included.
conc = plume(scn, GaussianPlume; plumerise=true);
Plotted below is the same downwind concentration plot as above, but incorporating the Briggs' plume rise model. Since this leads to a greater overall dispersion, the concentration is much smaller (everything is well below the AAQO at ground level, which is good news). However this adds another dimension along which the models can vary: plume rise is a function of windspeed, and overall dispersion is a function of plume rise. These different sets of correlations lead to the plume rising to a different elevation, and also dispersing to a differing degree, magnifying the differences between them. In this case there is up to a ~30× difference between the max concentrations predicted between the urban and rural case.
#hide
xs = range(1,5e5,1000)
kms = xs./1000
plt = plot()
plot!(plt, xlabel="Downwind distance (km)", ylabel="Concentration (ppbv)", title="Downwind concentration at 2m elevation,\n with plume rise", legend=:topright)
for s in sets
pl = plume(scn, GaussianPlume, s.eqn; plumerise=true)
plot!(plt, kms, pl.(xs,0,2).*1e9, color=s.color, linestyle=s.linestyle, lw=s.lw, label=s.name)
end
plot(plt)
I think the above illustrates the necessity of picking a standard set of correlations for use when screening scenarios at a particular plant (e.g. using either the CCPS urban or rural correlations as appropriate for the area around the plant) and being careful to keep these consistent. It also shows how seriously one should take the exact values generated by the models: not very. The dispersion model results are highly sensitive to the choice of correlations, and they are also quite sensitive to the other assumptions that go into a release scenario (e.g. atmospheric stability, wind-speed, mass emission rate). The results are really order of magnitude at best.
It is often the case that chemical plants are situated at the periphery of cities, in areas that blur the line between "urban" and "rural". Also, cities grow and industrial areas fill in. A plant that was essentially rural may, overtime, fill in such that the urban correlations better represent the area. I think it is worth comparing the urban/rural models for a range of plausible results and considering whether assumptions made in the past about the area around the plant are still valid given changes in the area.
There are other correlations, for wind-speed and for dispersion, that take into account the local surface roughness which could be used instead and the sensitivity to the models to assumptions about surface roughness could be evaluated. This would likely lead to a smaller range of values, and give a path for updating the screening model as the area around the plant changes (update the assumed surface roughness and re-run).