Saturday, December 28, 2024

Summary of dynwave.c, in SWMM5

 Below is a detailed overview of dynwave.c, which implements dynamic wave routing for the entire stormwater network in EPA SWMM5. This file orchestrates Picard iterations (successive approximations) on the full system of links (conduits, pumps, orifices, weirs) and nodes at each time step, using the momentum/continuity equations in an explicit manner.


1. Purpose of dynwave.c

  • dynwave.c is the top-level solver for dynamic wave routing in SWMM.
  • It calls or coordinates lower-level routines—dwflow.c for conduits and link_ / node_ functions for other links and nodes—to compute:
    1. Flow through each link (momentum eqn.),
    2. Depth at each node (continuity eqn.),
    3. Repeats these updates iteratively until convergence or a max iteration limit.
  • The dynamic wave engine supports:
    • Surcharged nodes (pressurized conditions),
    • Preissmann slot or EXTRAN surcharge handling,
    • Variable time stepping based on Courant or user criteria,
    • Ponding at nodes if allowed.

2. Key Data & Structures

2.1 Global Variables

  1. VariableStep
    • Holds the variable time step if CourantFactor != 0, otherwise defaults to the user RouteStep.
  2. Xnode[] (type TXnode)
    • An extended node structure used only by the dynamic wave solver each iteration, containing:
      • converged: boolean if the node’s depth is converged,
      • newSurfArea, oldSurfArea: surface area for node,
      • sumdqdh: sum of partial derivatives from adjacent links,
      • dYdT: estimated rate of depth change.
  3. Omega
    • Under-relaxation factor (0–1) for flow/depth updates in successive approximations. Typically 0.5.
  4. Steps
    • The iteration count in the current Picard loop for the time step.

2.2 Local (Static) Routines

  • getVariableStep(...), getLinkStep(...), getNodeStep(...)
    • Compute a variable (Courant-based) time step each iteration, limited by max user step or a minimum threshold.
  • findNodeDepths(...), setNodeDepth(...)
    • Solve for each node’s new depth using the net flows (inflows - outflows) and partial derivatives.
    • Enforce surcharging logic if water level goes above the node’s crown or full depth.
  • findLinkFlows(...)
    • Calls dwflow_findConduitFlow(...) (in dwflow.c) for conduits or special routines for pumps/orifices/weirs, then updates inflows/outflows at nodes.

3. Main Workflow per Time Step

  1. dynwave_execute(tStep) is called from the overall routing loop:

    1. Initialize iteration variables in initRoutingStep(), resetting Xnode data.
    2. Start a Picard iteration loop (up to MaxTrials times):
      • initNodeStates(): sets each node’s surface area, inflow/outflow, etc.
      • findLinkFlows(dt):
        • For each link, solve the momentum eqn. or specialized logic (pump, weir, orifice).
        • Update node inflow/outflow for each link.
      • findNodeDepths(dt):
        • Solve continuity eqn. for each node’s new depth.
        • Returns TRUE if all node depth changes are under HeadTol.
      • If converged, break the iteration loop; otherwise continue.
    3. If not converged after MaxTrials, record a non-convergence event (used by the reporting).
    4. findLimitedLinks() checks if any conduits are capacity-limited.
  2. Return the number of iterations used.

Hence, dynwave_execute() is the principal function that executes dynamic wave for one time step for the entire system, returning how many Picard steps it needed.


4. Important Functions in Detail

4.1 dynwave_init()

  • Allocates memory for Xnode[].
  • Initializes each node’s crownElev to the invert plus the highest possible conduit top.
  • Sets CrownCutoff for either SLOT or EXTRAN surcharge method.

4.2 dynwave_close()

  • Frees the memory allocated for Xnode[].

4.3 dynwave_validate()

  • Adjusts MinRouteStep, MinSurfArea, HeadTol, and MaxTrials if they aren’t set by the user.

4.4 dynwave_getRoutingStep(fixedStep)

  • If CourantFactor == 0.0, returns the user’s fixedStep.
  • Else calls getVariableStep(fixedStep):
    • getLinkStep(...): checks each conduit’s Courant condition.
    • getNodeStep(...): also checks node depth stability.
    • Takes the minimum of these, ensures it’s \ge MinRouteStep.

4.5 dynwave_execute(tStep)

(see main workflow above).

4.6 initRoutingStep()

  • Resets each link’s surfArea1, surfArea2 = 0, node array items, etc.
  • For each conduit, moves Conduit[k].a1 -> Conduit[k].a2.

4.7 initNodeStates()

  • For each node:
    • If AllowPonding, get ponded area from node_getPondedArea(...).
    • Else get surface area from node_getSurfArea(...).
    • Set node inflow/outflow from latflow or previous losses.

4.8 findLinkFlows(dt)

  • For each link:
    • If it’s a conduit and not flagged as bypassed, calls dwflow_findConduitFlow(...).
    • Else if it’s a pump/orifice/weir/dummy, calls specialized logic (some in link_* routines or findNonConduitFlow()).
    • Then calls updateNodeFlows() to add link flows to the upstream/outflow for the relevant nodes.

4.9 findNodeDepths(dt)

  • For each node (except outfalls), calls setNodeDepth(...).
  • setNodeDepth(...) uses net flow volume to compute new depth. If surcharged (pressure flow), it uses a different approach. If over the top, sets overflow, etc.
  • Returns TRUE if all changes in depth are under HeadTol, or FALSE otherwise.

4.10 Overflow & Ponding

  • If node volume tries to exceed fullVolume and AllowPonding is FALSE, the excess is node[i].overflow. Depth is capped at node[i].fullDepth + surDepth.
  • If ponding is TRUE, the node can store water beyond fullDepth in a “ponded area.”

5. Surcharge Methods

  • EXTRAN vs. SLOT:
    • If SurchargeMethod == EXTRAN, node surcharging uses the original EXTRAN approach (cutting off flow at crown, building head, etc.).
    • If SurchargeMethod == SLOT, uses a Preissmann slot in each closed conduit, letting depth exceed yFull while giving more stable pressurized flow modeling.

6. Time Step Calculation

  1. Link-based Courant: Δtlink=some factor×volumeflow \Delta t_{\mathrm{link}} = \text{some factor} \times \frac{\text{volume}}{\text{flow}} scaled by the Froude number and CourantFactor.
  2. Node-based stability:
    • If a node’s depth changes quickly, reduce time step to keep it from jumping more than 25% of the node’s “crown depth” (or something similar).

Combining these ensures a stable unsteady solution: the time step is not so large that water “teleports” across multiple nodes or that node depths jump uncontrollably.


7. Output & Convergence Tracking

  • SWMM tracks each node’s “converged” flag after each iteration. If all nodes are converged (depth changes < HeadTol), the iteration loop ends.
  • If not, it increments Steps and tries again up to MaxTrials.
  • If still not converged, increments NonConvergeCount for reporting.

8. Summary

dynwave.c is the driver for the dynamic wave routing solution in SWMM, implementing a Picard iteration approach for the system of flow equations. It:

  1. Initializes the nodes and links each time step,
  2. Finds conduit flows (using dwflow.c),
  3. Finds node depths,
  4. Checks for convergence,
  5. Adjusts the time step if CourantFactor is used.

Through these steps, SWMM can handle a variety of complex hydraulic phenomena (flooding, pressurization, backwater, surcharging, flow reversals) in a stable and iterative manner.

No comments:

SWMM5 Delphi GUI Dbackdrp.pas Summary

 The Dbackdrp unit provides a dialog form for selecting a backdrop image for the study area map within the SWMM (Storm Water Management Mod...