Saturday, December 28, 2024

SWMM5 findroot.c, Summary

 Below is a step‐by‐step explanation of findroot.c, which provides two root-finding methods for solving 

f(x)=0f(x) = 0: a Newton‐Raphson method (with fallback bisection) and Ridder’s Method. Both functions iterate until either the root is found within a given accuracy or a maximum iteration limit is reached.


1. Overall Purpose

//  findroot.c
//
//  Finds solution of func(x) = 0 using either ...
  • This file is for finding roots of functions f(x)f(x) = 0, i.e., we want to find xx such that f(x)f(x) is zero within a tolerance.

  • It contains two main functions:

    1. findroot_Newton(...): A combination of Newton–Raphson and bisection.
    2. findroot_Ridder(...): Implementation of Ridder’s Method.
  • The user supplies:

    • A bracket (x1, x2) such that f(x1)f(x1) and f(x2)f(x2) have opposite signs (i.e., the function’s sign changes, indicating a root in between).
    • A function pointer that either returns f(x)f(x) & derivative or just f(x)f(x).

2. findroot_Newton(...)

int findroot_Newton(
    double x1, double x2, double* rts, double xacc,
    void (*func) (double x, double* f, double* df, void* p),
    void* p)
  1. Parameters:

    • x1, x2: The initial bracket endpoints.
    • *rts: On input, an initial guess for the root; on output, the refined root.
    • xacc: The required accuracy for the solution.
    • func(x, &f, &df, p): A user function that returns:
      • f: the function value f(x)f(x),
      • df: the derivative f(x)f'(x).
    • p: A pointer to any extra data needed by func(). (It can be NULL if no extra data is needed.)
  2. Algorithm:

    1. Initial Setup:
      • xlo = x1, xhi = x2, x = *rts.
      • Evaluate func(x, &f, &df, p).
      • Keep track of iteration count in n.
    2. Loop (up to MAXIT=60 times):
      • The method tries a Newton‐Raphson step: dx = f/df.
      • But checks if that step is out of range or not decreasing well:
        if ( ( (x - xhi)*df - f)*((x - xlo)*df - f) >= 0.0
          || (fabs(2.0*f) > fabs(dxold*df)) )
        {
          // If so, do a bisection step
          dx = 0.5*(xhi - xlo);
          x = xlo + dx;
        }
        else
        {
          // Otherwise, do Newton step
          dx = f/df;
          x -= dx;
        }
        
      • After adjusting x, check if |dx| < xacc (i.e., the change is within tolerance).
      • Evaluate func(x, &f, &df, p) again.
      • If f < 0.0, set xlo = x; else xhi = x.
    3. Convergence:
      • If it converges or if maximum iterations are reached, store x in *rts.
      • The function returns the number of function evaluations used or 0 if MAXIT was exceeded (i.e., no convergence in time).
  3. Notes:

    • This approach tries Newton’s method if possible (fast convergence) but falls back to bisection if the Newton step might overshoot or not reduce the bracket effectively.

3. findroot_Ridder(...)

double findroot_Ridder(
    double x1, double x2, double xacc,
    double (*func)(double, void* p), void* p)
  1. Parameters:

    • x1, x2: The bracket endpoints (sign of f(x1) and f(x2) must differ).
    • xacc: Accuracy required.
    • func(x, p): The function returning only f(x).
    • p: Optional user data pointer.
  2. Algorithm: Ridder’s method is a bracketing method that uses intermediate function evaluations and a special formula to find a better midpoint.

    1. Evaluate flo = func(x1, p), fhi = func(x2, p).
    2. If flo == 0.0, root is x1; if fhi == 0.0, root is x2.
    3. Check (flo > 0.0 && fhi < 0.0) || (flo < 0.0 && fhi > 0.0) to confirm it’s bracketed.
    4. Then iterate up to MAXIT=60:
      • xm = midpoint: (xlo + xhi)/2.
      • Evaluate fm = func(xm, p).
      • Compute s = sqrt( fm^2 - flo * fhi ).
      • If s == 0.0 => can’t proceed, return the midpoint.
      • Then a new approximation xnew is found by a Ridder formula: xnew=xm+(xmxlo)((flofhi?1:1)fm/s)xnew = xm + (xm - xlo) * \bigl( (\text{flo} \ge \text{fhi} ? 1 : -1)*fm / s \bigr)
      • If |xnew - ans| <= xacc, we break (converged).
      • Evaluate fnew = func(xnew,p).
      • Then we decide how to re-bracket:
        if ( SIGN(fm, fnew) != fm ) {
            xlo = xm; flo = fm;
            xhi = xnew; fhi = fnew;
        }
        else if ( SIGN(flo, fnew) != flo ) {
            xhi = xnew; fhi = fnew;
        }
        else if ( SIGN(fhi, fnew) != fhi ) {
            xlo = xnew; flo = fnew;
        }
        
      • If bracket becomes very small or we converge, we return.
    5. If no bracket or no convergence, returns -1.e20 indicating failure.
  3. Notes:

    • Ridder’s method is a robust bracketing approach with guaranteed convergence if sign change is present, typically faster than simple bisection but less complicated than Newton.

4. Implementation Details

  • MAXIT = 60 is the maximum iteration count for both methods.
  • SIGN(a,b) macro returns |a| if b >= 0, else -|a|.
  • Each function uses the bracket [x1, x2] and ensures it always encloses the root (i.e., f(xlo)*f(xhi) < 0).
  • findroot_Newton(...) uses a “mixed” approach: Newton’s method but does a bisection if the Newton step is questionable.
  • findroot_Ridder(...) purely uses Ridder’s formula, a special bracket-based improvement over bisection.

5. Key Takeaways

  • These functions assume the user has already found an initial bracket [x1, x2] with f(x1)*f(x2) < 0.
  • findroot_Newton() returns an integer: the count of function evaluations or zero if it fails.
  • findroot_Ridder() returns a double root approximation or -1.e20 if bracket was invalid or no success.
  • Both methods can be integrated easily by providing a function pointer for f(x) (and derivative if needed).

Hence, findroot.c is a utility file for any part of the SWMM code (or related code) needing a robust root-finding procedure.

SWMM5 Error Message Code Summary

 Below is a step-by-step overview of this SWMM 5.2 error message listing. Each ERR(code, "message") line defines an error code and accompanying text that SWMM can output if a particular error condition occurs during a simulation. By centralizing them, the code can easily map each numerical code (e.g., 101, 201, 501, etc.) to a descriptive text message to help users diagnose what went wrong.


1. What These Error Macros Represent

  1. ERR(XXX, "message")

    • A macro typically expands into data or function calls that store an integer error code (e.g., 101) along with a format string (like "\n ERROR 101: memory allocation error.").
    • This allows SWMM to:
      1. Identify each unique error scenario (like an invalid parameter or a missing file).
      2. Print a meaningful message explaining the issue.
  2. These errors often:

    • Refer to specific objects (like a Node or Link), using %s placeholders for IDs.
    • Signal a missing or invalid input condition, a file read/write issue, or a runtime failure in the solver.

2. General Format: ERR(error_code, "Error message")

  • The error_code is an integer (e.g., 101, 203, 501).
  • The "Error message" is a string that SWMM can display to the user, often including a format specifier (%s) for an object name.

3. Specific Examples and Groups

  1. Memory/Runtime Issues

    ERR(101,"\n  ERROR 101: memory allocation error.")
    ERR(105,"\n  ERROR 105: cannot open ODE solver.")
    ERR(107,"\n  ERROR 107: cannot compute a valid time step.")
    
    • 101: The program could not allocate enough memory.
    • 105: The solver for ordinary differential equations couldn’t open (some internal module).
    • 107: The dynamic solver can’t find a stable time step.
  2. Link and Node Geometry

    ERR(103,"\n  ERROR 103: cannot solve KW equations for Link %s.")
    ERR(111,"\n  ERROR 111: invalid length for Conduit %s.")
    ERR(112,"\n  ERROR 112: elevation drop exceeds length for Conduit %s.")
    ERR(113,"\n  ERROR 113: invalid roughness for Conduit %s.")
    ERR(115,"\n  ERROR 115: adverse slope for Conduit %s.")
    
    • Usually triggered if the geometry data for a conduit is physically impossible (e.g., negative length, offset bigger than length).
  3. Outlets and Dividers

    ERR(133,"\n  ERROR 133: Node %s has more than one outlet link.")
    ERR(135,"\n  ERROR 135: Divider %s does not have two outlet links.")
    ERR(139,"\n  ERROR 139: Regulator %s is the outlet of a non-storage node.")
    
    • Dividers, outfalls, or special regulators have constraints on how many inlet/outlet links they can have.
  4. Flow Routing

    ERR(117,"\n  ERROR 117: no cross section defined for Link %s.")
    ERR(119,"\n  ERROR 119: invalid cross section for Link %s.")
    ERR(131,"\n  ERROR 131: the following links form cyclic loops ...")
    
    • If a link is missing a cross section or there’s a loop in the drainage network, these errors show up.
  5. Rainfall, Gages, and RDII

    ERR(156,"\n  ERROR 156: ambiguous station ID for Rain Gage %s.")
    ERR(159,"\n  ERROR 159: recording interval greater than time series interval for Rain Gage %s.")
    ERR(155,"\n  ERROR 155: invalid sewer area for RDII at node %s.")
    
    • Problems with rain gage data or infiltration/inflow (I/I) unit hydrograph data.
  6. Mass Balance

    ERR(10, "some older format ...")
    // Actually not seen, but you see "ERROR 10" is used as a partial code in "errorcode = 10" scenarios.
    
    • If the solver finds an inconsistent mass balance, it might raise certain error codes.
    • Not all codes are explicit for mass balance, but you'll see references to potential mass/flow issues in lines like 131 or 107.
  7. File Reading/Writing

    ERR(303,"\n  ERROR 303: cannot open input file.")
    ERR(305,"\n  ERROR 305: cannot open report file.")
    ERR(307,"\n  ERROR 307: cannot open binary results file.")
    ERR(309,"\n  ERROR 309: error writing to binary results file.")
    ERR(311,"\n  ERROR 311: error reading from binary results file.")
    
    • Opening or writing files fails: user lacks permission, path is invalid, or the file is locked.
  8. Interface Files

    ERR(313,"\n  ERROR 313: cannot open scratch rainfall interface file.")
    ERR(315,"\n  ERROR 315: cannot open rainfall interface file %s.")
    ERR(319,"\n  ERROR 319: unknown format for rainfall data file %s.")
    ERR(331,"\n  ERROR 331: cannot open hot start interface file %s.")
    
    • SWMM can use additional “interface files” (rainfall, runoff, hotstart, etc.). If they can’t be opened or are incorrectly formatted, these errors occur.
  9. Climate Files

    ERR(336,"\n  ERROR 336: no climate file specified for evaporation and/or wind speed.")
    ERR(337,"\n  ERROR 337: cannot open climate file %s.")
    ERR(338,"\n  ERROR 338: error in reading from climate file %s.")
    
    • If a climate file (with temperature, evaporation data) is needed but missing or unreadable.
  10. Inconsistent or Out-of-Sequence Data

ERR(173,"\n  ERROR 173: Time Series %s has its data out of sequence.")
ERR(223,"\n  ERROR 223: Transect %s has too few stations.") 
ERR(225,"\n  ERROR 225: Transect %s has too many stations.")
  • SWMM needs sorted or properly formatted data. If e.g. station cross-section data is out of order or incomplete, these errors appear.
  1. Input Format or Parsing
ERR(200,"\n  ERROR 200: one or more errors in input file.")
ERR(203,"\n  ERROR 203: too few items ")
ERR(205,"\n  ERROR 205: invalid keyword %s ")
ERR(209,"\n  ERROR 209: undefined object %s ")
  • The user’s input file is incorrect or references an unknown object or parameter.
  1. API-Specific Errors
ERR(500,"\n  ERROR 500: System exception thrown.")
ERR(501,"\n  API Error 501: project not opened.")
ERR(502,"\n  API Error 502: simulation not started.")
ERR(503,"\n  API Error 503: simulation not ended.")
ERR(509,"\n  API Error 509: invalid time period.")
  • These are used by the SWMM 5.2 API (functions in swmm5.h or swmm_output.h), indicating usage errors (like calling certain functions before swmm_open(...) or after swmm_close(...)).

4. How SWMM Uses These Error Messages

  • Internally, SWMM sets an error code in memory (like errorcode = 309;), then prints or logs the corresponding message.
  • If an error references an object (like a Node or Link), SWMM uses %s in the message to insert the object’s ID.

5. Key Takeaways

  1. Centralized: All SWMM errors are listed in one place.
  2. Codes range from around 100–500, each addressing a specific error scenario:
    • 100s often cover geometry or data errors,
    • 200s for invalid inputs or missing data,
    • 300s for file I/O problems,
    • 500s for API or system-level exceptions.
  3. Meaningful: Each line describes precisely what the user or developer must fix (like “invalid slope” or “file missing”).
  4. Integration: SWMM typically uses them in macros or string lookups, so the code can easily produce user-friendly messages.

By analyzing these lines, one can see the breadth of potential pitfalls in SWMM’s input data (like negative slopes, cyclical loops, out-of-sequence data) or runtime issues (like memory allocation or read/write failures).

SWMM5 enums.h Summary

 Below is a step‐by‐step explanation of enums.h, which defines the enumerations (i.e., sets of named integer constants) used throughout EPA SWMM5. These enumerations help the SWMM code identify everything from object types and link/node sub‐types to options, file usage types, flow classes, etc. By giving each concept a named integer constant (e.g., CONDUIT = 0, OUTFALL = 1), the code becomes more readable and maintainable.


1. Purpose

//   enums.h
//
//   Enumerated constants
  • enums.h is the central place where all major SWMM named constants are declared.
  • Each enum represents a conceptual category: object types, link types, measurement units, infiltration models, etc.

1.1 Why Enumerations?

  • They replace “magic numbers” with named constants, so code referencing an object type “LINK” uses LINK instead of a raw integer.
  • They ensure type safety (or at least consistent usage) in function parameters.
  • They facilitate switch-case logic or array indexing by enumerated types.

2. Major Enumerations

Below is a summary of each enumerated group in enums.h, typically sorted by conceptual group:

2.1 Object Types

enum ObjectType {
   GAGE, SUBCATCH, NODE, LINK, POLLUT, LANDUSE,
   TIMEPATTERN, CURVE, TSERIES, CONTROL, TRANSECT,
   AQUIFER, UNITHYD, SNOWMELT, SHAPE, LID, STREET, INLET,
   MAX_OBJ_TYPES
};
  • Identifies major SWMM objects: e.g., a rain gage (GAGE), subcatchment (SUBCATCH), or drainage system node (NODE).
  • MAX_OBJ_TYPES is not an actual object, but a sentinel indicating how many object types exist.

2.2 Node Sub‐types

enum NodeType {
   JUNCTION, OUTFALL, STORAGE, DIVIDER
};
  • Distinguishes types of nodes:
    • JUNCTION: normal node,
    • OUTFALL: boundary node where flow leaves the system,
    • STORAGE: a storage unit,
    • DIVIDER: splits flow in different directions.

2.3 Link Sub‐types

enum LinkType {
   CONDUIT, PUMP, ORIFICE, WEIR, OUTLET
};
  • Distinguishes link objects: conduit (pipe or channel), pump, orifice, weir, or outlet.

2.4 File Types & File Usage

enum FileType {
   RAINFALL_FILE, RUNOFF_FILE, HOTSTART_FILE, RDII_FILE,
   INFLOWS_FILE, OUTFLOWS_FILE
};

enum FileUsageType {
   NO_FILE, SCRATCH_FILE, USE_FILE, SAVE_FILE
};
  • SWMM can read/write various files (rainfall, runoff interface, hotstart, etc.).
  • “File usage” indicates how a file is used: none, temporary scratch, using an existing file, or saving to a new file.

2.5 Cross Section and Shape Types

enum XsectType {
   DUMMY, CIRCULAR, FILLED_CIRCULAR, ...
   RECT_CLOSED, RECT_OPEN, TRAPEZOIDAL, ...
   IRREGULAR, CUSTOM, FORCE_MAIN, STREET_XSECT
};
  • Identifies shapes of cross sections (like circular pipe, trapezoidal channel, custom shape, or a street cross section).

2.6 Measurement Units

enum UnitsType {
   US, SI
};

enum FlowUnitsType {
   CFS, GPM, MGD, CMS, LPS, MLD
};

enum ConcUnitsType {
   MG, UG, COUNT
};
  • For flow, we might see CFS (cubic feet/s) or CMS (cubic meters/s).
  • For concentration, we might see mg/L, ug/L, or count/L.

2.7 Runoff or Routing Computations

enum ConversionType {
   RAINFALL, RAINDEPTH, EVAPRATE, LENGTH,
   LANDAREA, VOLUME, WINDSPEED, TEMPERATURE,
   MASS, GWFLOW, FLOW
};
  • Each item indicates some quantity that might need unit conversions if switching between US and SI.

2.8 Computed Result Types

For Subcatchments:

enum SubcatchResultType {
   SUBCATCH_RAINFALL, SUBCATCH_SNOWDEPTH, SUBCATCH_EVAP, ...
   SUBCATCH_RUNOFF, ...
   SUBCATCH_WASHOFF
};
  • Summaries of subcatch results: rainfall intensity, infiltration, runoff flow rate, pollutant washoff, etc.

For Nodes:

enum NodeResultType {
   NODE_DEPTH, NODE_HEAD, NODE_VOLUME,
   NODE_LATFLOW, NODE_INFLOW, NODE_OVERFLOW, NODE_QUAL
};
  • E.g. water depth, hydraulic head, volume, etc.

For Links:

enum LinkResultType {
   LINK_FLOW, LINK_DEPTH, LINK_VELOCITY, LINK_VOLUME, LINK_CAPACITY, LINK_QUAL
};
  • E.g. flow, depth, velocity, volume in pipe, capacity ratio, etc.

For System‐wide:

enum SysFlowType {
   SYS_TEMPERATURE, SYS_RAINFALL, SYS_SNOWDEPTH,
   SYS_INFIL, SYS_RUNOFF, SYS_DWFLOW, ...
   SYS_STORAGE, SYS_EVAP, SYS_PET
};
  • Summaries for the entire system: e.g., total infiltration, total runoff, total outflow, etc.

2.9 Conduit Flow Classification

enum FlowClassType {
   DRY, UP_DRY, DN_DRY, SUBCRITICAL, SUPCRITICAL,
   UP_CRITICAL, DN_CRITICAL, MAX_FLOW_CLASSES,
   UP_FULL, DN_FULL, ALL_FULL
};
  • SWMM classifies a conduit’s flow state at each time step: is it dry, subcritical, supercritical, or surcharged? This helps shape the solver’s approach.

2.10 Additional Enumerations

  • RunoffFlowType and LoadingType describe mass balance flow categories or pollutant loading categories (build up, washoff, infiltration load, etc.).
  • RainfallType, TempType, WindType, EvapType: how precipitation, temperature, wind, or evaporation data are provided (time series, file, monthly, etc.).
  • RouteModelType enumerates routing methods: none, steady flow, kin wave, dynamic wave, etc.
  • ForceMainType: Darcy-Weisbach or Hazen-Williams for force mains.
  • OffsetType: depth offset vs elevation offset for node inverts.
  • InertialDampingType: how inertial terms are damped in dynamic wave calculations.
  • SurchargeMethodType: EXTRAN vs Preissmann slot method for node surcharging.
  • InflowType, PatternType, OutfallType, StorageType, ReactorType, TreatmentType, DividerType, PumpCurveType, OrificeType, WeirType, CurveType, NodeInletType: each enumerates specialized sub‐types or sub‐options for certain objects.

2.11 Input Section Types

enum InputSectionType {
   s_TITLE, s_OPTION, s_FILE, s_RAINGAGE, ... s_INLET
};
  • Identifies lines in the SWMM input file by which section they belong to (like [TITLE], [OPTIONS], [RAINGAGE], [MAP], etc.).

2.12 Additional Options

enum InputOptionType {
   FLOW_UNITS, INFIL_MODEL, ROUTE_MODEL, ...
   MIN_ROUTE_STEP, NUM_THREADS, SURCHARGE_METHOD
};
  • Lists all the possible [OPTIONS] keys that can appear in the SWMM input file.
  • E.g. FLOW_UNITS sets units system, NUM_THREADS sets # of threads for parallel logic, etc.

2.13 Generic Yes/No or All/None

enum NoYesType { NO, YES };
enum NoneAllType { NONE, ALL, SOME };
  • Basic enumerations used to interpret “YES/NO” or “ALL/NONE/SOME” type flags.

3. Usage in SWMM

  1. Throughout SWMM: these enumerations are used in switch‐cases or function parameters. For example, to check if a node is of type OUTFALL, or to store the method used for infiltration as an integer from RouteModelType.
  2. Compatibility: enumerations also help keep track of older SWMM versions or new features (like STREET or INLET).
  3. Extendability: If new object or method types appear, they can be added to enums.h without changing unrelated parts of the code.

4. Key Takeaways

  • enums.h is the master dictionary of SWMM’s enumerated constants.
  • Each enumerated group covers a domain of related categories (e.g., Link sub‐types, infiltration models, flow routing types).
  • This central approach ensures consistent usage of numeric IDs across the entire SWMM codebase, supporting clear and maintainable code.

SWMM5 swmm5.h Summary

 Below is a step‐by‐step explanation of swmm5.h, the public header file for SWMM 5.2:

  1. Overall Purpose

    • swmm5.h exposes the C API for SWMM 5.2. This allows applications (C, C++, or other languages via a DLL) to call SWMM’s functions directly, without needing internal source code.
    • Defines enums describing the SWMM object types and properties, plus function prototypes such as swmm_run, swmm_open, swmm_step, etc.
  2. Conditional Definitions

    // --- define WINDOWS
    #undef WINDOWS
    #ifdef _WIN32
      #define WINDOWS
    #endif
    #ifdef __WIN32__
      #define WINDOWS
    #endif
    
    • Checks if _WIN32 or __WIN32__ is defined. If so, we assume the Windows platform. This leads to #define WINDOWS.
    // --- define DLLEXPORT
    #ifdef WINDOWS
        #define DLLEXPORT __declspec(dllexport) __stdcall
    #else
        #define DLLEXPORT
    #endif
    
    • If on Windows, DLLEXPORT expands to __declspec(dllexport) __stdcall, meaning exported functions use the __stdcall calling convention.
    • Otherwise (non‐Windows), DLLEXPORT is empty.

    Why: This ensures the library can export the SWMM functions so that other programs can import them from a DLL (on Windows) or a shared object library on other platforms.

  3. C Linkage for C++

    #ifdef __cplusplus
    extern "C" {
    #endif
    ...
    #ifdef __cplusplus 
    }   
    #endif
    
    • Wraps everything in extern "C" if compiled under C++.
    • This prevents C++ name‐mangling and ensures the function names remain purely C (e.g., _swmm_run@12 on Windows).
  4. Enumerations

    The file defines a set of enums that represent different categories of objects or properties in SWMM:

    4.1 swmm_Object

    typedef enum {
        swmm_GAGE     = 0,
        swmm_SUBCATCH = 1,
        swmm_NODE     = 2,
        swmm_LINK     = 3,
        swmm_SYSTEM   = 100
    } swmm_Object;
    
    • Distinguishes a rain gage, subcatchment, node, link, or system.

    4.2 swmm_NodeType

    typedef enum {
        swmm_JUNCTION = 0,
        swmm_OUTFALL  = 1,
        swmm_STORAGE  = 2,
        swmm_DIVIDER  = 3
    } swmm_NodeType;
    
    • The type of a node object: junction, outfall, storage, or divider.

    4.3 swmm_LinkType

    typedef enum {
        swmm_CONDUIT = 0,
        swmm_PUMP    = 1,
        swmm_ORIFICE = 2,
        swmm_WEIR    = 3,
        swmm_OUTLET  = 4
    } swmm_LinkType;
    
    • The type of link object: conduit, pump, orifice, weir, or outlet.

    4.4 Additional Enums

    • swmm_GageProperty, swmm_SubcatchProperty, swmm_NodeProperty, swmm_LinkProperty:
      Each enumerates numeric IDs for various properties (like subcatch area, node invert, link slope, etc.) that can be accessed via the SWMM API (swmm_getValue, swmm_setValue).
    • swmm_SystemProperty enumerates system-level properties: e.g., swmm_STARTDATE, swmm_CURRENTDATE, swmm_FLOWUNITS, etc.
    • swmm_FlowUnitsProperty enumerates flow unit codes (CFS, GPM, MGD, etc.).
  5. Function Prototypes

    These are declared as int DLLEXPORT functionName(...) or void DLLEXPORT functionName(...). The DLLEXPORT macro expands to either __declspec(dllexport) __stdcall on Windows or empty on other platforms, ensuring they are properly exported from a compiled shared library (DLL).

    5.1 High-Level Running Functions

    int DLLEXPORT swmm_run(const char *f1, const char *f2, const char *f3);
    int DLLEXPORT swmm_open(const char *f1, const char *f2, const char *f3);
    int DLLEXPORT swmm_start(int saveFlag);
    int DLLEXPORT swmm_step(double *elapsedTime);
    int DLLEXPORT swmm_stride(int strideStep, double *elapsedTime);
    int DLLEXPORT swmm_end(void);
    int DLLEXPORT swmm_report(void);
    int DLLEXPORT swmm_close(void);
    
    • swmm_run(f1, f2, f3): A convenience function that in turn calls swmm_open(...), runs the simulation, writes a report, and closes.
    • swmm_open(...): Opens SWMM input file (f1), sets up results (f3), and report (f2).
    • swmm_start(...) & swmm_end(...): For advanced stepping usage (e.g., calling swmm_step(...) in a loop).
    • swmm_step(double *elapsedTime): Advances the simulation by one hydrodynamic routing step, returning the time advanced in elapsedTime.
    • swmm_stride(...): Same idea, but might skip multiple steps.
    • swmm_report() writes out final simulation results.
    • swmm_close() closes any remaining open structures/memory.

    5.2 Retrieving Information and Errors

    int DLLEXPORT swmm_getMassBalErr(float *runoffErr, float *flowErr, float *qualErr);
    int DLLEXPORT swmm_getVersion(void);
    int DLLEXPORT swmm_getError(char *errMsg, int msgLen);
    int DLLEXPORT swmm_getWarnings(void);
    
    • swmm_getMassBalErr(...): obtains % error in runoff, flow routing, water quality mass balance, etc.
    • swmm_getVersion(): returns an integer code for SWMM version (e.g. 52000 = 5.2.0).
    • swmm_getError(...): if an error occurred, returns > 0 and places an error message in errMsg.
    • swmm_getWarnings(): how many warning messages were generated?

    5.3 Object / Property Access

    int    DLLEXPORT swmm_getCount(int objType);
    void   DLLEXPORT swmm_getName(int objType, int index, char *name, int size);
    int    DLLEXPORT swmm_getIndex(int objType, const char *name);
    double DLLEXPORT swmm_getValue(int property, int index);
    void   DLLEXPORT swmm_setValue(int property, int index,  double value);
    double DLLEXPORT swmm_getSavedValue(int property, int index, int period);
    void   DLLEXPORT swmm_writeLine(const char *line);
    void   DLLEXPORT swmm_decodeDate(double date, int *year, int *month, int *day,
                  int *hour, int *minute, int *second, int *dayOfWeek);
    
    • swmm_getCount(objType): returns how many gages, subcatchments, nodes, or links are in the model.
    • swmm_getName(objType, index, name, size): retrieves the object’s name string.
    • swmm_getIndex(objType, name): gets the integer index given an object name.
    • swmm_getValue(property, index): fetches a real‐time property (like node depth, link flow, etc.).
    • swmm_setValue(property, index, value): modifies a real‐time property.
    • swmm_getSavedValue(property, index, period): fetches property from a stored result at a specific time period.
    • swmm_writeLine(...): writes a line to the SWMM report or console.
    • swmm_decodeDate(...): decodes SWMM’s internal date/time format (a double) into year/month/day/etc.

6. Why This Header?

  • This file is the main interface for external code to call SWMM 5.2.
  • By including swmm5.h, one can link against swmm5.dll (on Windows) or libswmm5.so (on Linux) to run or interact with SWMM from an external application or script.
  • The enumerations define the possible arguments to functions like swmm_getValue() or swmm_setValue(). For example, swmm_NODE_DEPTH is 303 to read or set a node’s depth.

7. Summary

swmm5.h is the crucial C API header for EPA SWMM:

  1. Detects if building on Windows, sets up DLL export macros.
  2. Enables external C/C++ (and other) code to call SWMM’s engine via swmm_run, swmm_step, etc.
  3. Enumerations map symbolic property/object types (e.g., swmm_NODE_DEPTH, swmm_SUBCATCH_AREA, swmm_LINK_FLOW) to integer constants.
  4. Function prototypes show how to open a SWMM run, step it, read results, and handle errors.

Hence, developers can embed SWMM into a larger program or create custom GUIs that manipulate or monitor a SWMM simulation in real time.

SWMM5 main.c Summary

 Below is a step‐by‐step explanation of main.c for the command line version of EPA SWMM 5.2 (often called runswmm). It’s a simple “stub” that uses swmm5.dll (the SWMM engine) to run a SWMM simulation from the command line. It handles argument parsing, help/usage messages, runs SWMM with the user‐specified input file, and displays results or errors.


1. High-Level Overview

//   main.c
//
//   Main stub for the command line version of EPA SWMM 5.2 to be run with swmm5.dll.
  • This file defines a main() function that expects command‐line arguments and calls the SWMM library (swmm5.h → compiled into swmm5.dll) for the actual simulation logic.

2. Command‐Line Arguments

int main(int argc, char *argv[])
  • argc is the number of command‐line arguments, and argv[] are the arguments themselves, including the program’s name as argv[0].

2.1 Usage

Typical usage is:

runswmm <inputFile> <reportFile> <optionalOutputFile>
  • If only --help or -h is passed, it shows help text.
  • If only --version or -v is passed, it prints version info.
  • If an incorrect or insufficient set of arguments is given, it prints an error message.

3. Version, Build, and Start Time

version = swmm_getVersion();
vMajor = version / 10000;
vMinor = (version - 10000 * vMajor) / 1000;
vRelease = (version - 10000 * vMajor - 1000 * vMinor);

start = time(0);
  1. swmm_getVersion() returns an integer, e.g. 52004 meaning 5.2.004.
  2. The code extracts vMajor, vMinor, and vRelease.
  3. start = time(0); records the current time at start, so we can show how long the run took.

4. Handling the Argument Cases

  1. argc == 1

    • “Not Enough Arguments (See Help --help)”
  2. argc == 2

    • Extract arg1 = argv[1].
      • If --help or -h: print usage instructions.
      • If --version or -v: print version info (like “5.2.0”).
      • Else: Unknown argument.
  3. Otherwise (argc >= 3)

    • We assume the user wants to run a SWMM simulation:
      1. inputFile = argv[1]
      2. reportFile = argv[2]
      3. binaryFile = argv[3] if argc > 3; otherwise blank.
      4. Print a banner:
        printf("\n... EPA SWMM %d.%d (Build %d.%d.%0d)\n", vMajor, vMinor, vMajor, vMinor, vRelease);
        
      5. Call swmm_run(inputFile, reportFile, binaryFile);
        • This single function call will internally do swmm_open(...), swmm_exec(...), swmm_close(...).
      6. Print run time:
        runTime = difftime(time(0), start);
        printf("\n\n... EPA SWMM completed in %.2f seconds.", runTime);
        
      7. Check for errors with swmm_getError(errMsg, msgLen) or warnings via swmm_getWarnings().

5. Checking Error and Warning Status

char errMsg[128];
if ( swmm_getError(errMsg, msgLen) > 0 ) printf(" There are errors.\n");
else if ( swmm_getWarnings() > 0 ) printf(" There are warnings.\n");
else printf("\n");
  • If swmm_getError(...) returns a code > 0, we know errors occurred.
  • If not, but swmm_getWarnings() > 0, we know there are warnings.
  • Otherwise, simulation completed cleanly.

6. Return Value

return 0;

The main() returns 0 (success) to the OS unless earlier logic might decide otherwise. By default, if the user passes fewer arguments or unknown arguments, it doesn’t set a special error code for the process—just prints a message and ends.


7. Summary

main.c is effectively the front end for SWMM in console mode:

  1. Parses command‐line arguments,
  2. If asked, prints help or version,
  3. If given correct arguments, calls the SWMM 5.2 engine (swmm_run(...) in swmm5.dll) with inputFile, reportFile, and optionally a binary results file name,
  4. Finally, reports how long the run took and whether any errors or warnings occurred.

This keeps the SWMM engine (in swmm5.dll) separate from the command line logic, allowing for easy integration with other front ends or GUIs as well.

SWMM5 errormanager.c Summary

 Below is a step‐by‐step explanation of how errormanager.c works. It provides a simple runtime error interface using a small “error handle” object that can store and retrieve an error code, look up the associated message, and clear the error.


1. Purpose

// Purpose: Provides a simple interface for managing runtime error messages.

This module is designed to handle error codes in a library or application. It supports:

  1. Storing an error code internally,
  2. Looking up a corresponding message string (via a user‐provided function),
  3. Clearing or checking for errors.

2. Data Structure: error_handle_t

// Presumably defined in errormanager.h:
typedef struct {
    int  error_status;                          // current error code
    void (*p_msg_lookup)(int, char*, int);      // pointer to function that fills a message buffer
} error_handle_t;
  • error_status: Stores the integer code of the current error.
  • p_msg_lookup: Points to a function that, given an error code, writes the corresponding message into a character buffer.

3. Functions

3.1 new_errormanager(...)

error_handle_t* new_errormanager(void (*p_error_message)(int, char*, int))
{
    error_handle_t* error_handle;
    error_handle = (error_handle_t*)calloc(1, sizeof(error_handle_t));

    error_handle->p_msg_lookup = p_error_message;

    return error_handle;
}
  • Purpose: Creates a new error_handle_t object and returns a pointer to it.
  • Steps:
    1. Allocates memory (using calloc for zero-initialization).
    2. Stores the function pointer p_error_message in p_msg_lookup.
    3. Returns the pointer.

This function is effectively a constructor—it sets up an empty error manager with a callback to look up error messages.


3.2 dst_errormanager(...)

void dst_errormanager(error_handle_t* error_handle)
{
    free(error_handle);
}
  • Purpose: Frees the memory for the given error_handle_t.
  • Steps:
    1. Calls free() on error_handle.
    2. No further cleanup logic is needed because there are no dynamic arrays inside error_handle_t.

Hence, it’s a simple destructor.


3.3 set_error(...)

int set_error(error_handle_t* error_handle, int errorcode)
{
    // If the error code is 0 no action is taken and 0 is returned.
    // This is a feature not a bug.
    if (errorcode)
        error_handle->error_status = errorcode;

    return errorcode;
}
  • Purpose: Stores an error code in the handle, unless it’s 0.
    • If errorcode == 0, nothing happens.
    • If nonzero, error_status is set to that code.
  • Returns the errorcode (mainly for convenience).

This allows upstream code to do:

if (some_check_fail) return set_error(err_handle, SOME_ERROR_CODE);

and know the code that was set.


3.4 check_error(...)

char* check_error(error_handle_t* error_handle)
{
    char* temp = NULL;

    if (error_handle->error_status != 0) {
        temp = (char*) calloc(ERR_MAXMSG, sizeof(char));

        if (temp)
            error_handle->p_msg_lookup(error_handle->error_status, temp, ERR_MAXMSG);
    }
    return temp;
}
  • Purpose: Retrieves the message text associated with the current error_status.
  • Steps:
    1. If error_status is 0, returns NULL (no error).
    2. If nonzero, allocates a temp buffer of size ERR_MAXMSG.
    3. Calls the user‐provided p_msg_lookup(error_status, temp, ERR_MAXMSG), which fills that buffer with a descriptive error string.
    4. Returns the allocated buffer pointer.

Important:

  • The caller must free() the returned string if not NULL.
  • This approach decouples storing the error code from the actual message creation.

3.5 clear_error(...)

void clear_error(error_handle_t* error_handle)
{
    error_handle->error_status = 0;
}
  • Purpose: Resets the stored error_status to 0 (no error).
  • This is analogous to clearing the “error flag”.

4. Typical Usage Flow

  1. Create an error manager:
    error_handle_t* hErr = new_errormanager(my_lookup_func);
    
  2. In various library calls:
    set_error(hErr, MY_ERR_CODE);
    
  3. The caller can check if an error occurred:
    char* msg = check_error(hErr);
    if (msg != NULL) {
        fprintf(stderr, "Error: %s\n", msg);
        free(msg);
        // handle error...
    }
    
  4. After handling error or if no error, optionally:
    clear_error(hErr);
    
  5. When done with the library:
    dst_errormanager(hErr);
    

5. Key Takeaways

  • This code uses a function pointer (p_msg_lookup) to convert an integer error code to a text message. This design allows flexibility: the library user can supply their own message lookup mechanism.
  • error_status = 0 means no error.
  • ERR_MAXMSG presumably is a predefined constant (like 256 or 1024) giving the maximum error message length.
  • The approach is lightweight: no complicated data structures, just a single error code plus a function pointer. It’s well‐suited for smaller C libraries that need a consistent error interface without adopting heavier frameworks.

SWMM5 swmm_output.c Summary

 Below is a step‐by‐step explanation of how swmm_output.c works. It is a C library that reads from a SWMM (Storm Water Management Model) binary output file, providing functions to:

  1. Open/Close the output file,
  2. Retrieve metadata (time steps, units, number of elements), and
  3. Extract time‐series or snapshot data (for subcatchments, nodes, links, and system).

It also manages an error mechanism via errormanager.h.


1. Purpose

/*
 * swmm_output.c - SWMM Output API
 *
 *  Author: Colleen Barr, ...
 *  Modified by: Michael E. Tryby, Bryant McDonnell
 */

This file defines a higher‐level API for reading SWMM’s binary results. A SWMM run produces a file containing flows, depths, water quality, etc. This API:

  • Opens that file,
  • Verifies it’s a valid SWMM output file,
  • Reads the internal file structure (metadata, offsets),
  • Retrieves data for subcatchments, nodes, links, pollutants, system variables, etc.

The main interface is via an opaque handle (SMO_Handle), which the library user requests and manipulates through the various SMO_* functions.


2. Data Structures

2.1 data_t

An internal struct that holds:

  1. File Info: path, FILE* file,
  2. Element Info: Nsubcatch, Nnodes, Nlinks, Npolluts, number of time periods Nperiods, etc.
  3. Positions/Offsets: for file sections (object IDs, results, etc.).
  4. An array elementNames storing strings for each subcatch/node/link/pollut.
  5. An embedded error_handle_t used for error management.

This struct is not exposed externally. Instead, callers see a SMO_Handle pointer (which is cast to data_t* internally).

2.2 File Format Constants

  • INT4, REAL4: 32-bit int/float types.
  • F_OFF: 64-bit integer type for large file support (off_t or __int64).
  • RECORDSIZE 4: data is stored in 4-byte increments for integers or floats.
  • DATESIZE 8: date/time is stored in an 8-byte double.
  • NELEMENTTYPES 5: subcatch, node, link, system, pollutant sets?

3. Key Functions

3.1 Initialization / Cleanup

  1. SMO_init(SMO_Handle *p_handle)

    • Allocates a data_t*, zero‐initialized with calloc.
    • Creates an error_handle_t by calling new_errormanager(&errorLookup).
    • Returns 0 if successful, -1 if allocation fails.
  2. SMO_close(SMO_Handle* p_handle)

    • Frees resources in data_t.
    • Closes the file if open, frees elementNames[], calls dst_errormanager, then frees data_t.
    • Sets the user’s *p_handle = NULL.

3.2 Opening the SWMM Output File

int SMO_open(SMO_Handle p_handle, const char *path)
{
    ...
    // open file in "rb" mode
    // call validateFile(p_data)
    // read essential metadata from the file header
    ...
}
  • validateFile() checks:
    • The “magic number” at start/end,
    • Nperiods is > 0,
    • an error code from the run in the file epilogue.
  • Reads counts of subcatchments, nodes, links, pollutants, plus offset positions in the file (IDPos, ObjPropPos, ResultsPos).
  • Also reads StartDate, ReportStep, and calculates BytesPerPeriod.

3.3 Retrieving Version, Units, Project Size

  • SMO_getVersion(...)
    • Reads integer in file near offset 2 * RECORDSIZE, which indicates SWMM version (like 52000).
  • SMO_getProjectSize(...)
    • Returns an array of counts: [Nsubcatch, Nnodes, Nlinks, 1, Npolluts].
  • SMO_getUnits(...) or SMO_getFlowUnits(...)
    • Gets integer code for flow units (CFS, GPM, etc.), and sets if the system is US or SI.
  • SMO_getPollutantUnits(...)
    • Retrieves concentration units for each pollutant (mg/L, ug/L, etc.).

3.4 Getting Time Steps

  • SMO_getTimes(...) can query:
    • SMO_reportStep: reporting time step in seconds,
    • SMO_numPeriods: total number of reported periods (Nperiods).

3.5 Getting Element Names

  • SMO_getElementName(...)
    • If p_data->elementNames is not yet built, calls initElementNames().
      • initElementNames() reads from IDPos in the file, for each object a 4‐byte length, followed by that many characters.
    • Depending on type (subcatch, node, link, pollut), it picks the correct index in elementNames[].
    • Allocates a buffer for the name.

3.6 Extracting Time Series

  • SMO_getSubcatchSeries(...) / SMO_getNodeSeries(...) / SMO_getLinkSeries(...) / SMO_getSystemSeries(...):
    • Return arrays of data for a particular object over a range of time indices (startPeriod to endPeriod).
    • Internally loops from k = 0 to len, calling getSubcatchValue(...) or getNodeValue(...), etc.

3.7 Snapshot Data (Single Period, Many Elements)

  • SMO_getSubcatchAttribute(...) for a single period and all subcatchments.
  • SMO_getNodeAttribute(...) for a single period and all nodes, etc.
  • Also variants for SMO_getLinkAttribute(...) and SMO_getSystemAttribute(...).

3.8 Low-Level Readers: getSubcatchValue(...), getNodeValue(...), etc.

These functions compute an offset in the output file that depends on:

  1. The current time period (timeIndex).
  2. The element’s index and variable code.
  3. The known layout (BytesPerPeriod, subcatch/node/link count, etc.).

They call:

_fseek(p_data->file, offset, SEEK_SET);
fread(&value, RECORDSIZE, 1, p_data->file);

to read a single float from the file.


4. Additional Utility

  • SMO_free(void **array): A convenience function to free memory allocated by the library for arrays (like time series). Sets pointer to NULL.
  • _fopen, _fseek, _ftell: Wrappers ensuring large file support or cross‐platform differences.

5. Error Handling

The code uses set_error(...) to store an integer code in p_data->error_handle->error_status (like 411, 422, 423, etc.). Then:

  • SMO_checkError(...) retrieves that code and calls check_error(...) (which returns a message string).
  • SMO_clearError(...) resets the code to 0.

errorLookup(...) is the local function mapping an error code to a message constant from messages.h.


6. Summary

swmm_output.c is a self-contained library to:

  1. Open a SWMM results file (binary),
  2. Check basic validity,
  3. Provide convenient functions to get metadata (counts, units, times),
  4. Read time series or snapshot data for subcatchments, nodes, links, pollutants, system,
  5. Return data as arrays of floats or single floats,
  6. Manage errors with an internal mechanism.

Hence, it abstracts the SWMM binary file format details away from the user, offering a simpler API for post-processing or analyzing SWMM results programmatically.

SWMM5 CMakeLists.txt Summary

 Below is a step‐by‐step explanation of this CMakeLists.txt file. It builds a shared library named swmm-output that exposes functions (like reading/writing SWMM’s binary output data), handles error management, and sets up an export header for cross‐platform symbol exporting. It also installs headers and library artifacts in a structured way and copies the built library for local testing when BUILD_TESTS is enabled.


1. Overall Purpose

# CMakeLists.txt - CMake configuration file for swmm/outfile
#
# Created: July 11, 2019
#
# Author: Michael E. Tryby
#         US EPA ORD/CESER
  • This file configures how the swmm-output library (in swmm/outfile) is built, installed, and exported.
  • The library will be named swmm-output in the final build artifacts.

2. Configure File Groups

set(SWMM_OUT_PUBLIC_HEADERS
    include/swmm_output.h
    include/swmm_output_enums.h
    include/swmm_output_export.h
)
  • SWMM_OUT_PUBLIC_HEADERS: a list of public header files that will be installed and included by other projects linking to swmm-output.
  • These typically define the library’s external API (like swmm_output.h).

3. Define the Library Target

add_library(swmm-output
    SHARED
        swmm_output.c
        errormanager.c
)
  • Creates a shared library target named swmm-output from sources swmm_output.c and errormanager.c.
  • This library is intended to be dynamically linked, so it’s SHARED.

3.1 Include Directories

target_include_directories(swmm-output
    PUBLIC
        $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
        $<INSTALL_INTERFACE:${INCLUDE_DIST}>
)
  • Tells CMake to add the include/ folder during the build (so the library can find its own headers) and to use ${INCLUDE_DIST} as the install-time include path.
  • BUILD_INTERFACE means the include path is used while building this library;
  • INSTALL_INTERFACE is used after installation, so dependent projects can also find the library’s headers.

4. Generate Export Header

include(GenerateExportHeader)
generate_export_header(swmm-output
    BASE_NAME swmm_output
    EXPORT_MACRO_NAME EXPORT_OUT_API
    EXPORT_FILE_NAME swmm_output_export.h
    STATIC_DEFINE SHARED_EXPORTS_BUILT_AS_STATIC
)
  • GenerateExportHeader is a CMake module that automatically generates a header defining export macros (like EXPORT_OUT_API) for different platforms (Windows, Linux, etc.).
    • This solves the typical problem of specifying __declspec(dllexport) vs. __declspec(dllimport) on Windows or empty macros on Linux.
  • BASE_NAME swmm_output => the base name for macros in the generated file.
  • EXPORT_MACRO_NAME EXPORT_OUT_API => the macro used in the code (e.g., EXPORT_OUT_API void somefunction();).
  • The file swmm_output_export.h is generated in the current binary directory.
file(COPY ${CMAKE_CURRENT_BINARY_DIR}/swmm_output_export.h
    DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/include
)
  • Copies that generated file into the include/ directory so it becomes part of the codebase (and is presumably versioned or installed).

5. Installation Rules

5.1 Install the Library

install(TARGETS swmm-output
    EXPORT
        swmm-outputTargets
    RUNTIME
        DESTINATION "${TOOL_DIST}"
    LIBRARY
        DESTINATION "${TOOL_DIST}"
    ARCHIVE
        DESTINATION "${LIBRARY_DIST}"
    FRAMEWORK
        DESTINATION "${TOOL_DIST}"
)
  • Installs the swmm-output library to different destinations depending on the build artifact type:
    • RUNTIME (i.e. the .dll on Windows) goes to $TOOL_DIST,
    • LIBRARY (i.e. shared objects on Linux or .dylib on macOS) also goes to $TOOL_DIST,
    • ARCHIVE (the .lib static library import library on Windows or .a on Linux) to $LIBRARY_DIST,
    • FRAMEWORK also to $TOOL_DIST (applies to macOS frameworks).
  • EXPORT swmm-outputTargets: means a usage file will be created that tracks how to link to swmm-output. This is used in the next step.

5.2 Export the Library’s Targets

install(
    EXPORT
        swmm-outputTargets
    DESTINATION
        "${CONFIG_DIST}"
    FILE
        swmm-output-config.cmake
)
  • This writes out a file named swmm-output-config.cmake with the target export data (like library path, include dirs, etc.) to ${CONFIG_DIST}.
  • Another CMake project can do find_package(swmm-output CONFIG) to import swmm-output library references.

5.3 Install the Public Headers

install(
    FILES
        ${SWMM_OUT_PUBLIC_HEADERS}
    DESTINATION
        "${INCLUDE_DIST}"
)
  • Copies the public headers we defined earlier (swmm_output.h, swmm_output_enums.h, swmm_output_export.h) into ${INCLUDE_DIST}.

6. Additional Commands

6.1 Copying the Library for Testing

if(BUILD_TESTS)
    add_custom_command(TARGET swmm-output POST_BUILD
        COMMAND ${CMAKE_COMMAND} -E copy
            $<TARGET_FILE:swmm-output>
            ${CMAKE_BINARY_DIR}/bin/$<CONFIGURATION>/$<TARGET_FILE_NAME:swmm-output>
    )
endif()
  • If BUILD_TESTS is set, after building swmm-output, it runs a POST_BUILD command to copy the built library into **${CMAKE_BINARY_DIR}/bin/<CONFIG>/**.
  • This might be for local test executables that expect the DLL or .so in a known folder.

7. Putting It All Together

In summary, this CMakeLists.txt file:

  1. Creates a library named swmm-output from swmm_output.c and errormanager.c.
  2. Uses CMake’s GenerateExportHeader to manage export macros for cross-platform shared library builds.
  3. Installs:
    • The library (DLL/.so/.dylib) to $TOOL_DIST,
    • The library’s import/export config to $CONFIG_DIST,
    • The library’s public headers to $INCLUDE_DIST.
  4. If building tests, it copies the resulting library into a local bin directory to be used by test executables.

Hence, developers and end-users can link to swmm-output by finding its installed config (swmm-output-config.cmake) and including the installed headers.

SWMM5 swmm5.def Summary

 Below is an overview of what this exports definition file indicates for SWMM5.DLL, typically found in a Windows DLL export definition (.def) file. It shows which functions are exported from the SWMM5 dynamic library and what their exported names (including calling conventions) are. In other words, this file tells consumers of SWMM5.DLL which routines they can call.


1. What Is a .def File?

In Windows development, a Module Definition File (.def file):

  • Lists all exported functions (the routines that can be called externally by programs or other libraries).
  • Potentially renames exports (e.g., = _swmm_close@0).
  • Allows controlling the ordinal and name of each export.

For SWMM5, this .def file ensures the C API functions of the SWMM engine are visible to external applications.


2. The Export List

LIBRARY     SWMM5.DLL

EXPORTS
    swmm_close                    = _swmm_close@0
    swmm_decodeDate               = _swmm_decodeDate@36
    swmm_end                      = _swmm_end@0
    swmm_getCount                 = _swmm_getCount@4
    swmm_getError                 = _swmm_getError@8
    swmm_getIndex                 = _swmm_getIndex@8
    swmm_getMassBalErr            = _swmm_getMassBalErr@12
    swmm_getName                  = _swmm_getName@16
    swmm_getSavedValue            = _swmm_getSavedValue@12
    swmm_getValue                 = _swmm_getValue@8
    swmm_getVersion               = _swmm_getVersion@0
    swmm_getWarnings              = _swmm_getWarnings@0
    swmm_open                     = _swmm_open@12
    swmm_report                   = _swmm_report@0
    swmm_run                      = _swmm_run@12
    swmm_setValue                 = _swmm_setValue@16
    swmm_start                    = _swmm_start@4
    swmm_step                     = _swmm_step@4
    swmm_stride                   = _swmm_stride@8
    swmm_writeLine                = _swmm_writeLine@4

2.1 Library Declaration

LIBRARY     SWMM5.DLL
  • Declares that the name of the library is SWMM5.DLL.

2.2 EXPORTS Section

  • Each line declares a function name, optionally followed by = _functionName@N, where:
    • functionName is how the function is known in user code (the “decorated” name in stdcall).
    • @N indicates the bytes of parameters on the stack (for example, _swmm_getError@8 typically means an stdcall function with 8 bytes of parameters).
    • For a C/C++ function using the __stdcall convention, the decorated name often looks like _functionName@stackbytes.

Below are descriptions of each function (these are typical from SWMM5’s C API, although details can vary slightly):

  1. swmm_close = _swmm_close@0
    Closes the opened SWMM project and frees memory.

  2. swmm_decodeDate = _swmm_decodeDate@36
    Decodes an internal SWMM datetime into year, month, day, hour, minute, second.

    • _@36 implies six parameters (6*4=24 bytes) plus possibly a return address, etc.
  3. swmm_end = _swmm_end@0
    Terminates a simulation after a swmm_start(). Typically calls internal housekeeping.

  4. swmm_getCount = _swmm_getCount@4
    Returns the number of objects (nodes, links, etc.) in a given category.

  5. swmm_getError = _swmm_getError@8
    Retrieves the last error code/message encountered.

  6. swmm_getIndex = _swmm_getIndex@8
    Retrieves the internal index of a named object (like a node name → index).

  7. swmm_getMassBalErr = _swmm_getMassBalErr@12
    Obtains system mass balance error after a run.

  8. swmm_getName = _swmm_getName@16
    Gets the name of an object from its index/type.

  9. swmm_getSavedValue = _swmm_getSavedValue@12
    Retrieves a saved result (e.g., flow, depth) for an object after or during a run.

  10. swmm_getValue = _swmm_getValue@8
    Gets a current simulation value for some property of a node/link.

  11. swmm_getVersion = _swmm_getVersion@0
    Returns the SWMM engine version code (e.g., 52004).

  12. swmm_getWarnings = _swmm_getWarnings@0
    Returns the number of warning messages encountered.

  13. swmm_open = _swmm_open@12
    Opens an input file and prepares data structures (typical signature: swmm_open(inFile, rptFile, outFile)).

  14. swmm_report = _swmm_report@0
    Writes the output reports (summary, statistics).

  15. swmm_run = _swmm_run@12
    A convenience function that calls swmm_open(), swmm_exec(), and swmm_close() in one shot.

  16. swmm_setValue = _swmm_setValue@16
    Sets a property for an object in the simulation (e.g., modifies parameters on-the-fly).

  17. swmm_start = _swmm_start@4
    Initializes simulation objects before stepping repeatedly.

  18. swmm_step = _swmm_step@4
    Executes one routing time step in SWMM (returns how much time actually advanced, or 0 if done).

  19. swmm_stride = _swmm_stride@8
    Possibly a variant of step that runs multiple steps at once or steps with a stride approach (depending on SWMM’s version).

  20. swmm_writeLine = _swmm_writeLine@4
    Writes a line of text to the SWMM report or output interface.


3. Why This .def File Matters

  • Windows exports: On Windows, __stdcall functions get decorated names, e.g. _function@N. A .def file can map a simpler C name (swmm_open) to that decorated name, ensuring external code can import the function by the simpler name.
  • This file ensures the DLL can be linked properly by external code that calls swmm_... functions.

4. Summary

This snippet:

  1. Declares the library name: SWMM5.DLL.
  2. Defines which C API functions SWMM exposes to external software:
    • Opening/Closing/Running SWMM simulations,
    • Getting/Setting object data (like node flows, index lookups),
    • Retrieving error info, version, mass balance, etc.
  3. Uses the calling convention and decoration typical for __stdcall functions on Windows.

Hence, end-users wanting to dynamically link against SWMM5.DLL can call these functions (like swmm_run) to control a SWMM simulation, query results, or modify parameters at runtime.

SWMM5 GitHub Actions Workflow

 Below is a step‐by‐step explanation of how this GitHub Actions workflow is structured and what each section does. This workflow has two jobs:

  1. unit_test: Builds the project with tests enabled and then runs unit tests.
  2. reg_test: Builds the project again (with a different method) and then runs regression tests using nrtest scripts.

It also demonstrates secrets usage, environment variables, artifact uploads, and different shell/working directory configurations.


1. High-Level Workflow Definition

name: Build and Test

on:
  push:
    branches: [ master, develop, release ]
  pull_request:
    branches: [ master, develop, release ]
  • name: Build and Test: The workflow is named “Build and Test.”
  • on:: The workflow triggers on:
    • push events and pull_request events targeting branches named master, develop, or release.
  • This ensures that whenever someone pushes code or opens a PR against one of those branches, the workflow is kicked off.

2. Environment Variables

env:
  OMP_NUM_THREADS: 1
  BUILD_HOME: build
  TEST_HOME: nrtests
  PACKAGE_NAME:  vcpkg-export-20240724-151754.1.0.0
  PKG_NAME: vcpkg-export-20240724-151754
  • These environment variables are global to the entire workflow, meaning all jobs can reference them. Examples:
    • OMP_NUM_THREADS: 1: Restricts OpenMP parallel to one thread.
    • BUILD_HOME: path or name for build output.
    • PACKAGE_NAME and PKG_NAME: used when installing packages from a private NuGet source.

3. Jobs Overview

The YAML defines two jobs: unit_test and reg_test. Each job:

  1. Runs on windows-2019 (runs-on: windows-2019).
  2. Has steps that check out code, build, and test in distinct ways.

3.1 unit_test Job

  unit_test:
    name: Build and unit test
    runs-on: windows-2019
    environment: testing
    defaults:
      run:
        shell: cmd
    steps: ...
  • unit_test runs on a Windows 2019 runner.
  • The environment: testing key can place the job into a specific environment named “testing,” if configured in your repository’s environments.
  • defaults.run.shell: cmd means all steps default to a CMD shell instead of PowerShell.

3.1.1 Steps in unit_test

  1. Checkout the repository:

    - name: Checkout repo
      uses: actions/checkout@v4
    
    • Grabs the code from the current repository/branch.
  2. Install boost-test via NuGet:

    - name: Install boost-test
      env:
        REMOTE_STORE: "https://nuget.pkg.github.com/USEPA/index.json"
        USERNAME: michaeltryby
      run: |
        nuget sources add -Name github -Source ${{ env.REMOTE_STORE }} -Username ${{ env.USERNAME }} -Password ${{ secrets.ACCESS_TOKEN }}
        nuget install ${{env.PKG_NAME}} -Source github
    
    • Adds a custom NuGet feed named “github.”
    • Installs a package identified by $env.PKG_NAME.
    • Uses secrets.ACCESS_TOKEN for authentication.
  3. Build:

    - name: Build
      env:
        TOOL_CHAIN_PATH: \scripts\buildsystems\vcpkg.cmake
      run: |
        cmake -B.\build -DBUILD_TESTS=ON -DCMAKE_TOOLCHAIN_FILE=.\${{env.PACKAGE_NAME}}${{env.TOOL_CHAIN_PATH}} .
        cmake --build .\build --config DEBUG
    
    • Sets an environment variable TOOL_CHAIN_PATH which is appended to PACKAGE_NAME.
    • Uses CMake out-of-source build:
      • cmake -B.\build ... . to generate the build system in a .\build folder, enabling tests.
      • cmake --build .\build --config DEBUG to actually compile in Debug config.
  4. Unit Test:

    - name: Unit Test
      run: ctest --test-dir .\build -C Debug --output-on-failure
    
    • Runs CTest tests located in .\build.
    • --output-on-failure shows failing test output.

This job thus compiles the code, runs the unit tests, and outputs any errors directly.


3.2 reg_test Job

  reg_test:
    name: Build and reg test
    runs-on: windows-2019
    defaults:
      run:
        shell: cmd
        working-directory: ci-tools/windows
    steps: ...
  • Also uses a Windows 2019 runner.
  • Defaults for all steps to run cmd shell and use working-directory: ci-tools/windows, meaning every step’s starting directory is ci-tools/windows (unless changed).

3.2.1 Steps in reg_test

  1. Checkout swmm repo:

    - name: Checkout swmm repo
      uses: actions/checkout@v4
    
    • Brings main repository code into the default GITHUB_WORKSPACE folder.
    • Because working-directory is set to ci-tools/windows, the actual location might be relative to that. Typically the top-level code is in a sibling directory.
  2. Checkout ci-tools repo:

    - name: Checkout ci-tools repo
      uses: actions/checkout@v4
      with:
        repository: USEPA/swmm-ci-tools
        ref: master
        path: ci-tools
    
    • Pulls a second repository USEPA/swmm-ci-tools into a folder named ci-tools/.
    • The code uses these scripts for regression tests.
  3. Setup python:

    - name: Setup python
      uses: actions/setup-python@v5
      with:
        python-version: '3.11'
    
    • Installs Python 3.11 so scripts can run.
  4. Install requirements:

    - name: Install requirements
      run: |
        python -m pip install --upgrade pip
        python -m pip install -r requirements-swmm.txt
    
    • Installs packages needed for the regression test environment from a requirements-swmm.txt.
  5. Build:

    - name: Build
      run: make.cmd /g "Visual Studio 16 2019"
    
    • Calls a custom script make.cmd in ci-tools/windows. Possibly calls MSBuild or runs the solution build for SWMM with Visual Studio 2019.
  6. Before reg test:

    - name: Before reg test
      env:
        NRTESTS_URL: https://github.com/USEPA/swmm-nrtestsuite
        BENCHMARK_TAG: v2.5.0-dev
      run: before-nrtest.cmd ${{ env.BENCHMARK_TAG }}
    
    • Possibly clones or updates an nrtests suite from GitHub, checking out a benchmark data set labeled v2.5.0-dev. The before-nrtest.cmd script presumably sets up the environment for regression tests.
  7. Run reg test:

    - name: Run reg test
      run: run-nrtests.cmd %GITHUB_RUN_ID%_%GITHUB_RUN_NUMBER%
    
    • Calls run-nrtests.cmd to run regression tests, using the run ID plus run number to label results.
    • nrtest typically compares SWMM’s output to baseline reference results for various test models.
  8. Upload artifacts:

    - name: Upload artifacts
      if: ${{ always() }}
      uses: actions/upload-artifact@v4
      with:
        name: build-test-artifacts
        path: upload/*.*
    
    • If the job completes (success or fail, thanks to always()), it uploads everything matching upload/*.* as an artifact named build-test-artifacts.
    • This typically includes logs, test reports, or other results.

4. Execution Flow & Key Points

  1. Branch triggers: Only on master, develop, or release branches for push/pull_request.
  2. Two jobs run in parallel by default:
    • unit_test: Focused on building and running unit tests (CTests).
    • reg_test: Focused on building in a different manner (via make.cmd) and running regression tests (via nrtest).
  3. NuGet references**: This workflow fetches a custom package from a GitHub NuGet feed, presumably containing dependencies like boost-test.
  4. Under the hood: The make.cmd, before-nrtest.cmd, and run-nrtests.cmd scripts do more specialized tasks (e.g., building via MSBuild, setting up test data, comparing outputs).

5. Summary

  • unit_test job:
    • Uses CMake + a custom vcpkg toolchain to build the code in Debug mode.
    • Runs CTest for unit tests.
  • reg_test job:
    • Uses custom scripts in ci-tools/windows for building and then runs nrtest for regression comparisons.
    • Uploads test artifacts for further inspection.

This approach separates quick unit testing from heavier regression checks, ensuring thorough coverage of the SWMM codebase. By splitting them into two jobs, they can run in parallel, speeding up the overall CI process.

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.

InfoSWMM: A 2030 AI-Assisted Study Guide

  InfoSWMM: A 2030 AI-Assisted Study Guide delete   InfoSWMM: A 2030 AI-Assisted Study Guide A comprehensive study guide for someone in 2030...