Monday, December 30, 2024

SWMMIO profiler.py Summary

Below is an extended summary of the code that describes how it constructs a SWMM model profile plot in Python, focusing on node/link geometry, profile configuration, and plot labeling:


Overview

This code supplies a set of matplotlib-based utilities for generating a profile plot (a side-view cut) of a SWMM model network, including nodes (manholes), links (conduits, weirs, orifices, pumps, outlets), ground elevation, and an optional hydraulic grade line (HGL). The profile is built from a path (a sequential chain of upstream-to-downstream elements) and rendered onto a given matplotlib axis (ax).

Core functionalities:

  1. Profile Building
    • build_profile_plot(ax, model, path_selection) – the central function that orchestrates retrieving geometry from a SWMM model, placing each node/link at the correct horizontal position, and returning a config dictionary (profile_config) for subsequent use.
  2. Node & Link Drawing
    • _add_node_plot(...) and _add_link_plot(...) – low-level routines to plot rectangles/trapezoids for manholes or link cross-sections (differentiating between conduits, weirs, orifices, pumps, etc.).
  3. Ground Line & HGL
    • _add_ground_plot(...) – draws the ground-level profile (brown dashed line) across the entire path.
    • add_hgl_plot(...) – overlays a line that represents the computed or observed water surface elevation at each node (or between nodes).
  4. Labeling
    • add_node_labels_plot(...) – attaches text labels/arrows for each node in the profile.
    • add_link_labels_plot(...) – attaches text labels/arrows for each link in the profile.

The net result is a layered side-view diagram of how nodes, links, ground line, and HGL align along a chosen path in a SWMM model.


Key Components

1. build_profile_plot(ax, model, path_selection)

Purpose:

  • Assembles the entire profile by iterating over a list of (us_node, ds_node, link_name) tuples, effectively describing a chain from upstream to downstream.
  • Produces a dictionary with details on each node/link’s placement, enabling further annotation or plotting.

Process:

  1. Initialization

    • Retrieves nodes = model.nodes.dataframe and links = model.links.dataframe.
    • Declares profile_config with empty lists for “nodes” and “links,” plus “path_selection.”
    • Sets up arrays for ground_levels['x'] & ground_levels['level'].
  2. Iterating over Path

    • For each tuple (us_node, ds_node, link_id) in path_selection:
      • Plots the first node if ind == 0, calling _add_node_plot(...) and storing the node geometry in profile_config["nodes"].
      • Accumulates a “rolling x-position” (rolling_x_pos) that simulates the horizontal distance for each link, dependent on link length or a default link length (if it’s a weir, orifice, pump, outlet).
      • Calls _add_node_plot(...) again for the downstream node and _add_link_plot(...) for the link geometry between them.
        • Each node gets appended to profile_config['nodes'], each link to profile_config['links'].
    • After finishing the loop, _add_ground_plot(...) is used to draw a dashed line indicating ground elevation.
  3. Return

    • A profile_config dictionary with:
      • nodes: A list of dicts containing node IDs, rolling x-positions, invert elevations.
      • links: A list of dicts with link IDs, midpoints, link type.
      • path_selection: Echo of the input path for reference.

2. _add_node_plot(ax, x, model, node_name, link_set, ... )

Purpose:

  • Draws a rectangular manhole (or node structure) at position x on the x-axis, with the correct invert elevation and max depth.

Details:

  • Extracts invert_el from nodes.loc[node_name].
  • Determines the node’s depth from model’s input data (model.inp.junctions, model.inp.outfalls, model.inp.storage), defaulting to MaxDepth.
  • Plots a rectangle with corners [(x−width, invert_el), (x+width, invert_el+depth), ...] in black lines.
  • Optionally draws a gradient fill on the sides for a 3D effect.

Returns:

  • A dictionary of {'x': [ul_x, ur_x], 'level': [ul_y, ur_y]}, used by _add_ground_plot to incorporate node’s top edge as part of ground level, if needed.

3. _add_link_plot(ax, us_x_position, ds_x_position, model, link_set, ...)

Purpose:

  • Draws a link cross-section between the upstream node at us_x_position and downstream node at ds_x_position, considering the link type (conduit, weir, orifice, pump, outlet).

Process:

  1. Determine link type from links.loc[link_id].Type.
  2. For CONDUIT:
    • Use links.loc[link_id].Length (already accounted for in the x offset) plus geometry fields like Geom1 for diameter/height, InOffset, OutOffset for vertical offsets.
  3. For ORIFICE, WEIR, PUMP, OUTLET:
    • Uses specialized default lengths (like DEFAULT_WEIR_LENGTH) or a single crest height for orifices/weirs.
    • Draw polygons or lines to represent the side profile.
  4. Returns a dict with {'x': [...], 'bottom': [...], 'link_type': link_type, 'mid_x': [...], 'mid_y': [...]}, enabling the main function or labeling routines to know how to position text or the HGL line.

4. _add_ground_plot(ax, ground_levels)

Purpose:

  • Joins the ground-level points computed from each node or link in a dashed brown line ('--', 'brown').
  • The ground_levels['x'] and ground_levels['level'] data are accumulated during node draws in build_profile_plot.

5. add_hgl_plot(ax, profile_config, hgl=None, depth=None, color='b', label="HGL")

Purpose:

  • Overlays a line representing the hydraulic grade line (HGL) or water surface.
  • Two usage patterns:
    1. hgl – a dictionary/Series mapping node IDs to HGL elevations, or
    2. depth – a dictionary/Series mapping node IDs to water depth, which is then added to the node invert.
  • The function loops through each node in profile_config['nodes'], builds an array of HGL values, and does a single ax.plot(...).

Weir Link Special Handling:

  • If a link is type “WEIR,” it tries to insert a midpoint to reflect any possible “step” between upstream and downstream side. This ensures the HGL transitions properly if weirs create abrupt changes.

6. add_node_labels_plot(ax, model, profile_config, ...) and add_link_labels_plot(ax, model, profile_config, ...)

Purpose:

  • Adds textual labels for each node or link above/below the structure.
  • Each label is anchored with an arrow (arrowprops=dict(...)) from the label text down to the structure.
  • Offers optional stagger so that labels alternate to avoid collisions.

Implementation details:

  • For nodes:
    • Extract invert + depth = top of node.
    • Place an annotation arrow from the node top to a label above the highest node in the plot.
  • For links:
    • Use the link’s midpoint or bottom to place the arrow.
    • Place the text below the entire group if needed (to avoid crowding).

Typical Usage Flow

  1. Call build_profile_plot(ax, model, path_selection).
    • Returns profile_config with node/link geometry.
    • Draws the profile lines for manholes, conduits, ground, etc.
  2. Optionally overlay:
    • add_hgl_plot to show the water surface profile.
    • add_node_labels_plot or add_link_labels_plot to label items.
  3. Render or save the figure:
    import matplotlib.pyplot as plt
    
    fig, ax = plt.subplots()
    pcfg = build_profile_plot(ax, my_model, my_path)
    add_hgl_plot(ax, pcfg, hgl=node_water_surface_dict)
    add_node_labels_plot(ax, my_model, pcfg)
    add_link_labels_plot(ax, my_model, pcfg)
    plt.show()
    

Constants and Defaults

  • MH_WIDTH = 10: Horizontal half-width for manholes when drawing a node profile.
  • DEFAULT_ORIFICE_LENGTH, DEFAULT_PUMP_LENGTH, DEFAULT_WEIR_LENGTH, DEFAULT_OUTLET_LENGTH: The “visual” length used for these special link types if the model doesn’t have a length (or if a simplified approach is desired).

Error Handling

  • error.InvalidDataTypes – raised if add_hgl_plot(...) sees neither hgl nor depth in the correct form.
  • The code also uses model.inp references (like model.inp.junctions) to check how to interpret node depth or offset. If data is missing, it might cause an exception or result in an incomplete profile.

Conclusion

This code forms a profile-plot toolkit for SWMM or SWMM-like hydraulic models:

  1. build_profile_plot systematically places nodes and links horizontally, respecting lengths and offsets.
  2. Node/Link subroutines handle the geometry difference among CONDUIT, WEIR, ORIFICE, PUMP, OUTLET.
  3. Ground line and HGL lines add topographical and hydraulic context.
  4. Labeling routines annotate each node/link, making the final plot self-explanatory.

Together, these help engineers or researchers visualize how water flows and see the relative elevations of nodes, the shape/length of links, and changes in water surface (HGL) across a chosen path in the SWMM network.

No comments:

A comprehensive explanation of how minimum travel distance relates to link length in InfoSewer

In hydraulic modeling of sewer networks, the minimum travel distance is a fundamental parameter that affects how accurately the model can si...