Saturday, December 28, 2024

SWMM5 inflow.c Summary

Below is a step-by-step explanation of the inflow.c file in EPA SWMM5. This file manages any external (direct) or dry-weather flow (DWF) inflows assigned to nodes in the drainage network. It covers:

  • Direct/External Inflows: e.g., time series flows or concentrations added directly to a node.
  • Dry-Weather Flows (DWF): e.g., constant or pattern-based baseline inflows for a node (like average daily sewage flows).

1. Overview of inflow.c

EPA SWMM5 allows you to specify additional inflows entering a drainage system node, beyond normal subcatchment runoff. They can be:

  1. External inflows:

    • Typically from time series, e.g., industrial wastewater discharges or other user-defined flows.
    • Can be flows, or pollutant concentrations/mass loads.
  2. Dry Weather Flows (DWF):

    • Typically average flows that vary by monthly/daily/hourly patterns.
    • E.g., base sanitary flows in a network.

The inflow.c file implements reading and storing these inflows, plus computing their actual flow/concentration at a given time step.


2. Data Structures

2.1 TExtInflow

typedef struct ExtInflow {
    int    param;        // -1 for FLOW or pollutant index
    int    type;         // type of inflow: FLOW_INFLOW, CONCEN_INFLOW, MASS_INFLOW
    int    tSeries;      // index of time series for inflow
    double cFactor;      // units conversion factor
    double sFactor;      // scaling factor
    double baseline;     // baseline inflow value
    int    basePat;      // baseline time pattern
    struct ExtInflow* next;
}  TExtInflow;
  • param: Which inflow parameter is assigned (either flow or pollutant). If -1, it’s FLOW; otherwise it’s a pollutant index.
  • type: Tells if it’s FLOW_INFLOW, CONCEN_INFLOW, or MASS_INFLOW.
  • tSeries: Index of a time series controlling the inflow variation over time.
  • cFactor, sFactor: Additional conversion and scaling factors.
  • baseline and basePat: A constant baseline inflow plus an optional time pattern multiplier.
  • Linked together in a linked list via next.

2.2 TDwfInflow

typedef struct DwfInflow {
    int     param;                // -1 for FLOW or pollutant index
    double  avgValue;             // average baseline DWF
    int     patterns[4];          // up to 4 pattern indexes
    struct  DwfInflow* next;
} TDwfInflow;
  • For dry-weather flow:
    • param = -1 for flow, otherwise pollutant index.
    • avgValue = average DWF. For flow, it’s eventually converted to user’s flow units.
    • patterns = monthly/daily/hourly/weekend patterns. The code sorts them by pattern type for speed.

3. Reading Inflow Inputs

3.1 inflow_readExtInflow()

int inflow_readExtInflow(char* tok[], int ntoks)
  • Parses a line of input data describing an external (direct) inflow.
  • Data line format:
    1. nodeID
    2. FLOW | pollutID
    3. tSeriesID (time series name, could be blank)
    4. (optional) CONCEN/MASS for pollutant, plus numeric fields:
      • A unitsFactor if MASS,
      • A scaleFactor,
      • A baseline,
      • A basePat (pattern).
  • If the parameter is FLOW, then param=-1; otherwise, it’s the pollutant index.
  • If it’s mass inflow, an additional factor in cf (conversion factor) is read.
  • This eventually calls inflow_setExtInflow() to create or update the TExtInflow object and link it into the node’s list of external inflows.

Key: The code sets:

  • Type = FLOW_INFLOW, CONCEN_INFLOW, or MASS_INFLOW.
  • Time Series reference.
  • Baseline flow or concentration.
  • Optional time pattern.

3.2 inflow_setExtInflow()

int inflow_setExtInflow(int j, int param, int type, int tseries, int basePat,
                        double cf, double baseline, double sf)
  • Actually allocates (or re-uses) a TExtInflow struct for a node j.
  • Fills in the parameters for external inflow:
    • param, type, tseries, cFactor, sFactor, baseline, basePat.
  • Returns 0 if success, or error code otherwise.

3.3 inflow_readDwfInflow()

int inflow_readDwfInflow(char* tok[], int ntoks)
  • Reads a line describing dry weather flow for a node.
  • Format:
    1. nodeID
    2. FLOW or pollutant name
    3. avgValue
    4. Up to 4 pattern IDs
  • Identifies the node, the param (flow or pollut index), the average value.
  • Finds up to 4 pattern references.
  • Creates a TDwfInflow object and attaches it to Node[j].dwfInflow.

4. Managing Inflow Lists

4.1 Deleting Inflows

  • inflow_deleteExtInflows(int j): frees the linked list of external inflows on node j.
  • inflow_deleteDwfInflows(int j): frees the linked list of dry-weather inflows on node j.

These are typically called at the end of a run or if the user re-initializes objects.


5. Computing Inflow Rates

During a simulation step, SWMM needs to compute the actual inflow from these data. Two main calls:

  1. inflow_getExtInflow(): for external inflows
  2. inflow_getDwfInflow(): for dry-weather flows

5.1 inflow_getExtInflow()

double inflow_getExtInflow(TExtInflow* inflow, DateTime aDate)
  • For a given date/time, returns the current external inflow.
  • Steps:
    • Possibly apply the baseline pattern multiplier (monthly/daily/hourly/weekend).
    • If time series is present, lookup the time series value at aDate and scale it.
    • Then combine those plus the conversion factor: flow or conc.=cFactor×(TimeSeriesValue×sFactor+baseline)\text{flow or conc.} = cFactor \times (\text{TimeSeriesValue} \times sFactor + baseline)

5.2 inflow_getDwfInflow()

double inflow_getDwfInflow(TDwfInflow* inflow, int month, int day, int hour)
  • For the current time’s month, day, hour, it collects pattern multipliers from up to 4 patterns:
    • monthly,
    • daily,
    • hourly,
    • weekend hourly.
  • A final factor f is then multiplied by inflow->avgValue.
  • Returns that product as the current DWF for the parameter (flow or pollutant).

5.3 Pattern Lookups

getPatternFactor(int p, int month, int day, int hour)

  • Takes pattern index p plus the current date/time, returns the pattern factor.
  • The code checks if pattern type is monthly/daily/hourly/weekend, picks the right factor from the pattern array. If out of range, defaults to 1.0.

6. Dry-Weather Flow Patterns

inflow_initDwfPattern(int j)

  • Initializes the Pattern[j] object with all factors = 1.0, no ID. Called when a Time Pattern is first created.

inflow_readDwfPattern(char* tok[], int ntoks)

  • Reads lines defining a pattern’s values.
  • Sets the pattern type = monthly/daily/hourly/weekend, then reads the numeric factors for up to 24 slots (for hourly) or 12 slots (monthly), etc.

7. Summary

  1. Reading Inputs:

    • External inflows from lines containing node, pollutant, time series, scaling, baseline, etc.
    • DWF inflows from lines containing node, pollutant, average value, up to 4 patterns.
  2. Managing Node Inflow Lists:

    • Each node has a linked list of TExtInflow objects and a linked list of TDwfInflow objects.
    • inflow_delete...() frees them.
  3. Using Patterns:

    • Patterns can adjust baseline or time series inflow by monthly/daily/hourly/weekend multipliers.
  4. Computing Inflows:

    • External inflows use inflow_getExtInflow() => time series + baseline pattern.
    • DWF inflows use inflow_getDwfInflow() => average flow + pattern multipliers.

In short, inflow.c parses inflow inputs and manages their time-dependent retrieval so that SWMM’s hydraulic/quality engine can incorporate these flows and pollutant loads at each node in each time step.


SWMM5 iface.c Summary

Below is a step‐by‐step explanation of iface.c, which manages routing interface files in EPA SWMM5. These files store node flow and pollutant concentration data at specific time intervals, typically used to integrate external or separate simulation results into SWMM's routing process or to export SWMM results to other systems.


1. Purpose

//   iface.c
//   Routing interface file functions.
  • SWMM can read inflows from an external file (Finflows) and write outflows to a file (Foutflows) at specified intervals.
  • It’s commonly used to couple SWMM with other models or to use prior simulation results as inflows for the current simulation.

2. Key Global Variables

Below are the main static variables that iface.c manages:

static int      IfaceFlowUnits;      // Flow units (CFS, CMS, etc.) in interface file
static int      IfaceStep;           // Time step (sec) used by the inflows file
static int      NumIfacePolluts;     // Number of pollutants on interface file (excluding flow)
static int*     IfacePolluts;        // Array mapping project's pollutants to file pollutants
static int      NumIfaceNodes;       // Number of nodes with flow/quality data in inflows file
static int*     IfaceNodes;          // Mapping of file node index to project node index
static double** OldIfaceValues;      // Node flow & WQ from the previous file time
static double** NewIfaceValues;      // Node flow & WQ from the next file time
static double   IfaceFrac;           // Fraction of time between old & new interface data
static DateTime OldIfaceDate;        // Date/time of OldIfaceValues
static DateTime NewIfaceDate;        // Date/time of NewIfaceValues

These variables track how the interface data is read and interpolated over time. For instance, OldIfaceValues[i] and NewIfaceValues[i] contain the flow and pollutant concentrations for node i at two bracketing times. Then IfaceFrac is used to interpolate for the current time step.


3. Reading File Parameters

3.1 iface_readFileParams(tok, ntoks)

int iface_readFileParams(char* tok[], int ntoks)
  • Reads a line of input specifying whether SWMM should USE or SAVE a particular type of interface file (rainfall, runoff, hotstart, RDII, inflows, outflows).
  • For instance:
    USE INFLOWS <filename>  
    SAVE OUTFLOWS <filename>
    
  • If USE INFLOWS, it means we want to read node flows/quality from <filename>.
  • If SAVE OUTFLOWS, it means we want to write node outflows/quality to <filename>.
  • The result is stored in the appropriate global file variable: e.g., Finflows or Foutflows.

4. Opening and Closing Routing Interface Files

4.1 iface_openRoutingFiles()

void iface_openRoutingFiles()
{
    // Initialize variables
    // If outflows file is to be saved, openFileForOutput()
    // If inflows file is to be used, openFileForInput()
}
  • Initializes the static global variables.
  • Checks if the same file name is used for both inflows and outflows => error.
  • Calls openFileForOutput() if Foutflows.mode == SAVE_FILE.
  • Calls openFileForInput() if Finflows.mode == USE_FILE.

4.2 iface_closeRoutingFiles()

void iface_closeRoutingFiles()
{
    // free memory for IfacePolluts, IfaceNodes, OldIfaceValues, NewIfaceValues
    // close Finflows.file & Foutflows.file
}
  • Releases all memory allocated for the node/pollut arrays and data matrices.
  • Closes the inflows/outflows files if they were open.

5. Accessing Interface Inflows During Routing

5.1 iface_getNumIfaceNodes(currentDate)

int iface_getNumIfaceNodes(DateTime currentDate)
{
    // check if OldIfaceDate > currentDate => no data
    // while NewIfaceDate < currentDate => shift OldIface -> NewIface, read next
    // compute IfaceFrac = fraction of time between OldIfaceDate & NewIfaceDate
    // return NumIfaceNodes
}
  • Called at each routing time step to ensure we have up‐to‐date interface inflows.
  • If the simulation time has advanced beyond the next interface time, it reads the next record from the interface file.
  • IfaceFrac = currentDate - OldIfaceDateNewIfaceDate - OldIfaceDate\frac{\text{currentDate - OldIfaceDate}}{\text{NewIfaceDate - OldIfaceDate}} is used for linear interpolation of flow/quality data.
  • Returns the number of nodes for which inflow data is available.

5.2 iface_getIfaceNode(index)

int iface_getIfaceNode(int index)
{
    if (index in [0..NumIfaceNodes-1]) return IfaceNodes[index];
    else return -1;
}
  • Maps an interface file node index 0..NumIfaceNodes-1 to the actual SWMM node index.

5.3 iface_getIfaceFlow(index)

double iface_getIfaceFlow(int index)
{
    // Interpolates flow between OldIfaceValues[index][0] and NewIfaceValues[index][0]
    return (1.0 - IfaceFrac)*q1 + IfaceFrac*q2;
}
  • For node index, obtains the old and new flows and interpolates them with IfaceFrac.

5.4 iface_getIfaceQual(index, pollut)

double iface_getIfaceQual(int index, int pollut)
{
    // find j = IfacePolluts[pollut], then interpolate OldIfaceValues[index][j+1]
    // and NewIfaceValues[index][j+1]
    return ...
}
  • Similarly interpolates pollutant concentration.

6. Saving Outflows

6.1 iface_saveOutletResults(reportDate, file)

void iface_saveOutletResults(DateTime reportDate, FILE* file)
{
    // For each node i:
    //    if node is an outlet:
    //       write node ID, reportDate, flow, and pollutants to file
}
  • Called at each reporting time in SWMM.
  • Writes the node ID, date/time, flow, and each pollutant concentration.

6.2 openFileForOutput()

void openFileForOutput()
{
    // open Foutflows.name for writing text
    // write a header:
    //   "SWMM5 Interface File"
    //   project Title
    //   the ReportStep
    //   number of constituents: 1 + #pollutants
    //   each pollutant's ID & units
    //   number of nodes that are "outlets"
    //   each outlet node's name
    //   column headings
    // if start date == report start date, save initial outflows
}

7. Reading Interface Inflows

7.1 openFileForInput()

void openFileForInput()
{
    // open Finflows.name for reading
    // check first line for "SWMM5"
    // read IfaceStep
    // read # of constituents (Flow + pollutants) => parse pollutant names
    // read # of interface nodes => parse node names
    // allocate memory for OldIfaceValues, NewIfaceValues
    // readNewIfaceValues() => sets NewIfaceDate
    // OldIfaceDate = NewIfaceDate
}
  • The file is ASCII text, not binary. The first line must contain “SWMM5”.
  • IfaceFlowUnits is set based on the textual units.
  • If we can’t find matching pollutants or nodes, we raise an error.

7.2 readNewIfaceValues()

void readNewIfaceValues()
{
    // read lines for each interface node
    // parse date/time (yr, mon, day, hr, min, sec)
    // parse flow => convert to SWMM's flow units
    // parse pollutant concentrations
    // store them in NewIfaceValues[i]
    // update NewIfaceDate from parsed date/time
}
  • The interface file lines have a format like:
    NodeID Year Mon Day Hr Min Sec Flow Pollut1 Pollut2 ...
    
    but is read ignoring NodeID since we already have node order established.
  • Finally, NewIfaceDate is updated to the date/time on that line.

7.3 setOldIfaceValues()

void setOldIfaceValues()
{
    OldIfaceDate = NewIfaceDate;
    copy NewIfaceValues[][] into OldIfaceValues[][]
}
  • After a new set of data is read from file, the previous “new” values become the “old” ones for the next time step.

8. Checking for Outlet Nodes

int isOutletNode(int i)
{
    // If dynamic wave, only outfalls are outlets
    if (RouteModel == DW) return (Node[i].type == OUTFALL);
    else return (Node[i].degree == 0);
}
  • If using dynamic wave, only outfall nodes are considered “outlets.”
  • Otherwise, a node with zero outflow links is an outlet.

9. Summary

  1. iface.c allows SWMM to read node flows & pollutant data from an external file, or write outflow results to an external file at each reporting interval.
  2. Reading the inflows file:
    • We parse a header to get time step, pollutants, node IDs, and store them in arrays.
    • We read lines for each node in order, each line representing a single time step’s data.
    • We interpolate between the “old” and “new” data rows so that we can provide a continuous flow/time correlation to SWMM’s time steps.
  3. Writing outflows for outlet nodes:
    • The output file is also textual.
    • Each row contains date/time, node ID, flow, plus pollutant concentrations.
  4. This mechanism is how SWMM can couple with other models (e.g., to provide boundary conditions or to export results).

SWMM5 hotstart.c Summary

 Below is a step‐by‐step explanation of hotstart.c, which handles saving and reading SWMM’s hot start files. A hot start file captures the internal state of an ongoing SWMM simulation (runoff and routing) at the end of a run, so that a subsequent run can begin from those exact conditions.


1. General Context

Hot start files help avoid spinning up from initial conditions of zero depth, flow, etc. Instead, a new simulation picks up at the state of the system (subcatchment infiltration, groundwater, node depths, link flows, water quality) at the end of a previous run.

SWMM can produce two types (or versions) of hot start files:

  1. Version 1 (original format): Everything about nodes, links, subcatchments including infiltration states, but not the “abridged” version items.
  2. Version 2/3/4: A more abridged version. For example, version 2 includes only variables that appear in the binary output file (groundwater, node & link states, etc.). Versions 3 & 4 add more items (like subcatchment states, infiltration states, stored water quality).

2. Global File Variables

Fhotstart1 is the input hotstart file (the file we read from).
Fhotstart2 is the output hotstart file (the file we write to).


3. Opening and Closing Hot Start Files

3.1 hotstart_open()

int hotstart_open()
{
    if ( !openHotstartFile1() ) return FALSE;  // open input hotstart
    if ( !openHotstartFile2() ) return FALSE;  // open output hotstart
    return TRUE;
}
  1. openHotstartFile1(): open the (optional) input hotstart file to read states into the current simulation.
  2. openHotstartFile2(): open the (optional) output hotstart file to save states at simulation’s end.

If these steps fail, returns FALSE.

3.2 hotstart_close()

void hotstart_close()
{
    if ( Fhotstart2.file )
    {
        saveRunoff();
        saveRouting();
        fclose(Fhotstart2.file);
    }
}
  1. Called after a simulation ends.
  2. If we have an output hotstart file open, it calls:
    • saveRunoff(): store subcatchment states (ponded depth, infiltration, groundwater, snowpack, etc.).
    • saveRouting(): store node & link states (depth, flow, quality).
  3. Then closes Fhotstart2.file.

4. Opening an Input Hot Start File (openHotstartFile1())

int openHotstartFile1()
{
    // skip if not using an input hotstart
    if (Fhotstart1.mode != USE_FILE) return TRUE;

    // open the file
    ...
    // read a "file stamp" indicating SWMM hotstart format
    // read file version, object counts
    // check that they match the current SWMM project
    // read states with readRunoff(), readRouting()
    // close file
}
  • The file has a header that starts with "SWMM5-HOTSTART" plus possibly version numbers.
  • We read integer counts of subcatchments, landuses, nodes, links, pollutants, plus flow units.
  • We verify that they match the current SWMM project.
  • If a mismatch occurs, we raise an error.
  • If version >= 3, we call readRunoff() to read subcatchment states. Then we always call readRouting() to load node and link states.

fileVersion is stored locally to know how many data items to read for each object.


5. Opening an Output Hot Start File (openHotstartFile2())

int openHotstartFile2()
{
    if ( Fhotstart2.mode != SAVE_FILE ) return TRUE;
    // open the file
    // write a file stamp "SWMM5-HOTSTART4"
    // write #subcatch, #landuse, #node, #link, #pollut, flowUnits
    return TRUE;
}
  • The file gets stamped with "SWMM5-HOTSTART4" for the latest version.
  • We store the object counts and flow units so that a subsequent run can verify matching data.

6. Saving and Reading Runoff States

6.1 saveRunoff()

void saveRunoff(void)
{
    for (i in subcatchments)
    {
        // ponded depths in subareas & newRunoff
        fwrite(...) 4 doubles

        // infiltration state
        infil_getState(i, x) => up to 6 doubles
        fwrite(..., 6, ...)

        // groundwater state (4 doubles)
        gwater_getState(i, x) => { upperMoisture, waterTable, flow, infiltrationVol? }
        fwrite(..., 4, ...)

        // snowpack state (5 doubles) for up to 3 surfaces
        snow_getState(i, j, x)
        fwrite(..., 5, ...)

        // water quality: runoffQual, pondedQual, then buildup info
    }
}

For each subcatchment we save:

  1. Depth in up to 3 subareas and the final runoff rate.
  2. Infiltration states for whichever infiltration method is used.
  3. GW states: upper zone moisture content, water table depth, etc.
  4. Snowpack states: e.g., snow depth, temperature, etc. for each subarea.
  5. Water quality in runoff, ponding, land buildup, etc.

6.2 readRunoff()

void readRunoff()
{
    for (i in subcatchments)
    {
        // read 4 doubles for subarea depths & newRunoff
        // read 6 doubles for infiltration
        // read 4 doubles for GW if present
        // read 5 doubles for each snow surface if present
        // read water quality arrays
    }
}
  • Populates subarea depths, infiltration states, groundwater states, snow states, and quality states.
  • Calls e.g. snow_setState() to restore snowpack states, gwater_setState() for groundwater, etc.

7. Saving and Reading Routing States

7.1 saveRouting()

void saveRouting()
{
    // For each node:
    //   write depth, newLatFlow
    //   if node is STORAGE, write storage residence time
    //   write node pollutant concentrations

    // For each link:
    //   write flow, depth, setting
    //   write link pollutant concentrations
}
  • Node depth = Node[i].newDepth
  • Node lateral inflow = Node[i].newLatFlow
  • Storage node’s hrt (hydraulic residence time).
  • Link flow, depth, control setting, plus each pollutant’s newQual.

7.2 readRouting()

void readRouting()
{
    // if fileVersion == 2 => read subcatch GW states
    // For each node: read newDepth, newLatFlow, (storage hrt), nodeQuality
    // For each link: read newFlow, newDepth, newSetting => also set link->targetSetting
    //                read linkQuality
}
  • If fileVersion == 2, we read just subcatchment’s groundwater moisture/water table.
  • Then read each node’s depth, lateral flow, possibly storage hrt, pollutants.
  • Then each link’s flow, depth, setting, and pollutants.
  • We also set the link’s targetSetting to the read setting so any control or orifice/pump adjusts accordingly.

8. Low-Level Reading and Writing

8.1 readFloat(float* x, FILE* f) / readDouble(double* x, FILE* f)

  • Pulls 1 float or double from the file.
  • Checks for end‐of‐file or if the read value is NaN => signals an error.

8.2 Byte Ordering

  • SWMM writes/reads binary floating values. If the environment using it has the same endianness, it works. Otherwise, there might be issues across different platforms or compilers.

9. Final Observations

  1. The hotstart system is optional. If not used, openHotstartFile1() or openHotstartFile2() does nothing.
  2. The file version determines which items get read/written. The newest version 4 includes the most complete set.
  3. On reading, it verifies that the file’s subcatch/node/link/pollut counts match the current model. No verification of shape or structure is done beyond that.

Hence, hotstart.c allows SWMM to persist simulation states to disk, then reload them for a continuation or subsequent scenario run—saving the time and inaccuracy from starting at zero.

SWMM5 hash.c and hash.h Summary

 Below is a step-by-step explanation of hash.c and hash.h, which together implement a simple case-insensitive hash table for storing and retrieving strings mapped to integer values. The key points are how the hash function is computed, how collisions are handled, and how strings are compared in a case‐insensitive way.


1. Overview of the Hash Table

1.1 Purpose

These files implement a hash table that:

  • Stores string keys (case-insensitive) and their associated integer data.
  • Retrieves data given a string key.
  • Handles collisions with a simple linked-list approach (a “chaining” method).

1.2 Data Structures

hash.h defines:

#define HTMAXSIZE 1999
#define NOTFOUND  -1

struct HTentry {
    char* key;            // pointer to the string key
    int   data;           // integer data associated with this key
    struct HTentry* next; // next item in linked list for collisions
};

typedef struct HTentry* HTtable;
  • HTMAXSIZE: The size of the hash table array (1,999 “buckets”).
  • HTentry: A singly-linked list node storing:
    • key: the string pointer,
    • data: the integer value,
    • next: pointer to the next entry in this bucket.
  • HTtable: A pointer to an array of pointers (struct HTentry*). Each pointer is the head of a linked list for that hash bucket.

2. Hash Table API

hash.h declares the following functions:

  1. HTtable* HTcreate(void);
    Creates a new hash table array of size HTMAXSIZE.
  2. int HTinsert(HTtable*, char*, int);
    Inserts (key, data) into the table.
  3. int HTfind(HTtable*, const char*);
    Finds the integer data for a given key. Returns NOTFOUND if absent.
  4. char* HTfindKey(HTtable*, const char*);
    Finds the actual stored key pointer if the key is found (useful if you want the exact stored string). Returns NULL if absent.
  5. void HTfree(HTtable*);
    Frees memory used by the table.

3. Implementation Details in hash.c

3.1 Case‐Insensitive String Comparison: samestr(...)

int samestr(const char *s1, const char *s2)
{
   int i;
   for (i=0; UCHAR(s1[i]) == UCHAR(s2[i]); i++)
     if (!s1[i+1] && !s2[i+1]) return(1);
   return(0);
}
  • Uses UCHAR(x) macro, which:
    #define UCHAR(x) (((x) >= 'a' && (x) <= 'z') ? ((x)&~32) : (x))
    
    ensures letters a-z are converted to uppercase by turning off the 32-bit (lowercase) bit, thus making the comparison case-insensitive.
  • The loop checks if each character is the same in a case-insensitive manner; it stops if they differ or if both strings end simultaneously.

3.2 The Hash Function: hash(const char *str)

unsigned int hash(const char *str)
{
    unsigned int sum1= 0, check1;
    unsigned long sum2= 0L;
    while( '\0' != *str  )
    {
        sum1 += UCHAR(*str);
        str++;
        if (255 <= sum1) sum1 -= 255;
        sum2 += sum1;
    }
    check1 = sum2;
    check1 %= 255;
    check1 = 255 - (sum1+check1) % 255;
    sum1   = 255 - (sum1+check1) % 255;
    return( ( ( check1 << 8 )  |  sum1  ) % HTMAXSIZE );
}
  • This function applies a Fletcher’s checksum approach (or variation of it) to produce a 2-byte hash from the string.
  • Then it takes the result mod HTMAXSIZE (1999) so the final hash index is in range [0..1998].

3.3 HTcreate()

HTtable *HTcreate()
{
    HTtable *ht = (HTtable *) calloc(HTMAXSIZE, sizeof(HTtable));
    ...
    return(ht);
}
  • Allocates an array of size HTMAXSIZE (1999) of HTtable entries (actually pointers to struct HTentry).
  • Initializes each entry to NULL.

3.4 HTinsert(...)

int HTinsert(HTtable *ht, char *key, int data)
{
    unsigned int i = hash(key);
    struct HTentry *entry;
    entry = (struct HTentry *) malloc(sizeof(struct HTentry));
    ...
    entry->key = key;
    entry->data = data;
    entry->next = ht[i];
    ht[i] = entry;
    return(1);
}
  • Computes hash index i = hash(key).
  • Allocates a new HTentry.
  • Stores key, data, and inserts it at the head of the linked list at ht[i].
  • Returns 1 for success.

Note that it does no check for duplicates. If the same key is inserted twice, the new entry is pushed on front of the list.

3.5 HTfind(...)

int HTfind(HTtable *ht, const char *key)
{
    unsigned int i = hash(key);
    struct HTentry *entry = ht[i];
    while (entry != NULL)
    {
        if ( samestr(entry->key, key) ) return(entry->data);
        entry = entry->next;
    }
    return(NOTFOUND);
}
  • Hashes the key to index i.
  • Walks the linked list at ht[i].
  • Uses samestr(...) (case-insensitive) to check if the stored key matches.
  • If found, returns the stored data.
  • If it reaches the end without finding, returns NOTFOUND (-1).

3.6 HTfindKey(...)

char *HTfindKey(HTtable *ht, const char *key)
{
    ...
    while (entry != NULL)
    {
        if ( samestr(entry->key, key) ) return(entry->key);
        entry = entry->next;
    }
    return(NULL);
}
  • Very similar to HTfind, but returns a pointer to the actual stored key string (rather than the data).

3.7 HTfree(...)

void HTfree(HTtable *ht)
{
    int i;
    struct HTentry *entry, *nextentry;
    for (i=0; i<HTMAXSIZE; i++)
    {
        entry = ht[i];
        while (entry != NULL)
        {
            nextentry = entry->next;
            free(entry);
            entry = nextentry;
        }
    }
    free(ht);
}
  • Walks through each bucket’s linked list, freeing each HTentry.
  • Then frees the entire hash table array.
  • Note: this doesn’t free the strings themselves (entry->key), because it expects that those might be managed elsewhere.

4. Collision Handling

  1. The table is a fixed array of size 1999.
  2. If two different keys hash to the same index, we store the new entry by:
    entry->next = ht[i];
    ht[i] = entry;
    
  3. This is a chaining technique with a singly‐linked list at each array slot.

5. Case-Insensitivity

  • The function samestr(...) compares characters by converting them to uppercase with the macro:
    #define UCHAR(x) (((x) >= 'a' && (x) <= 'z') ? ((x)&~32) : (x))
    
  • The hash function also calls UCHAR(*str) so that the same string in different case forms yields the same hash index.

Thus, "Rain" and "rain" or "RAIN" produce the same hash index, and samestr(...) deems them equal.


6. Typical Use Example

HTtable* myTable = HTcreate();

// Insert some strings
HTinsert(myTable, "Example", 100);
HTinsert(myTable, "example", 200);  // same key ignoring case

int val = HTfind(myTable, "EXAMPLE"); // should return 200 (the newest entry)
if (val != NOTFOUND) printf("Found data = %d\n", val);

// Cleanup
HTfree(myTable);

7. Key Takeaways

  1. Case-Insensitive: Both the hash calculation and the key comparison ignore case.
  2. Fletcher’s Checksum: The hash function uses a two‐byte sum, modded by HTMAXSIZE.
  3. Linked-List Collision: Each bucket is a singly‐linked list of HTentry nodes.
  4. No Duplicate Check: Inserting the same key multiple times just adds a new entry in front.
  5. Memory: Only the HTentry structures are freed in HTfree; the string data is not freed, so the calling code must manage that.

This design is straightforward, but you must keep in mind that it lacks rehashing, duplicate detection, or string memory management. It’s still quite effective and simple for moderate use cases needing case‐insensitive hashing of strings.

SWMM5 gwater.c Summary

 Below is a step-by-step explanation of gwater.c, which manages groundwater (GW) flow in EPA SWMM5. SWMM can model saturated and unsaturated subsurface zones, compute exchange with surface infiltration, evapotranspiration, and flow to or from the sewer system at a node. This file implements all GW computations for each subcatchment’s groundwater component.


1. Purpose

//   gwater.c
//   Groundwater functions.
//   ...
  • gwater.c is responsible for reading aquifer/GW parameters, initializing states, updating the moisture content and depth in an upper/lower zone model each time step, and computing lateral outflow to a connected node as well as deep percolation from the lower zone.

2. Key Data Structures

  • TAquifer: defines aquifer properties (porosity, field capacity, conductivity, etc.).
  • TGroundwater: defines the subcatchment’s local groundwater parameters (node ID, surface elevation, the user’s custom parameters a1,b1,a2,b2,a3, etc.).

The array Aquifer[] holds all defined aquifers, and each subcatchment can reference one via Subcatch[j].groundwater->aquifer.


3. Reading Aquifer and Subcatchment GW Parameters

3.1 gwater_readAquiferParams()

int gwater_readAquiferParams(int j, char* tok[], int ntoks)
  • Parses one line of data for aquifer jj.
  • Data format (13 tokens, the 14th is optional):
    1. Aquifer ID,
    2. Porosity,
    3. Wilting point,
    4. Field capacity,
    5. Hydraulic conductivity,
    6. Conductivity slope,
    7. Tension slope,
    8. Upper evaporation fraction,
    9. Lower evap. depth,
    10. Groundwater recession constant,
    11. Aquifer bottom elevation,
    12. Water table elevation,
    13. Initial upper moisture content,
    14. (optional) monthly pattern for upper evaporation.
  • Assigns them to Aquifer[j] with correct unit conversions (like dividing length by UCF(LENGTH)).

3.2 gwater_readGroundwaterParams()

int gwater_readGroundwaterParams(char* tok[], int ntoks)
  • Reads the subcatchment's GW parameters for the line:
    subcatch  aquifer  node  surfElev  a1  b1  a2  b2  a3  fixedDepth
               nodeElev  bottomElev  waterTableElev  upperMoisture
    
  • This sets up TGroundwater within Subcatch[j]:
    • aquifer: index of the aquifer used,
    • node: index of node that subcatch’s groundwater can flow into,
    • surfElev: subcatch's ground surface elevation,
    • a1, b1, a2, b2, a3, fixedDepth: user’s curve parameters for computing GW infiltration,
    • optionally: nodeElev, bottomElev, waterTableElev, upperMoisture.

4. Custom Flow Expressions

4.1 gwater_readFlowExpression(...)

  • SWMM allows user‐supplied math expressions for lateral or deep GW flow:
    subcatch LATERAL <expr>
    subcatch DEEP    <expr>
    
  • This function:
    1. Finds the subcatch index,
    2. Checks if the expression is for LATERAL or DEEP,
    3. Tokenizes and parses the expression (via mathexpr_create) into a syntax tree stored in Subcatch[j].gwLatFlowExpr or Subcatch[j].gwDeepFlowExpr.

4.2 gwater_deleteFlowExpression(...)

  • Releases memory for the expression trees associated with one subcatch.

5. Validation and Initialization

5.1 gwater_validateAquifer(int j)

  • Checks correctness of aquifer parameters (ensuring porosity > fieldCapacity > wiltingPoint, etc.).
  • Prints error if invalid.

5.2 gwater_validate(int j)

  • For subcatch j, if it has groundwater, ensures:
    • surfElev >= waterTableElev (otherwise error).

5.3 gwater_initState(int j)

  • Sets the subcatch’s initial groundwater state:
    • theta = upperMoisture (the initial soil moisture).
    • lowerDepth = waterTableElev - bottomElev (the saturated thickness).
    • If that is higher than surfElev - bottomElev, reduce it to avoid overflow.
    • maxInfilVol is how much infiltration can fill the upper zone.

6. State Saving/Reading

6.1 gwater_getState(int j, double x[])

  • Copies the subcatch’s theta, lowerDepth (absolute water table height), newFlow, and maxInfilVol into array x.

6.2 gwater_setState(int j, double x[])

  • Assigns these state variables back from x[]. Used for hotstart or re‐initializing from saved states.

7. Primary GW Computation

7.1 gwater_getGroundwater(int j, double evap, double infil, double tStep)

  1. Finds subcatch’s TGroundwater* GW and the TAquifer object.
  2. Calculate infiltration rate infil/area/tStepinfil / area / tStep.
  3. Convert any surface evap. to a rate that might reduce available GW evaporation.
  4. Determine total aquifer thickness, the node invert, water surface in node, etc.
  5. Sets up initial conditions for an ODE system:
    • upper zone moisture theta
    • lower zone depth lowerDepth.
  6. Defines constraints for maximum percolation, max outflow, etc.
  7. Calls an ODE solver (odesolve_integrate(...)) with the function getDxDt(...), which calculates the time derivative:
    • d(θ)dt\frac{d(\theta)}{dt} and d(lowerDepth)dt\frac{d(\text{lowerDepth})}{dt}.
  8. After integration, calls getFluxes(...) to compute the actual flux rates:
    • UpperEvap, LowerEvap, UpperPerc, GWFlow, LowerLoss.
    • updateMassBal(...) updates the water balance.
  9. Saves new states back to GW->theta and GW->lowerDepth.

7.2 getDxDt(t, x, dxdt)

  • The ODE solver calls this function at each step.
  • x[THETA]: the upper moisture content, x[LOWERDEPTH]: depth of the lower saturated zone.
  • Calls getFluxes(...), which sets global flux variables (Infil, UpperEvap, etc.).
  • Then: d(θ)dt=(Infil - UpperEvap - UpperPerc)(upperDepth), \frac{d(\theta)}{dt} = \frac{\text{(Infil - UpperEvap - UpperPerc)}}{\text{(upperDepth)}}, d(lowerDepth)dt=(UpperPerc - LowerLoss - LowerEvap - GWFlow)(porosity - Î¸), \frac{d(\text{lowerDepth})}{dt} = \frac{\text{(UpperPerc - LowerLoss - LowerEvap - GWFlow)}}{\text{(porosity - }\theta)}, if denominators are nonzero.

7.3 getFluxes(double theta, double lowerDepth)

  1. upperDepth = total depth minus lowerDepth.
  2. Calls getEvapRates(...) to compute UpperEvap & LowerEvap.
  3. Calls getUpperPerc(...) to compute percolation from upper to lower zone.
  4. If a user expression for DeepFlowExpr is defined, that sets LowerLoss. Otherwise use Aquifer[j].lowerLossCoeff * lowerDepth / totalDepth.
  5. If a user expression for LatFlowExpr is defined, we add that to the base getGWFlow(...).
  6. GWFlow is also limited to max possible flow based on the unsaturated or saturated zone capacity.

7.4 getEvapRates(double theta, double upperDepth)

  • Disallows infiltration and evaporation at the same time: if infiltration is > 0, then no GW evaporation.
  • Otherwise, partition potential evap (MaxEvap) between the upper zone (some fraction upperEvapFrac) and the lower zone if the lower zone is within the “lowerEvapDepth.”

7.5 getUpperPerc(double theta, double upperDepth)

  • If the upper zone is deeper than 0 and theta > fieldCapacity, compute an exponent on unsaturated conductivity as: hydcon=conductivity×e(thetaporosity)×conductSlope. \text{hydcon} = \text{conductivity} \times e^{(theta - porosity)\times conductSlope}.
  • Then modifies for tension slope. Returns this percolation rate in ft/sec.

7.6 getGWFlow(double lowerDepth)

  • The “traditional” flow formula: GWFlow=(a1(lowerDepthHstar)b1    a2(HswHstar)b2  +  a3lowerDepthHsw)UCF(GWFLOW) GWFlow = \frac{(a1 * (lowerDepth - Hstar)^{b1} \;-\; a2 * (Hsw - Hstar)^{b2} \;+\; a3 * lowerDepth * Hsw) } { \text{UCF(GWFLOW)} } when (lowerDepthHstar)>0(lowerDepth - Hstar) > 0.
  • If a user lateral flow expression is present, add that as well.

7.7 updateMassBal(area, tStep)

  • Converts flux rates to volumes over subcatchment area times time step, calls massbal_updateGwaterTotals(...).

8. Helper Routines for Expressions

8.1 getVariableIndex(char* s)

  • Maps a variable name ("HGW", "THETA", etc.) to an integer ID used in the math expression parser.

8.2 getVariableValue(int varIndex)

  • Returns the current numerical value of that variable from the many global placeholders (like Hgw, Theta, etc.).

9. Summary

gwater.c implements SWMM’s two-zone groundwater model:

  • Upper unsaturated zone with moisture content theta.
  • Lower saturated zone with depth lowerDepth.

At each time step:

  1. Convert infiltration from surface, potential evap, node water surface, etc.
  2. Use an ODE solver to integrate the 2 state variables.
  3. Compute fluxes: infiltration, evap from each zone, percolation, deep loss, and lateral GW flow to the node.
  4. Update the mass balance.

This model can be augmented by custom expressions for lateral or deep flow, allowing flexible definitions for real-world groundwater behavior.

SWMM5 globals.h Summary

 Below is a step-by-step explanation of globals.h, which contains global variables shared across EPA SWMM5. These variables store the current state of an SWMM simulation, including file handles, simulation options, time parameters, and large arrays for subcatchments, nodes, links, etc.


1. Header and Update History

//   globals.h
//   Project: EPA SWMM5
//   ...
//   Global Variables
//   ...
  • The header comment explains that this file holds global variables for the SWMM project.
  • The update history lines describe changes in different builds of SWMM (like added or modified variables).

2. Include Guard

#ifndef GLOBALS_H
#define GLOBALS_H
...
#endif //GLOBALS_H
  • Prevents multiple inclusion errors if globals.h is included in more than one source file.

3. Global File Handles (TFile)

EXTERN TFile
  Finp, Fout, Frpt, Fclimate, Frain, Frunoff, Frdii,
  Fhotstart1, Fhotstart2, Finflows, Foutflows;
  • Each TFile object references a file used by SWMM:
    1. Finp: The primary input file (the .inp format).
    2. Fout: The binary results file (internal format).
    3. Frpt: The report file (the .rpt text output).
    4. Fclimate: The climate file (daily temperature, evaporation, wind speed, etc.).
    5. Frain: A rainfall interface file (external format).
    6. Frunoff: A runoff interface file (converting subcatchment runoff to routing).
    7. Frdii: The RDII inflow file (rainfall-derived infiltration/inflow).
    8. Fhotstart1 & Fhotstart2: The hotstart files for saving/initializing states.
    9. Finflows & Foutflows: Additional interface files for inflows/outflows.

EXTERN means these variables are declared as external to be defined once in another .c file (usually globals.c).


4. Simulation Counters and Text Buffers

EXTERN long
  Nperiods, TotalStepCount, ReportStepCount, NonConvergeCount;

EXTERN char
  Msg[MAXMSG+1],
  ErrorMsg[MAXMSG+1],
  Title[MAXTITLE][MAXMSG+1],
  TempDir[MAXFNAME+1],
  InpDir[MAXFNAME+1];
  • Nperiods: Number of times the program saves results (reporting periods).
  • TotalStepCount, ReportStepCount: Count how many times steps SWMM has performed in routing, and how many of those were at reporting intervals.
  • NonConvergeCount: Number of time steps that failed to converge in dynamic wave.
  • Msg[] and ErrorMsg[]: Buffers for storing text messages or error messages.
  • Title[][]: Up to MAXTITLE lines of project title text.
  • TempDir and InpDir: Directories used for temporary files or the input file’s directory.

5. Reporting Flags and Object Counts

EXTERN TRptFlags RptFlags; // Reporting options

EXTERN int
  Nobjects[MAX_OBJ_TYPES],
  Nnodes[MAX_NODE_TYPES],
  Nlinks[MAX_LINK_TYPES],
  ...
  • RptFlags: A structure controlling which data SWMM reports.
  • Nobjects[]: how many objects (gages, subcatchments, nodes, links, etc.) appear.
  • Nnodes[] and Nlinks[] store how many node or link sub‐types (like outfalls or pumps) exist.

6. Major Simulation Options / Flags

EXTERN int
  UnitSystem,
  FlowUnits,
  InfilModel,
  RouteModel,
  ForceMainEqn,
  LinkOffsets,
  SurchargeMethod,
  AllowPonding,
  InertDamping,
  NormalFlowLtd,
  SlopeWeighting,
  Compatibility,
  SkipSteadyState,
  IgnoreRainfall,
  IgnoreRDII,
  IgnoreSnowmelt,
  IgnoreGwater,
  IgnoreRouting,
  IgnoreQuality,
  ErrorCode,
  Warnings,
  WetStep,
  DryStep,
  ReportStep,
  RuleStep,
  SweepStart, SweepEnd,
  MaxTrials,
  NumThreads,
  NumEvents;

Each integer configures a major piece of SWMM logic:

  • UnitSystem: US or SI.
  • FlowUnits: CFS, GPM, CMS, etc.
  • InfilModel: infiltration method (Horton, Green-Ampt, etc.).
  • RouteModel: routing method (None, Kinematic Wave, Dynamic Wave).
  • ForceMainEqn: Hazen-Williams or Darcy-Weisbach for force mains.
  • LinkOffsets: depth vs. elevation offset.
  • SurchargeMethod: EXTRAN or Preissmann slot.
  • AllowPonding: node ponding.
  • InertDamping, NormalFlowLtd, SlopeWeighting, Compatibility, SkipSteadyState: advanced dynamic wave or compatibility options.
  • IgnoreRainfall, IgnoreRDII, IgnoreSnowmelt, IgnoreGwater, IgnoreRouting, IgnoreQuality: skip certain processes.
  • ErrorCode: tracks the last error code.
  • Warnings: number of warnings issued so far.
  • WetStep, DryStep, ReportStep, RuleStep: time steps (seconds) for runoff and reporting.
  • SweepStart, SweepEnd: day of year for street sweeping.
  • MaxTrials: max trials in dynamic wave.
  • NumThreads: parallel threads for dynamic wave.
  • NumEvents: number of detailed routing events.

7. Time Step Values

EXTERN double
  RouteStep,
  MinRouteStep,
  LengtheningStep,
  StartDryDays,
  CourantFactor,
  MinSurfArea,
  MinSlope,
  RunoffError,
  GwaterError,
  FlowError,
  QualError,
  HeadTol,
  SysFlowTol,
  LatFlowTol,
  CrownCutoff;
  • RouteStep: current routing time step in seconds (may be smaller than ReportStep).
  • MinRouteStep: lower limit for dynamic wave variable stepping.
  • LengtheningStep: an alternative approach to reduce wave speeds.
  • StartDryDays: how many dry days before simulation start.
  • CourantFactor: factor in dynamic wave stepping.
  • MinSurfArea: min. nodal area to avoid continuity issues.
  • MinSlope: min. conduit slope.
  • *_Error: store continuity errors for runoff, groundwater, flow, or quality.
  • HeadTol, SysFlowTol, LatFlowTol: tolerances for dynamic wave or system flow stability.
  • CrownCutoff: fraction for partial pipe flow classification.

8. Simulation Date/Time Variables

EXTERN DateTime
  StartDate, StartTime, StartDateTime,
  EndDate, EndTime, EndDateTime,
  ReportStartDate, ReportStartTime, ReportStart;
  
EXTERN double
  ReportTime, OldRunoffTime, NewRunoffTime,
  OldRoutingTime, NewRoutingTime, TotalDuration, ElapsedTime;
  • Dates are stored in DateTime (a double representing days from some reference).
  • StartDateTime = StartDate + StartTime.
  • EndDateTime = EndDate + EndTime.
  • ReportStart = ReportStartDate + ReportStartTime.
  • ReportTime: current reporting time in milliseconds.
  • OldRunoffTime, NewRunoffTime, OldRoutingTime, NewRoutingTime: track the previous/current times in msec for runoff or routing steps.
  • TotalDuration: total sim length in msec.
  • ElapsedTime: current elapsed simulation time in days.

9. Climate & Weather Structures

EXTERN TTemp Temp;
EXTERN TEvap Evap;
EXTERN TWind Wind;
EXTERN TSnow Snow;
EXTERN TAdjust Adjust;
  • These structures store temperature data, evaporation settings, wind speed, snow data, and monthly adjustments (like Adjust has monthly multipliers for infiltration or rainfall, etc.).

10. Dynamic Arrays of Objects

EXTERN TSnowmelt* Snowmelt;
EXTERN TGage*     Gage;
EXTERN TSubcatch* Subcatch;
EXTERN TAquifer*  Aquifer;
EXTERN TUnitHyd*  UnitHyd;
EXTERN TNode*     Node;
EXTERN TOutfall*  Outfall;
EXTERN TDivider*  Divider;
EXTERN TStorage*  Storage;
EXTERN TLink*     Link;
EXTERN TConduit*  Conduit;
EXTERN TPump*     Pump;
EXTERN TOrifice*  Orifice;
EXTERN TWeir*     Weir;
EXTERN TOutlet*   Outlet;
EXTERN TPollut*   Pollut;
EXTERN TLanduse*  Landuse;
EXTERN TPattern*  Pattern;
EXTERN TTable*    Curve;
EXTERN TTable*    Tseries;
EXTERN TTransect* Transect;
EXTERN TStreet*   Street;
EXTERN TShape*    Shape;
EXTERN TEvent*    Event;
  • Each pointer references a block of memory allocated at runtime. For example, once SWMM knows Nobjects[GAGE] is 5, it allocates TGage *Gage of length 5.
  • Node, Link, Pollut, etc. each reference an array of their respective structures.

11. Final Observations

  • This file does not define these variables; it only declares them with EXTERN.
  • Typically, globals.c or similar is where they’re defined.

Hence, globals.h is a single point that gathers all cross‐module global variables, so the entire SWMM codebase can access them consistently.

SWMM5 gage.c Summary

 Below is a step‐by‐step explanation of gage.c, which implements rain gage functions in EPA SWMM5. A rain gage in SWMM is an object that provides rainfall data (either from a time series or external file) for one or more subcatchments. This file tracks:

  1. Gage parameters (data source, units, data intervals).
  2. Recorded rainfall (both current and historical).
  3. Updates to rainfall at each time step.

1. Purpose and Context

//   gage.c
//   Rain gage functions.
//
//   Update History ...
  • This file is part of the runoff or global modules, providing the interface for reading and storing rainfall data from either:

    • A time series (in .inp file or from external time series definitions), or
    • A rainfall interface file (binary data file).
  • The main public functions declared in headers.h and defined here are:

    1. gage_readParams: parse input line for a gage.
    2. gage_validate: checks correctness.
    3. gage_initState: initializes gage states.
    4. gage_setState: sets current rainfall at a certain time.
    5. gage_getPrecip: returns the rainfall (and whether it's rain or snow).
    6. gage_getNextRainDate: next date/time with non-zero rainfall.
    7. gage_updatePastRain: maintains an array of previous hours' rainfall totals.
    8. gage_getPastRain: retrieves the N-hour rainfall total.
    9. gage_setReportRainfall: sets the gage’s rainfall specifically for reporting.

2. Data Flow

  • Each Gage[j] object has fields like:
    • dataSource: RAIN_TSERIES or RAIN_FILE.
    • rainType: RAINFALL_INTENSITY, RAINFALL_VOLUME, or CUMULATIVE_RAINFALL.
    • rainInterval: recording interval (seconds).
    • rainfall: the current rainfall intensity (in/hr or mm/hr).
    • apiRainfall: rainfall that might have been directly set via an API call (bypasses default reading).
    • startDate, endDate: the start and end times of the current rainfall record.
    • nextDate, nextRainfall: the next rainfall record after the current interval, etc.

3. Reading Gage Parameters

3.1 gage_readParams(j, tok, ntoks)

int gage_readParams(int j, char* tok[], int ntoks)
  • Parses a line from the SWMM input file describing a gage’s configuration, storing the values in Gage[j].

  • The data format can be:

    1. Time Series:
      GageID  RainType  RecdFreq  SCF  TIMESERIES  TSeriesName
      
    2. File:
      GageID  RainType  RecdFreq  SCF  FILE  FileName  Station  Units  StartDate
      
  • The function delegates to either:

    • readGageSeriesFormat(...) (for TIMESERIES lines),
    • readGageFileFormat(...) (for FILE lines).

Key fields:

  • rainType can be:
    • RAINFALL_INTENSITY,
    • RAINFALL_VOLUME, or
    • CUMULATIVE_RAINFALL.
  • rainInterval = how often data is recorded (seconds).
  • snowFactor = an adjustment factor for snowfall catch deficiency.
  • rainUnits = US or SI units for file-based gages.

3.2 readGageSeriesFormat(tok, ntoks, x[])

static int readGageSeriesFormat(char* tok[], int ntoks, double x[])
  • Handles time series format lines.
  • Looks up the type of rainfall from RainTypeWords.
  • Converts the user’s input of RecdFreq into seconds.
  • Finds the time series index for the name tok[5].

3.3 readGageFileFormat(tok, ntoks, x[])

static int readGageFileFormat(char* tok[], int ntoks, double x[])
  • For lines referencing an external Rainfall file.
  • Also sets start/end date for the file if provided.
  • The user can supply:
    • Station ID,
    • Units (like “IN” or “MM”),
    • StartDate (the earliest date to read from file).

4. Validation: gage_validate(j)

  • For a gage that’s not used by any subcatchment (isUsed == FALSE), no checks are done.
  • If it uses a time series that’s also used by another gage, we set coGage so they share the same data.
  • If intervals mismatch or are bigger than time series intervals, we produce warnings/errors.

5. Initialization: gage_initState(j)

void gage_initState(int j)
  • Called at simulation start:
    1. If IgnoreRainfall = TRUE, sets rainfall=0.0.
    2. If RAIN_FILE, sets file pointer to the start of record, sets unitsFactor if needed, etc.
    3. getFirstRainfall(j): obtains the first date & rainfall record from the time series or file.
    4. If the first record starts after StartDateTime, adjusts accordingly.
    5. Calls initPastRain(j) to reset the past hour accumulation array.

6. Updating Rainfall: gage_setState(j, t)

void gage_setState(int j, DateTime t)
  • Called each step to update the gage’s rainfall at time t.
  • If coGage >= 0, just copy that gage’s rainfall.
  • If the user’s code set apiRainfall, use that.
  • Otherwise, we compare t to the current interval [startDate, endDate].
    • If t < startDate => 0.0 rainfall,
    • If startDate <= t < endDate => same rainfall,
    • If t >= endDate => shift to the next interval by calling getNextRainfall(j), etc.

7. Past Hourly Rainfall: gage_updatePastRain & gage_getPastRain

  • gage_updatePastRain(j, tStep):

    • The current rainfall intensity is Gage[j].rainfall / 3600. ( ft or mm per second).
    • It accumulates partial amounts in pastRain[] array, each entry representing an hour’s total.
    • If pastInterval has reached 3600 seconds, shift the array by 1 hour.
  • gage_getPastRain(j, n):

    • Returns sum of the past n hours of rainfall from the pastRain[] array.

This supports logic like control rules that might require “past 6-hour rainfall total.”


8. Next Rain Date: gage_getNextRainDate(j, aDate)

DateTime gage_getNextRainDate(int j, DateTime aDate)
  • Returns the date/time after aDate when the next rainfall interval starts.
  • If coGage >= 0, the co-gage’s next date is used.

9. Retrieving Actual or Reported Rain

  1. gage_getPrecip(j, &rainfall, &snowfall):

    • Distinguishes rain vs. snow. If Temp.ta <= Snow.snotmp, we treat it as snow times snowFactor; else it’s rain.
    • The total precipitation is (rainfall + snowfall) in ft/sec or mm/sec.
  2. gage_setReportRainfall(j, reportDate):

    • For writing results. If coGage >= 0, again copy. If we are exactly on the boundary between intervals, we might use the “next” rainfall.
    • Or if the user set an API rainfall, that’s used.

10. Internal Routines

10.1 initPastRain(j)

  • Zeros out the Gage[j].pastRain[] array and sets pastInterval=0.

10.2 getFirstRainfall(j) & getNextRainfall(j)

  • For the first/next record from:
    • A file: read DateTime and float volume from Frain.file. Convert to in/hr or mm/hr.
    • A time series: from Tseries[k].
  • Skips zero rainfall records (for getNextRainfall) so we can keep distinct intervals for real wet/dry transitions.

10.3 convertRainfall(j, r)

  • Switches on rainType:
    • RAINFALL_INTENSITY => no transformation.
    • RAINFALL_VOLUME => convert total volume to intensity = (volume / interval) * 3600.
    • CUMULATIVE_RAINFALL => difference from last accumulation is the new volume.
  • Multiplies by unitsFactor if in metric and also a global Adjust.rainFactor.

11. Summary

gage.c handles:

  1. Reading gage definitions from the SWMM input file (time series or external file).
  2. Initialization and per-step updates of rainfall states.
  3. Distinguishing rain vs. snow.
  4. Managing past hourly rainfall accumulation arrays.

It ensures each subcatchment referencing a gage sees the correct rainfall for that step, optionally referencing the same time series or file as other gages.

SWMM5 funcs.h Summary

 Below is a step-by-step explanation of funcs.h, a header file for EPA SWMM 5.2 that collects all global function prototypes used across the SWMM source code. It is primarily a forward declaration file: each group of functions deals with a specific module (e.g., project setup, input reading, reporting, climate, runoff, routing, etc.). By centralizing these prototypes, SWMM ensures that different source files can call each other's routines consistently.


1. Guard Macros

#ifndef FUNCS_H
#define FUNCS_H
  • #ifndef FUNCS_H / #define FUNCS_H: a typical include guard that prevents multiple redefinitions if funcs.h is included in multiple source files.

2. File Organization

The file is divided into sections by comment blocks, each grouping function prototypes by subsystem or module. For example:

  1. Project Methods
  2. Input Reader Methods
  3. Report Writer Methods
  4. Temperature/Evaporation
  5. Rainfall Processing
  6. Snowmelt Processing
  7. Runoff Analyzer
  8. Conveyance System Routing
  9. Output Filer
  10. Groundwater
  11. RDII (Rainfall‐Derived Infiltration/Inflow)
  12. Landuse
  13. Flow/Quality Routing
  14. Treatment
  15. Mass Balance
  16. Simulation Statistics
  17. Raingage
  18. Subcatchment
  19. Surface Pollutant
  20. Node
  21. Inflow
  22. Routing Interface File
  23. Hot Start File
  24. Link
  25. Link Cross‐Section
  26. Culvert/Roadway
  27. Force Main
  28. Transect
  29. Street
  30. Custom Shape
  31. Control Rule
  32. Table & Time Series
  33. Utility

Each grouping matches a piece of SWMM’s domain logic.


3. Project Methods

void project_open(const char *f1, const char *f2, const char *f3);
void project_close(void);

void project_readInput(void);
...
  • Functions for opening or closing an SWMM “project,” reading input data, validating the project’s consistency, creating/finding objects, allocating or freeing memory matrices, etc.

4. Input Reader Methods

int input_countObjects(void);
int input_readData(void);
  • input_countObjects(): scans the input file to count how many objects (nodes, links, etc.) are present.
  • input_readData(): actually reads the data from the input file (like .inp file lines) and populates SWMM data structures.

5. Report Writer Methods

void report_writeLine(const char* line);
void report_writeSysTime(void);
void report_writeLogo(void);
...
void report_writeErrorMsg(int code, char* msg);
...
  • Functions to generate or format textual output for SWMM’s report file (the .rpt file). They might write lines of text, system times, warnings/errors, or final summary tables (rain stats, RDII stats, flow error, etc.).

6. Temperature/Evaporation Methods

int climate_readParams(char* tok[], int ntoks);
...
DateTime climate_getNextEvapDate(void);
  • Used to handle climate data (temps, evaporation) from user input.
  • Might open an external climate file, parse monthly or time-series data, store and retrieve daily evaporation rates, or track next date/time evaporation changes.

7. Rainfall Processing Methods

void rain_open(void);
void rain_close(void);
  • Opens/closes or sets up SWMM’s internal rainfall processing system. Could handle interface files or time-series for each gage.

8. Snowmelt Processing Methods

int snow_readMeltParams(char* tok[], int ntoks);
...
double snow_getSnowMelt(int subcatch, double rainfall, double snowfall,
    double tStep, double netPrecip[]);
  • SWMM can model snow accumulation and melt. These functions read snowmelt parameters, track and update snowpack states, and compute daily melt rates or infiltration from snow cover.

9. Runoff Analyzer Methods

int runoff_open(void);
void runoff_execute(void);
void runoff_close(void);
  • These handle the runoff portion of SWMM, including subcatchment runoff simulation.
  • runoff_execute() typically runs an iteration or loop over all subcatchments, computing runoff flows.

10. Conveyance System Routing Methods

int routing_open(void);
double routing_getRoutingStep(int routingModel, double fixedStep);
void routing_execute(int routingModel, double routingStep);
void routing_close(int routingModel);
  • For the hydraulic part of SWMM (i.e., flow routing).
  • If the model chooses a certain routing method (steady flow, kinematic wave, dynamic wave), these calls handle the next time step’s flow updates.

11. Output Filer Methods

int  output_open(void);
void output_end(void);
void output_close(void);
void output_saveResults(double reportTime);
  • Manage the binary output file that SWMM can produce, storing results at each reporting time step.
  • Reading/writing times, subcatch/node/link results, etc.

12. Groundwater Methods

int  gwater_readAquiferParams(int aquifer, char* tok[], int ntoks);
...
double gwater_getVolume(int subcatch);
  • SWMM can model groundwater infiltration from aquifers.
  • Functions for reading aquifer and groundwater parameters, computing infiltration at each step, etc.

13. RDII Methods

int  rdii_readRdiiInflow(char* tok[], int ntoks);
...
void rdii_getRdiiFlow(int index, int* node, double* q);
  • Rainfall‐Derived Infiltration/Inflow (RDII) computations.
  • Reading parameters, storing them, then retrieving infiltration flows for a node.

14. Landuse Methods

int landuse_readParams(int landuse, char* tok[], int ntoks);
...
double landuse_getWashoffLoad(int landuse, int p, double area,
    TLandFactor landFactor[], double runoff, double vOutflow);
  • SWMM’s built‐in pollutant buildup and washoff logic.
  • Tracks pollutant accumulation on land surfaces and their removal in runoff events.

15. Flow/Quality Routing Methods

void flowrout_init(int routingModel);
void flowrout_close(int routingModel);
double flowrout_getRoutingStep(int routingModel, double fixedStep);
int flowrout_execute(int links[], int routingModel, double tStep);
...
  • More detailed hydraulic and quality routing.
  • Possibly references submodules for dynamic wave, kin. wave, etc.

16. Treatment Methods

int  treatmnt_open(void);
...
void treatmnt_treat(int node, double q, double v, double tStep);
  • Allows user-specified treatment functions at nodes.
  • For instance, a node might have a formula describing how pollutants are removed from inflow.

17. Mass Balance Methods

int  massbal_open(void);
void massbal_close(void);
void massbal_report(void);
...
double massbal_getFlowError(void);
  • SWMM tracks water volume and pollutant mass to ensure there’s no large “missing” or “created” mass.
  • These methods update mass balance each time step, final error reporting, etc.

18. Simulation Statistics Methods

int  stats_open(void);
void stats_close(void);
void stats_report(void);
...
void stats_updateFlowStats(double tStep, DateTime aDate);
  • Summaries of flow, depth, infiltration, flooding, etc., plus tracking maximum node depths or maximum flows.
  • Writes out final stats in the .rpt file.

19. Raingage Methods

int      gage_readParams(int gage, char* tok[], int ntoks);
...
void     gage_updatePastRain(int j, int tStep);
double   gage_getPastRain(int gage, int hrs);
  • Each rain gage object can store time‐series or interface file rainfall data.
  • gage_updatePastRain(...) helps track past rainfall totals for certain hours (like 24‐hr accumulations).

20. Subcatchment Methods

int   subcatch_readParams(int subcatch, char* tok[], int ntoks);
...
void  subcatch_getRunon(int subcatch);
double subcatch_getRunoff(int subcatch, double tStep);
  • Subcatchment infiltration, runoff generation.
  • subcatch_getRunoff(...) is where the subcatch’s infiltration and runoff are computed each time step.

21. Surface Pollutant Buildup/Washoff Methods

void surfqual_initState(int subcatch);
...
void surfqual_getWashoff(int subcatch, double runoff, double tStep);
  • Ties into the landuse module.
  • Tracks how pollutants accumulate on the surface and are removed by runoff.

22. Conveyance System Node Methods

int   node_readParams(int node, int type, int subIndex, char* tok[], int ntoks);
...
double node_getSurfArea(int node, double depth);
  • For SWMM’s network nodes.
  • node_* routines handle node geometry, inflows, computing overflow, surface area for ponding, etc.

23. Conveyance System Inflow Methods

int  inflow_readExtInflow(char* tok[], int ntoks);
...
double inflow_getExtInflow(TExtInflow* inflow, DateTime aDate);
  • Deals with external inflows or dry-weather flows assigned to nodes.
  • E.g., reading from user input or time series.

24. Routing Interface File Methods

int iface_readFileParams(char* tok[], int ntoks);
...
double iface_getIfaceQual(int index, int pollut);
  • If SWMM uses routing interface files to pass flows/quality between sub-systems, these handle that data.

25. Hot Start File Methods

int hotstart_open(void);
...
void hotstart_close(void);
  • SWMM can save or read a hotstart (internal state file) to skip spin‐up on re‐runs.

26. Conveyance System Link Methods

int link_readParams(int link, int type, int subIndex, char* tok[], int ntoks);
...
double link_getLossRate(int link, int routeModel, double q, double tstep);
  • For pipes, pumps, weirs, or outlets.
  • Reading link geometry, computing flows, infiltration or evaporation loss from pipes, etc.

27. Link Cross-Section Methods

int   xsect_isOpen(int type);
...
double xsect_getAofY(TXsect* xsect, double y);
  • For specialized cross‐section geometry calculations: area, radius, top width, etc., given a water depth y.

28. Culvert/Roadway Methods

double culvert_getInflow(int link, double q, double h);
double roadway_getInflow(int link, double dir, double hcrest, double h1, double h2);
  • SWMM can model culverts or roadway overflows.
  • These functions compute how much flow can pass given heads and crest geometry.

29. Force Main Methods

double forcemain_getEquivN(int j, int k);
double forcemain_getFricSlope(int j, double v, double hrad);
  • For pressurized pipes using Hazen-Williams or Darcy-Weisbach equations.

30. Cross-Section Transect Methods

int    transect_create(int n);
void   transect_delete(void);
int    transect_readParams(int* count, char* tok[], int ntoks);
  • For irregular channel cross-sections described by transect data.

31. Street Cross-Section Methods

int street_create(int nStreets);
int street_readParams(char* tok[], int ntoks);
  • SWMM 5.2 can model streets as separate cross-sections, used with inlet node logic.

32. Custom Shape Cross-Section Methods

int shape_validate(TShape *shape, TTable *curve);
  • For user-defined cross‐section shapes (like a parametric shape plus a curve describing geometry).

33. Control Rule Methods

int controls_create(int n);
...
int controls_evaluate(DateTime currentTime, DateTime elapsedTime, double tStep);
  • SWMM’s rule-based controls to adjust link settings (like orifices or pumps) based on conditions.
  • E.g., “IF Node depth > 5 THEN Pump = ON.”

34. Table & Time Series Methods

int  table_readCurve(char* tok[], int ntoks);
...
double table_tseriesLookup(TTable* table, double t, char extend);
  • For look‐up tables (like rating curves) or time series that SWMM uses in many contexts.
  • Insert, validate, search by interpolation, etc.

35. Utility Methods

double UCF(int quantity);
...
int findmatch(char *s, char *keyword[]);
int match(char *str, char *substr);
...
DateTime getDateTime(double elapsedMsec);
...
char* addAbsolutePath(char *fname);
  • Various helper routines:
    • UCF(...): unit conversion factors for different quantities.
    • getInt/getFloat/getDouble(...): parse numeric from strings.
    • findmatch(...), match(...): compare strings to a list of keywords.
    • addAbsolutePath(...): prefix a filename with the application’s directory.

36. Summary

In short, funcs.h is a master header:

  • Declares function prototypes from various SWMM modules (subcatchment, node, routing, infiltration, etc.).
  • Ensures consistent linkage across multiple .c files.
  • Allows large pieces of code to remain in separate C source modules, referencing each other via a single header of forward declarations.

It covers all major processes: project reading/validation, runoff & routing computations, pollutant buildup/washoff, mass balance tracking, reporting, time‐series/table lookups, and so forth—the entire scope of SWMM’s architecture.

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...