Below is a step‐by‐step explanation of how flowrout.c
implements and coordinates flow routing in SWMM5. The functions here support Steady Flow, Kinematic Wave, and (indirectly) Dynamic Wave routing methods. They initialize link/node states, compute flow propagation each time step, and update depths and volumes at nodes and links.
1. Role of flowrout.c
- In SWMM5, flow routing is the process of moving water through the network of nodes and links based on continuity and momentum principles.
flowrout.c
is a high‐level module that:- Initializes node/link depths and volumes when the simulation starts.
- Chooses the correct solver approach (steady, kinematic, or dynamic wave).
- Executes a routing step, updating flow rates, depths, and volumes at each node/link for the current time step.
Note that Dynamic Wave routing has much of its logic in dynwave.c
, but flowrout.c
still coordinates calls to it.
2. Key Externally Called Functions
-
flowrout_init(routingModel)
- Called when the routing process is opened (one time at start).
- If Dynamic Wave:
- Calls
validateGeneralLayout()
to ensure the network is topologically consistent for dynamic wave. - Calls
dynwave_init()
to set up dynamic wave parameters. - If no hotstart file is used, initializes node and link depths with
initNodeDepths()
andinitLinkDepths()
.
- Calls
- If Kinematic Wave or Steady Flow:
- Calls
validateTreeLayout()
which checks for a “tree-like” network structure: each node has at most one (or two) outlets, no adverse slopes unless dummy links, etc.
- Calls
- Then calls
initNodes()
andinitLinks(routingModel)
to set initial volumes/flows.
-
flowrout_close(routingModel)
- Called at the end of the simulation.
- If dynamic wave was used, calls
dynwave_close()
to free memory used by the dynamic wave engine.
-
flowrout_getRoutingStep(routingModel, fixedStep)
- Returns the routing time step to use for the next iteration.
- If Dynamic Wave, calls
dynwave_getRoutingStep()
to find a variable time step that meets Courant stability criteria. Otherwise, returnsfixedStep
.
-
flowrout_execute(links, routingModel, tStep)
- Main function that routes flow through the network for a single time step (
tStep
). - Steps:
- Resets node overflow to 0.
- If
routingModel == DW
, callsdynwave_execute(tStep)
and returns. - Else (Steady or Kinematic wave), processes links in topological order:
- If the upstream node is a storage, calls
updateStorageState()
to iteratively find the node’s outflow and final water level. - Gets inflow to the link (
getLinkInflow()
) and routes flow either with:steadyflow_execute()
orkinwave_execute()
- The result is assigned to
Link[j].newFlow
.
- If the upstream node is a storage, calls
- After going through all links, updates each node’s and link’s final state (
setNewNodeState()
andsetNewLinkState()
).
- Returns the number of computational sub-steps performed (e.g., for partial iterative updates in kinematic or steady flow).
- Main function that routes flow through the network for a single time step (
3. Important Internal Functions
3.1 Initialization & Validation
-
validateTreeLayout()
- Ensures the network is “tree-like” for Kinematic Wave or Steady Flow.
- Checks each node’s outlet links (dividers, outfalls, storage) and ensures no node has multiple downstream links (unless it’s a valid exception).
-
validateGeneralLayout()
- Used by Dynamic Wave. Checks for conditions like dummy links, ideal pumps, or outfalls that must have limited connectivity. Ensures at least one outlet node, etc.
-
initNodeDepths()
- Assigns initial water depth to nodes if not specified.
- E.g., for a non-storage node with no user-supplied depth, it might average the depths of its connecting links.
-
initLinkDepths()
- For Dynamic Wave only—initial depth in each conduit is the average of the depths at its end nodes, unless a user-supplied
q0
is given.
- For Dynamic Wave only—initial depth in each conduit is the average of the depths at its end nodes, unless a user-supplied
-
initNodes()
&initLinks()
initNodes()
: sets initial inflow/outflow, volume, overflow, etc.initLinks()
: sets initial link volumes (or zero flow for Steady Flow) and prepares internal states in conduits/pumps.
3.2 Steady/Kinematic Wave Execution
-
flowrout_execute(...)
(as explained above). -
updateStorageState(i, j, links, dt)
- For a storage node
i
, we do an iterative approach to find the node’s new depth. - Because outflow from a storage node depends on the water depth in it, we do multiple iterations up to
MAXITER
(10) or until the depth change is underSTOPTOL
(0.005 ft). - We call
getStorageOutflow(...)
to sum flows leaving the node’s connected links.
- For a storage node
-
getLinkInflow(link, dt)
- Finds how much flow can enter a link from its upstream node.
- For example, we might limit it if the node cannot supply more than
node_getMaxOutflow(...)
.
-
steadyflow_execute(...)
- A simplified approach for Steady Flow:
- If it’s a conduit, tries to see if the input flow exceeds full capacity; if so, it limits flow to full capacity.
- Adjusts for evaporation/infiltration losses via
link_getLossRate(...)
.
- A simplified approach for Steady Flow:
-
kinwave_execute(...)
- (Not shown in detail in this code snippet.) Similar approach but uses kinematic wave formulas.
3.3 Updating Final States
- After each link is routed, we do:
setNewNodeState(...)
:- For non‐storage nodes, updates volume = oldVolume + netInflow × dt.
- If volume > fullVolume and ponding not allowed, set overflow and cap volume.
- Depth is then
node_getDepth(j, volume)
.
setNewLinkState(...)
:- For a conduit, we find average area from upstream and downstream cross-sectional areas, compute link volume, and set new depth based on
y1
+ offset,y2
+ offset, etc.
- For a conduit, we find average area from upstream and downstream cross-sectional areas, compute link volume, and set new depth based on
4. Putting It All Together in a Typical Time Step
- SWMM calls
flowrout_getRoutingStep()
to findtStep
. flowrout_execute(links, model, tStep)
is invoked:- If Dynamic Wave: goes directly to
dynwave_execute(tStep)
, which solves St. Venant PDEs iteratively. - Else (Steady or Kinematic):
- Loops through each link in a topologically sorted order.
- If the link’s upstream node is a storage, calls
updateStorageState(...)
to iteratively refine the storage node’s depth. - Finds link inflow, routes the flow with steady or kinematic formula.
- Updates link’s newFlow, increments node outflow/inflow.
- After all links are processed, calls
setNewNodeState(...)
for each node andsetNewLinkState(...)
for each link to finalize volumes and depths.
- If Dynamic Wave: goes directly to
- Return the count of computational steps used (1 step per link in steady/kinwave, or the iterative steps in dynamic wave).
5. Key Constants & Notes
OMEGA = 0.55
: Under‐relaxation factor for storage iteration—blends old depth with newly calculated depth to stabilize.MAXITER = 10
: Max iteration passes for updating storage nodes.STOPTOL = 0.005 ft
: Convergence tolerance on storage node depth.AllowPonding
: If set, a node can have indefinite capacity beyond fullDepth by storing water in a “ponded area.”
6. Conclusions
flowrout.c
is the routing orchestrator. It sets up initial states (volumes/depths), picks the correct solver path, and after each step updates node/link states.- For Steady and Kinematic wave, it uses simplified, topological progression: from upstream to downstream links, adjusting flows and volumes in each storage node.
- For Dynamic Wave, it delegates to
dynwave_execute()
, but still handles some setup/close routines. - The iterative logic in
updateStorageState()
is a key mechanism for bridging the fact that outflow from a storage node depends on the node’s depth, and the node’s depth depends on how much outflow it has—hence the repeated approximation each time step.
No comments:
Post a Comment