Below is a step-by-step explanation of gwater.c
, which manages groundwater (GW) flow in EPA SWMM5. SWMM can model saturated and unsaturated subsurface zones, compute exchange with surface infiltration, evapotranspiration, and flow to or from the sewer system at a node. This file implements all GW computations for each subcatchment’s groundwater component.
1. Purpose
// gwater.c
// Groundwater functions.
// ...
gwater.c
is responsible for reading aquifer/GW parameters, initializing states, updating the moisture content and depth in an upper/lower zone model each time step, and computing lateral outflow to a connected node as well as deep percolation from the lower zone.
2. Key Data Structures
TAquifer
: defines aquifer properties (porosity, field capacity, conductivity, etc.).TGroundwater
: defines the subcatchment’s local groundwater parameters (node ID, surface elevation, the user’s custom parametersa1,b1,a2,b2,a3
, etc.).
The array Aquifer[]
holds all defined aquifers, and each subcatchment can reference one via Subcatch[j].groundwater->aquifer
.
3. Reading Aquifer and Subcatchment GW Parameters
3.1 gwater_readAquiferParams()
int gwater_readAquiferParams(int j, char* tok[], int ntoks)
- Parses one line of data for aquifer .
- Data format (13 tokens, the 14th is optional):
- Aquifer ID,
- Porosity,
- Wilting point,
- Field capacity,
- Hydraulic conductivity,
- Conductivity slope,
- Tension slope,
- Upper evaporation fraction,
- Lower evap. depth,
- Groundwater recession constant,
- Aquifer bottom elevation,
- Water table elevation,
- Initial upper moisture content,
- (optional) monthly pattern for upper evaporation.
- Assigns them to
Aquifer[j]
with correct unit conversions (like dividing length byUCF(LENGTH)
).
3.2 gwater_readGroundwaterParams()
int gwater_readGroundwaterParams(char* tok[], int ntoks)
- Reads the subcatchment's GW parameters for the line:
subcatch aquifer node surfElev a1 b1 a2 b2 a3 fixedDepth nodeElev bottomElev waterTableElev upperMoisture
- This sets up
TGroundwater
withinSubcatch[j]
:aquifer
: index of the aquifer used,node
: index of node that subcatch’s groundwater can flow into,surfElev
: subcatch's ground surface elevation,a1, b1, a2, b2, a3, fixedDepth
: user’s curve parameters for computing GW infiltration,- optionally:
nodeElev, bottomElev, waterTableElev, upperMoisture
.
4. Custom Flow Expressions
4.1 gwater_readFlowExpression(...)
- SWMM allows user‐supplied math expressions for lateral or deep GW flow:
subcatch LATERAL <expr> subcatch DEEP <expr>
- This function:
- Finds the subcatch index,
- Checks if the expression is for
LATERAL
orDEEP
, - Tokenizes and parses the expression (via
mathexpr_create
) into a syntax tree stored inSubcatch[j].gwLatFlowExpr
orSubcatch[j].gwDeepFlowExpr
.
4.2 gwater_deleteFlowExpression(...)
- Releases memory for the expression trees associated with one subcatch.
5. Validation and Initialization
5.1 gwater_validateAquifer(int j)
- Checks correctness of aquifer parameters (ensuring
porosity > fieldCapacity > wiltingPoint
, etc.). - Prints error if invalid.
5.2 gwater_validate(int j)
- For subcatch
j
, if it hasgroundwater
, ensures:surfElev >= waterTableElev
(otherwise error).
5.3 gwater_initState(int j)
- Sets the subcatch’s initial groundwater state:
theta = upperMoisture
(the initial soil moisture).lowerDepth = waterTableElev - bottomElev
(the saturated thickness).- If that is higher than
surfElev - bottomElev
, reduce it to avoid overflow. maxInfilVol
is how much infiltration can fill the upper zone.
6. State Saving/Reading
6.1 gwater_getState(int j, double x[])
- Copies the subcatch’s theta, lowerDepth (absolute water table height), newFlow, and maxInfilVol into array
x
.
6.2 gwater_setState(int j, double x[])
- Assigns these state variables back from
x[]
. Used for hotstart or re‐initializing from saved states.
7. Primary GW Computation
7.1 gwater_getGroundwater(int j, double evap, double infil, double tStep)
- Finds subcatch’s
TGroundwater* GW
and theTAquifer
object. - Calculate infiltration rate .
- Convert any surface evap. to a rate that might reduce available GW evaporation.
- Determine total aquifer thickness, the node invert, water surface in node, etc.
- Sets up initial conditions for an ODE system:
- upper zone moisture
theta
- lower zone depth
lowerDepth
.
- upper zone moisture
- Defines constraints for maximum percolation, max outflow, etc.
- Calls an ODE solver (
odesolve_integrate(...)
) with the functiongetDxDt(...)
, which calculates the time derivative:- and .
- After integration, calls
getFluxes(...)
to compute the actual flux rates:UpperEvap
,LowerEvap
,UpperPerc
,GWFlow
,LowerLoss
.updateMassBal(...)
updates the water balance.
- Saves new states back to
GW->theta
andGW->lowerDepth
.
7.2 getDxDt(t, x, dxdt)
- The ODE solver calls this function at each step.
x[THETA]
: the upper moisture content,x[LOWERDEPTH]
: depth of the lower saturated zone.- Calls
getFluxes(...)
, which sets global flux variables (Infil
,UpperEvap
, etc.). - Then: if denominators are nonzero.
7.3 getFluxes(double theta, double lowerDepth)
- upperDepth = total depth minus
lowerDepth
. - Calls
getEvapRates(...)
to computeUpperEvap
&LowerEvap
. - Calls
getUpperPerc(...)
to compute percolation from upper to lower zone. - If a user expression for
DeepFlowExpr
is defined, that setsLowerLoss
. Otherwise useAquifer[j].lowerLossCoeff * lowerDepth / totalDepth
. - If a user expression for
LatFlowExpr
is defined, we add that to the basegetGWFlow(...)
. GWFlow
is also limited to max possible flow based on the unsaturated or saturated zone capacity.
7.4 getEvapRates(double theta, double upperDepth)
- Disallows infiltration and evaporation at the same time: if infiltration is > 0, then no GW evaporation.
- Otherwise, partition potential evap (
MaxEvap
) between the upper zone (some fractionupperEvapFrac
) and the lower zone if the lower zone is within the “lowerEvapDepth.”
7.5 getUpperPerc(double theta, double upperDepth)
- If the upper zone is deeper than 0 and
theta > fieldCapacity
, compute an exponent on unsaturated conductivity as: - Then modifies for tension slope. Returns this percolation rate in ft/sec.
7.6 getGWFlow(double lowerDepth)
- The “traditional” flow formula: when .
- If a user lateral flow expression is present, add that as well.
7.7 updateMassBal(area, tStep)
- Converts flux rates to volumes over subcatchment area times time step, calls
massbal_updateGwaterTotals(...)
.
8. Helper Routines for Expressions
8.1 getVariableIndex(char* s)
- Maps a variable name (
"HGW"
,"THETA"
, etc.) to an integer ID used in the math expression parser.
8.2 getVariableValue(int varIndex)
- Returns the current numerical value of that variable from the many global placeholders (like
Hgw
,Theta
, etc.).
9. Summary
gwater.c
implements SWMM’s two-zone groundwater model:
- Upper unsaturated zone with moisture content
theta
. - Lower saturated zone with depth
lowerDepth
.
At each time step:
- Convert infiltration from surface, potential evap, node water surface, etc.
- Use an ODE solver to integrate the 2 state variables.
- Compute fluxes: infiltration, evap from each zone, percolation, deep loss, and lateral GW flow to the node.
- Update the mass balance.
This model can be augmented by custom expressions for lateral or deep flow, allowing flexible definitions for real-world groundwater behavior.
No comments:
Post a Comment