Gaussian dispersion toolkit¶
Implementation details for the Gaussian dispersion package (hera/simulations/gaussian/).
The gaussianToolkit provides Gaussian puff models for atmospheric pollutant transport: a meteorology object (wind profile, stability class), a dispersion-coefficient (sigma) model, and a gas cloud that evaluates concentration / dosage / TIAC on a regular space-time grid as xarray objects.
For user-facing documentation, see Toolkits > Simulations > Gaussian.
Package structure¶
hera/simulations/gaussian/
toolkit.py # gaussianToolkit + presentationLayer
gasCloud.py # abstractGasCloud, instantaneousReleaseGasCloud, continuousReleaseGasCloud
Sigma.py # AbstractSigma, BriggsRural
Meteorology.py # StandardMeteorolgyConstant_* profiles, MeteorologyFactory
MeshUtils.py # GaussianToMesh, GaussianIntegrationToMesh
DropletCloud.py # droplet cloud position models (fixed, line, clipped, circle)
FallingNonEvaporatingDroplets.py # settling (non-evaporating) droplet model
source.py # unfinished stub (does not parse; not imported anywhere)
| Item | Value |
|---|---|
toolkitHome constant |
toolkitHome.GAUSSIANDISPERSION ("GaussianDispersion") |
| Class | hera.simulations.gaussian.toolkit.gaussianToolkit (inherits abstractToolkit) |
toolkitName passed to base |
"gaussianToolkit" |
| Constructor | gaussianToolkit(projectName, filesDirectory=None) (no connectionName) |
| Layers | presentation (presentationLayer); no analysis layer |
The toolkit does not store anything in the database; it is a computational front-end over the classes below.
Toolkit API¶
| Method | Description |
|---|---|
listSigmaTypes() |
Names in the sigma registry (_sigmaDict), currently ["briggsRural"] |
getSigmaType(sigmaName) |
Instance of the sigma class; ValueError for unknown names |
getMeteorologyFromU10(u10, inversion, verticalProfileType="powerLaw", temperature=20 degC, stability="D", z0=0.1 m, ustar=0.3 m/s, skinSurfaceTemperature=35 degC) |
Meteorology object from the 10 m wind speed (via MeteorologyFactory) |
getMeteorologyFromURefHeight(u, refHeight, inversion, verticalProfileType="powerLaw", ...) |
Same, from the wind speed u at refHeight |
getSpaceTime(meteorology, sourceHeight, wind_profile_type, maxx, maxz, dt, dz, dxdy_multiplier, minimal_maxy, initialCloudSize) |
Build the grid dict (minx, maxx, miny, maxy, minz, maxz, dxdy, dz, timeSpan, dt). dxdy = dt * u(sourceHeight) * dxdy_multiplier; timeSpan is chosen so that maxx is 3 sigma behind the cloud centre |
getGasCloud(sourceQ, sourceHeight, initialCloudSize, meteorology, wind_profile_type, spaceTime, deposition_velocity, sigmaTypeName="briggsRural") |
Gas cloud object (see below) |
wind_profile_type is "default" (the meteorology's getWindVelocity) or "HotSpot" (getWindVelocity_hotSpot); anything else raises ValueError.
Note: MeteorologyFactory's own default verticalProfileType is "log", while the toolkit wrappers default to "powerLaw".
Data flow¶
graph LR
U["u10 / u at refHeight"] -->|getMeteorologyFrom*| MET["StandardMeteorolgyConstant_*<br/>(powerLaw / log / uniformWind)"]
MET --> ST["getSpaceTime -> spaceTime dict"]
SIG["getSigmaType('briggsRural')"] --> GC
MET --> GC["getGasCloud -> abstractGasCloud.createGasCloud"]
ST --> GC
GC -->|"sourceQ in [mass]"| INST["instantaneousReleaseGasCloud"]
GC -->|"sourceQ in [mass/time]"| CONT["continuousReleaseGasCloud"]
INST --> XR["xarray (time, x, y, z)<br/>concentration / dosage / TIAC"]
XR --> PRES["presentationLayer plots"]
Meteorology (Meteorology.py)¶
StandardMeteorolgyConstant_powerLaw(inversion, temperature=20, stability="D", z0=0.1, ustar=0.3, skinSurfaceTemperature=35, **kwargs) takes either u10 or u + refHeight in kwargs. It provides getWindVelocity(height), getWindVelocity_hotSpot(height), getAirPressure, getAirTemperature, getAirDensity, getAirDynamicViscosity and getTKE (all by height). Subclasses override getWindVelocity:
Key in MeteorologyFactory |
Class | Profile |
|---|---|---|
powerLaw |
StandardMeteorolgyConstant_powerLaw |
Power law with stability-dependent exponent (wind_p) |
log |
StandardMeteorolgyConstant_log |
Logarithmic profile anchored at u(refHeight) with roughness z0 |
uniformWind |
StandardMeteorolgyConstant_uniformWind |
Height-independent wind |
MeteorologyProfile is a further powerLaw subclass that is not registered in the factory. hera/simulations/evaporation/monaghan.py also uses StandardMeteorolgyConstant_powerLaw.
Dispersion coefficients (Sigma.py)¶
AbstractSigma implements getVirtualDistance(sigma0, stability) (distance that produces the initial cloud size) and getSigma(x, stability, sigma0=None, ...). BriggsRural holds the Briggs rural coefficient tables for stability classes A-F; its getSigma(x, stability, sigma0=None, units=True) returns sigmaX, sigmaY, sigmaZ.
To add a new sigma model, subclass AbstractSigma and register it in gaussianToolkit._sigmaDict (set in __init__).
Gas clouds (gasCloud.py)¶
abstractGasCloud.createGasCloud(...) converts sourceQ to pint and dispatches on its dimensions: mass → instantaneousReleaseGasCloud, mass per time → continuousReleaseGasCloud; otherwise ValueError("Must be mass or mass per time!"). The constructor resolves the transport speed u at sourceHeight from the meteorology.
The fields are built from separable terms on the spaceTime grid: downwind (time, x) (_getTXterm, or an erf-integrated _getTXDosage), crosswind (x, y) (_getXYterm) and vertical (x, z) with ground reflections summed over -numOfReflections..numOfReflections (_getXZterm). getDF_xarray() gives the dry-deposition depletion factor from deposition_velocity, applied when DF=True.
| Family (instantaneous) | Methods |
|---|---|
| Concentration | getConcentration_inst, getConcentration_inst_noQ, getConcentration_inst_noQ_bounded(start_time, end_time, ...) |
| Dosage | getDosage_inst, getDosage_inst_noQ, getDosage_inst_NoERF, getDosage_inst_NoERF_noQ, getDosage_inst_NoERF_noQ_bounded, getDosageFromConcentration_inst_NoERF_noQ(C) |
| TIAC (radiological) | getTIAC_inst(specifitActivity, ...), getTIAC_inst_noQ, getTIAC_inst_NoERF, getTIAC_inst_NoERF_noQ, getTIACFromConcentration_inst_NoERF(C, specifitActivity, ...), getTIACFromConcentration_inst_NoERF_noQ(C), getTIAC_inst_NoERF_noQ_bounded, get_TIAC_for_dist(data, y, z, dist_list), concentrationConversion_mass_to_Bq(C, outputUnits, specificActivity) |
*_noQ variants return the field per unit release; the others multiply by sourceQ (in mg) and record the units in attrs['Q'] (e.g. 1 mg/m**3), which the presentation layer reads for axis labels. NoERF variants integrate concentration over time with trapezoidal_integration instead of the analytical erf form.
continuousReleaseGasCloud defines getConcentration_cont, getConcentration_cont_NoERF, getDosage_cont_NoERF and getDosage_cont_doubleNoERF.
Known issue in master
The continuousReleaseGasCloud methods call getDosage_inst_noQ / getDosage_inst_NoERF_noQ, which exist only on instantaneousReleaseGasCloud (gasCloud.py:596-638), so they raise AttributeError. Only instantaneous releases work.
Presentation layer¶
presentationLayer (module-level class in toolkit.py, no back-reference to the toolkit) plots slices of the xarray results with matplotlib: plotMaxConcentrationOverTime, plotFixedPointConcentrationOverTime, plotDosagePerDistance, plotFixedPointDosageOverTime, and the per-unit-release variants plotMaxConcentrationOverTime_noQ, plotFixedPointConcentrationOverTime_noQ, plotTIACPerDistance_noQ, plotFixedPointTIACOverTime_noQ. Coordinates may be pint or unum quantities.
Other modules¶
MeshUtils.GaussianToMeshevaluates a DataFrame of gaussians (x,sigmaX,sigmaYcolumns, names configurable) on a regular grid (gaussianToMesh(gaussians, groupby=None, QName="Q", QUnits=ureg.kg));GaussianIntegrationToMeshintegrates each gaussian over the cells with erf differences instead of point sampling.DropletCloud/FallingNonEvaporatingDropletsmodel settling droplets (terminal velocity, ground hit time viasolveToTime(T), ground footprint). They are not wired intogaussianToolkit.